/*
 * Copyright (c) 2002-2007 TeamDev Ltd. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * The complete licence text can be found at
 * http://www.teamdev.com/winpack/license.jsf
 */
package com.jniwrapper.win32.ie.samples.demo;

import com.jniwrapper.Function;
import com.jniwrapper.Int;
import com.jniwrapper.Str;
import com.jniwrapper.UInt;
import com.jniwrapper.util.Logger;
import com.jniwrapper.win32.FunctionName;
import com.jniwrapper.win32.com.ComException;
import com.jniwrapper.win32.gdi.Icon;
import com.jniwrapper.win32.ie.ReadyState;
import com.jniwrapper.win32.ie.WebBrowser;
import com.jniwrapper.win32.ie.event.NavigationEventAdapter;
import com.jniwrapper.win32.ie.event.NavigationEventListener;
import com.jniwrapper.win32.ie.event.StatusEventAdapter;
import com.jniwrapper.win32.ie.samples.demo.components.ActionButton;
import com.jniwrapper.win32.ie.samples.demo.components.LineBevel;
import com.jniwrapper.win32.ie.samples.demo.components.LineBorder;
import com.jniwrapper.win32.ie.samples.demo.components.URLField;
import com.jniwrapper.win32.ui.User32;
import com.jniwrapper.win32.ui.Wnd;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

/**
 * @author Alexander Kireyev
 * @author Serge Piletsky
 */
public class JExplorerDemo implements Runnable
{
    private static final Logger LOG = Logger.getInstance(JExplorerDemo.class);

    private JTextArea _dumpArea = new JTextArea();
    private JPanel _statusPanel = new JPanel();
    private JLabel _statusText = new JLabel();
    private JSplitPane _content;
    private JSplitPane _subcontent;
    private URLField _fldGoTo;
    private JProgressBar _progressBar;
    private DOMStructurePanel _domStructurePanel;
    private TitledPane _domPane;
    private TitledPane _consolePane;
    private TabbedBrowsersPane _browsersPane;
    private JPopupMenu _browserMenu;
    private CreateNewBrowserTabAction _createNewBrowserTabAction;
    private CloseCurrentBrowserAction _closeCurrentBrowserAction;

    private SettingsMenu _settingsMenu;
    private JMenuItem _back;
    private JMenuItem _forward;

    private static void setupLF()
    {
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (Exception e)
        {
            LOG.error("", e);
        }
        UIDefaults defaults = UIManager.getDefaults();
        defaults.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 1, 0));
    }

    static
    {
        setupLF();
        JPopupMenu.setDefaultLightWeightPopupEnabled(false);
    }

    public static void main(String[] args) throws ComException
    {
        SwingUtilities.invokeLater(new JExplorerDemo());
    }

    public void run()
    {
        createGUI();
    }

    private void createGUI()
    {
        _createNewBrowserTabAction = new CreateNewBrowserTabAction();
        _closeCurrentBrowserAction = new CloseCurrentBrowserAction();

        JMenu fileMenu = new JMenu("File");

        JMenuItem exit = new JMenuItem(new ExitApplicationAction());
        JMenuItem newTab = new JMenuItem(_createNewBrowserTabAction);
        JMenuItem closeTab = new JMenuItem(_closeCurrentBrowserAction);

        _browserMenu = new JPopupMenu();
        _browserMenu.add(_createNewBrowserTabAction);
        _browserMenu.add(_closeCurrentBrowserAction);

        _browserMenu.addPopupMenuListener(new PopupMenuListener()
        {
            public void popupMenuCanceled(PopupMenuEvent e)
            {
            }

            public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
            {
            }

            public void popupMenuWillBecomeVisible(PopupMenuEvent e)
            {
                _closeCurrentBrowserAction.setEnabled(_browsersPane.getTabbedPane().getTabCount() > 1);
            }
        });

        fileMenu.add(newTab);
        fileMenu.add(closeTab);
        fileMenu.addSeparator();
        fileMenu.add(exit);

        _browsersPane = new TabbedBrowsersPane(_browserMenu, _closeCurrentBrowserAction);

        final JFrame frame = new JFrame();
        frame.getContentPane().setLayout(new BorderLayout());

        _content = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true);
        _content.setBorder(null);
        _content.setDividerSize(2);
        _content.setResizeWeight(0.7);

        _subcontent = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
        _subcontent.setBorder(null);
        _subcontent.setDividerSize(2);

        _domStructurePanel = new DOMStructurePanel();
        _domStructurePanel.setBorder(null);

        JPopupMenu domStructureMenu = new JPopupMenu();
        final LoadDOMStructureAction loadDOMStructureAction = new LoadDOMStructureAction();
        JMenuItem loadDomStrucure = new JMenuItem(loadDOMStructureAction);
        JMenuItem expandTree = new JMenuItem(new AbstractAction("Expand All")
        {
            public void actionPerformed(ActionEvent e)
            {
                JTree tree = _domStructurePanel.getTree();
                for (int i = 1; i < tree.getRowCount(); i++)
                {
                    tree.expandRow(i);
                }
            }
        });
        JMenuItem collapseTree = new JMenuItem(new AbstractAction("Collapse All")
        {
            public void actionPerformed(ActionEvent e)
            {
                JTree tree = _domStructurePanel.getTree();
                for (int i = tree.getRowCount(); i >= 1; i--)
                {
                    tree.collapseRow(i);
                }
            }
        });
        domStructureMenu.add(loadDomStrucure);
        domStructureMenu.addSeparator();
        domStructureMenu.add(expandTree);
        domStructureMenu.add(collapseTree);

        new PopupMenuTracker(_domStructurePanel.getTree(), domStructureMenu);
        final ShowDOMStructureAction showDOMStructureAction = new ShowDOMStructureAction(false);
        _domPane = new TitledPane("DOM Structure", _domStructurePanel, domStructureMenu, showDOMStructureAction);
        _domPane.setVisible(false);

        _subcontent.setLeftComponent(_browsersPane);
        _subcontent.setRightComponent(_domPane);
        _subcontent.setResizeWeight(0.7);
        _subcontent.setDividerLocation(0.7);

        _content.add(_subcontent, JSplitPane.TOP);

        JScrollPane scrollPane = new JScrollPane(_dumpArea);
        _dumpArea.setEditable(false);
        scrollPane.setBorder(null);

        JPopupMenu consoleMenu = new JPopupMenu();
        JMenuItem clearConsole = new JMenuItem(new ClearConsoleAction());
        consoleMenu.add(clearConsole);

        new PopupMenuTracker(_dumpArea, consoleMenu);

        final ShowConsoleAction showConsoleActions = new ShowConsoleAction(false);
        _consolePane = new TitledPane("Console", scrollPane, consoleMenu, showConsoleActions);
        _content.add(_consolePane, JSplitPane.BOTTOM);
        _consolePane.setVisible(false);

        frame.getContentPane().add(_content, BorderLayout.CENTER);

        _progressBar = new JProgressBar();
        _statusPanel.setLayout(new BorderLayout());
        _statusPanel.add(_statusText, BorderLayout.WEST);
        _statusPanel.add(_progressBar, BorderLayout.EAST);
        _statusPanel.setPreferredSize(new Dimension(20, 18));

        final LineBevel horisontalBevel = new LineBevel();
        _statusPanel.add(horisontalBevel, BorderLayout.NORTH);

        frame.getContentPane().add(_statusPanel, BorderLayout.SOUTH);

        JPanel toolPanel = new JPanel();
        toolPanel.setLayout(new GridBagLayout());
        frame.getContentPane().add(toolPanel, BorderLayout.NORTH);
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new FlowLayout());
        buttonPanel.setBorder(null);

        final JButton backButton = createButton("Back", new GoBackAction());
        backButton.setEnabled(false);
        buttonPanel.add(backButton);

        final JButton forwardButton = createButton("Forward", new GoForwardAction());
        forwardButton.setEnabled(false);
        buttonPanel.add(forwardButton);

        _browsersPane.addPropertyChangeListener(TabbedBrowsersPane.PROPERTY_COMMAND_STATE, new PropertyChangeListener()
        {
            public void propertyChange(final PropertyChangeEvent evt)
            {
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        CommandStateChangeEvent event = (CommandStateChangeEvent)evt.getNewValue();
                        if (event.isBackButton())
                        {
                            boolean enabled = event.isEnabled();
                            backButton.setEnabled(enabled);
                            _back.setEnabled(enabled);
                        }
                        else
                        {
                            boolean enabled = event.isEnabled();
                            forwardButton.setEnabled(enabled);
                            _forward.setEnabled(enabled);
                        }
                    }
                });
            }
        });
        _browsersPane.addPropertyChangeListener(TabbedBrowsersPane.PROPERTY_PROGRESS, new PropertyChangeListener()
        {
            public void propertyChange(final PropertyChangeEvent evt)
            {
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        Integer maxProgress = (Integer)evt.getNewValue();
                        _progressBar.setMaximum(maxProgress.intValue());

                        Integer progress = (Integer)evt.getOldValue();
                        _progressBar.setValue(progress.intValue());

                        _progressBar.setVisible(progress.intValue() > 0);
                    }
                });
            }
        });

        buttonPanel.add(createButton("Refresh", new RefreshAction()));
        buttonPanel.add(createButton("Stop", new StopAction()));
        final LineBevel lineBevel = new LineBevel(LineBorder.VERTICAL);
        lineBevel.setPreferredSize(new Dimension(2, 18));
        buttonPanel.add(lineBevel);
        buttonPanel.add(createButton("Home", new GoHomeAction()));

        toolPanel.add(buttonPanel,
                new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
                        , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));

        final LineBevel bevel1 = new LineBevel();
        toolPanel.add(bevel1, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0
                , GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

        JLabel lblAddress = new JLabel("Address:");
        toolPanel.add(lblAddress,
                new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0
                        , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));

        _fldGoTo = new URLField();
        _fldGoTo.setAction(new NavigateToURLAction());

        toolPanel.add(_fldGoTo,
                new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0
                        , GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0));

        final InterceptLinksAction interceptLinksAction = new InterceptLinksAction(frame, false);
        final JPopupMenu browserActionsPopupMenu = new JPopupMenu();
        browserActionsPopupMenu.add(new JMenuItem(new ClearPageAction()));
        final DumpHTMLAction dumpHTMLAction = new DumpHTMLAction(frame);
        browserActionsPopupMenu.add(new JMenuItem(dumpHTMLAction));
        final JCheckBoxMenuItem interceptLinksPopup = new JCheckBoxMenuItem(interceptLinksAction);
        browserActionsPopupMenu.add(interceptLinksPopup);

        _browsersPane.getTabbedPane().addChangeListener(new ChangeListener()
        {
            private NavigationEventListener _listener = new NavigationEventAdapter()
            {
                public void entireDocumentCompleted(WebBrowser webBrowser, String url)
                {
                    _fldGoTo.setURLText(url);
                }
            };

            public void stateChanged(ChangeEvent e)
            {
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        final WebBrowser activeBrowser = _browsersPane.getActiveBrowser();
                        if (activeBrowser == null)
                        {
                            return;
                        }

                        _settingsMenu.setProperties(activeBrowser.getProperties());

                        String locationURL = activeBrowser.getLocationURL();
                        if (activeBrowser.getParentBrowser() != null)
                        {
                            locationURL = activeBrowser.getParentBrowser().getLocationURL();
                        }
                        _fldGoTo.setURLText(locationURL);

                        if (!activeBrowser.getNavigationListeners().contains(_listener))
                        {
                            activeBrowser.addNavigationListener(_listener);

                            activeBrowser.addStatusListener(new StatusEventAdapter()
                            {
                                public void statusTextChanged(String statusText)
                                {
                                    _statusText.setText(statusText);
                                }
                            });

                            activeBrowser.addPropertyChangeListener(WebBrowser.Properties.READY_STATE, new PropertyChangeListener()
                            {
                                public void propertyChange(PropertyChangeEvent evt)
                                {
                                    if (evt.getNewValue().equals(ReadyState.READYSTATE_COMPLETE))
                                    {
                                        final String locationURL = activeBrowser.getLocationURL();
                                        _fldGoTo.setURL(locationURL);
                                    }

                                    SwingUtilities.invokeLater(new Runnable()
                                    {
                                        public void run()
                                        {
                                            interceptLinksAction.setLinkListening(interceptLinksAction.isSelected());
                                        }
                                    });

                                    SwingUtilities.invokeLater(new Runnable()
                                    {
                                        public void run()
                                        {
                                            if (showDOMStructureAction.isSelected())
                                            {
                                                loadDOMStructureAction.run();
                                            }
                                        }
                                    });
                                }
                            });
                        }
                    }
                });
            }
        });

        JMenuBar mainMenu = new JMenuBar();

        JMenu view = new JMenu("View");
        JMenuItem refresh = new JMenuItem(new RefreshAction());
        JMenuItem stop = new JMenuItem(new StopAction());
        final JCheckBoxMenuItem showDOMStructure = new JCheckBoxMenuItem(showDOMStructureAction);

        showDOMStructureAction.addPropertyChangeListener(new PropertyChangeListener()
        {
            public void propertyChange(PropertyChangeEvent evt)
            {
                if (evt.getPropertyName().equals(InterceptLinksAction.PROPERTY_SELECTED))
                {
                    boolean value = ((Boolean)evt.getNewValue()).booleanValue();
                    showDOMStructure.setSelected(value);
                    _domPane.setVisible(value);
                }
            }
        });
        showDOMStructure.setSelected(false);

        final JCheckBoxMenuItem showConsole = new JCheckBoxMenuItem(showConsoleActions);
        showConsoleActions.addPropertyChangeListener(new PropertyChangeListener()
        {
            public void propertyChange(PropertyChangeEvent evt)
            {
                if (evt.getPropertyName().equals(InterceptLinksAction.PROPERTY_SELECTED))
                {
                    boolean value = ((Boolean)evt.getNewValue()).booleanValue();
                    showConsole.setSelected(value);
                    _consolePane.setVisible(value);
                }
            }
        });

        view.add(refresh);
        view.add(stop);
        view.add(new JSeparator());
        view.add(showDOMStructure);
        view.add(showConsole);

        _back = new JMenuItem(new GoBackAction());
        _forward = new JMenuItem(new GoForwardAction());
        JMenuItem goHome = new JMenuItem(new GoHomeAction());
        JMenu go = new JMenu("Go");
        go.add(_back);
        go.add(_forward);
        go.add(goHome);

        JMenu browserActionsMenu = new JMenu("Actions");
        browserActionsMenu.add(new JMenuItem(new ClearPageAction()));
        browserActionsMenu.add(new JMenuItem(dumpHTMLAction));
        browserActionsMenu.add(new JSeparator());
        final JCheckBoxMenuItem interceptLinks = new JCheckBoxMenuItem(interceptLinksAction);
        browserActionsMenu.add(interceptLinks);
        interceptLinksAction.addPropertyChangeListener(new PropertyChangeListener()
        {
            public void propertyChange(PropertyChangeEvent evt)
            {
                if (evt.getPropertyName().equals(InterceptLinksAction.PROPERTY_SELECTED))
                {
                    boolean value = ((Boolean)evt.getNewValue()).booleanValue();
                    interceptLinks.setSelected(value);
                    interceptLinksPopup.setSelected(value);
                }
            }
        });

        _settingsMenu = new SettingsMenu();

        JMenu help = new JMenu("Help");
        JMenuItem about = new JMenuItem(new ShowAboutAction(frame));
        help.add(about);

        mainMenu.add(fileMenu);
        mainMenu.add(view);
        mainMenu.add(go);
        mainMenu.add(browserActionsMenu);
        mainMenu.add(_settingsMenu);
        mainMenu.add(help);


        frame.setTitle("JExplorer Demo");
        frame.setJMenuBar(mainMenu);
        frame.setSize(830, 600);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        _browsersPane.createBrowser();

        frame.addWindowListener(new WindowAdapter()
        {
            public void windowOpened(WindowEvent e)
            {
                setupFrameIcon(frame);
                SwingUtilities.invokeLater(new GoHomeAction());
            }
        });

        KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        JRootPane rootPane = frame.getRootPane();
        rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE");
        rootPane.getActionMap().put("ESCAPE", new StopAction());

        frame.setVisible(true);
    }

    private JButton createButton(String caption, Action action)
    {
        ActionButton button = new ActionButton(caption, action);
        button.setPreferredSize(new Dimension(23, 20));
        final String imageName = caption.toLowerCase();
        button.setIcon(new ImageIcon(getClass().getResource("res/" + imageName + ".png")));
        button.setRolloverIcon(new ImageIcon(getClass().getResource("res/" + imageName + "A.png")));
        return button;
    }

    private void setupFrameIcon(Window owner)
    {
        try
        {
            final Wnd winWnd = new Wnd(owner);
            final Icon bigIcon = new Icon(JExplorerDemo.class.getResourceAsStream("res/jexplorer.ico"), new Dimension(32, 32));
            final Icon smallIcon = new Icon(JExplorerDemo.class.getResourceAsStream("res/jexplorer.ico"), new Dimension(16, 16));
            winWnd.setWindowIcon(smallIcon, Icon.IconType.SMALL);
            winWnd.setWindowIcon(bigIcon, Icon.IconType.BIG);
        }
        catch (Exception e)
        {
            LOG.error("", e);
        }
    }

    private void println(String str)
    {
        _dumpArea.append(str + "\r\n");
    }

    class TriggerAction extends AbstractAction implements PropertyChangeListener
    {
        public static final String PROPERTY_SELECTED = "Selected";

        TriggerAction(String name, boolean selected)
        {
            super(name);
            setSelected(selected);
            addPropertyChangeListener(this);
        }

        public void propertyChange(PropertyChangeEvent evt)
        {
            if (evt.getPropertyName().equals(PROPERTY_SELECTED))
            {
                onSelectionChanged();
            }
        }

        public void onSelectionChanged()
        {
        }


        public void setSelected(boolean value)
        {
            putValue(PROPERTY_SELECTED, new Boolean(value));
        }

        public boolean getSelected()
        {
            final Boolean value = (Boolean)getValue(PROPERTY_SELECTED);
            if (value == null)
            {
                setSelected(false);
                return false;
            }
            else
            {
                return value.booleanValue();
            }
        }

        public boolean isSelected()
        {
            return getSelected();
        }

        public final void actionPerformed(ActionEvent e)
        {
            setSelected(!getSelected());
        }
    }

    class GoBackAction extends AbstractAction
    {
        GoBackAction()
        {
            super("Back");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("alt LEFT"));
        }

        public void actionPerformed(ActionEvent e)
        {
            _browsersPane.getActiveBrowser().goBack();
        }
    }

    class GoForwardAction extends AbstractAction
    {
        GoForwardAction()
        {
            super("Forward");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("alt RIGHT"));
        }

        public void actionPerformed(ActionEvent e)
        {
            _browsersPane.getActiveBrowser().goForward();
        }
    }

    class GoHomeAction extends AbstractAction implements Runnable
    {
        private static final String JNIWRAPPER_URL = "http://www.teamdev.com";

        GoHomeAction()
        {
            super("Home");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("alt HOME"));
        }

        public void run()
        {
            _fldGoTo.requestFocus();
            actionPerformed(null);
        }

        public void actionPerformed(ActionEvent e)
        {
            _fldGoTo.setURL(JNIWRAPPER_URL);
            _browsersPane.getActiveBrowser().navigate(JNIWRAPPER_URL);
        }
    }

    class RefreshAction extends AbstractAction
    {
        RefreshAction()
        {
            super("Refresh");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("F5"));
        }

        public void actionPerformed(ActionEvent e)
        {
            _browsersPane.getActiveBrowser().refresh();
        }
    }

    class StopAction extends AbstractAction
    {
        StopAction()
        {
            super("Stop");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("CANCEL"));
        }

        public void actionPerformed(ActionEvent e)
        {
            _browsersPane.getActiveBrowser().stop();
        }
    }

    class ShowDOMStructureAction extends TriggerAction
    {
        ShowDOMStructureAction(boolean selected)
        {
            super("DOM Structure", selected);
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("alt D"));
        }

        public void onSelectionChanged()
        {
            boolean selected = getSelected();
            _domPane.setVisible(selected);
            if (selected)
            {
                _subcontent.setDividerLocation(0.7);
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        LoadDOMStructureAction action = new LoadDOMStructureAction();
                        action.run();
                    }
                });
            }
        }
    }

    class LoadDOMStructureAction extends AbstractAction implements Runnable
    {
        LoadDOMStructureAction()
        {
            super("Reload");
        }

        public void run()
        {
            WebBrowser activeBrowser = _browsersPane.getActiveBrowser();
            _domStructurePanel.setDocument(activeBrowser.getDocument());
            _domStructurePanel.getTree().setSelectionRow(0);
        }

        public void actionPerformed(ActionEvent e)
        {
            run();
        }
    }

    class ShowConsoleAction extends TriggerAction
    {
        ShowConsoleAction(boolean selected)
        {
            super("Console", selected);
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("alt C"));
        }

        public void onSelectionChanged()
        {
            boolean selected = getSelected();
            _consolePane.setVisible(selected);
            if (selected)
            {
                _content.setDividerLocation(0.7);
            }
        }
    }

    class ClearConsoleAction extends AbstractAction
    {
        ClearConsoleAction()
        {
            super("Clear");
        }

        public void actionPerformed(ActionEvent e)
        {
            _dumpArea.setText("");
        }
    }

    class NavigateToURLAction extends AbstractAction
    {
        NavigateToURLAction()
        {
            super("Navigate to URL");
        }

        public void actionPerformed(ActionEvent e)
        {
            WebBrowser activeBrowser = _browsersPane.getActiveBrowser();

            String locationURL = activeBrowser.getLocationURL();
            if (locationURL.startsWith("file:///"))
            {
                locationURL = locationURL.substring(8);
            }

            String newLocation = (String)_fldGoTo.getSelectedItem();
            while (newLocation.startsWith(" "))
            {
                newLocation = newLocation.substring(1);
            }
            try
            {
                locationURL = URLDecoder.decode(locationURL.replaceAll("/", "\\\\"), "UTF-8");
                newLocation = URLDecoder.decode(newLocation.replaceAll("/", "\\\\"), "UTF-8");
            }
            catch (UnsupportedEncodingException e1)
            {
                LOG.error("", e1);
            }
            if (!newLocation.equals(locationURL))
            {
                try
                {
                    activeBrowser.navigate(newLocation);
                }
                catch (Exception e1)
                {
                }
            }
        }
    }

    class ExitApplicationAction extends AbstractAction
    {
        ExitApplicationAction()
        {
            super("Exit");
        }

        public void actionPerformed(ActionEvent e)
        {
            System.exit(0);
        }
    }

    class CreateNewBrowserTabAction extends AbstractAction
    {
        CreateNewBrowserTabAction()
        {
            super("New Tab");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl T"));
        }

        public void actionPerformed(ActionEvent e)
        {
            _closeCurrentBrowserAction.setEnabled(true);
            _browsersPane.createBrowser();
            _fldGoTo.requestFocus();
        }
    }

    class CloseCurrentBrowserAction extends AbstractAction
    {
        CloseCurrentBrowserAction()
        {
            super("Close Tab");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl F4"));
            setEnabled(false);
        }

        public void actionPerformed(ActionEvent e)
        {
            _browsersPane.removeBrowser();
            WebBrowser activeBrowser = _browsersPane.getActiveBrowser();
            _fldGoTo.setURLText(activeBrowser.getLocationURL());
            _closeCurrentBrowserAction.setEnabled(_browsersPane.getTabbedPane().getTabCount() > 1);
        }
    }

    class DumpHTMLAction extends AbstractAction implements Runnable
    {
        Window _owner;

        DumpHTMLAction(Window owner)
        {
            super("Dump HTML to Console");
            _owner = owner;
        }

        public void run()
        {
            _owner.setCursor(new Cursor(Cursor.WAIT_CURSOR));
            try
            {
                println(_browsersPane.getActiveBrowser().getContent());
            }
            finally
            {
                _owner.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
        }

        public void actionPerformed(ActionEvent e)
        {
            SwingUtilities.invokeLater(this);
        }
    }

    class ClearPageAction extends AbstractAction
    {
        ClearPageAction()
        {
            super("Clear Page");
        }

        public void actionPerformed(ActionEvent e)
        {
            _browsersPane.getActiveBrowser().setContent("<html><head></head><body onload='alert(\"Cleared!\")'></body></html>");
        }
    }

    static class AboutDialog extends JDialog
    {
        private JLabel _text;
        private JButton _closeButton;

        public AboutDialog(Frame owner, String title, String message, ImageIcon icon)
        {
            super(owner, title, true);
            _text = new JLabel()
            {
                public void paint(Graphics g)
                {
                    ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    super.paint(g);
                }
            };
            AbstractAction closeAction = new AbstractAction("  OK  ")
            {
                public void actionPerformed(ActionEvent e)
                {
                    setVisible(false);
                }
            };
            _closeButton = new JButton(closeAction);
            _closeButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
            Container contentPane = getContentPane();
            contentPane.setBackground(Color.WHITE);
            contentPane.setLayout(new BorderLayout());

            _text.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            _text.setText(message);
            _text.setIcon(icon);

            JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
            buttonsPanel.setOpaque(false);
            buttonsPanel.add(_closeButton);
            contentPane.add(_text, BorderLayout.CENTER);
            contentPane.add(buttonsPanel, BorderLayout.SOUTH);

            setResizable(false);
            pack();
            setLocationRelativeTo(null);

            KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
            JRootPane rootPane = getRootPane();
            rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE");
            rootPane.getActionMap().put("ESCAPE", closeAction);
        }
    }

    class ShowAboutAction extends AbstractAction
    {
        Window _owner;

        ShowAboutAction(Window owner)
        {
            super("About JExplorer");
            _owner = owner;
        }

        public void actionPerformed(ActionEvent e)
        {
            AboutDialog aboutDialog = new AboutDialog((Frame)_owner,
                    "About JExplorer Demo",
                    "<html><H2>JExplorer Demo</H2><H3></H3>" +
                            "Copyright \u00A9 2000-2007 TeamDev Ltd.",
                    new ImageIcon(getClass().getResource("res/Logo.gif")));

            aboutDialog.setVisible(true);
        }
    }

    class InterceptLinksAction extends TriggerAction implements EventListener
    {
        private static final String ON_CLICK = "onclick";
        private Window _owner;
        private boolean _set = false;

        InterceptLinksAction(Window owner, boolean selected)
        {
            super("Intercept Link Clicks", selected);
            _owner = owner;
        }

        void dumpEvent(org.w3c.dom.events.MouseEvent event)
        {
            println("Dumping event data:");
            println("Type: " + event.getType());
            println("Target UID: " + ((Element)event.getTarget()).getAttribute("uniqueID"));
            println("Client coords: (" + event.getClientX() + ", " + event.getClientY() + ")");
            println("Screen coords: (" + event.getScreenX() + ", " + event.getScreenY() + ")");
            println("Button: " + event.getButton());
            println("Ctrl pressed: " + event.getCtrlKey());
            println("Alt pressed: " + event.getAltKey());
            println("Shift pressed: " + event.getShiftKey());
        }

        private class NativeMessageBox
        {
            public static final int IDNO = 7;
            public static final int YESNO = 0x00000004;
            public static final int ICONQUESTION = 0x00000020;

            public int showConfirmDialog(Wnd hWnd, String title, String message)
            {
                int flags = YESNO | ICONQUESTION;
                final Function function = User32.getInstance().getFunction(new FunctionName("MessageBox").toString());
                Int result = new Int();
                function.invoke(result, hWnd, new Str(message), new Str(title), new UInt(flags));
                return (int)result.getValue();
            }
        }

        public void handleEvent(Event evt)
        {
            final org.w3c.dom.events.MouseEvent event = (org.w3c.dom.events.MouseEvent)evt;
            final Element target = (Element)event.getTarget();
            dumpEvent(event);
            final String href = target.getAttribute("href");

            NativeMessageBox messageBox = new NativeMessageBox();
            int result = messageBox.showConfirmDialog(new Wnd(_owner),
                    "JExplorer",
                    "The link " + href + " was clicked.\nDo you want to follow this URL?");
            if (result == NativeMessageBox.IDNO)
            {
                event.preventDefault();
            }
        }

        public void setLinkListening(boolean set)
        {
            if (_set != set)
            {
                _set = set;
                Document document = _browsersPane.getActiveBrowser().getDocument();
                NodeList links = document.getElementsByTagName("a");
                for (int i = 0; i < links.getLength(); i++)
                {
                    EventTarget link = (EventTarget)links.item(i);
                    if (set)
                    {
                        link.addEventListener(ON_CLICK, this, false);
                    }
                    else
                    {
                        link.removeEventListener(ON_CLICK, this, false);
                    }
                }
            }
        }

        public void onSelectionChanged()
        {
            setLinkListening(isSelected());
        }
    }
}