Package net.sourceforge.marathon.recorder

Source Code of net.sourceforge.marathon.recorder.RecordingEventListener

/*******************************************************************************
*  Copyright (C) 2010 Jalian Systems Private Ltd.
*  Copyright (C) 2010 Contributors to Marathon OSS Project
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Library General Public
*  License as published by the Free Software Foundation; either
*  version 2 of the License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Library General Public License for more details.
*
*  You should have received a copy of the GNU Library General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*  Project website: http://www.marathontesting.com
*  Help: Marathon help forum @ http://groups.google.com/group/marathon-testing
*
*******************************************************************************/
package net.sourceforge.marathon.recorder;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceAdapter;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.event.AWTEventListener;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.KeyStroke;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;

import net.sourceforge.marathon.action.ClickAction;
import net.sourceforge.marathon.action.DragAction;
import net.sourceforge.marathon.action.DragAndDropAction;
import net.sourceforge.marathon.action.KeyStrokeAction;
import net.sourceforge.marathon.action.SelectAction;
import net.sourceforge.marathon.action.SelectMenuAction;
import net.sourceforge.marathon.action.UndoOperation;
import net.sourceforge.marathon.action.WindowClosingAction;
import net.sourceforge.marathon.action.WindowState;
import net.sourceforge.marathon.action.WindowStateAction;
import net.sourceforge.marathon.api.IMarathonRuntime;
import net.sourceforge.marathon.api.IRecorder;
import net.sourceforge.marathon.api.IScriptModelServerPart;
import net.sourceforge.marathon.api.RuntimeLogger;
import net.sourceforge.marathon.api.WindowId;
import net.sourceforge.marathon.component.ComponentFinder;
import net.sourceforge.marathon.component.MCellComponent;
import net.sourceforge.marathon.component.MCollectionComponent;
import net.sourceforge.marathon.component.MComponent;
import net.sourceforge.marathon.component.MUnknownComponent;
import net.sourceforge.marathon.component.WindowIdCreator;
import net.sourceforge.marathon.util.ContextMenuTriggers;
import net.sourceforge.marathon.util.ExceptionUtil;
import net.sourceforge.marathon.util.OSUtils;

public class RecordingEventListener implements AWTEventListener {
    private static RecordingEventListener instance;

    private IRecorder recorder;
    private ComponentFinder finder;
    private RecordingEventQueue eventQueue;
    private MComponent focusComponent;
    private String focusComponentText;
    private MComponent mouseComponent;

    private MouseListener mouseListener = new MouseListener() {
        private boolean contextMenuSequence;

        public void mousePressed(MouseEvent e) {
            e = OSUtils.convert(e);
            clearDrag();
            contextMenuSequence = ContextMenuTriggers.isContextMenuSequence(e);
            if (contextMenuSequence)
                return;
            if (!(e.getSource() instanceof Component))
                return;
            Component source = SwingUtilities.getDeepestComponentAt((Component) e.getSource(), e.getPoint().x, e.getPoint().y);
            if (source == null)
                source = (Component) e.getSource();
            MComponent component = finder.getMComponentByComponent(source, e.getPoint());
            storeDragContext(component);
            storeDragStart(component, e);
            if (component != null) {
                RecordingEventListener.this.focusGained(component);
                RecordingEventListener.this.mousePressed(component, e);
            }
        }

        public void mouseClicked(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseReleased(MouseEvent e1) {
            final MouseEvent e = OSUtils.convert(e1);
            if (contextMenuSequence)
                return;
            if (!(e.getSource() instanceof Component))
                return;
            final MComponent component = finder.getMComponentByComponent((Component) e.getSource(), e.getPoint());
            storeDragEnd(component, e);
            clearDrag();
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    if (component != null && component.isMComponentEditable() && component.recordOnMouseRelease()
                            && component.clickNeeded(e) == ClickAction.RECORD_NONE) {
                        /*
                         * e.g. it's a toggle button or slider which would
                         * otherwise only fire on de-focus. fake a de-focus to
                         * fire the state change immediately after a click.
                         */
                        focusLost(null);
                        if (component != null) {
                            RecordingEventListener.this.mouseReleased(component, e);
                        }
                    }
                }
            });
        }
    };
    private KeyListener keyListener = new KeyAdapter() {
        private MenuSelectionManager msm = MenuSelectionManager.defaultManager();

        public void keyPressed(KeyEvent e) {
            if (!isRecordableKeyCode(e))
                return;
            MenuElement[] selectedPath = msm.getSelectedPath();
            if (!isJMenuActive(selectedPath)) {
                if (!(e.getSource() instanceof Component))
                    return;
                MComponent component = finder.getMComponentByComponent((Component) e.getSource());
                if (component == null)
                    return;
                RecordingEventListener.this.focusGained(component);
                RecordingEventListener.this.keyPressed(component, e, false);
                return;
            }
            Component menuComponent = selectedPath[selectedPath.length - 1].getComponent();
            MComponent component = finder.getMComponentByComponent(menuComponent);
            if (component == null)
                return;
            RecordingEventListener.this.focusGained(component);
            if (e.getKeyCode() == KeyEvent.VK_SPACE || e.getKeyCode() == KeyEvent.VK_ENTER) {
                if (component != null && !(menuComponent instanceof JMenu) && !(menuComponent instanceof JPopupMenu)) {
                    recordMenuClicks(component, true);
                    return;
                }
            }
            /*
             * Find the menuitem that might respond to this key click from the
             * last JMenu /JPopup available
             */
            for (int i = selectedPath.length - 1; i >= 0; i--) {
                Component displayedMenuComponent = selectedPath[i].getComponent();
                if (!isMenuContainer(displayedMenuComponent))
                    continue;
                MenuElement[] elem = getSubElements(displayedMenuComponent);
                for (int j = 0; j < elem.length; j++) {
                    if (!(elem[j].getComponent() instanceof JMenuItem) || (elem[j] instanceof JMenu))
                        continue;
                    JMenuItem menuItem = (JMenuItem) elem[j].getComponent();
                    if (keyActivatesItem(e, menuItem)) {
                        MComponent activeComponent = finder.getMComponentByComponent(menuItem);
                        if (activeComponent != null) {
                            recordMenuClicks(activeComponent, true);
                        }
                        return;
                    }
                }
                if (!OSUtils.isJava5OrLater()) {
                    /*
                     * On linux it looks like only the last JMenu displayed is
                     * active . So once we look at the last menu component we go
                     * back . Need to check how this behaves on windows
                     */
                    break;
                }
            }
            RecordingEventListener.this.keyPressed(component, e, true);
        }

        private boolean isRecordableKeyCode(KeyEvent e) {
            return e.getKeyCode() != KeyEvent.VK_ALT && e.getKeyCode() != KeyEvent.VK_CONTROL
                    && e.getKeyCode() != KeyEvent.VK_SHIFT && e.getKeyCode() != KeyEvent.VK_META
                    && !ContextMenuTriggers.isContextMenuKeySequence(e) && !RecordingEventQueue.isContextMenuOn();
        }

        private boolean keyActivatesItem(KeyEvent e, JMenuItem menuItem) {
            return e.getKeyCode() == menuItem.getMnemonic();
        }

        private boolean isMenuContainer(Component displayedMenuComponent) {
            return displayedMenuComponent instanceof JMenu || displayedMenuComponent instanceof JPopupMenu;
        }

        private MenuElement[] getSubElements(Component displayedMenuComponent) {
            MenuElement[] elem;
            if (displayedMenuComponent instanceof JMenu)
                elem = ((JMenu) displayedMenuComponent).getSubElements();
            else
                elem = ((JPopupMenu) displayedMenuComponent).getSubElements();
            return elem;
        }

        public void keyReleased(KeyEvent e) {
            if (!isRecordableKeyCode(e))
                return;
            /*
             * We check for the keyReleased event to record any changed value in
             * the previous focusComponent . (Fix: Mac OSX table cells).
             */
            if (!(e.getSource() instanceof Component))
                return;
            MComponent component = finder.getMComponentByComponent((Component) e.getSource());
            if (component == null)
                return;
            RecordingEventListener.this.focusGained(component);
        }

        private boolean isJMenuActive(MenuElement[] selectedPath) {
            return selectedPath != null
                    && selectedPath.length > 0
                    && ((selectedPath[0].getComponent() instanceof JMenuBar) || (selectedPath[0].getComponent() instanceof JPopupMenu));
        }
    };
    private FocusListener focusListener = new FocusAdapter() {
        public void focusGained(FocusEvent e) {
            MComponent component = finder.getMComponentByComponent((Component) e.getSource());
            if (component != null && !(component instanceof MCollectionComponent) && component.isFocusNeeded()) {
                RecordingEventListener.this.focusGained(component);
            }
        }
    };

    private DragSourceAdapter dragSourceListener = new DragSourceAdapter() {
        public void dragDropEnd(DragSourceDropEvent dsde) {
            if (!dsde.getDropSuccess())
                return;
            Point location = dsde.getLocation();
            Component source = dsde.getDragSourceContext().getComponent();
            MComponent dragSourceComponent = finder.getMComponentByComponent(source);
            Component window = finder.getTopLevelWindow(source);
            SwingUtilities.convertPointFromScreen(location, window);
            Component drop = SwingUtilities.getDeepestComponentAt(window, location.x, location.y);
            if (drop == null)
                return;
            location = dsde.getLocation();
            SwingUtilities.convertPointFromScreen(location, drop);
            MComponent dragTargetComponent = finder.getMComponentByComponent(drop, location);
            RecordingEventListener.this.recordDragAndDrop(dragSourceComponent, dragTargetComponent, dsde.getDropAction());
        }
    };
    private WindowStateAction lastWindowStateAction;

    private final IMarathonRuntime runtime;

    private final IScriptModelServerPart scriptModel;

    private final WindowMonitor windowMonitor;

    public MComponent getFocusComponent() {
        return focusComponent;
    }

    protected void recordWindowState(final Window window) {
        if (!finder.isRawRecording())
            return;
        List<Window> windows = windowMonitor.getAllWindows();
        if (!windows.contains(window))
            return;

        WindowState windowState = new WindowState(window);
        if (windowState.isEmpty())
            return;

        WindowId id = WindowIdCreator.createWindowId(window, windowMonitor);
        WindowStateAction action = new WindowStateAction(id, windowState, scriptModel, windowMonitor);

        if (lastWindowStateAction != null && lastWindowStateAction.equals(action))
            recorder.record(new UndoOperation(lastWindowStateAction, scriptModel, windowMonitor).enscript(id));
        recorder.record(action.enscript(id));
        lastWindowStateAction = action;
    }

    private void recordWindowClosing(Window window) {
        List<Window> windows = windowMonitor.getAllWindows();
        if (!windows.contains(window))
            return;

        WindowClosingAction action = new WindowClosingAction(WindowIdCreator.createWindowId(window, windowMonitor), scriptModel);
        recorder.record(action.enscript());
    }

    public RecordingEventListener(IRecorder recorder, IMarathonRuntime runtime, IScriptModelServerPart scriptModel,
            WindowMonitor windowMonitor) {
        this.runtime = runtime;
        this.scriptModel = scriptModel;
        this.windowMonitor = windowMonitor;
        setInstance(this);
        this.recorder = recorder;
        reset();
    }

    private static void setInstance(RecordingEventListener inst) {
        instance = inst;
    }

    protected void keyPressed(MComponent component, KeyEvent e, boolean isMenuActive) {
        /* First give a chance to the component to eat away keystrokes */
        if (!component.keyNeeded(e))
            return;
        char keyChar = e.getKeyChar();
        KeyStroke ks;
        ks = KeyStroke.getKeyStrokeForEvent(e);
        /* If the keystroke is going to activate a menu item - ignore it */
        /* This will get recorded using select_menu */
        JMenuBar menuBar = getJMenuBar(component);
        if (isMenuBarKeyStroke(ks, menuBar))
            return;
        /* Search for accelerator keys */
        if (menuBar != null) {
            ArrayList<Object> menuList = getMenuForAccelerator(menuBar, ks, null);
            if (menuList != null) {
                /*
                 * Mac accepts accelerator keys while Menu is active while Linux
                 * doesn't. Need to check how Windows behave.
                 */
                if (!isMenuActive || OSUtils.isMac() || OSUtils.isJava5OrLater()) {
                    if (finder != null) {
                        finder.markUsed(component);
                        for (Object object : menuList) {
                            finder.markUsed((MComponent) object);
                        }
                    }
                    recorder.record(new SelectMenuAction(menuList, ks, scriptModel, windowMonitor).enscript(component));
                }
                return;
            }
        }
        /* If the menu is active only accelerator keys have effect */
        if (isMenuActive) {
            return;
        }
        /* Bailout option if we face problems - by default set to false */
        if (component instanceof MUnknownComponent) {
            if (finder != null)
                finder.markUsed(component);
            recorder.record(new KeyStrokeAction(component.getComponentId(), ks, keyChar, scriptModel, windowMonitor)
                    .enscript(component));
            return;
        }
        /*
         * The final check. See whether anyone in the component heirarchy needs
         * the key. We are handling the TabbedPane using change event - so do
         * not record his keys.
         */
        Component keyForComponent = whoNeedsTheKey(ks, component.getComponent());
        if (keyForComponent == null)
            return;
        MComponent c = finder.getMComponentByComponent(keyForComponent);
        if (c == null || c.recordOtherKeys())
            return;
        keyForComponent = checkInputMap(ks, finder.getTopLevelWindow(component.getComponent()));
        if (keyForComponent == null)
            return;
        if (keyForComponent instanceof JLabel && ((JLabel) keyForComponent).getDisplayedMnemonic() == ks.getKeyCode())
            return;
        focusLost(null);
        if (finder != null)
            finder.markUsed(component);
        recorder.record(new KeyStrokeAction(component.getComponentId(), ks, keyChar, scriptModel, windowMonitor)
                .enscript(component));
    }

    private Component checkInputMap(KeyStroke ks, Component component) {
        if (component instanceof JComponent) {
            JComponent jcomponent = (JComponent) component;
            if (jcomponent.getConditionForKeyStroke(ks) == JComponent.WHEN_IN_FOCUSED_WINDOW)
                return jcomponent;
        }
        if (component instanceof Container) {
            Component[] components = ((Container) component).getComponents();
            for (int i = 0; i < components.length; i++) {
                Component c;
                if ((c = checkInputMap(ks, components[i])) != null)
                    return c;
            }
        }
        return null;
    }

    private Component whoNeedsTheKey(KeyStroke ks, Component component) {
        if (!(component instanceof JComponent))
            return null;
        JComponent jcomponent = (JComponent) component;
        if (jcomponent.getActionForKeyStroke(ks) != null)
            return jcomponent;
        return whoNeedsTheKey(ks, jcomponent.getParent());
    }

    private boolean isMenuBarKeyStroke(KeyStroke ks, JMenuBar menuBar) {
        boolean isMenuBarKey = false;
        if (menuBar != null) {
            MenuElement[] subElements = menuBar.getSubElements();
            for (int i = 0; i < subElements.length; i++) {
                Component component2 = subElements[i].getComponent();
                if (component2 instanceof JComponent)
                    isMenuBarKey = isMenuBarKey || ((JComponent) component2).getActionForKeyStroke(ks) != null;
            }
        }
        return isMenuBarKey;
    }

    private JMenuBar getJMenuBar(MComponent component) {
        Component window = finder.getTopLevelWindow(component.getComponent());
        JMenuBar menuBar = null;
        if (window instanceof JFrame)
            menuBar = ((JFrame) window).getJMenuBar();
        else if (window instanceof JDialog)
            menuBar = ((JDialog) window).getJMenuBar();
        return menuBar;
    }

    private ArrayList<Object> getMenuForAccelerator(MenuElement element, KeyStroke ks, ArrayList<Object> menuList) {
        if (menuList == null) {
            menuList = new ArrayList<Object>();
        }
        MenuElement[] subElements = element.getSubElements();
        if (subElements.length == 0) {
            if (element.getComponent() instanceof JMenuItem && !(element.getComponent() instanceof JMenu)) {
                if (ks.equals(((JMenuItem) element.getComponent()).getAccelerator())) {
                    menuList.add(element);
                    return menuList;
                }
            }
            return null;
        }
        for (int i = 0; i < subElements.length; i++) {
            if (getMenuForAccelerator(subElements[i], ks, menuList) != null) {
                if (element.getComponent() instanceof JMenuItem) {
                    menuList.add(0, element);
                }
                return menuList;
            }
        }
        return null;
    }

    private void reset() {
        focusComponent = null;
        focusComponentText = null;
    }

    public void startListening(ComponentFinder finder) {
        this.finder = finder;
        this.recording = true;
        Toolkit.getDefaultToolkit().addAWTEventListener(
                this,
                AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK | AWTEvent.WINDOW_STATE_EVENT_MASK
                        | AWTEvent.WINDOW_EVENT_MASK | AWTEvent.COMPONENT_EVENT_MASK);
        eventQueue = new RecordingEventQueue(recorder, this.finder, this.runtime, scriptModel, windowMonitor);
        eventQueue.attach();
        DragSource.getDefaultDragSource().addDragSourceListener(dragSourceListener);
    }

    public void stopListening() {
        DragSource.getDefaultDragSource().removeDragSourceListener(dragSourceListener);
        Toolkit.getDefaultToolkit().removeAWTEventListener(this);
        eventQueue.detach();
        eventQueue = null;
        reset();
        this.recording = false;
    }

    protected void mousePressed(MComponent component, MouseEvent e) {
        recordClick(component, e);
    }

    private long lastClickRecordedTime = 0;
    private ClickAction lastClickRecorded;

    private void recordClick(MComponent component, MouseEvent e) {
        int record_click = 0;
        // Fixed: select_menu recording misses sometimes
        if (component.getComponent() instanceof JMenuItem
                && (!(component.getComponent() instanceof JMenu) || ((JMenu) component.getComponent()).getMenuComponentCount() == 0)) {
            recordMenuClicks(component, true);
            clearDrag(); // prevent accidental drags being recorded on menu
                         // items
            return;
        } else if (component.getComponent() instanceof JMenuItem) {
            /*
             * We handle JMenuItems in recordMenuClicks
             */
            return;
        } else if ((record_click = component.clickNeeded(e)) != ClickAction.RECORD_NONE) {
            /* Record clickable tells us that we do not need regular clicks */
            /* See that the latest value in the component is recorded */
            /** We don't know why this focusLost being called **/
            if (e.isPopupTrigger())
                focusLost(null);
            mouseComponent = component;

            // For non-menu components added to menus, we need to reveal the
            // menu:
            recordMenuClicks(component, false); // reveal the enclosing menu
            if (e.getWhen() - lastClickRecordedTime < 500) {
                // Remove the previous "click" command, as it is replaced with a
                // "doubleclick" command:
                MouseEvent e2 = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiersEx(), e.getX(),
                        e.getY(), e.getClickCount() - 1, e.isPopupTrigger(), e.getButton());
                if (finder != null)
                    finder.markUsed(component);
                recorder.record(new UndoOperation(new ClickAction(component.getComponentId(), e2, record_click, scriptModel,
                        windowMonitor), scriptModel, windowMonitor).enscript(component));
            }
            ClickAction click = new ClickAction(component.getComponentId(), e, record_click, scriptModel, windowMonitor);
            if (finder != null)
                finder.markUsed(component);
            recorder.record(click.enscript(component));
            lastClickRecorded = click;
            lastClickRecordedTime = e.getWhen();
        }
    }

    protected void mouseReleased(MComponent component, MouseEvent e) {
        Component at = SwingUtilities.getDeepestComponentAt((Component) e.getSource(), e.getPoint().x, e.getPoint().y);
        if (mouseComponent != null && at == null && mouseComponent.equals(component)) {
            if (finder != null)
                finder.markUsed(component);
            recorder.record(new UndoOperation(new ClickAction(component.getComponentId(), e, ClickAction.RECORD_CLICK, scriptModel,
                    windowMonitor), scriptModel, windowMonitor).enscript(component));
        }
        mouseComponent = null;
        // focusGained(component);
    }

    private void recordMenuClicks(MComponent mComponent, boolean includeComponentItself) {
        ArrayList<Object> menuList = new ArrayList<Object>();

        Component comp = mComponent.getComponent();
        JPopupMenu popup;
        if (comp instanceof JPopupMenu)
            popup = (JPopupMenu) comp;
        else
            popup = (JPopupMenu) SwingUtilities.getAncestorOfClass(JPopupMenu.class, comp);

        if (popup != null) {

            Component c = popup;
            while (true) {
                if (c instanceof JMenu) {
                    MComponent mc = finder.getMComponentByComponent(c);
                    if (mc == null) {
                        /*
                         * This happens when recording clicking on a non-menu
                         * component buried in a submenu, when, while the
                         * submenu remains open, one of the parent menus gets
                         * rebuilt. This leads to a weird state in the menu
                         * system where the submenus and original menu remain
                         * showing, but the link between the menus has been
                         * severed at some point. This is probably a bug in
                         * Omniscope. This results in Marathon recording the
                         * menus up to the disconnect point. We don't need to
                         * change anything in Marathon, because playback works
                         * given the same situation.
                         */
                        // We may need to change this code to break out at this
                        // point, however.
                    } else {
                        menuList.add(0, mc);
                    }
                    c = c.getParent();
                } else if (c instanceof JPopupMenu) {
                    c = ((JPopupMenu) c).getInvoker();
                } else {
                    break; // null, or not a menu
                }
            }
        }

        if (includeComponentItself && popup != comp) {
            menuList.add(mComponent);
        }

        if (menuList.size() > 0) {
            if (finder != null) {
                finder.markUsed(mComponent);
                for (Object object : menuList) {
                    finder.markUsed((MComponent) object);
                }
            }
            recorder.record(new SelectMenuAction(menuList, scriptModel, windowMonitor).enscript(mComponent));
        }
    }

    public void recordSelect(MComponent component) {
        recordSelect(component, component.getText());
    }

    private void recordSelect(MComponent component, String text) {
        // For non-menu components added to menus, we need to reveal the menu:
        recordMenuClicks(component, false); // reveal the enclosing menu
        if (text != null) {
            if (finder != null)
                finder.markUsed(component);
            recorder.record(new SelectAction(component.getComponentId(), text, scriptModel, windowMonitor).enscript(component));
        }
    }

    protected void recordDragAndDrop(MComponent source, MComponent target, int action) {
        recordDragContext();
        if (finder != null) {
            finder.markUsed(source);
            finder.markUsed(target);
        }
        recorder.record(new DragAndDropAction(source.getComponentId(), target.getComponentId(), action, scriptModel, windowMonitor)
                .enscript(source));
    }

    public void focusGained(MComponent component) {
        if (!component.equals(focusComponent)) {
            focusLost(component);
            if (component.isMComponentEditable() && !component.recordAlways())
                focusComponentText = getText(component);
            else {
                focusComponentText = null;
            }
            focusComponent = component;
        }
    }

    public void focusLost(MComponent currentComponent) {
        MComponent focusComponentContainer = null;
        if (focusComponent instanceof MCellComponent)
            focusComponentContainer = ((MCellComponent) focusComponent).getCollectionComponentWithWindowID();
        if (focusComponent != null && focusComponent.isMComponentEditable() && focusComponent.getText() != null
                && !focusComponent.getText().equals(focusComponentText) && focusComponent.isFocusNeeded()) {
            if (focusComponent.recordAlways() && RecordingEventQueue.isContextMenuOn())
                ;
            else
                recordSelect(focusComponent);
        }
        focusComponent = null;
        if (currentComponent != null && currentComponent instanceof MCellComponent) {
            MComponent container = ((MCellComponent) currentComponent).getCollectionComponent();
            if (container.equals(focusComponentContainer)) {
                return;
            }
        }
        if (focusComponentContainer == null)
            return;
        if (focusComponentContainer.getText() != null) {
            recordSelect(focusComponentContainer);
        }
    }

    private String getText(MComponent component) {
        return component != null ? component.getText() : null;
    }

    private MComponent dragComponent = null;
    private MComponent dragComponentContainer = null;
    private String dragComponentText = null;
    private String dragComponentContainerText = null;
    private boolean recording;

    private void storeDragContext(MComponent component) {
        if (component == null) {
            dragComponent = null;
            dragComponentContainer = null;
            return;
        }
        if (component.isMComponentEditable())
            dragComponentText = getText(component);
        else
            dragComponentText = null;
        dragComponent = component;
        MComponent container = null;
        if (dragComponent instanceof MCellComponent)
            container = ((MCellComponent) dragComponent).getCollectionComponentWithWindowID();
        if (container != null && container.isMComponentEditable()) {
            dragComponentContainer = container;
            dragComponentContainerText = container.getText();
        } else {
            dragComponentContainer = null;
            dragComponentContainerText = null;
        }
    }

    private void recordDragContext() {
        if (dragComponent != null && dragComponentText != null)
            recordSelect(dragComponent, dragComponentText);
        if (dragComponentContainer != null && dragComponentContainerText != null)
            recordSelect(dragComponentContainer, dragComponentContainerText);
    }

    /*
     * Unlike the above methods, which are to support drag and drop, this
     * section deals with dragging within a custom component.
     */
    private MComponent dragStartComponent = null;
    private Point dragStartPoint;
    private long dragStartTime;

    private void storeDragStart(MComponent component, MouseEvent e) {
        if (component == null || component.isMComponentEditable())
            return; // ignore drags on sliders and text boxes - this is done by
                    // value as an editable field on de-focus
        dragStartComponent = component;
        dragStartPoint = e.getPoint();
        dragStartTime = e.getWhen();
    }

    private void storeDragEnd(MComponent component, MouseEvent e) {
        if (dragStartComponent == null || dragStartPoint == null || component == null)
            return;
        if (component.getComponent() instanceof JScrollBar) {
            // Ignore drags on scrollbars - the scrollbar might not be present
            // at a different
            // screen size, and scrolling to components works automatically.
            // Note that once Marathon ignored everything on scrollbars but we
            // brought it back -
            // can't recall why. Clicks still take effect.
        } else if (dragStartPoint.distance(e.getPoint()) < 5) {
            // Less than 5 px is not considered a drag - this is how we ignore
            // clicks
        } else if (dragStartComponent.getComponent() != component.getComponent()) {
            // Event is on a different component - for a drag to be really a
            // drag,
            // the press and release must be on the same one, even if dragged
            // outside its bounds.
        } else {
            if (!finder.isRawRecording())
                return;
            if (lastClickRecorded != null && lastClickRecordedTime >= dragStartTime) {
                // undo the click:
                recorder.record(new UndoOperation(lastClickRecorded, scriptModel, windowMonitor).enscript(component));
            }
            if (finder != null)
                finder.markUsed(component);
            recorder.record(new DragAction(component.getComponentId(), e, dragStartPoint, e.getPoint(), scriptModel, windowMonitor)
                    .enscript(component));
        }
        // reset
        clearDrag();
    }

    private void clearDrag() {
        dragStartComponent = null;
        dragStartPoint = null;
    }

    public void eventDispatched(AWTEvent event) {
        try {
            eventDispatchedX(event);
        } catch(Throwable t) {
            RuntimeLogger.getRuntimeLogger().error("Recorder", t.getMessage(), ExceptionUtil.getTrace(t));
        }
    }
   
    public void eventDispatchedX(AWTEvent event) {
        if (RecordingEventQueue.isContextMenuOn()) {
            return;
        }
        if (event instanceof MouseEvent) {
            switch (event.getID()) {
            case MouseEvent.MOUSE_PRESSED:
                mouseListener.mousePressed((MouseEvent) event);
                break;
            case MouseEvent.MOUSE_CLICKED:
                mouseListener.mouseClicked((MouseEvent) event);
                break;
            case MouseEvent.MOUSE_ENTERED:
                mouseListener.mouseEntered((MouseEvent) event);
                break;
            case MouseEvent.MOUSE_EXITED:
                mouseListener.mouseExited((MouseEvent) event);
                break;
            case MouseEvent.MOUSE_RELEASED:
                mouseListener.mouseReleased((MouseEvent) event);
                break;
            }
        } else if (event instanceof FocusEvent) {
            switch (event.getID()) {
            case FocusEvent.FOCUS_GAINED:
                focusListener.focusGained((FocusEvent) event);
                break;
            case FocusEvent.FOCUS_LOST:
                focusListener.focusLost((FocusEvent) event);
                break;
            }
        } else if (event instanceof KeyEvent) {
            switch (event.getID()) {
            case KeyEvent.KEY_PRESSED:
                keyListener.keyPressed((KeyEvent) event);
                break;
            case KeyEvent.KEY_RELEASED:
                keyListener.keyReleased((KeyEvent) event);
                break;
            case KeyEvent.KEY_TYPED:
                keyListener.keyTyped((KeyEvent) event);
                break;
            }
        } else if (event instanceof WindowEvent) {
            switch (event.getID()) {
            case WindowEvent.WINDOW_CLOSING:
                recordWindowClosing(((WindowEvent) event).getWindow());
                break;
            case WindowEvent.WINDOW_STATE_CHANGED:
                recordWindowState(((WindowEvent) event).getWindow());
                break;
            }
        } else if (event instanceof ComponentEvent) {
            switch (event.getID()) {
            case ComponentEvent.COMPONENT_MOVED:
            case ComponentEvent.COMPONENT_RESIZED:
                if (((ComponentEvent) event).getComponent() instanceof Window) {
                    recordWindowState((Window) ((ComponentEvent) event).getComponent());
                }
            }
        }
    }

    public static RecordingEventListener getInstance() {
        return instance;
    }

    public IRecorder getRecorder() {
        return recorder;
    }

    public ComponentFinder getFinder() {
        return finder;
    }

    public boolean isRecording() {
        return recording;
    }

}
TOP

Related Classes of net.sourceforge.marathon.recorder.RecordingEventListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.