diff dwtx/jface/action/ActionContributionItem.d @ 70:46a6e0e6ccd4

Merge with d-fied sources of 3.4M7
author Frank Benoit <benoit@tionex.de>
date Thu, 22 May 2008 01:36:46 +0200
parents ea8ff534f622
children 4878bef4a38e
line wrap: on
line diff
--- a/dwtx/jface/action/ActionContributionItem.d	Mon May 19 13:41:06 2008 +0200
+++ b/dwtx/jface/action/ActionContributionItem.d	Thu May 22 01:36:46 2008 +0200
@@ -22,7 +22,6 @@
 import dwt.DWT;
 import dwt.graphics.GC;
 import dwt.graphics.Point;
-import dwt.graphics.Rectangle;
 import dwt.widgets.Button;
 import dwt.widgets.Composite;
 import dwt.widgets.Display;
@@ -35,6 +34,8 @@
 import dwt.widgets.ToolItem;
 import dwt.widgets.Widget;
 import dwtx.jface.action.ExternalActionManager;
+import dwtx.core.commands.ExecutionException;
+import dwtx.core.commands.NotEnabledException;
 import dwtx.jface.bindings.Trigger;
 import dwtx.jface.bindings.TriggerSequence;
 import dwtx.jface.bindings.keys.IKeyLookup;
@@ -61,11 +62,11 @@
  */
 public class ActionContributionItem : ContributionItem {
     alias ContributionItem.fill fill;
-
+ 
     /**
-     * Mode bit: Show text on tool items, even if an image is present. If this
-     * mode bit is not set, text is only shown on tool items if there is no
-     * image present.
+     * Mode bit: Show text on tool items or buttons, even if an image is
+     * present. If this mode bit is not set, text is only shown on tool items if
+     * there is no image present.
      *
      * @since 3.0
      */
@@ -74,6 +75,11 @@
     /** a string inserted in the middle of text that has been shortened */
     private static const String ellipsis = "..."; //$NON-NLS-1$
 
+    /**
+     * Stores the result of the action. False when the action returned failure.
+     */
+    private Boolean result = null;
+
     private static bool USE_COLOR_ICONS = true;
 
     /**
@@ -144,6 +150,8 @@
      */
     private Widget widget = null;
 
+    private Listener menuCreatorListener;
+
     /**
      * Creates a new contribution item from the given action. The id of the
      * action is used as the id of the item.
@@ -262,7 +270,6 @@
      */
     public override void fill(Menu parent, int index) {
         if (widget is null && parent !is null) {
-            Menu subMenu = null;
             int flags = DWT.PUSH;
             if (action !is null) {
                 int style = action.getStyle();
@@ -271,11 +278,7 @@
                 } else if (style is IAction.AS_RADIO_BUTTON) {
                     flags = DWT.RADIO;
                 } else if (style is IAction.AS_DROP_DOWN_MENU) {
-                    IMenuCreator mc = action.getMenuCreator();
-                    if (mc !is null) {
-                        subMenu = mc.getMenu(parent);
-                        flags = DWT.CASCADE;
-                    }
+                    flags = DWT.CASCADE;
                 }
             }
 
@@ -294,7 +297,12 @@
                 mi.addHelpListener(action.getHelpListener());
             }
 
-            if (subMenu !is null) {
+            if (flags is DWT.CASCADE) {
+                // just create a proxy for now, if the user shows it then 
+                // fill it in
+                Menu subMenu = new Menu(parent);
+                subMenu.addListener(DWT.Show, getMenuCreatorListener());
+                subMenu.addListener(DWT.Hide, getMenuCreatorListener());
                 mi.setMenu(subMenu);
             }
 
@@ -477,7 +485,8 @@
         // Check if our widget is the one being disposed.
         if (e.widget is widget) {
             // Dispose of the menu creator.
-            if (action.getStyle() is IAction.AS_DROP_DOWN_MENU) {
+            if (action.getStyle() is IAction.AS_DROP_DOWN_MENU
+                    && menuCreatorCalled) {
                 IMenuCreator mc = action.getMenuCreator();
                 if (mc !is null) {
                     mc.dispose();
@@ -525,6 +534,7 @@
                 if (e.detail is 4) { // on drop-down button
                     if (action.getStyle() is IAction.AS_DROP_DOWN_MENU) {
                         IMenuCreator mc = action.getMenuCreator();
+                        menuCreatorCalled = true;
                         ToolItem ti = cast(ToolItem) item;
                         // we create the menu as a sub-menu of "dummy" so that
                         // we can use
@@ -538,11 +548,11 @@
                             Menu m = mc.getMenu(ti.getParent());
                             if (m !is null) {
                                 // position the menu below the drop down item
-                                Rectangle b = ti.getBounds();
-                                Point p = ti.getParent().toDisplay(
-                                        new Point(b.x, b.y + b.height));
-                                m.setLocation(p.x, p.y); // waiting for DWT
-                                                            // 0.42
+                                Point point = ti.getParent().toDisplay(
+                                        new Point(e.x, e.y));
+                                m.setLocation(point.x, point.y); // waiting
+                                                                    // for DWT
+                                // 0.42
                                 m.setVisible(true);
                                 return; // we don't fire the action
                             }
@@ -551,6 +561,16 @@
                 }
             }
 
+            ExternalActionManager.IExecuteCallback callback = null;
+            String actionDefinitionId = action.getActionDefinitionId();
+            if (actionDefinitionId !is null) {
+                Object obj = ExternalActionManager.getInstance()
+                        .getCallback();
+                if (obj instanceof ExternalActionManager.IExecuteCallback) {
+                    callback = (ExternalActionManager.IExecuteCallback) obj;
+                }
+            }
+
             // Ensure action is enabled first.
             // See 1GAN3M6: ITPUI:WINNT - Any IAction in the workbench can be
             // executed while disabled.
@@ -561,13 +581,49 @@
                 if (trace) {
                     ms = System.currentTimeMillis();
                     Stdout.formatln("Running action: {}", action.getText()); //$NON-NLS-1$
+                }               
+                
+                IPropertyChangeListener resultListener = null;
+                if (callback !is null) {
+                    resultListener = new IPropertyChangeListener() {
+                        public void propertyChange(PropertyChangeEvent event) {
+                            // Check on result
+                            if (event.getProperty().equals(IAction.RESULT)) {
+                                if (event.getNewValue() instanceof Boolean) {
+                                    result = (Boolean) event.getNewValue();
+                                }
+                            }
+                        }
+                    };
+                    action.addPropertyChangeListener(resultListener);
+                    callback.preExecute(action, e);
                 }
 
                 action.runWithEvent(e);
 
+                if (callback !is null) {
+                    if (result is null || result.equals(Boolean.TRUE)) {
+                        callback.postExecuteSuccess(action, Boolean.TRUE);
+                    } else {
+                        callback.postExecuteFailure(action,
+                                new ExecutionException(action.getText()
+                                        + " returned failure.")); //$NON-NLS-1$
+                    }
+                }
+
+                if (resultListener!isnull) {
+                    result = null;
+                    action.removePropertyChangeListener(resultListener);
+                }
                 if (trace) {
                     Stdout.formatln("{} ms to run action: {}",(System.currentTimeMillis() - ms), action.getText()); //$NON-NLS-1$
                 }
+            } else {
+                if (callback !is null) {
+                    callback.notEnabled(action, new NotEnabledException(action
+                            .getText()
+                            + " is not enabled.")); //$NON-NLS-1$
+                }
             }
         }
     }
@@ -753,9 +809,12 @@
                     ExternalActionManager.ICallback callback = ExternalActionManager
                             .getInstance().getCallback();
                     String commandId = action.getActionDefinitionId();
-                    if ((callback !is null) && (commandId !is null) && (toolTip !is null)) {
-                        String acceleratorText = callback.getAcceleratorText(commandId);
-                        if (acceleratorText !is null && acceleratorText.length !is 0) {
+                    if ((callback !is null) && (commandId !is null)
+                            && (toolTip !is null)) {
+                        String acceleratorText = callback
+                                .getAcceleratorText(commandId);
+                        if (acceleratorText !is null
+                                && acceleratorText.length !is 0) {
                             toolTip = JFaceResources.format(
                                     "Toolbar_Tooltip_Accelerator", //$NON-NLS-1$
                                     [ toolTip, acceleratorText ]);
@@ -872,10 +931,14 @@
                     }
 
                     if (text !is null && acceleratorText is null) {
-                        // use extracted accelerator text in case accelerator cannot be fully represented in one int (e.g. multi-stroke keys)
-                        acceleratorText = LegacyActionTools.extractAcceleratorText(text);
+                        // use extracted accelerator text in case accelerator
+                        // cannot be fully represented in one int (e.g.
+                        // multi-stroke keys)
+                        acceleratorText = LegacyActionTools
+                                .extractAcceleratorText(text);
                         if (acceleratorText is null && accelerator !is 0) {
-                            acceleratorText= Action.convertAccelerator(accelerator);
+                            acceleratorText = Action
+                                    .convertAccelerator(accelerator);
                         }
                     }
 
@@ -919,19 +982,19 @@
             if (cast(Button)widget ) {
                 Button button = cast(Button) widget;
 
-                if (imageChanged && updateImages(false)) {
-                    textChanged = false; // don't update text if it has an
-                                            // image
+                if (imageChanged) {
+                    updateImages(false);
                 }
 
                 if (textChanged) {
                     String text = action.getText();
-                    if (text is null) {
-                        text = ""; //$NON-NLS-1$
-                    } else {
+                    bool showText = text !is null && ((getMode() & MODE_FORCE_TEXT) !is 0 || !hasImages(action));
+                    // only do the trimming if the text will be used
+                    if (showText) {
                         text = Action.removeAcceleratorText(text);
                     }
-                    button.setText(text);
+                    String textToSet = showText ? text : ""; //$NON-NLS-1$
+                    button.setText(textToSet);
                 }
 
                 if (tooltipTextChanged) {
@@ -1134,4 +1197,187 @@
         // If for some reason we fall through abort
         return textValue;
     }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see dwtx.jface.action.ContributionItem#dispose()
+     */
+    public void dispose() {
+        if (widget !is null) {
+            widget.dispose();
+            widget = null;
+        }
+        holdMenu = null;
+    }
+    
+    /**
+     * Handle show and hide on the proxy menu for IAction.AS_DROP_DOWN_MENU
+     * actions.
+     * 
+     * @return the appropriate listener
+     * @since 3.4
+     */
+    private Listener getMenuCreatorListener() {
+        if (menuCreatorListener is null) {
+            menuCreatorListener = new Listener() {
+                public void handleEvent(Event event) {
+                    switch (event.type) {
+                    case DWT.Show:
+                        handleShowProxy((Menu) event.widget);
+                        break;
+                    case DWT.Hide:
+                        handleHideProxy((Menu) event.widget);
+                        break;
+                    }
+                }
+            };
+        }
+        return menuCreatorListener;
+    }
+    
+    /**
+     * This is the easiest way to hold the menu until we can swap it in to the
+     * proxy.
+     */
+    private Menu holdMenu = null;
+
+    private bool menuCreatorCalled = false;
+    
+    /**
+     * The proxy menu is being shown, we better get the real menu.
+     * 
+     * @param proxy
+     *            the proxy menu
+     * @since 3.4
+     */
+    private void handleShowProxy(Menu proxy) {
+        proxy.removeListener(DWT.Show, getMenuCreatorListener());
+        IMenuCreator mc = action.getMenuCreator();
+        menuCreatorCalled  = true;
+        if (mc is null) {
+            return;
+        }
+        holdMenu = mc.getMenu(proxy.getParentMenu());
+        if (holdMenu is null) {
+            return;
+        }
+        copyMenu(holdMenu, proxy);
+    }
+
+    /**
+     * Create MenuItems in the proxy menu that can execute the real menu items
+     * if selected. Create proxy menus for any real item submenus.
+     * 
+     * @param realMenu
+     *            the real menu to copy from
+     * @param proxy
+     *            the proxy menu to populate
+     * @since 3.4
+     */
+    private void copyMenu(Menu realMenu, Menu proxy) {
+        if (realMenu.isDisposed() || proxy.isDisposed()) {
+            return;
+        }
+        
+        // we notify the real menu so it can populate itself if it was
+        // listening for DWT.Show
+        realMenu.notifyListeners(DWT.Show, null);
+
+        final Listener passThrough = new Listener() {
+            public void handleEvent(Event event) {
+                if (!event.widget.isDisposed()) {
+                    Widget realItem = (Widget) event.widget.getData();
+                    if (!realItem.isDisposed()) {
+                        int style = event.widget.getStyle();
+                        if (event.type is DWT.Selection
+                                && ((style & (DWT.TOGGLE | DWT.CHECK)) !is 0)
+                                && realItem instanceof MenuItem) {
+                            ((MenuItem) realItem)
+                                    .setSelection(((MenuItem) event.widget)
+                                            .getSelection());
+                        }
+                        event.widget = realItem;
+                        realItem.notifyListeners(event.type, event);
+                    }
+                }
+            }
+        };
+
+        MenuItem[] items = realMenu.getItems();
+        for (int i = 0; i < items.length; i++) {
+            final MenuItem realItem = items[i];
+            final MenuItem proxyItem = new MenuItem(proxy, realItem.getStyle());
+            proxyItem.setData(realItem);
+            proxyItem.setAccelerator(realItem.getAccelerator());
+            proxyItem.setEnabled(realItem.getEnabled());
+            proxyItem.setImage(realItem.getImage());
+            proxyItem.setSelection(realItem.getSelection());
+            proxyItem.setText(realItem.getText());
+
+            // pass through any events
+            proxyItem.addListener(DWT.Selection, passThrough);
+            proxyItem.addListener(DWT.Arm, passThrough);
+            proxyItem.addListener(DWT.Help, passThrough);
+
+            final Menu itemMenu = realItem.getMenu();
+            if (itemMenu !is null) {
+                // create a proxy for any sub menu items
+                final Menu subMenu = new Menu(proxy);
+                subMenu.setData(itemMenu);
+                proxyItem.setMenu(subMenu);
+                subMenu.addListener(DWT.Show, new Listener() {
+                    public void handleEvent(Event event) {
+                        event.widget.removeListener(DWT.Show, this);
+                        if (event.type is DWT.Show) {
+                            copyMenu(itemMenu, subMenu);
+                        }
+                    }
+                });
+                subMenu.addListener(DWT.Help, passThrough);
+                subMenu.addListener(DWT.Hide, passThrough);
+            }
+        }
+    }
+    
+    /**
+     * The proxy menu is being hidden, so we need to make it go away.
+     * 
+     * @param proxy
+     *            the proxy menu
+     * @since 3.4
+     */
+    private void handleHideProxy(final Menu proxy) {
+        proxy.removeListener(DWT.Hide, getMenuCreatorListener());
+        proxy.getDisplay().asyncExec(new Runnable() {
+            public void run() {
+                if (!proxy.isDisposed()) {
+                    MenuItem parentItem = proxy.getParentItem();
+                    proxy.dispose();
+                    parentItem.setMenu(holdMenu);
+                }
+                if (holdMenu !is null && !holdMenu.isDisposed()) {
+                    holdMenu.notifyListeners(DWT.Hide, null);
+                }
+                holdMenu = null;
+            }
+        });
+    }
+    
+    /**
+     * Return the widget associated with this contribution item. It should not
+     * be cached, as it can be disposed and re-created by its containing
+     * ContributionManager, which controls all of the widgets lifecycle methods.
+     * <p>
+     * This can be used to set layout data on the widget if appropriate. The
+     * actual type of the widget can be any valid control for this
+     * ContributionItem's current ContributionManager.
+     * </p>
+     * 
+     * @return the widget, or <code>null</code> depending on the lifecycle.
+     * @since 3.4
+     */
+    public Widget getWidget() {
+        return widget;
+    }
 }