diff dwtx/jface/util/OpenStrategy.d @ 4:c87617952847

some JFace modules
author Frank Benoit <benoit@tionex.de>
date Fri, 28 Mar 2008 17:08:33 +0100
parents
children 644f1334b451
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/util/OpenStrategy.d	Fri Mar 28 17:08:33 2008 +0100
@@ -0,0 +1,495 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.util.OpenStrategy;
+
+import dwtx.jface.util.IOpenEventListener;
+
+import dwt.DWT;
+import dwt.custom.TableTree;
+import dwt.custom.TableTreeItem;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Point;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Table;
+import dwt.widgets.TableItem;
+import dwt.widgets.Tree;
+import dwt.widgets.TreeItem;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.ListenerList;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * Implementation of single-click and double-click strategies.
+ * <p>
+ * Usage:
+ * <pre>
+ *  OpenStrategy handler = new OpenStrategy(control);
+ *  handler.addOpenListener(new IOpenEventListener() {
+ *      public void handleOpen(SelectionEvent e) {
+ *          ... // code to handle the open event.
+ *      }
+ *  });
+ * </pre>
+ * </p>
+ */
+public class OpenStrategy {
+    /**
+     * Default behavior. Double click to open the item.
+     */
+    public static const int DOUBLE_CLICK = 0;
+
+    /**
+     * Single click will open the item.
+     */
+    public static const int SINGLE_CLICK = 1;
+
+    /**
+     * Hover will select the item.
+     */
+    public static const int SELECT_ON_HOVER = 1 << 1;
+
+    /**
+     * Open item when using arrow keys
+     */
+    public static const int ARROW_KEYS_OPEN = 1 << 2;
+
+    /** A single click will generate
+     * an open event but key arrows will not do anything.
+     *
+     * @deprecated
+     */
+    public static const int NO_TIMER = SINGLE_CLICK;
+
+    /** A single click will generate an open
+     * event and key arrows will generate an open event after a
+     * small time.
+     *
+     * @deprecated
+     */
+    public static const int FILE_EXPLORER = SINGLE_CLICK | ARROW_KEYS_OPEN;
+
+    /** Pointing to an item will change the selection
+     * and a single click will gererate an open event
+     *
+     * @deprecated
+     */
+    public static const int ACTIVE_DESKTOP = SINGLE_CLICK | SELECT_ON_HOVER;
+
+    // Time used in FILE_EXPLORER and ACTIVE_DESKTOP
+    private static const int TIME = 500;
+
+    /* SINGLE_CLICK or DOUBLE_CLICK;
+     * In case of SINGLE_CLICK, the bits SELECT_ON_HOVER and ARROW_KEYS_OPEN
+     * my be set as well. */
+    private static int CURRENT_METHOD = DOUBLE_CLICK;
+
+    private Listener eventHandler;
+
+    private ListenerList openEventListeners;
+
+    private ListenerList selectionEventListeners;
+
+    private ListenerList postSelectionEventListeners;
+
+    /**
+     * @param control the control the strategy is applied to
+     */
+    public this(Control control) {
+        openEventListeners = new ListenerList();
+        selectionEventListeners = new ListenerList();
+        postSelectionEventListeners = new ListenerList();
+        initializeHandler(control.getDisplay());
+        addListener(control);
+    }
+
+    /**
+     * Adds an IOpenEventListener to the collection of openEventListeners
+     * @param listener the listener to add
+     */
+    public void addOpenListener(IOpenEventListener listener) {
+        openEventListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes an IOpenEventListener to the collection of openEventListeners
+     * @param listener the listener to remove
+     */
+    public void removeOpenListener(IOpenEventListener listener) {
+        openEventListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Adds an SelectionListener to the collection of selectionEventListeners
+     * @param listener the listener to add
+     */
+    public void addSelectionListener(SelectionListener listener) {
+        selectionEventListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes an SelectionListener to the collection of selectionEventListeners
+     * @param listener the listener to remove
+     */
+    public void removeSelectionListener(SelectionListener listener) {
+        selectionEventListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * Adds an SelectionListener to the collection of selectionEventListeners
+     * @param listener the listener to add
+     */
+    public void addPostSelectionListener(SelectionListener listener) {
+        postSelectionEventListeners.add(cast(Object)listener);
+    }
+
+    /**
+     * Removes an SelectionListener to the collection of selectionEventListeners
+     * @param listener the listener to remove
+     */
+    public void removePostSelectionListener(SelectionListener listener) {
+        postSelectionEventListeners.remove(cast(Object)listener);
+    }
+
+    /**
+     * This method is internal to the framework; it should not be implemented outside
+     * the framework.
+     * @return the current used single/double-click method
+     *
+     */
+    public static int getOpenMethod() {
+        return CURRENT_METHOD;
+    }
+
+    /**
+     * Set the current used single/double-click method.
+     *
+     * This method is internal to the framework; it should not be implemented outside
+     * the framework.
+     * @param method the method to be used
+     * @see OpenStrategy#DOUBLE_CLICK
+     * @see OpenStrategy#SINGLE_CLICK
+     * @see OpenStrategy#SELECT_ON_HOVER
+     * @see OpenStrategy#ARROW_KEYS_OPEN
+     */
+    public static void setOpenMethod(int method) {
+        if (method is DOUBLE_CLICK) {
+            CURRENT_METHOD = method;
+            return;
+        }
+        if ((method & SINGLE_CLICK) is 0) {
+            throw new IllegalArgumentException("Invalid open mode"); //$NON-NLS-1$
+        }
+        if ((method & (SINGLE_CLICK | SELECT_ON_HOVER | ARROW_KEYS_OPEN)) is 0) {
+            throw new IllegalArgumentException("Invalid open mode"); //$NON-NLS-1$
+        }
+        CURRENT_METHOD = method;
+    }
+
+    /**
+     * @return true if editors should be activated when opened.
+     */
+    public static bool activateOnOpen() {
+        return getOpenMethod() is DOUBLE_CLICK;
+    }
+
+    /*
+     * Adds all needed listener to the control in order to implement
+     * single-click/double-click strategies.
+     */
+    private void addListener(Control c) {
+        c.addListener(DWT.MouseEnter, eventHandler);
+        c.addListener(DWT.MouseExit, eventHandler);
+        c.addListener(DWT.MouseMove, eventHandler);
+        c.addListener(DWT.MouseDown, eventHandler);
+        c.addListener(DWT.MouseUp, eventHandler);
+        c.addListener(DWT.KeyDown, eventHandler);
+        c.addListener(DWT.Selection, eventHandler);
+        c.addListener(DWT.DefaultSelection, eventHandler);
+        c.addListener(DWT.Collapse, eventHandler);
+        c.addListener(DWT.Expand, eventHandler);
+    }
+
+    /*
+     * Fire the selection event to all selectionEventListeners
+     */
+    private void fireSelectionEvent(SelectionEvent e) {
+        if (e.item !is null && e.item.isDisposed()) {
+            return;
+        }
+        Object l[] = selectionEventListeners.getListeners();
+        for (int i = 0; i < l.length; i++) {
+            (cast(SelectionListener) l[i]).widgetSelected(e);
+        }
+    }
+
+    /*
+     * Fire the default selection event to all selectionEventListeners
+     */
+    private void fireDefaultSelectionEvent(SelectionEvent e) {
+        Object l[] = selectionEventListeners.getListeners();
+        for (int i = 0; i < l.length; i++) {
+            (cast(SelectionListener) l[i]).widgetDefaultSelected(e);
+        }
+    }
+
+    /*
+     * Fire the post selection event to all postSelectionEventListeners
+     */
+    private void firePostSelectionEvent(SelectionEvent e) {
+        if (e.item !is null && e.item.isDisposed()) {
+            return;
+        }
+        Object l[] = postSelectionEventListeners.getListeners();
+        for (int i = 0; i < l.length; i++) {
+            (cast(SelectionListener) l[i]).widgetSelected(e);
+        }
+    }
+
+    /*
+     * Fire the open event to all openEventListeners
+     */
+    private void fireOpenEvent(SelectionEvent e) {
+        if (e.item !is null && e.item.isDisposed()) {
+            return;
+        }
+        Object l[] = openEventListeners.getListeners();
+        for (int i = 0; i < l.length; i++) {
+            (cast(IOpenEventListener) l[i]).handleOpen(e);
+        }
+    }
+
+    //Initialize event handler.
+    private void initializeHandler( Display display_) {
+        eventHandler = new class() Listener {
+            Display display;
+            bool timerStarted = false;
+
+            Event mouseUpEvent = null;
+
+            Event mouseMoveEvent = null;
+
+            SelectionEvent selectionPendent = null;
+
+            bool enterKeyDown = false;
+
+            SelectionEvent defaultSelectionPendent = null;
+
+            bool arrowKeyDown = false;
+
+            int[] count;
+
+            long startTime;
+
+            bool collapseOccurred = false;
+
+            bool expandOccurred = false;
+
+            this(){
+                display = display_;
+                startTime = System.currentTimeMillis();
+                count = new int[1];
+            }
+
+            public void handleEvent( Event e) {
+                if (e.type is DWT.DefaultSelection) {
+                    SelectionEvent event = new SelectionEvent(e);
+                    fireDefaultSelectionEvent(event);
+                    if (CURRENT_METHOD is DOUBLE_CLICK) {
+                        fireOpenEvent(event);
+                    } else {
+                        if (enterKeyDown) {
+                            fireOpenEvent(event);
+                            enterKeyDown = false;
+                            defaultSelectionPendent = null;
+                        } else {
+                            defaultSelectionPendent = event;
+                        }
+                    }
+                    return;
+                }
+
+                switch (e.type) {
+                case DWT.MouseEnter:
+                case DWT.MouseExit:
+                    mouseUpEvent = null;
+                    mouseMoveEvent = null;
+                    selectionPendent = null;
+                    break;
+                case DWT.MouseMove:
+                    if ((CURRENT_METHOD & SELECT_ON_HOVER) is 0) {
+                        return;
+                    }
+                    if (e.stateMask !is 0) {
+                        return;
+                    }
+                    if (e.widget.getDisplay().getFocusControl() !is e.widget) {
+                        return;
+                    }
+                    mouseMoveEvent = e;
+                    Runnable runnable = new class() Runnable {
+                        public void run() {
+                            long time = System.currentTimeMillis();
+                            int diff = cast(int) (time - startTime);
+                            if (diff <= TIME) {
+                                display.timerExec(diff * 2 / 3, this );
+                            } else {
+                                timerStarted = false;
+                                setSelection(mouseMoveEvent);
+                            }
+                        }
+                    };
+                    startTime = System.currentTimeMillis();
+                    if (!timerStarted) {
+                        timerStarted = true;
+                        display.timerExec(TIME * 2 / 3, runnable );
+                    }
+                    break;
+                case DWT.MouseDown:
+                    mouseUpEvent = null;
+                    arrowKeyDown = false;
+                    break;
+                case DWT.Expand:
+                    expandOccurred = true;
+                    break;
+                case DWT.Collapse:
+                    collapseOccurred = true;
+                    break;
+                case DWT.MouseUp:
+                    mouseMoveEvent = null;
+                    if ((e.button !is 1) || ((e.stateMask & ~DWT.BUTTON1) !is 0)) {
+                        return;
+                    }
+                    if (selectionPendent !is null
+                            && !(collapseOccurred || expandOccurred)) {
+                        mouseSelectItem(selectionPendent);
+                    } else {
+                        mouseUpEvent = e;
+                        collapseOccurred = false;
+                        expandOccurred = false;
+                    }
+                    break;
+                case DWT.KeyDown:
+                    mouseMoveEvent = null;
+                    mouseUpEvent = null;
+                    arrowKeyDown = ((e.keyCode is DWT.ARROW_UP) || (e.keyCode is DWT.ARROW_DOWN))
+                            && e.stateMask is 0;
+                    if (e.character is DWT.CR) {
+                        if (defaultSelectionPendent !is null) {
+                            fireOpenEvent(new SelectionEvent(e));
+                            enterKeyDown = false;
+                            defaultSelectionPendent = null;
+                        } else {
+                            enterKeyDown = true;
+                        }
+                    }
+                    break;
+                case DWT.Selection:
+                    SelectionEvent event = new SelectionEvent(e);
+                    fireSelectionEvent(event);
+                    mouseMoveEvent = null;
+                    if (mouseUpEvent !is null) {
+                        mouseSelectItem(event);
+                    } else {
+                        selectionPendent = event;
+                    }
+                    count[0]++;
+                    // In the case of arrowUp/arrowDown when in the arrowKeysOpen mode, we
+                    // want to delay any selection until the last arrowDown/Up occurs.  This
+                    // handles the case where the user presses arrowDown/Up successively.
+                    // We only want to open an editor for the last selected item.
+                    display.asyncExec(new class() Runnable {
+                        int id;
+                        Event e2;
+                        this(){ id = count[0]; e2 = e; }
+                        public void run() {
+                            if (arrowKeyDown) {
+                                display.timerExec(TIME, new class() Runnable {
+                                    int id2;
+                                    Event e3;
+                                    this(){ id2 = id; e3 = e2; }
+                                    public void run() {
+                                        if (id2 is count[0]) {
+                                            firePostSelectionEvent(new SelectionEvent(e3));
+                                            if ((CURRENT_METHOD & ARROW_KEYS_OPEN) !is 0) {
+                                                fireOpenEvent(new SelectionEvent(e3));
+                                            }
+                                        }
+                                    }
+                                });
+                            } else {
+                                firePostSelectionEvent(new SelectionEvent(e2));
+                            }
+                        }
+                    });
+                    break;
+                }
+            }
+
+            void mouseSelectItem(SelectionEvent e) {
+                if ((CURRENT_METHOD & SINGLE_CLICK) !is 0) {
+                    fireOpenEvent(e);
+                }
+                mouseUpEvent = null;
+                selectionPendent = null;
+            }
+
+            void setSelection(Event e) {
+                if (e is null) {
+                    return;
+                }
+                Widget w = e.widget;
+                if (w.isDisposed()) {
+                    return;
+                }
+
+                SelectionEvent selEvent = new SelectionEvent(e);
+
+                /*ISSUE: May have to create a interface with method:
+                 setSelection(Point p) so that user's custom widgets
+                 can use this class. If we keep this option. */
+                if ( auto tree = cast(Tree)w) {
+                    TreeItem item = tree.getItem(new Point(e.x, e.y));
+                    if (item !is null) {
+                        tree.setSelection([ item ]);
+                    }
+                    selEvent.item = item;
+                } else if ( auto table = cast(Table)w) {
+                    TableItem item = table.getItem(new Point(e.x, e.y));
+                    if (item !is null) {
+                        table.setSelection([ item ]);
+                    }
+                    selEvent.item = item;
+                } else if ( auto table = cast(TableTree)w) {
+                    TableTreeItem item = table.getItem(new Point(e.x, e.y));
+                    if (item !is null) {
+                        table.setSelection([ item ]);
+                    }
+                    selEvent.item = item;
+                } else {
+                    return;
+                }
+                if (selEvent.item is null) {
+                    return;
+                }
+                fireSelectionEvent(selEvent);
+                firePostSelectionEvent(selEvent);
+            }
+        };
+    }
+}