# HG changeset patch
# User Frank Benoit
+ * Some common functionality to share between implementations of
+ *
+ * Clients may neither instantiate nor extend this class.
+ *
+ * This class is not intended to be subclassed outside the framework.
+ *
+ * Subclasses must implement the
+ * The following key names are known (case is ignored):
+ * IAction
. This functionality deals with the property change
+ * event mechanism.
+ * firePropertyChange(PropertyChangeEvent)
if there are.
+ *
+ * @param propertyName
+ * the name of the property that has changed
+ * @param oldValue
+ * the old value of the property, or null
if none
+ * @param newValue
+ * the new value of the property, or null
if none
+ *
+ * @see dwtx.jface.util.IPropertyChangeListener#propertyChange(PropertyChangeEvent)
+ */
+ protected final void firePropertyChange(String propertyName,
+ Object oldValue, Object newValue) {
+ if (isListenerAttached()) {
+ firePropertyChange(new PropertyChangeEvent(this, propertyName,
+ oldValue, newValue));
+ }
+ }
+
+ public void removePropertyChangeListener(
+ IPropertyChangeListener listener) {
+ removeListenerObject(cast(Object)listener);
+ }
+
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/AbstractGroupMarker.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/AbstractGroupMarker.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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 null
or the empty string.
+ * The group name is also used as the item id.
+ *
+ * @param groupName the name of the group
+ */
+ protected this(String groupName) {
+ super(groupName);
+ Assert.isTrue(groupName !is null && groupName.length > 0);
+ }
+
+ /**
+ * Returns the group name.
+ *
+ * @return the group name
+ */
+ public String getGroupName() {
+ return getId();
+ }
+
+ /**
+ * The AbstractGroupMarker
implementation of this IContributionItem
+ * method returns true
iff the id is not null
. Subclasses may override.
+ */
+ public bool isGroupMarker() {
+ return getId() !is null;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/Action.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/Action.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,721 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 IAction.run
method to carry out
+ * the action's semantics.
+ *
+ *
+ * "BACKSPACE"
"TAB"
"RETURN"
"ENTER"
"ESC"
"ESCAPE"
"DELETE"
"SPACE"
"ARROW_UP"
, "ARROW_DOWN"
,
+ * "ARROW_LEFT"
, and "ARROW_RIGHT"
"PAGE_UP"
and "PAGE_DOWN"
"HOME"
"END"
"INSERT"
"F1"
, "F2"
through "F12"
-1
if no match was found
+ * @see dwt.DWT
+ */
+ public static int findKeyCode(String token) {
+ return LegacyActionTools.findKeyCode(token);
+ }
+
+ /**
+ * Maps an DWT key code to a standard keyboard key name. The key code is
+ * stripped of modifiers (DWT.CTRL, DWT.ALT, DWT.SHIFT, and DWT.COMMAND). If
+ * the key code is not an DWT code (for example if it a key code for the key
+ * 'S'), a string containing a character representation of the key code is
+ * returned.
+ *
+ * @param keyCode
+ * the key code to be translated
+ * @return the string representation of the key code
+ * @see dwt.DWT
+ * @since 2.0
+ */
+ public static String findKeyString(int keyCode) {
+ return LegacyActionTools.findKeyString(keyCode);
+ }
+
+ /**
+ * Maps standard keyboard modifier key names to the corresponding DWT
+ * modifier bit. The following modifier key names are recognized (case is
+ * ignored): "CTRL"
, "SHIFT"
,
+ * "ALT"
, and "COMMAND"
. The given modifier
+ * key name is converted to upper case before comparison.
+ *
+ * @param token
+ * the modifier key name
+ * @return the DWT modifier bit, or 0
if no match was found
+ * @see dwt.DWT
+ */
+ public static int findModifier(String token) {
+ return LegacyActionTools.findModifier(token);
+ }
+
+ /**
+ * Returns a string representation of an DWT modifier bit (DWT.CTRL,
+ * DWT.ALT, DWT.SHIFT, and DWT.COMMAND). Returns null
if the
+ * key code is not an DWT modifier bit.
+ *
+ * @param keyCode
+ * the DWT modifier bit to be translated
+ * @return the string representation of the DWT modifier bit, or
+ * null
if the key code was not an DWT modifier bit
+ * @see dwt.DWT
+ * @since 2.0
+ */
+ public static String findModifierString(int keyCode) {
+ return LegacyActionTools.findModifierString(keyCode);
+ }
+
+ /**
+ * Convenience method for removing any optional accelerator text from the
+ * given string. The accelerator text appears at the end of the text, and is
+ * separated from the main part by a single tab character '\t'
.
+ *
+ * @param text
+ * the text
+ * @return the text sans accelerator
+ */
+ public static String removeAcceleratorText(String text) {
+ return LegacyActionTools.removeAcceleratorText(text);
+ }
+
+ /**
+ * Convenience method for removing any mnemonics from the given string. For
+ * example, removeMnemonics("&Open")
will return
+ * "Open"
.
+ *
+ * @param text
+ * the text
+ * @return the text sans mnemonics
+ *
+ * @since 3.0
+ */
+ public static String removeMnemonics(String text) {
+ return LegacyActionTools.removeMnemonics(text);
+ }
+
+ /**
+ * This action's accelerator; 0
means none.
+ */
+ private int accelerator = 0;
+
+ /**
+ * This action's action definition id, or null
if none.
+ */
+ private String actionDefinitionId;
+
+ /**
+ * This action's description, or null
if none.
+ */
+ private String description;
+
+ /**
+ * This action's disabled image, or null
if none.
+ */
+ private ImageDescriptor disabledImage;
+
+ /**
+ * Indicates this action is enabled.
+ */
+ private bool enabled = true;
+
+ /**
+ * An action's help listener, or null
if none.
+ */
+ private HelpListener helpListener;
+
+ /**
+ * This action's hover image, or null
if none.
+ */
+ private ImageDescriptor hoverImage;
+
+ /**
+ * This action's id, or null
if none.
+ */
+ private String id;
+
+ /**
+ * This action's image, or null
if none.
+ */
+ private ImageDescriptor image;
+
+ /**
+ * This action's text, or null
if none.
+ */
+ private String text;
+
+ /**
+ * This action's tool tip text, or null
if none.
+ */
+ private String toolTipText;
+
+ /**
+ * Holds the action's menu creator (an IMenuCreator) or checked state (a
+ * bool for toggle button, or an Integer for radio button), or
+ * null
if neither have been set.
+ *
+ * The value of this field affects the value of getStyle()
.
+ *
+ * Configure the action later using the set methods. + *
+ */ + protected this() { + // do nothing + } + + /** + * Creates a new action with the given text and no image. Calls the zero-arg + * constructor, thensetText
.
+ *
+ * @param text
+ * the string used as the text for the action, or
+ * null
if there is no text
+ * @see #setText
+ */
+ protected this(String text) {
+ this();
+ setText(text);
+ }
+
+ /**
+ * Creates a new action with the given text and image. Calls the zero-arg
+ * constructor, then setText
and
+ * setImageDescriptor
.
+ *
+ * @param text
+ * the action's text, or null
if there is no text
+ * @param image
+ * the action's image, or null
if there is no
+ * image
+ * @see #setText
+ * @see #setImageDescriptor
+ */
+ protected this(String text, ImageDescriptor image) {
+ this(text);
+ setImageDescriptor(image);
+ }
+
+ /**
+ * Creates a new action with the given text and style.
+ *
+ * @param text
+ * the action's text, or null
if there is no text
+ * @param style
+ * one of AS_PUSH_BUTTON
,
+ * AS_CHECK_BOX
, AS_DROP_DOWN_MENU
,
+ * AS_RADIO_BUTTON
, and
+ * AS_UNSPECIFIED
.
+ */
+ protected this(String text, int style) {
+ this(text);
+ switch (style) {
+ case AS_PUSH_BUTTON:
+ value = stringcast(VAL_PUSH_BTN);
+ break;
+ case AS_CHECK_BOX:
+ value = VAL_TOGGLE_BTN_OFF;
+ break;
+ case AS_DROP_DOWN_MENU:
+ value = cast(Object)VAL_DROP_DOWN_MENU;
+ break;
+ case AS_RADIO_BUTTON:
+ value = VAL_RADIO_BTN_OFF;
+ break;
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public int getAccelerator() {
+ return accelerator;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ *
+ */
+ public String getActionDefinitionId() {
+ return actionDefinitionId;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public String getDescription() {
+ if (description !is null) {
+ return description;
+ }
+ return getToolTipText();
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public ImageDescriptor getDisabledImageDescriptor() {
+ return disabledImage;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public HelpListener getHelpListener() {
+ return helpListener;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public ImageDescriptor getHoverImageDescriptor() {
+ return hoverImage;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public ImageDescriptor getImageDescriptor() {
+ return image;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public IMenuCreator getMenuCreator() {
+ // The default drop down menu value is only used
+ // to mark this action requested style. So do not
+ // return it. For backward compatibility reasons.
+ if (value is cast(Object)VAL_DROP_DOWN_MENU) {
+ return null;
+ }
+ if ( auto v = cast(IMenuCreator) value ) {
+ return v;
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public int getStyle() {
+ // Infer the style from the value field.
+ if (value is stringcast(VAL_PUSH_BTN) || value is null) {
+ return AS_PUSH_BUTTON;
+ }
+ if (value is VAL_TOGGLE_BTN_ON || value is VAL_TOGGLE_BTN_OFF) {
+ return AS_CHECK_BOX;
+ }
+ if (value is VAL_RADIO_BTN_ON || value is VAL_RADIO_BTN_OFF) {
+ return AS_RADIO_BUTTON;
+ }
+ if (cast(IMenuCreator)value ) {
+ return AS_DROP_DOWN_MENU;
+ }
+
+ // We should never get to this line...
+ return AS_PUSH_BUTTON;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public String getText() {
+ return text;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public String getToolTipText() {
+ return toolTipText;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public bool isChecked() {
+ return value is VAL_TOGGLE_BTN_ON || value is VAL_RADIO_BTN_ON;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public bool isEnabled() {
+ return enabled;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public bool isHandled() {
+ return true;
+ }
+
+ /**
+ * Reports the outcome of the running of this action via the
+ * {@link IAction#RESULT} property.
+ *
+ * @param success
+ * true
if the action succeeded and
+ * false
if the action failed or was not completed
+ * @see IAction#RESULT
+ * @since 3.0
+ */
+ public final void notifyResult(bool success) {
+ // avoid bool.valueOf(bool) to allow compilation against JCL
+ // Foundation (bug 80059)
+ firePropertyChange(RESULT, null, new ValueWrapperBool(success));
+ }
+
+ /**
+ * The default implementation of this IAction
method does
+ * nothing. Subclasses should override this method if they do not need
+ * information from the triggering event, or override
+ * runWithEvent(Event)
if they do.
+ */
+ public void run() {
+ // do nothing
+ }
+
+ /**
+ * The default implementation of this IAction
method ignores
+ * the event argument, and simply calls run()
. Subclasses
+ * should override this method if they need information from the triggering
+ * event, or override run()
if not.
+ *
+ * @param event
+ * the DWT event which triggered this action being run
+ * @since 2.0
+ */
+ public void runWithEvent(Event event) {
+ run();
+ }
+
+ /*
+ * @see IAction#setAccelerator(int)
+ */
+ public void setAccelerator(int keycode) {
+ this.accelerator = keycode;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setActionDefinitionId(String id) {
+ actionDefinitionId = id;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setChecked(bool checked) {
+ Object newValue = null;
+
+ // For backward compatibility, if the style is not
+ // set yet, then convert it to a toggle button.
+ if (value is null || value is VAL_TOGGLE_BTN_ON
+ || value is VAL_TOGGLE_BTN_OFF) {
+ newValue = checked ? VAL_TOGGLE_BTN_ON : VAL_TOGGLE_BTN_OFF;
+ } else if (value is VAL_RADIO_BTN_ON || value is VAL_RADIO_BTN_OFF) {
+ newValue = checked ? VAL_RADIO_BTN_ON : VAL_RADIO_BTN_OFF;
+ } else {
+ // Some other style already, so do nothing.
+ return;
+ }
+
+ if (newValue !is value) {
+ value = newValue;
+ if (checked) {
+ firePropertyChange(CHECKED, new ValueWrapperBool(false), new ValueWrapperBool(true));
+ } else {
+ firePropertyChange(CHECKED, new ValueWrapperBool(true), new ValueWrapperBool(false));
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setDescription(String text) {
+
+ if ((description is null && text !is null)
+ || (description !is null && text is null)
+ || (description !is null && text !is null && !text
+ .equals(description))) {
+ String oldDescription = description;
+ description = text;
+ firePropertyChange(DESCRIPTION, stringcast(oldDescription), stringcast(description));
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setDisabledImageDescriptor(ImageDescriptor newImage) {
+ if (disabledImage !is newImage) {
+ ImageDescriptor oldImage = disabledImage;
+ disabledImage = newImage;
+ firePropertyChange(IMAGE, oldImage, newImage);
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setEnabled(bool enabled) {
+ if (enabled !is this.enabled) {
+ this.enabled = enabled;
+ firePropertyChange(ENABLED, new ValueWrapperBool(this.enabled), new ValueWrapperBool(enabled));
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setHelpListener(HelpListener listener) {
+ helpListener = listener;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setHoverImageDescriptor(ImageDescriptor newImage) {
+ if (hoverImage !is newImage) {
+ ImageDescriptor oldImage = hoverImage;
+ hoverImage = newImage;
+ firePropertyChange(IMAGE, oldImage, newImage);
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IAction.
+ */
+ public void setImageDescriptor(ImageDescriptor newImage) {
+ if (image !is newImage) {
+ ImageDescriptor oldImage = image;
+ image = newImage;
+ firePropertyChange(IMAGE, oldImage, newImage);
+ }
+ }
+
+ /**
+ * Sets the menu creator for this action.
+ * + * Note that if this method is called, it overrides the check status. + *
+ * + * @param creator + * the menu creator, ornull
if none
+ */
+ public void setMenuCreator(IMenuCreator creator) {
+ // For backward compatibility, if the style is not
+ // set yet, then convert it to a drop down menu.
+ if (value is null) {
+ value = cast(Object)creator;
+ return;
+ }
+
+ if ( cast(IMenuCreator)value ) {
+ value = creator is null ? cast(Object)VAL_DROP_DOWN_MENU : cast(Object)creator;
+ }
+ }
+
+ /**
+ * Sets the text for this action.
+ *
+ * Fires a property change event for the TEXT
property if the
+ * text actually changes as a consequence.
+ *
+ * The accelerator is identified by the last index of a tab character. If + * there are no tab characters, then it is identified by the last index of a + * '@' character. If neither, then there is no accelerator text. Note that + * if you want to insert a '@' character into the text (but no accelerator, + * you can simply insert a '@' or a tab at the end of the text. + *
+ * + * @param text + * the text, ornull
if none
+ */
+ public void setText(String text) {
+ String oldText = this.text;
+ int oldAccel = this.accelerator;
+ this.text = text;
+ if (text !is null) {
+ String acceleratorText = LegacyActionTools
+ .extractAcceleratorText(text);
+ if (acceleratorText !is null) {
+ int newAccelerator = LegacyActionTools
+ .convertLocalizedAccelerator(acceleratorText);
+ // Be sure to not wipe out the accelerator if nothing found
+ if (newAccelerator > 0) {
+ setAccelerator(newAccelerator);
+ }
+ }
+ }
+ if (!(this.accelerator is oldAccel && (oldText is null ? this.text is null
+ : oldText.equals(this.text)))) {
+ firePropertyChange(TEXT, stringcast(oldText), stringcast(this.text));
+ }
+ }
+
+ /**
+ * Sets the tool tip text for this action.
+ *
+ * Fires a property change event for the TOOL_TIP_TEXT
+ * property if the tool tip text actually changes as a consequence.
+ *
null
if none
+ */
+ public void setToolTipText(String toolTipText) {
+ String oldToolTipText = this.toolTipText;
+ if (!(oldToolTipText is null ? toolTipText is null : oldToolTipText
+ .equals(toolTipText))) {
+ this.toolTipText = toolTipText;
+ firePropertyChange(TOOL_TIP_TEXT, stringcast(oldToolTipText), stringcast(toolTipText));
+ }
+ }
+
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/ActionContributionItem.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/ActionContributionItem.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,1136 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 + * This class may be instantiated; it is not intended to be subclassed. + *
+ */ +public class ActionContributionItem : ContributionItem { + + /** + * 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. + * + * @since 3.0 + */ + public static int MODE_FORCE_TEXT = 1; + + /** a string inserted in the middle of text that has been shortened */ + private static const String ellipsis = "..."; //$NON-NLS-1$ + + private static bool USE_COLOR_ICONS = true; + + /** + * Returns whether color icons should be used in toolbars. + * + * @returntrue
if color icons should be used in toolbars,
+ * false
otherwise
+ */
+ public static bool getUseColorIconsInToolbars() {
+ return USE_COLOR_ICONS;
+ }
+
+ /**
+ * Sets whether color icons should be used in toolbars.
+ *
+ * @param useColorIcons
+ * true
if color icons should be used in toolbars,
+ * false
otherwise
+ */
+ public static void setUseColorIconsInToolbars(bool useColorIcons) {
+ USE_COLOR_ICONS = useColorIcons;
+ }
+
+ /**
+ * The presentation mode.
+ */
+ private int mode = 0;
+
+ /**
+ * The action.
+ */
+ private IAction action;
+
+ /**
+ * The listener for changes to the text of the action contributed by an
+ * external source.
+ */
+ private const IPropertyChangeListener actionTextListener;
+
+ /**
+ * Remembers all images in use by this contribution item
+ */
+ private LocalResourceManager imageManager;
+
+ /**
+ * Listener for DWT button widget events.
+ */
+ private Listener buttonListener;
+
+ /**
+ * Listener for DWT menu item widget events.
+ */
+ private Listener menuItemListener;
+
+ /**
+ * Listener for action property change notifications.
+ */
+ private const IPropertyChangeListener propertyListener;
+
+ /**
+ * Listener for DWT tool item widget events.
+ */
+ private Listener toolItemListener;
+
+ /**
+ * The widget created for this item; null
before creation and
+ * after disposal.
+ */
+ private Widget widget = null;
+
+ /**
+ * Creates a new contribution item from the given action. The id of the
+ * action is used as the id of the item.
+ *
+ * @param action
+ * the action
+ */
+ public this(IAction action) {
+ super(action.getId());
+ this.action = action;
+ actionTextListener = new class IPropertyChangeListener {
+ /**
+ * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ update(event.getProperty());
+ }
+ };
+ propertyListener = new class IPropertyChangeListener {
+ public void propertyChange(PropertyChangeEvent event) {
+ actionPropertyChange(event);
+ }
+ };
+ }
+
+ /**
+ * Handles a property change event on the action (forwarded by nested
+ * listener).
+ */
+ private void actionPropertyChange(PropertyChangeEvent e) {
+ // This code should be removed. Avoid using free asyncExec
+
+ if (isVisible() && widget !is null) {
+ Display display = widget.getDisplay();
+ if (display.getThread() is Thread.getThis()) {
+ update(e.getProperty());
+ } else {
+ display.asyncExec(new class Runnable {
+ PropertyChangeEvent e_;
+ this(){
+ e_=e;
+ }
+ public void run() {
+ update(e_.getProperty());
+ }
+ });
+ }
+
+ }
+ }
+
+ /**
+ * Compares this action contribution item with another object. Two action
+ * contribution items are equal if they refer to the identical Action.
+ */
+ public override int opEquals(Object o) {
+ if (!(cast(ActionContributionItem)o )) {
+ return false;
+ }
+ return (cast(Object)action).opEquals(cast(Object)(cast(ActionContributionItem) o).action);
+ }
+
+ /**
+ * The ActionContributionItem
implementation of this
+ * IContributionItem
method creates an DWT
+ * Button
for the action using the action's style. If the
+ * action's checked property has been set, the button is created and primed
+ * to the value of the checked property.
+ */
+ public void fill(Composite parent) {
+ if (widget is null && parent !is null) {
+ int flags = DWT.PUSH;
+ if (action !is null) {
+ if (action.getStyle() is IAction.AS_CHECK_BOX) {
+ flags = DWT.TOGGLE;
+ }
+ if (action.getStyle() is IAction.AS_RADIO_BUTTON) {
+ flags = DWT.RADIO;
+ }
+ }
+
+ Button b = new Button(parent, flags);
+ b.setData(this);
+ b.addListener(DWT.Dispose, getButtonListener());
+ // Don't hook a dispose listener on the parent
+ b.addListener(DWT.Selection, getButtonListener());
+ if (action.getHelpListener() !is null) {
+ b.addHelpListener(action.getHelpListener());
+ }
+ widget = b;
+
+ update(null);
+
+ // Attach some extra listeners.
+ action.addPropertyChangeListener(propertyListener);
+ if (action !is null) {
+ String commandId = action.getActionDefinitionId();
+ ExternalActionManager.ICallback callback = ExternalActionManager
+ .getInstance().getCallback();
+
+ if ((callback !is null) && (commandId !is null)) {
+ callback.addPropertyChangeListener(commandId,
+ actionTextListener);
+ }
+ }
+ }
+ }
+
+ /**
+ * The ActionContributionItem
implementation of this
+ * IContributionItem
method creates an DWT
+ * MenuItem
for the action using the action's style. If the
+ * action's checked property has been set, a button is created and primed to
+ * the value of the checked property. If the action's menu creator property
+ * has been set, a cascading submenu is created.
+ */
+ public 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();
+ if (style is IAction.AS_CHECK_BOX) {
+ flags = DWT.CHECK;
+ } 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;
+ }
+ }
+ }
+
+ MenuItem mi = null;
+ if (index >= 0) {
+ mi = new MenuItem(parent, flags, index);
+ } else {
+ mi = new MenuItem(parent, flags);
+ }
+ widget = mi;
+
+ mi.setData(this);
+ mi.addListener(DWT.Dispose, getMenuItemListener());
+ mi.addListener(DWT.Selection, getMenuItemListener());
+ if (action.getHelpListener() !is null) {
+ mi.addHelpListener(action.getHelpListener());
+ }
+
+ if (subMenu !is null) {
+ mi.setMenu(subMenu);
+ }
+
+ update(null);
+
+ // Attach some extra listeners.
+ action.addPropertyChangeListener(propertyListener);
+ if (action !is null) {
+ String commandId = action.getActionDefinitionId();
+ ExternalActionManager.ICallback callback = ExternalActionManager
+ .getInstance().getCallback();
+
+ if ((callback !is null) && (commandId !is null)) {
+ callback.addPropertyChangeListener(commandId,
+ actionTextListener);
+ }
+ }
+ }
+ }
+
+ /**
+ * The ActionContributionItem
implementation of this ,
+ * IContributionItem
method creates an DWT
+ * ToolItem
for the action using the action's style. If the
+ * action's checked property has been set, a button is created and primed to
+ * the value of the checked property. If the action's menu creator property
+ * has been set, a drop-down tool item is created.
+ */
+ public void fill(ToolBar parent, int index) {
+ if (widget is null && parent !is null) {
+ int flags = DWT.PUSH;
+ if (action !is null) {
+ int style = action.getStyle();
+ if (style is IAction.AS_CHECK_BOX) {
+ flags = DWT.CHECK;
+ } else if (style is IAction.AS_RADIO_BUTTON) {
+ flags = DWT.RADIO;
+ } else if (style is IAction.AS_DROP_DOWN_MENU) {
+ flags = DWT.DROP_DOWN;
+ }
+ }
+
+ ToolItem ti = null;
+ if (index >= 0) {
+ ti = new ToolItem(parent, flags, index);
+ } else {
+ ti = new ToolItem(parent, flags);
+ }
+ ti.setData(this);
+ ti.addListener(DWT.Selection, getToolItemListener());
+ ti.addListener(DWT.Dispose, getToolItemListener());
+
+ widget = ti;
+
+ update(null);
+
+ // Attach some extra listeners.
+ action.addPropertyChangeListener(propertyListener);
+ if (action !is null) {
+ String commandId = action.getActionDefinitionId();
+ ExternalActionManager.ICallback callback = ExternalActionManager
+ .getInstance().getCallback();
+
+ if ((callback !is null) && (commandId !is null)) {
+ callback.addPropertyChangeListener(commandId,
+ actionTextListener);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the action associated with this contribution item.
+ *
+ * @return the action
+ */
+ public IAction getAction() {
+ return action;
+ }
+
+ /**
+ * Returns the listener for DWT button widget events.
+ *
+ * @return a listener for button events
+ */
+ private Listener getButtonListener() {
+ if (buttonListener is null) {
+ buttonListener = new class Listener {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case DWT.Dispose:
+ handleWidgetDispose(event);
+ break;
+ case DWT.Selection:
+ Widget ew = event.widget;
+ if (ew !is null) {
+ handleWidgetSelection(event, (cast(Button) ew)
+ .getSelection());
+ }
+ break;
+ }
+ }
+ };
+ }
+ return buttonListener;
+ }
+
+ /**
+ * Returns the listener for DWT menu item widget events.
+ *
+ * @return a listener for menu item events
+ */
+ private Listener getMenuItemListener() {
+ if (menuItemListener is null) {
+ menuItemListener = new class Listener {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case DWT.Dispose:
+ handleWidgetDispose(event);
+ break;
+ case DWT.Selection:
+ Widget ew = event.widget;
+ if (ew !is null) {
+ handleWidgetSelection(event, (cast(MenuItem) ew)
+ .getSelection());
+ }
+ break;
+ }
+ }
+ };
+ }
+ return menuItemListener;
+ }
+
+ /**
+ * Returns the presentation mode, which is the bitwise-or of the
+ * MODE_*
constants. The default mode setting is 0, meaning
+ * that for menu items, both text and image are shown (if present), but for
+ * tool items, the text is shown only if there is no image.
+ *
+ * @return the presentation mode settings
+ *
+ * @since 3.0
+ */
+ public int getMode() {
+ return mode;
+ }
+
+ /**
+ * Returns the listener for DWT tool item widget events.
+ *
+ * @return a listener for tool item events
+ */
+ private Listener getToolItemListener() {
+ if (toolItemListener is null) {
+ toolItemListener = new class Listener {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case DWT.Dispose:
+ handleWidgetDispose(event);
+ break;
+ case DWT.Selection:
+ Widget ew = event.widget;
+ if (ew !is null) {
+ handleWidgetSelection(event, (cast(ToolItem) ew)
+ .getSelection());
+ }
+ break;
+ }
+ }
+ };
+ }
+ return toolItemListener;
+ }
+
+ /**
+ * Handles a widget dispose event for the widget corresponding to this item.
+ */
+ private void handleWidgetDispose(Event e) {
+ // 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) {
+ IMenuCreator mc = action.getMenuCreator();
+ if (mc !is null) {
+ mc.dispose();
+ }
+ }
+
+ // Unhook all of the listeners.
+ action.removePropertyChangeListener(propertyListener);
+ if (action !is null) {
+ String commandId = action.getActionDefinitionId();
+ ExternalActionManager.ICallback callback = ExternalActionManager
+ .getInstance().getCallback();
+
+ if ((callback !is null) && (commandId !is null)) {
+ callback.removePropertyChangeListener(commandId,
+ actionTextListener);
+ }
+ }
+
+ // Clear the widget field.
+ widget = null;
+
+ disposeOldImages();
+ }
+ }
+
+ /**
+ * Handles a widget selection event.
+ */
+ private void handleWidgetSelection(Event e, bool selection) {
+
+ Widget item = e.widget;
+ if (item !is null) {
+ int style = item.getStyle();
+
+ if ((style & (DWT.TOGGLE | DWT.CHECK)) !is 0) {
+ if (action.getStyle() is IAction.AS_CHECK_BOX) {
+ action.setChecked(selection);
+ }
+ } else if ((style & DWT.RADIO) !is 0) {
+ if (action.getStyle() is IAction.AS_RADIO_BUTTON) {
+ action.setChecked(selection);
+ }
+ } else if ((style & DWT.DROP_DOWN) !is 0) {
+ if (e.detail is 4) { // on drop-down button
+ if (action.getStyle() is IAction.AS_DROP_DOWN_MENU) {
+ IMenuCreator mc = action.getMenuCreator();
+ ToolItem ti = cast(ToolItem) item;
+ // we create the menu as a sub-menu of "dummy" so that
+ // we can use
+ // it in a cascading menu too.
+ // If created on a DWT control we would get an DWT
+ // error...
+ // Menu dummy= new Menu(ti.getParent());
+ // Menu m= mc.getMenu(dummy);
+ // dummy.dispose();
+ if (mc !is null) {
+ 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
+ m.setVisible(true);
+ return; // we don't fire the action
+ }
+ }
+ }
+ }
+ }
+
+ // Ensure action is enabled first.
+ // See 1GAN3M6: ITPUI:WINNT - Any IAction in the workbench can be
+ // executed while disabled.
+ if (action.isEnabled()) {
+ bool trace = Policy.TRACE_ACTIONS;
+
+ long ms = 0L;
+ if (trace) {
+ ms = System.currentTimeMillis();
+ Stdout.formatln("Running action: {}", action.getText()); //$NON-NLS-1$
+ }
+
+ action.runWithEvent(e);
+
+ if (trace) {
+ Stdout.formatln("{} ms to run action: {}",(System.currentTimeMillis() - ms), action.getText()); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on Object.
+ */
+ public override hash_t toHash() {
+ return (cast(Object)action).toHash();
+ }
+
+ /**
+ * Returns whether the given action has any images.
+ *
+ * @param actionToCheck
+ * the action
+ * @return true
if the action has any images,
+ * false
if not
+ */
+ private bool hasImages(IAction actionToCheck) {
+ return actionToCheck.getImageDescriptor() !is null
+ || actionToCheck.getHoverImageDescriptor() !is null
+ || actionToCheck.getDisabledImageDescriptor() !is null;
+ }
+
+ /**
+ * Returns whether the command corresponding to this action is active.
+ */
+ private bool isCommandActive() {
+ IAction actionToCheck = getAction();
+
+ if (actionToCheck !is null) {
+ String commandId = actionToCheck.getActionDefinitionId();
+ ExternalActionManager.ICallback callback = ExternalActionManager
+ .getInstance().getCallback();
+
+ if (callback !is null) {
+ return callback.isActive(commandId);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * The action item implementation of this IContributionItem
+ * method returns true
for menu items and false
+ * for everything else.
+ */
+ public bool isDynamic() {
+ if (cast(MenuItem)widget ) {
+ // Optimization. Only recreate the item is the check or radio style
+ // has changed.
+ bool itemIsCheck = (widget.getStyle() & DWT.CHECK) !is 0;
+ bool actionIsCheck = getAction() !is null
+ && getAction().getStyle() is IAction.AS_CHECK_BOX;
+ bool itemIsRadio = (widget.getStyle() & DWT.RADIO) !is 0;
+ bool actionIsRadio = getAction() !is null
+ && getAction().getStyle() is IAction.AS_RADIO_BUTTON;
+ return (itemIsCheck !is actionIsCheck)
+ || (itemIsRadio !is actionIsRadio);
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionItem.
+ */
+ public bool isEnabled() {
+ return action !is null && action.isEnabled();
+ }
+
+ /**
+ * Returns true
if this item is allowed to enable,
+ * false
otherwise.
+ *
+ * @return if this item is allowed to be enabled
+ * @since 2.0
+ */
+ protected bool isEnabledAllowed() {
+ if (getParent() is null) {
+ return true;
+ }
+ auto value = getParent().getOverrides().getEnabled(this);
+ return (value is null) ? true : value.value;
+ }
+
+ /**
+ * The ActionContributionItem
implementation of this
+ * ContributionItem
method extends the super implementation
+ * by also checking whether the command corresponding to this action is
+ * active.
+ */
+ public bool isVisible() {
+ return super.isVisible() && isCommandActive();
+ }
+
+ /**
+ * Sets the presentation mode, which is the bitwise-or of the
+ * MODE_*
constants.
+ *
+ * @param mode
+ * the presentation mode settings
+ *
+ * @since 3.0
+ */
+ public void setMode(int mode) {
+ this.mode = mode;
+ update();
+ }
+
+ /**
+ * The action item implementation of this IContributionItem
+ * method calls update(null)
.
+ */
+ public final void update() {
+ update(null);
+ }
+
+ /**
+ * Synchronizes the UI with the given property.
+ *
+ * @param propertyName
+ * the name of the property, or null
meaning all
+ * applicable properties
+ */
+ public void update(String propertyName) {
+ if (widget !is null) {
+ // determine what to do
+ bool textChanged = propertyName is null
+ || propertyName.equals(IAction.TEXT);
+ bool imageChanged = propertyName is null
+ || propertyName.equals(IAction.IMAGE);
+ bool tooltipTextChanged = propertyName is null
+ || propertyName.equals(IAction.TOOL_TIP_TEXT);
+ bool enableStateChanged = propertyName is null
+ || propertyName.equals(IAction.ENABLED)
+ || propertyName
+ .equals(IContributionManagerOverrides.P_ENABLED);
+ bool checkChanged = (action.getStyle() is IAction.AS_CHECK_BOX || action
+ .getStyle() is IAction.AS_RADIO_BUTTON)
+ && (propertyName is null || propertyName
+ .equals(IAction.CHECKED));
+
+ if (cast(ToolItem)widget ) {
+ ToolItem ti = cast(ToolItem) widget;
+ String text = action.getText();
+ // the set text is shown only if there is no image or if forced
+ // by MODE_FORCE_TEXT
+ 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 !is null) {
+ text = Action.removeAcceleratorText(text);
+ text = Action.removeMnemonics(text);
+ }
+
+ if (textChanged) {
+ String textToSet = showText ? text : ""; //$NON-NLS-1$
+ bool rightStyle = (ti.getParent().getStyle() & DWT.RIGHT) !is 0;
+ if (rightStyle || !ti.getText().equals(textToSet)) {
+ // In addition to being required to update the text if
+ // it
+ // gets nulled out in the action, this is also a
+ // workaround
+ // for bug 50151: Using DWT.RIGHT on a ToolBar leaves
+ // blank space
+ ti.setText(textToSet);
+ }
+ }
+
+ if (imageChanged) {
+ // only substitute a missing image if it has no text
+ updateImages(!showText);
+ }
+
+ if (tooltipTextChanged || textChanged) {
+ String toolTip = action.getToolTipText();
+ if ((toolTip is null) || (toolTip.length is 0)) {
+ toolTip = text;
+ }
+
+ 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) {
+ toolTip = JFaceResources.format(
+ "Toolbar_Tooltip_Accelerator", //$NON-NLS-1$
+ [ toolTip, acceleratorText ]);
+ }
+ }
+
+ // if the text is showing, then only set the tooltip if
+ // different
+ if (!showText || toolTip !is null && !toolTip.equals(text)) {
+ ti.setToolTipText(toolTip);
+ } else {
+ ti.setToolTipText(null);
+ }
+ }
+
+ if (enableStateChanged) {
+ bool shouldBeEnabled = action.isEnabled()
+ && isEnabledAllowed();
+
+ if (ti.getEnabled() !is shouldBeEnabled) {
+ ti.setEnabled(shouldBeEnabled);
+ }
+ }
+
+ if (checkChanged) {
+ bool bv = action.isChecked();
+
+ if (ti.getSelection() !is bv) {
+ ti.setSelection(bv);
+ }
+ }
+ return;
+ }
+
+ if (cast(MenuItem)widget ) {
+ MenuItem mi = cast(MenuItem) widget;
+
+ if (textChanged) {
+ int accelerator = 0;
+ String acceleratorText = null;
+ IAction updatedAction = getAction();
+ String text = null;
+ accelerator = updatedAction.getAccelerator();
+ ExternalActionManager.ICallback callback = ExternalActionManager
+ .getInstance().getCallback();
+
+ // Block accelerators that are already in use.
+ if ((accelerator !is 0) && (callback !is null)
+ && (callback.isAcceleratorInUse(accelerator))) {
+ accelerator = 0;
+ }
+
+ /*
+ * Process accelerators on GTK in a special way to avoid Bug
+ * 42009. We will override the native input method by
+ * allowing these reserved accelerators to be placed on the
+ * menu. We will only do this for "Ctrl+Shift+[0-9A-FU]".
+ */
+ String commandId = updatedAction
+ .getActionDefinitionId();
+ if (("gtk".equals(DWT.getPlatform())) && (cast(ExternalActionManager.IBindingManagerCallback)callback ) //$NON-NLS-1$
+ && (commandId !is null)) {
+ ExternalActionManager.IBindingManagerCallback bindingManagerCallback = cast(ExternalActionManager.IBindingManagerCallback) callback;
+ IKeyLookup lookup = KeyLookupFactory.getDefault();
+ TriggerSequence[] triggerSequences = bindingManagerCallback
+ .getActiveBindingsFor(commandId);
+ for (int i = 0; i < triggerSequences.length; i++) {
+ TriggerSequence triggerSequence = triggerSequences[i];
+ Trigger[] triggers = triggerSequence
+ .getTriggers();
+ if (triggers.length is 1) {
+ Trigger trigger = triggers[0];
+ if (cast(KeyStroke)trigger ) {
+ KeyStroke currentKeyStroke = cast(KeyStroke) trigger;
+ int currentNaturalKey = currentKeyStroke
+ .getNaturalKey();
+ if ((currentKeyStroke.getModifierKeys() is (lookup
+ .getCtrl() | lookup.getShift()))
+ && ((currentNaturalKey >= '0' && currentNaturalKey <= '9')
+ || (currentNaturalKey >= 'A' && currentNaturalKey <= 'F') || (currentNaturalKey is 'U'))) {
+ accelerator = currentKeyStroke
+ .getModifierKeys()
+ | currentNaturalKey;
+ acceleratorText = triggerSequence
+ .format();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (accelerator is 0) {
+ if ((callback !is null) && (commandId !is null)) {
+ acceleratorText = callback
+ .getAcceleratorText(commandId);
+ }
+ }
+
+ IContributionManagerOverrides overrides = null;
+
+ if (getParent() !is null) {
+ overrides = getParent().getOverrides();
+ }
+
+ if (overrides !is null) {
+ text = getParent().getOverrides().getText(this);
+ }
+
+ mi.setAccelerator(accelerator);
+
+ if (text is null) {
+ text = updatedAction.getText();
+ }
+
+ 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);
+ if (acceleratorText is null && accelerator !is 0) {
+ acceleratorText= Action.convertAccelerator(accelerator);
+ }
+ }
+
+ if (text is null) {
+ text = ""; //$NON-NLS-1$
+ } else {
+ text = Action.removeAcceleratorText(text);
+ }
+
+ if (acceleratorText is null) {
+ mi.setText(text);
+ } else {
+ mi.setText(text ~ '\t' ~ acceleratorText);
+ }
+ }
+
+ if (imageChanged) {
+ updateImages(false);
+ }
+
+ if (enableStateChanged) {
+ bool shouldBeEnabled = action.isEnabled()
+ && isEnabledAllowed();
+
+ if (mi.getEnabled() !is shouldBeEnabled) {
+ mi.setEnabled(shouldBeEnabled);
+ }
+ }
+
+ if (checkChanged) {
+ bool bv = action.isChecked();
+
+ if (mi.getSelection() !is bv) {
+ mi.setSelection(bv);
+ }
+ }
+
+ return;
+ }
+
+ 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 (textChanged) {
+ String text = action.getText();
+ if (text is null) {
+ text = ""; //$NON-NLS-1$
+ } else {
+ text = Action.removeAcceleratorText(text);
+ }
+ button.setText(text);
+ }
+
+ if (tooltipTextChanged) {
+ button.setToolTipText(action.getToolTipText());
+ }
+
+ if (enableStateChanged) {
+ bool shouldBeEnabled = action.isEnabled()
+ && isEnabledAllowed();
+
+ if (button.getEnabled() !is shouldBeEnabled) {
+ button.setEnabled(shouldBeEnabled);
+ }
+ }
+
+ if (checkChanged) {
+ bool bv = action.isChecked();
+
+ if (button.getSelection() !is bv) {
+ button.setSelection(bv);
+ }
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Updates the images for this action.
+ *
+ * @param forceImage
+ * true
if some form of image is compulsory, and
+ * false
if it is acceptable for this item to have
+ * no image
+ * @return true
if there are images for this action,
+ * false
if not
+ */
+ private bool updateImages(bool forceImage) {
+
+ ResourceManager parentResourceManager = JFaceResources.getResources();
+
+ if (cast(ToolItem)widget ) {
+ if (USE_COLOR_ICONS) {
+ ImageDescriptor image = action.getHoverImageDescriptor();
+ if (image is null) {
+ image = action.getImageDescriptor();
+ }
+ ImageDescriptor disabledImage = action
+ .getDisabledImageDescriptor();
+
+ // Make sure there is a valid image.
+ if (image is null && forceImage) {
+ image = ImageDescriptor.getMissingImageDescriptor();
+ }
+
+ LocalResourceManager localManager = new LocalResourceManager(
+ parentResourceManager);
+
+ // performance: more efficient in DWT to set disabled and hot
+ // image before regular image
+ (cast(ToolItem) widget)
+ .setDisabledImage(disabledImage is null ? null
+ : localManager
+ .createImageWithDefault(disabledImage));
+ (cast(ToolItem) widget).setImage(image is null ? null
+ : localManager.createImageWithDefault(image));
+
+ disposeOldImages();
+ imageManager = localManager;
+
+ return image !is null;
+ }
+ ImageDescriptor image = action.getImageDescriptor();
+ ImageDescriptor hoverImage = action.getHoverImageDescriptor();
+ ImageDescriptor disabledImage = action.getDisabledImageDescriptor();
+
+ // If there is no regular image, but there is a hover image,
+ // convert the hover image to gray and use it as the regular image.
+ if (image is null && hoverImage !is null) {
+ image = ImageDescriptor.createWithFlags(action
+ .getHoverImageDescriptor(), DWT.IMAGE_GRAY);
+ } else {
+ // If there is no hover image, use the regular image as the
+ // hover image,
+ // and convert the regular image to gray
+ if (hoverImage is null && image !is null) {
+ hoverImage = image;
+ image = ImageDescriptor.createWithFlags(action
+ .getImageDescriptor(), DWT.IMAGE_GRAY);
+ }
+ }
+
+ // Make sure there is a valid image.
+ if (hoverImage is null && image is null && forceImage) {
+ image = ImageDescriptor.getMissingImageDescriptor();
+ }
+
+ // Create a local resource manager to remember the images we've
+ // allocated for this tool item
+ LocalResourceManager localManager = new LocalResourceManager(
+ parentResourceManager);
+
+ // performance: more efficient in DWT to set disabled and hot image
+ // before regular image
+ (cast(ToolItem) widget).setDisabledImage(disabledImage is null ? null
+ : localManager.createImageWithDefault(disabledImage));
+ (cast(ToolItem) widget).setHotImage(hoverImage is null ? null
+ : localManager.createImageWithDefault(hoverImage));
+ (cast(ToolItem) widget).setImage(image is null ? null : localManager
+ .createImageWithDefault(image));
+
+ // Now that we're no longer referencing the old images, clear them
+ // out.
+ disposeOldImages();
+ imageManager = localManager;
+
+ return image !is null;
+ } else if (cast(Item)widget || cast(Button)widget ) {
+
+ // Use hover image if there is one, otherwise use regular image.
+ ImageDescriptor image = action.getHoverImageDescriptor();
+ if (image is null) {
+ image = action.getImageDescriptor();
+ }
+ // Make sure there is a valid image.
+ if (image is null && forceImage) {
+ image = ImageDescriptor.getMissingImageDescriptor();
+ }
+
+ // Create a local resource manager to remember the images we've
+ // allocated for this widget
+ LocalResourceManager localManager = new LocalResourceManager(
+ parentResourceManager);
+
+ if (cast(Item)widget) {
+ (cast(Item) widget).setImage(image is null ? null : localManager
+ .createImageWithDefault(image));
+ } else if (cast(Button)widget) {
+ (cast(Button) widget).setImage(image is null ? null : localManager
+ .createImageWithDefault(image));
+ }
+
+ // Now that we're no longer referencing the old images, clear them
+ // out.
+ disposeOldImages();
+ imageManager = localManager;
+
+ return image !is null;
+ }
+ return false;
+ }
+
+ /**
+ * Dispose any images allocated for this contribution item
+ */
+ private void disposeOldImages() {
+ if (imageManager !is null) {
+ imageManager.dispose();
+ imageManager = null;
+ }
+ }
+
+ /**
+ * Shorten the given text t
so that its length doesn't exceed
+ * the width of the given ToolItem.The default implementation replaces
+ * characters in the center of the original string with an ellipsis ("...").
+ * Override if you need a different strategy.
+ *
+ * @param textValue
+ * the text to shorten
+ * @param item
+ * the tool item the text belongs to
+ * @return the shortened string
+ *
+ */
+ protected String shortenText(String textValue, ToolItem item) {
+ if (textValue is null) {
+ return null;
+ }
+
+ GC gc = new GC(item.getParent());
+
+ int maxWidth = item.getImage().getBounds().width * 4;
+
+ if (gc.textExtent(textValue).x < maxWidth) {
+ gc.dispose();
+ return textValue;
+ }
+
+ for (int i = textValue.length; i > 0; i--) {
+ String test = textValue.substring(0, i);
+ test = test ~ ellipsis;
+ if (gc.textExtent(test).x < maxWidth) {
+ gc.dispose();
+ return test;
+ }
+
+ }
+ gc.dispose();
+ // If for some reason we fall through abort
+ return textValue;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/ContributionItem.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/ContributionItem.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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 null
if none.
+ */
+ private String id = null;
+
+ /**
+ * Indicates this item is visible in its manager; true
+ * by default.
+ */
+ private bool visible = true;
+
+ /**
+ * The parent contribution manager for this item
+ */
+ private IContributionManager parent;
+
+ /**
+ * Creates a contribution item with a null
id.
+ * Calls this(String)
with null
.
+ */
+ protected this() {
+ this(null);
+ }
+
+ /**
+ * Creates a contribution item with the given (optional) id.
+ * The given id is used to find items in a contribution manager,
+ * and for positioning items relative to other items.
+ *
+ * @param id the contribution item identifier, or null
+ */
+ protected this(String id) {
+ this.id = id;
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method does nothing. Subclasses may override.
+ */
+ public void dispose() {
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method does nothing. Subclasses may override.
+ */
+ public void fill(Composite parent) {
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method does nothing. Subclasses may override.
+ */
+ public void fill(Menu menu, int index) {
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method does nothing. Subclasses may override.
+ */
+ public void fill(ToolBar parent, int index) {
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method does nothing. Subclasses may override.
+ *
+ * @since 3.0
+ */
+ public void fill(CoolBar parent, int index) {
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method does nothing. Subclasses may override.
+ *
+ * @since 3.0
+ */
+ public void saveWidgetState() {
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the parent contribution manager, or null
if this
+ * contribution item is not currently added to a contribution manager.
+ *
+ * @return the parent contribution manager, or null
+ * @since 2.0
+ */
+ public IContributionManager getParent() {
+ return parent;
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method returns false
. Subclasses may override.
+ */
+ public bool isDirty() {
+ // @issue should this be false instead of calling isDynamic()?
+ return isDynamic();
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method returns true
. Subclasses may override.
+ */
+ public bool isEnabled() {
+ return true;
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method returns false
. Subclasses may override.
+ */
+ public bool isDynamic() {
+ return false;
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method returns false
. Subclasses may override.
+ */
+ public bool isGroupMarker() {
+ return false;
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method returns false
. Subclasses may override.
+ */
+ public bool isSeparator() {
+ return false;
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method returns the value recorded in an internal state variable,
+ * which is true
by default. setVisible
+ * should be used to change this setting.
+ */
+ public bool isVisible() {
+ return visible;
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method stores the value in an internal state variable,
+ * which is true
by default.
+ */
+ public void setVisible(bool visible) {
+ this.visible = visible;
+ }
+
+ /**
+ * Returns a string representation of this contribution item
+ * suitable only for debugging.
+ */
+ public String toString() {
+ return this.classinfo.name ~ "(id=" ~ getId() ~ ")";//$NON-NLS-2$//$NON-NLS-1$
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * method does nothing. Subclasses may override.
+ */
+ public void update() {
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public void setParent(IContributionManager parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * The ContributionItem
implementation of this
+ * method declared on IContributionItem
does nothing.
+ * Subclasses should override to update their state.
+ */
+ public void update(String id) {
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/ContributionManager.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/ContributionManager.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,594 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 IContributionManager
. This class provides
+ * functionality common across the specific managers defined by this framework.
+ *
+ * This class maintains a list of contribution items and a dirty flag, both as
+ * internal state. In addition to providing implementations of most
+ * IContributionManager
methods, this class automatically
+ * coalesces adjacent separators, hides beginning and ending separators, and
+ * deals with dynamically changing sets of contributions. When the set of
+ * contributions does change dynamically, the changes are propagated to the
+ * control via the update
method, which subclasses must
+ * implement.
+ *
+ * Note: A ContributionItem
cannot be shared between different
+ * ContributionManager
s.
+ *
true
to add to the end of the group, and
+ * false
to add the beginning of the group
+ * @exception IllegalArgumentException
+ * if there is no group with the given name
+ */
+ private void addToGroup(String groupName, IContributionItem item,
+ bool append) {
+ int i;
+ auto items = contributions.elements();
+ for (i = 0; items.more(); i++) {
+ IContributionItem o = cast(IContributionItem) items.get();
+ if (o.isGroupMarker()) {
+ String id = o.getId();
+ if (id !is null && id.equalsIgnoreCase(groupName)) {
+ i++;
+ if (append) {
+ for (; items.more(); i++) {
+ IContributionItem ci = cast(IContributionItem) items
+ .get();
+ if (ci.isGroupMarker()) {
+ break;
+ }
+ }
+ }
+ if (allowItem(item)) {
+ //TODO: does this corrupt the iterator?
+ contributions.addAt(i, item);
+ itemAdded(item);
+ }
+ return;
+ }
+ }
+ }
+ throw new IllegalArgumentException("Group not found: " ~ groupName);//$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void appendToGroup(String groupName, IAction action) {
+ addToGroup(groupName, new ActionContributionItem(action), true);
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void appendToGroup(String groupName, IContributionItem item) {
+ addToGroup(groupName, item, true);
+ }
+
+ /**
+ * This method allows subclasses of ContributionManager
to
+ * prevent certain items in the contributions list.
+ * ContributionManager
will either block or allow an addition
+ * based on the result of this method call. This can be used to prevent
+ * duplication, for example.
+ *
+ * @param itemToAdd
+ * The contribution item to be added; may be null
.
+ * @return true
if the addition should be allowed;
+ * false
otherwise. The default implementation allows
+ * all items.
+ * @since 3.0
+ */
+ protected bool allowItem(IContributionItem itemToAdd) {
+ return true;
+ }
+
+ /**
+ * Internal debug method for printing statistics about this manager to
+ * System.out
.
+ */
+ protected void dumpStatistics() {
+ int size = 0;
+ if (contributions !is null) {
+ size = contributions.size();
+ }
+
+ Stdout.formatln(this.toString());
+ Stdout.formatln(" Number of elements: {}", size);//$NON-NLS-1$
+ int sum = 0;
+ for (int i = 0; i < size; i++) {
+ if ((cast(IContributionItem) contributions.get(i)).isVisible()) {
+ sum++;
+ }
+ }
+ Stdout.formatln(" Number of visible elements: {}", sum);//$NON-NLS-1$
+ Stdout.formatln(" Is dirty: {}", isDirty()); //$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public IContributionItem find(String id) {
+ auto e = contributions.elements();
+ while (e.more()) {
+ IContributionItem item = cast(IContributionItem) e.get();
+ String itemId = item.getId();
+ if (itemId !is null && itemId.equalsIgnoreCase(id)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public IContributionItem[] getItems() {
+ return contributions.toArray();
+ }
+
+ /**
+ * Return the number of contributions in this manager.
+ *
+ * @return the number of contributions in this manager
+ * @since 3.3
+ */
+ public int getSize() {
+ return contributions.size();
+ }
+
+ /**
+ * The ContributionManager
implementation of this method
+ * declared on IContributionManager
returns the current
+ * overrides. If there is no overrides it lazily creates one which overrides
+ * no item state.
+ *
+ * @since 2.0
+ */
+ public IContributionManagerOverrides getOverrides() {
+ if (overrides is null) {
+ overrides = new class IContributionManagerOverrides {
+ public ValueWrapperBool getEnabled(IContributionItem item) {
+ return null;
+ }
+
+ public ValueWrapperInt getAccelerator(IContributionItem item) {
+ return null;
+ }
+
+ public String getAcceleratorText(IContributionItem item) {
+ return null;
+ }
+
+ public String getText(IContributionItem item) {
+ return null;
+ }
+ };
+ }
+ return overrides;
+ }
+
+ /**
+ * Returns whether this contribution manager contains dynamic items. A
+ * dynamic contribution item contributes items conditionally, dependent on
+ * some internal state.
+ *
+ * @return true
if this manager contains dynamic items, and
+ * false
otherwise
+ */
+ protected bool hasDynamicItems() {
+ return (dynamicItems > 0);
+ }
+
+ /**
+ * Returns the index of the item with the given id.
+ *
+ * @param id
+ * The id of the item whose index is requested.
+ *
+ * @return int
the index or -1 if the item is not found
+ */
+ public int indexOf(String id) {
+ for (int i = 0; i < contributions.size(); i++) {
+ IContributionItem item = cast(IContributionItem) contributions.get(i);
+ String itemId = item.getId();
+ if (itemId !is null && itemId.equalsIgnoreCase(id)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index of the object in the internal structure. This is
+ * different from indexOf(String id)
since some contribution
+ * items may not have an id.
+ *
+ * @param item
+ * The contribution item
+ * @return the index, or -1 if the item is not found
+ * @since 3.0
+ */
+ protected int indexOf(IContributionItem item) {
+ int res = -1;
+ int idx = 0;
+ foreach( e; contributions ){
+ if( e == item ) {
+ res = idx;
+ break;
+ }
+ idx++;
+ }
+ return res;
+ }
+
+ /**
+ * Insert the item at the given index.
+ *
+ * @param index
+ * The index to be used for insertion
+ * @param item
+ * The item to be inserted
+ */
+ public void insert(int index, IContributionItem item) {
+ if (index > contributions.size()) {
+ throw new IndexOutOfBoundsException( Format(
+ "inserting {} at {}", item.getId(), index)); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (allowItem(item)) {
+ contributions.addAt(index, item);
+ itemAdded(item);
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void insertAfter(String ID, IAction action) {
+ insertAfter(ID, new ActionContributionItem(action));
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void insertAfter(String ID, IContributionItem item) {
+ IContributionItem ci = find(ID);
+ if (ci is null) {
+ throw new IllegalArgumentException(Format("can't find ID{}", ID));//$NON-NLS-1$
+ }
+ int ix = SeqIndexOf!(IContributionItem)( contributions, ci );
+ if (ix >= 0) {
+ // System.out.println("insert after: " + ix);
+ if (allowItem(item)) {
+ contributions.addAt(ix + 1, item);
+ itemAdded(item);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void insertBefore(String ID, IAction action) {
+ insertBefore(ID, new ActionContributionItem(action));
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void insertBefore(String ID, IContributionItem item) {
+ IContributionItem ci = find(ID);
+ if (ci is null) {
+ throw new IllegalArgumentException(Format("can't find ID {}", ID));//$NON-NLS-1$
+ }
+ int ix = SeqIndexOf!(IContributionItem)(contributions,ci);
+ if (ix >= 0) {
+ // System.out.println("insert before: " + ix);
+ if (allowItem(item)) {
+ contributions.addAt(ix, item);
+ itemAdded(item);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public bool isDirty() {
+ if (isDirty_) {
+ return true;
+ }
+ if (hasDynamicItems()) {
+ foreach( e; contributions ){
+ IContributionItem item = cast(IContributionItem) e;
+ if (item.isDirty()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public bool isEmpty() {
+ return contributions.drained();
+ }
+
+ /**
+ * The given item was added to the list of contributions. Marks the manager
+ * as dirty and updates the number of dynamic items, and the memento.
+ *
+ * @param item
+ * the item to be added
+ *
+ */
+ protected void itemAdded(IContributionItem item) {
+ item.setParent(this);
+ markDirty();
+ if (item.isDynamic()) {
+ dynamicItems++;
+ }
+ }
+
+ /**
+ * The given item was removed from the list of contributions. Marks the
+ * manager as dirty and updates the number of dynamic items.
+ *
+ * @param item
+ * remove given parent from list of contributions
+ */
+ protected void itemRemoved(IContributionItem item) {
+ item.setParent(null);
+ markDirty();
+ if (item.isDynamic()) {
+ dynamicItems--;
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void markDirty() {
+ setDirty(true);
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void prependToGroup(String groupName, IAction action) {
+ addToGroup(groupName, new ActionContributionItem(action), false);
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void prependToGroup(String groupName, IContributionItem item) {
+ addToGroup(groupName, item, false);
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public IContributionItem remove(String ID) {
+ IContributionItem ci = find(ID);
+ if (ci is null) {
+ return null;
+ }
+ return remove(ci);
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public IContributionItem remove(IContributionItem item) {
+ bool contained = contributions.contains(item);
+ contributions.remove(item);
+ if (contained) {
+ itemRemoved(item);
+ return item;
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IContributionManager.
+ */
+ public void removeAll() {
+ IContributionItem[] items = getItems();
+ contributions.clear();
+ for (int i = 0; i < items.length; i++) {
+ IContributionItem item = items[i];
+ itemRemoved(item);
+ }
+ dynamicItems = 0;
+ markDirty();
+ }
+
+ /**
+ * Replaces the item of the given identifier with another contribution item.
+ * This can be used, for example, to replace large contribution items with
+ * placeholders to avoid memory leaks. If the identifier cannot be found in
+ * the current list of items, then this does nothing. If multiple
+ * occurrences are found, then the replacement items is put in the first
+ * position and the other positions are removed.
+ *
+ * @param identifier
+ * The identifier to look for in the list of contributions;
+ * should not be null
.
+ * @param replacementItem
+ * The contribution item to replace the old item; must not be
+ * null
. Use
+ * {@link dwtx.jface.action.ContributionManager#remove(java.lang.String) remove}
+ * if that is what you want to do.
+ * @return true
if the given identifier can be;
+ * @since 3.0
+ */
+ public bool replaceItem(String identifier,
+ IContributionItem replacementItem) {
+ if (identifier is null) {
+ return false;
+ }
+
+ int index = indexOf(identifier);
+ if (index < 0) {
+ return false; // couldn't find the item.
+ }
+
+ // Remove the old item.
+ IContributionItem oldItem = cast(IContributionItem) contributions
+ .get(index);
+ itemRemoved(oldItem);
+
+ // Add the new item.
+ contributions.replaceAt(index, replacementItem);
+ itemAdded(replacementItem); // throws NPE if (replacementItem is null)
+
+ // Go through and remove duplicates.
+ for (int i = contributions.size() - 1; i > index; i--) {
+ IContributionItem item = cast(IContributionItem) contributions.get(i);
+ if ((item !is null) && (identifier.equals(item.getId()))) {
+ if (Policy.TRACE_TOOLBAR) {
+ Stdout.formatln("Removing duplicate on replace: {}", identifier); //$NON-NLS-1$
+ }
+ contributions.removeAt(i);
+ itemRemoved(item);
+ }
+ }
+
+ return true; // success
+ }
+
+ /**
+ * Sets whether this manager is dirty. When dirty, the list of contributions
+ * is not accurately reflected in the corresponding widgets.
+ *
+ * @param dirty
+ * true
if this manager is dirty, and
+ * false
if it is up-to-date
+ */
+ protected void setDirty(bool dirty) {
+ isDirty_ = dirty;
+ }
+
+ /**
+ * Sets the overrides for this contribution manager
+ *
+ * @param newOverrides
+ * the overrides for the items of this manager
+ * @since 2.0
+ */
+ public void setOverrides(IContributionManagerOverrides newOverrides) {
+ overrides = newOverrides;
+ }
+
+ /**
+ * An internal method for setting the order of the contribution items.
+ *
+ * @param items
+ * the contribution items in the specified order
+ * @since 3.0
+ */
+ protected void internalSetItems(IContributionItem[] items) {
+ contributions.clear();
+ for (int i = 0; i < items.length; i++) {
+ if (allowItem(items[i])) {
+ contributions.append(items[i]);
+ }
+ }
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/ExternalActionManager.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/ExternalActionManager.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,564 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+module dwtx.jface.action.ExternalActionManager;
+
+import dwtx.jface.action.IAction;
+
+import tango.util.collection.HashMap;
+import tango.util.collection.HashSet;
+import tango.util.collection.model.Map;
+import tango.util.collection.model.Set;
+
+import dwtx.core.commands.Command;
+import dwtx.core.commands.CommandEvent;
+import dwtx.core.commands.CommandManager;
+import dwtx.core.commands.ICommandListener;
+import dwtx.core.commands.ParameterizedCommand;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.Status;
+import dwtx.jface.bindings.BindingManager;
+import dwtx.jface.bindings.BindingManagerEvent;
+import dwtx.jface.bindings.IBindingManagerListener;
+import dwtx.jface.bindings.Trigger;
+import dwtx.jface.bindings.TriggerSequence;
+import dwtx.jface.bindings.keys.KeySequence;
+import dwtx.jface.bindings.keys.KeyStroke;
+import dwtx.jface.bindings.keys.SWTKeySupport;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.Policy;
+import dwtx.jface.util.PropertyChangeEvent;
+import dwtx.jface.util.Util;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.ResourceBundle;
+import dwt.dwthelper.Integer;
+import tango.text.convert.Format;
+
+/**
+ *
+ * A manager for a callback facility which is capable of querying external
+ * interfaces for additional information about actions and action contribution
+ * items. This information typically includes things like accelerators and
+ * textual representations.
+ *
+ *
+ * It is only necessary to use this mechanism if you will be using a mix of
+ * actions and commands, and wish the interactions to work properly.
+ *
+ *
+ * For example, in the Eclipse workbench, this mechanism is used to allow the
+ * command architecture to override certain values in action contribution items.
+ *
+ *
+ * This class is not intended to be called or extended by any external clients.
+ *
+ *
+ * @since 3.0
+ */
+public final class ExternalActionManager {
+
+ /**
+ * A simple implementation of the ICallback
mechanism that
+ * simply takes a BindingManager
and a
+ * CommandManager
.
+ *
+ * @since 3.1
+ */
+ public static final class CommandCallback :
+ IBindingManagerListener, IBindingManagerCallback {
+
+ /**
+ * The internationalization bundle for text produced by this class.
+ */
+ private static const ResourceBundle RESOURCE_BUNDLE;
+
+ /**
+ * The callback capable of responding to whether a command is active.
+ */
+ private const IActiveChecker activeChecker;
+
+ /**
+ * The binding manager for your application. Must not be
+ * null
.
+ */
+ private const BindingManager bindingManager;
+
+ /**
+ * Whether a listener has been attached to the binding manager yet.
+ */
+ private bool bindingManagerListenerAttached = false;
+
+ /**
+ * The command manager for your application. Must not be
+ * null
.
+ */
+ private const CommandManager commandManager;
+
+ /**
+ * A set of all the command identifiers that have been logged as broken
+ * so far. For each of these, there will be a listener on the
+ * corresponding command. If the command ever becomes defined, the item
+ * will be removed from this set and the listener removed. This value
+ * may be empty, but never null
.
+ */
+ private const Set!(String) loggedCommandIds;
+
+ /**
+ * The list of listeners that have registered for property change
+ * notification. This is a map of command identifiers (String
)
+ * to listeners (IPropertyChangeListener
).
+ */
+ private const Map!(String,IPropertyChangeListener) registeredListeners;
+
+ static this(){
+ RESOURCE_BUNDLE = ResourceBundle.getBundle(ExternalActionManager.classinfo.name);
+ }
+ /**
+ * Constructs a new instance of CommandCallback
with the
+ * workbench it should be using. All commands will be considered active.
+ *
+ * @param bindingManager
+ * The binding manager which will provide the callback; must
+ * not be null
.
+ * @param commandManager
+ * The command manager which will provide the callback; must
+ * not be null
.
+ *
+ * @since 3.1
+ */
+ public this(BindingManager bindingManager,
+ CommandManager commandManager) {
+ this(bindingManager, commandManager, new class IActiveChecker {
+ public bool isActive(String commandId) {
+ return true;
+ }
+
+ });
+ }
+
+ /**
+ * Constructs a new instance of CommandCallback
with the
+ * workbench it should be using.
+ *
+ * @param bindingManager
+ * The binding manager which will provide the callback; must
+ * not be null
.
+ * @param commandManager
+ * The command manager which will provide the callback; must
+ * not be null
.
+ * @param activeChecker
+ * The callback mechanism for checking whether a command is
+ * active; must not be null
.
+ *
+ * @since 3.1
+ */
+ public this(BindingManager bindingManager,
+ CommandManager commandManager,
+ IActiveChecker activeChecker) {
+ loggedCommandIds = new HashSet!(String);
+ registeredListeners = new HashMap!(String,IPropertyChangeListener);
+ if (bindingManager is null) {
+ throw new NullPointerException(
+ "The callback needs a binding manager"); //$NON-NLS-1$
+ }
+
+ if (commandManager is null) {
+ throw new NullPointerException(
+ "The callback needs a command manager"); //$NON-NLS-1$
+ }
+
+ if (activeChecker is null) {
+ throw new NullPointerException(
+ "The callback needs an active callback"); //$NON-NLS-1$
+ }
+
+ this.activeChecker = activeChecker;
+ this.bindingManager = bindingManager;
+ this.commandManager = commandManager;
+ }
+
+ /**
+ * @see dwtx.jface.action.ExternalActionManager.ICallback#addPropertyChangeListener(String,
+ * IPropertyChangeListener)
+ */
+ public final void addPropertyChangeListener(String commandId,
+ IPropertyChangeListener listener) {
+ registeredListeners.add(commandId, listener);
+ if (!bindingManagerListenerAttached) {
+ bindingManager.addBindingManagerListener(this);
+ bindingManagerListenerAttached = true;
+ }
+ }
+
+ public final void bindingManagerChanged(BindingManagerEvent event) {
+ if (event.isActiveBindingsChanged()) {
+ foreach( k,v; registeredListeners ){
+// Iterator listenerItr = registeredListeners.entrySet()
+// .iterator();
+// while (listenerItr.hasNext()) {
+// Map.Entry entry = cast(Map.Entry) listenerItr.next();
+ String commandId = k;//stringcast(k);// entry.getKey();
+ Command command = commandManager.getCommand(commandId);
+ ParameterizedCommand parameterizedCommand = new ParameterizedCommand(
+ command, null);
+ if (event.isActiveBindingsChangedFor(parameterizedCommand)) {
+ IPropertyChangeListener listener = cast(IPropertyChangeListener) v;
+ listener.propertyChange(new PropertyChangeEvent(event
+ .getManager(), IAction.TEXT, null, null));
+ }
+ }
+ }
+ }
+
+ /**
+ * @see dwtx.jface.action.ExternalActionManager.ICallback#getAccelerator(String)
+ */
+ public ValueWrapperInt getAccelerator(String commandId) {
+ TriggerSequence triggerSequence = bindingManager
+ .getBestActiveBindingFor(commandId);
+ if (triggerSequence !is null) {
+ Trigger[] triggers = triggerSequence.getTriggers();
+ if (triggers.length is 1) {
+ Trigger trigger = triggers[0];
+ if ( auto keyStroke = cast(KeyStroke) trigger ) {
+ int accelerator = SWTKeySupport
+ .convertKeyStrokeToAccelerator(keyStroke);
+ return new ValueWrapperInt(accelerator);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @see dwtx.jface.action.ExternalActionManager.ICallback#getAcceleratorText(String)
+ */
+ public final String getAcceleratorText(String commandId) {
+ TriggerSequence triggerSequence = bindingManager
+ .getBestActiveBindingFor(commandId);
+ if (triggerSequence is null) {
+ return null;
+ }
+
+ return triggerSequence.format();
+ }
+
+ /**
+ * Returns the active bindings for a particular command identifier.
+ *
+ * @param commandId
+ * The identifier of the command whose bindings are
+ * requested. This argument may be null
. It
+ * is assumed that the command has no parameters.
+ * @return The array of active triggers (TriggerSequence
)
+ * for a particular command identifier. This value is guaranteed
+ * not to be null
, but it may be empty.
+ * @since 3.2
+ */
+ public final TriggerSequence[] getActiveBindingsFor(
+ String commandId) {
+ return bindingManager.getActiveBindingsFor(commandId);
+ }
+
+ /**
+ * @see dwtx.jface.action.ExternalActionManager.ICallback#isAcceleratorInUse(int)
+ */
+ public final bool isAcceleratorInUse(int accelerator) {
+ KeySequence keySequence = KeySequence
+ .getInstance(SWTKeySupport
+ .convertAcceleratorToKeyStroke(accelerator));
+ return bindingManager.isPerfectMatch(keySequence)
+ || bindingManager.isPartialMatch(keySequence);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Calling this method with an undefined command id will generate a log
+ * message.
+ */
+ public final bool isActive(String commandId) {
+ if (commandId !is null) {
+ Command command = commandManager.getCommand(commandId);
+
+ if (!command.isDefined()
+ && (!loggedCommandIds.contains(commandId))) {
+ // The command is not yet defined, so we should log this.
+ String message = Format(Util
+ .translateString(RESOURCE_BUNDLE,
+ "undefinedCommand.WarningMessage", null), //$NON-NLS-1$
+ [ command.getId() ]);
+ IStatus status = new Status(IStatus.ERROR,
+ "dwtx.jface", //$NON-NLS-1$
+ 0, message, new Exception(null));
+ Policy.getLog().log(status);
+
+ // And remember this item so we don't log it again.
+ loggedCommandIds.add(commandId);
+ command.addCommandListener(new class ICommandListener {
+ Command command_;
+ String commandId_;
+ this(){
+ command_=command;
+ commandId_=commandId;
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.ui.commands.ICommandListener#commandChanged(dwtx.ui.commands.CommandEvent)
+ */
+ public final void commandChanged(
+ CommandEvent commandEvent) {
+ if (command_.isDefined()) {
+ command_.removeCommandListener(this);
+ loggedCommandIds.remove(commandId_);
+ }
+ }
+ });
+
+ return true;
+ }
+
+ return activeChecker.isActive(commandId);
+ }
+
+ return true;
+ }
+
+ /**
+ * @see dwtx.jface.action.ExternalActionManager.ICallback#removePropertyChangeListener(String,
+ * IPropertyChangeListener)
+ */
+ public final void removePropertyChangeListener(String commandId,
+ IPropertyChangeListener listener) {
+ IPropertyChangeListener existingListener = cast(IPropertyChangeListener) registeredListeners
+ .get(commandId);
+ if (existingListener is listener) {
+ registeredListeners.removeKey(commandId);
+ if (registeredListeners.drained()) {
+ bindingManager.removeBindingManagerListener(this);
+ bindingManagerListenerAttached = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Defines a callback mechanism for developer who wish to further control
+ * the visibility of legacy action-based contribution items.
+ *
+ * @since 3.1
+ */
+ public static interface IActiveChecker {
+ /**
+ * Checks whether the command with the given identifier should be
+ * considered active. This can be used in systems using some kind of
+ * user interface filtering (e.g., activities in the Eclipse workbench).
+ *
+ * @param commandId
+ * The identifier for the command; must not be
+ * null
+ * @return true
if the command is active;
+ * false
otherwise.
+ */
+ public bool isActive(String commandId);
+ }
+
+ /**
+ *
+ * A callback which communicates with the applications binding manager. This
+ * interface provides more information from the binding manager, which
+ * allows greater integration. Implementing this interface is preferred over
+ * {@link ExternalActionManager.ICallback}.
+ *
+ *
+ * Clients may implement this interface, but must not extend.
+ *
+ *
+ * @since 3.2
+ */
+ public static interface IBindingManagerCallback : ICallback {
+
+ /**
+ *
+ * Returns the active bindings for a particular command identifier.
+ *
+ *
+ * @param commandId
+ * The identifier of the command whose bindings are
+ * requested. This argument may be null
. It
+ * is assumed that the command has no parameters.
+ * @return The array of active triggers (TriggerSequence
)
+ * for a particular command identifier. This value is guaranteed
+ * not to be null
, but it may be empty.
+ */
+ public TriggerSequence[] getActiveBindingsFor(String commandId);
+ }
+
+ /**
+ * A callback mechanism for some external tool to communicate extra
+ * information to actions and action contribution items.
+ *
+ * @since 3.0
+ */
+ public static interface ICallback {
+
+ /**
+ *
+ * Adds a listener to the object referenced by identifier
.
+ * This listener will be notified if a property of the item is to be
+ * changed. This identifier is specific to mechanism being used. In the
+ * case of the Eclipse workbench, this is the command identifier.
+ *
+ *
+ * A single instance of the listener may only ever be associated with
+ * one identifier. Attempts to add the listener twice (without a removal
+ * in between) has undefined behaviour.
+ *
+ *
+ * @param identifier
+ * The identifier of the item to which the listener should be
+ * attached; must not be null
.
+ * @param listener
+ * The listener to be added; must not be null
.
+ */
+ public void addPropertyChangeListener(String identifier,
+ IPropertyChangeListener listener);
+
+ /**
+ * An accessor for the accelerator associated with the item indicated by
+ * the identifier. This identifier is specific to mechanism being used.
+ * In the case of the Eclipse workbench, this is the command identifier.
+ *
+ * @param identifier
+ * The identifier of the item from which the accelerator
+ * should be obtained ; must not be null
.
+ * @return An integer representation of the accelerator. This is the
+ * same accelerator format used by DWT.
+ */
+ public Integer getAccelerator(String identifier);
+
+ /**
+ * An accessor for the accelerator text associated with the item
+ * indicated by the identifier. This identifier is specific to mechanism
+ * being used. In the case of the Eclipse workbench, this is the command
+ * identifier.
+ *
+ * @param identifier
+ * The identifier of the item from which the accelerator text
+ * should be obtained ; must not be null
.
+ * @return A string representation of the accelerator. This is the
+ * string representation that should be displayed to the user.
+ */
+ public String getAcceleratorText(String identifier);
+
+ /**
+ * Checks to see whether the given accelerator is being used by some
+ * other mechanism (outside of the menus controlled by JFace). This is
+ * used to keep JFace from trying to grab accelerators away from someone
+ * else.
+ *
+ * @param accelerator
+ * The accelerator to check -- in DWT's internal accelerator
+ * format.
+ * @return true
if the accelerator is already being used
+ * and shouldn't be used again; false
otherwise.
+ */
+ public bool isAcceleratorInUse(int accelerator);
+
+ /**
+ * Checks whether the item matching this identifier is active. This is
+ * used to decide whether a contribution item with this identifier
+ * should be made visible. An inactive item is not visible.
+ *
+ * @param identifier
+ * The identifier of the item from which the active state
+ * should be retrieved; must not be null
.
+ * @return true
if the item is active; false
+ * otherwise.
+ */
+ public bool isActive(String identifier);
+
+ /**
+ * Removes a listener from the object referenced by
+ * identifier
. This identifier is specific to mechanism
+ * being used. In the case of the Eclipse workbench, this is the command
+ * identifier.
+ *
+ * @param identifier
+ * The identifier of the item to from the listener should be
+ * removed; must not be null
.
+ * @param listener
+ * The listener to be removed; must not be null
.
+ */
+ public void removePropertyChangeListener(String identifier,
+ IPropertyChangeListener listener);
+ }
+
+ /**
+ * The singleton instance of this class. This value may be null
--
+ * if it has not yet been initialized.
+ */
+ private static ExternalActionManager instance;
+
+ /**
+ * Retrieves the current singleton instance of this class.
+ *
+ * @return The singleton instance; this value is never null
.
+ */
+ public static ExternalActionManager getInstance() {
+ if (instance is null) {
+ instance = new ExternalActionManager();
+ }
+
+ return instance;
+ }
+
+ /**
+ * The callback mechanism to use to retrieve extra information.
+ */
+ private ICallback callback;
+
+ /**
+ * Constructs a new instance of ExternalActionManager
.
+ */
+ private this() {
+ // This is a singleton class. Only this class should create an instance.
+ }
+
+ /**
+ * An accessor for the current call back.
+ *
+ * @return The current callback mechanism being used. This is the callback
+ * that should be queried for extra information about actions and
+ * action contribution items. This value may be null
+ * if there is no extra information.
+ */
+ public ICallback getCallback() {
+ return callback;
+ }
+
+ /**
+ * A mutator for the current call back
+ *
+ * @param callbackToUse
+ * The new callback mechanism to use; this value may be
+ * null
if the default is acceptable (i.e., no
+ * extra information will provided to actions).
+ */
+ public void setCallback(ICallback callbackToUse) {
+ callback = callbackToUse;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/GroupMarker.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/GroupMarker.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwtx.jface.action.GroupMarker;
+
+import dwtx.jface.action.AbstractGroupMarker;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A group marker is a special kind of contribution item denoting
+ * the beginning of a group. These groups are used to structure
+ * the list of items. Unlike regular contribution items and
+ * separators, group markers have no visual representation.
+ * The name of the group is synonymous with the contribution item id.
+ *
+ * This class may be instantiated; it is not intended to be
+ * subclassed outside the framework.
+ *
+ */
+public class GroupMarker : AbstractGroupMarker {
+ /**
+ * Create a new group marker with the given name.
+ * The group name must not be null
or the empty string.
+ * The group name is also used as the item id.
+ *
+ * @param groupName the name of the group
+ */
+ public this(String groupName) {
+ super(groupName);
+ }
+
+ /**
+ * The GroupMarker
implementation of this method
+ * returns false
since group markers are always invisible.
+ */
+ public bool isVisible() {
+ return false;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/IAction.d
--- a/dwtx/jface/action/IAction.d Mon Mar 31 02:00:41 2008 +0200
+++ b/dwtx/jface/action/IAction.d Tue Apr 01 08:00:31 2008 +0200
@@ -57,25 +57,25 @@
*
* @since 2.1
*/
- public static int AS_UNSPECIFIED = 0x00;
+ public static const int AS_UNSPECIFIED = 0x00;
/**
* Action style constant (value 1
) indicating action is
* a simple push button.
*/
- public static int AS_PUSH_BUTTON = 0x01;
+ public static const int AS_PUSH_BUTTON = 0x01;
/**
* Action style constant (value 2
) indicating action is
* a check box (or a toggle button).
*/
- public static int AS_CHECK_BOX = 0x02;
+ public static const int AS_CHECK_BOX = 0x02;
/**
* Action style constant (value 4
) indicating action is
* a drop down menu.
*/
- public static int AS_DROP_DOWN_MENU = 0x04;
+ public static const int AS_DROP_DOWN_MENU = 0x04;
/**
* Action style constant (value 8
) indicating action is
@@ -83,7 +83,7 @@
*
* @since 2.1
*/
- public static int AS_RADIO_BUTTON = 0x08;
+ public static const int AS_RADIO_BUTTON = 0x08;
/**
* Property name of an action's text (value "text"
).
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/IContributionItem.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/IContributionItem.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ *******************************************************************************/
+
+module dwtx.jface.action.IContributionItem;
+
+import dwtx.jface.action.IContributionManager;
+
+import dwt.widgets.Composite;
+import dwt.widgets.CoolBar;
+import dwt.widgets.Menu;
+import dwt.widgets.ToolBar;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A contribution item represents a contribution to a shared UI resource such as a
+ * menu or tool bar. More generally, contribution items are managed by a contribution
+ * manager.
+ * For instance, in a tool bar a contribution item is a tool bar button or a separator.
+ * In a menu bar a contribution item is a menu, and in a menu a contribution item
+ * is a menu item or separator.
+ *
+ * A contribution item can realize itself in different DWT widgets, using the different
+ * fill
methods. The same type of contribution item can be used with a
+ * MenuBarManager
, ToolBarManager
, CoolBarManager
,
+ *
or a StatusLineManager
.
+ *
+ * + * This interface is internal to the framework; it should not be implemented outside + * the framework. + *
+ * + * @see IContributionManager + */ +public interface IContributionItem { + + /** + * Disposes of this contribution item. Called by the parent + * contribution manager when the manager is being disposed. + * Clients should not call this method directly, unless they + * have removed this contribution item from the containing + * IContributionManager before the contribution lifecycle + * has ended. + * + * @since 2.1 + */ + public void dispose(); + + /** + * Fills the given composite control with controls representing this + * contribution item. Used byStatusLineManager
.
+ *
+ * @param parent the parent control
+ */
+ public void fill(Composite parent);
+
+ /**
+ * Fills the given menu with controls representing this contribution item.
+ * Used by MenuManager
.
+ *
+ * @param parent the parent menu
+ * @param index the index where the controls are inserted,
+ * or -1
to insert at the end
+ */
+ public void fill(Menu parent, int index);
+
+ /**
+ * Fills the given tool bar with controls representing this contribution item.
+ * Used by ToolBarManager
.
+ *
+ * @param parent the parent tool bar
+ * @param index the index where the controls are inserted,
+ * or -1
to insert at the end
+ */
+ public void fill(ToolBar parent, int index);
+
+ /**
+ * Fills the given cool bar with controls representing this contribution item.
+ * Used by CoolBarManager
.
+ *
+ * @param parent the parent cool bar
+ * @param index the index where the controls are inserted,
+ * or -1
to insert at the end
+ * @since 3.0
+ */
+ public void fill(CoolBar parent, int index);
+
+ /**
+ * Returns the identifier of this contribution item.
+ * The id is used for retrieving an item from its manager.
+ *
+ * @return the contribution item identifier, or null
+ * if none
+ */
+ public String getId();
+
+ /**
+ * Returns whether this contribution item is enabled.
+ *
+ * @return true
if this item is enabled
+ */
+ public bool isEnabled();
+
+ /**
+ * Returns whether this contribution item is dirty. A dirty item will be
+ * recreated when the action bar is updated.
+ *
+ * @return true
if this item is dirty
+ */
+ public bool isDirty();
+
+ /**
+ * Returns whether this contribution item is dynamic. A dynamic contribution
+ * item contributes items conditionally, dependent on some internal state.
+ *
+ * @return true
if this item is dynamic, and
+ * false
for normal items
+ */
+ public bool isDynamic();
+
+ /**
+ * Returns whether this contribution item is a group marker.
+ * This information is used when adding items to a group.
+ *
+ * @return true
if this item is a group marker, and
+ * false
for normal items
+ *
+ * @see GroupMarker
+ * @see IContributionManager#appendToGroup(String, IContributionItem)
+ * @see IContributionManager#prependToGroup(String, IContributionItem)
+ */
+ public bool isGroupMarker();
+
+ /**
+ * Returns whether this contribution item is a separator.
+ * This information is used to enable hiding of unnecessary separators.
+ *
+ * @return true
if this item is a separator, and
+ * false
for normal items
+ * @see Separator
+ */
+ public bool isSeparator();
+
+ /**
+ * Returns whether this contribution item is visibile within its manager.
+ *
+ * @return true
if this item is visible, and
+ * false
otherwise
+ */
+ public bool isVisible();
+
+ /**
+ * Saves any state information of the control(s) owned by this contribution item.
+ * The contribution manager calls this method before disposing of the controls.
+ *
+ * @since 3.0
+ */
+ public void saveWidgetState();
+
+ /**
+ * Sets the parent manager of this item
+ *
+ * @param parent the parent contribution manager
+ * @since 2.0
+ */
+ public void setParent(IContributionManager parent);
+
+ /**
+ * Sets whether this contribution item is visibile within its manager.
+ *
+ * @param visible true
if this item should be visible, and
+ * false
otherwise
+ */
+ public void setVisible(bool visible);
+
+ /**
+ * Updates any DWT controls cached by this contribution item with any
+ * changes which have been made to this contribution item since the last update.
+ * Called by contribution manager update methods.
+ */
+ public void update();
+
+ /**
+ * Updates any DWT controls cached by this contribution item with changes
+ * for the the given property.
+ *
+ * @param id the id of the changed property
+ * @since 2.0
+ */
+ public void update(String id);
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/IContributionManager.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/IContributionManager.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * 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 + * A contribution manager keeps track of a list of contribution + * items. Each contribution item may has an optional identifier, which can be used + * to retrieve items from a manager, and for positioning items relative to + * each other. The list of contribution items can be subdivided into named groups + * using special contribution items that serve as group markers. + *
+ *
+ * The IContributionManager
interface provides general
+ * protocol for adding, removing, and retrieving contribution items.
+ * It also provides convenience methods that make it convenient
+ * to contribute actions. This interface should be implemented
+ * by all objects that wish to manage contributions.
+ *
+ * There are several implementions of this interface in this package,
+ * including ones for menus ({@link MenuManager MenuManager
}),
+ * tool bars ({@link ToolBarManager ToolBarManager
}),
+ * and status lines ({@link StatusLineManager StatusLineManager
}).
+ *
add(new ActionContributionItem(action))
.
+ *
+ * @param action the action
+ */
+ public void add(IAction action);
+
+ /**
+ * Adds a contribution item to this manager.
+ *
+ * @param item the contribution item
+ */
+ public void add(IContributionItem item);
+
+ /**
+ * Adds a contribution item for the given action at the end of the group
+ * with the given name.
+ * Equivalent to
+ * appendToGroup(groupName,new ActionContributionItem(action))
.
+ *
+ * @param groupName the name of the group
+ * @param action the action
+ * @exception IllegalArgumentException if there is no group with
+ * the given name
+ */
+ public void appendToGroup(String groupName, IAction action);
+
+ /**
+ * Adds a contribution item to this manager at the end of the group
+ * with the given name.
+ *
+ * @param groupName the name of the group
+ * @param item the contribution item
+ * @exception IllegalArgumentException if there is no group with
+ * the given name
+ */
+ public void appendToGroup(String groupName, IContributionItem item);
+
+ /**
+ * Finds the contribution item with the given id.
+ *
+ * @param id the contribution item id
+ * @return the contribution item, or null
if
+ * no item with the given id can be found
+ */
+ public IContributionItem find(String id);
+
+ /**
+ * Returns all contribution items known to this manager.
+ *
+ * @return a list of contribution items
+ */
+ public IContributionItem[] getItems();
+
+ /**
+ * Returns the overrides for the items of this manager.
+ *
+ * @return the overrides for the items of this manager
+ * @since 2.0
+ */
+ public IContributionManagerOverrides getOverrides();
+
+ /**
+ * Inserts a contribution item for the given action after the item
+ * with the given id.
+ * Equivalent to
+ * insertAfter(id,new ActionContributionItem(action))
.
+ *
+ * @param id the contribution item id
+ * @param action the action to insert
+ * @exception IllegalArgumentException if there is no item with
+ * the given id
+ */
+ public void insertAfter(String id, IAction action);
+
+ /**
+ * Inserts a contribution item after the item with the given id.
+ *
+ * @param id the contribution item id
+ * @param item the contribution item to insert
+ * @exception IllegalArgumentException if there is no item with
+ * the given id
+ */
+ public void insertAfter(String id, IContributionItem item);
+
+ /**
+ * Inserts a contribution item for the given action before the item
+ * with the given id.
+ * Equivalent to
+ * insertBefore(id,new ActionContributionItem(action))
.
+ *
+ * @param id the contribution item id
+ * @param action the action to insert
+ * @exception IllegalArgumentException if there is no item with
+ * the given id
+ */
+ public void insertBefore(String id, IAction action);
+
+ /**
+ * Inserts a contribution item before the item with the given id.
+ *
+ * @param id the contribution item id
+ * @param item the contribution item to insert
+ * @exception IllegalArgumentException if there is no item with
+ * the given id
+ */
+ public void insertBefore(String id, IContributionItem item);
+
+ /**
+ * Returns whether the list of contributions has recently changed and
+ * has yet to be reflected in the corresponding widgets.
+ *
+ * @return true
if this manager is dirty, and false
+ * if it is up-to-date
+ */
+ public bool isDirty();
+
+ /**
+ * Returns whether this manager has any contribution items.
+ *
+ * @return true
if there are no items, and
+ * false
otherwise
+ */
+ public bool isEmpty();
+
+ /**
+ * Marks this contribution manager as dirty.
+ */
+ public void markDirty();
+
+ /**
+ * Adds a contribution item for the given action at the beginning of the
+ * group with the given name.
+ * Equivalent to
+ * prependToGroup(groupName,new ActionContributionItem(action))
.
+ *
+ * @param groupName the name of the group
+ * @param action the action
+ * @exception IllegalArgumentException if there is no group with
+ * the given name
+ */
+ public void prependToGroup(String groupName, IAction action);
+
+ /**
+ * Adds a contribution item to this manager at the beginning of the
+ * group with the given name.
+ *
+ * @param groupName the name of the group
+ * @param item the contribution item
+ * @exception IllegalArgumentException if there is no group with
+ * the given name
+ */
+ public void prependToGroup(String groupName, IContributionItem item);
+
+ /**
+ * Removes and returns the contribution item with the given id from this manager.
+ * Returns null
if this manager has no contribution items
+ * with the given id.
+ *
+ * @param id the contribution item id
+ * @return the item that was found and removed, or null
if none
+ */
+ public IContributionItem remove(String id);
+
+ /**
+ * Removes the given contribution item from the contribution items
+ * known to this manager.
+ *
+ * @param item the contribution item
+ * @return the item
parameter if the item was removed,
+ * and null
if it was not found
+ */
+ public IContributionItem remove(IContributionItem item);
+
+ /**
+ * Removes all contribution items from this manager.
+ */
+ public void removeAll();
+
+ /**
+ * Updates this manager's underlying widget(s) with any changes which
+ * have been made to it or its items. Normally changes to a contribution
+ * manager merely mark it as dirty, without updating the underlying widgets.
+ * This brings the underlying widgets up to date with any changes.
+ *
+ * @param force true
means update even if not dirty,
+ * and false
for normal incremental updating
+ */
+ public void update(bool force);
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/IContributionManagerOverrides.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/IContributionManagerOverrides.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * 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 IContributionItem
+ * to determine if the values for certain properties have been overriden
+ * by their manager.
+ * + * This interface is internal to the framework; it should not be implemented outside + * the framework. + *
+ * + * @since 2.0 + */ +public interface IContributionManagerOverrides { + /** + * Id for the enabled property. Value is"enabled"
.
+ *
+ * @since 2.0
+ */
+ public const static String P_ENABLED = "enabled"; //$NON-NLS-1$
+
+ /**
+ * Find out the enablement of the item
+ * @param item the contribution item for which the enable override value is
+ * determined
+ * @return bool.TRUE
if the given contribution item should be enabledbool.FALSE
if the item should be disablednull
if the item may determine its own enablementIMenuManager
interface provides protocol for managing
+ * contributions to a menu bar and its sub menus.
+ * An IMenuManager
is also an IContributionItem
,
+ * allowing sub-menus to be nested in parent menus.
+ * + * This interface is internal to the framework; it should not be implemented outside + * the framework. + *
+ *
+ * This package provides a concrete menu manager implementation,
+ * {@link MenuManager MenuManager
}.
+ *
'/'
.
+ *
+ * Convenience for findUsingPath(path)
which
+ * extracts an IMenuManager
if possible.
+ *
null
+ * if there is no such contribution item or if the item does
+ * not have an associated menu manager
+ */
+ public IMenuManager findMenuUsingPath(String path);
+
+ /**
+ * Finds the contribution item at the given path. A path
+ * consists of contribution item ids separated by the separator
+ * character. The path separator character is '/'
.
+ *
+ * @param path the path string
+ * @return the contribution item, or null
if there is no
+ * such contribution item
+ */
+ public IContributionItem findUsingPath(String path);
+
+ /**
+ * Returns whether all items should be removed when the menu is about to
+ * show, but before notifying menu listeners. The default is
+ * false
.
+ *
+ * @return true
if all items should be removed when shown,
+ * false
if not
+ */
+ public bool getRemoveAllWhenShown();
+
+ /**
+ * Returns whether this menu should be enabled or not.
+ *
+ * @return true
if enabled, and
+ * false
if disabled
+ */
+ public bool isEnabled();
+
+ /**
+ * Removes the given menu listener from this menu.
+ * Has no effect if an identical listener is not registered.
+ *
+ * @param listener the menu listener
+ */
+ public void removeMenuListener(IMenuListener listener);
+
+ /**
+ * Sets whether all items should be removed when the menu is about to show,
+ * but before notifying menu listeners.
+ *
+ * @param removeAll
+ * true
if all items should be removed when shown,
+ * false
if not
+ */
+ public void setRemoveAllWhenShown(bool removeAll);
+
+ /**
+ * Incrementally builds the menu from the contribution items, and
+ * does so recursively for all submenus.
+ *
+ * @param force true
means update even if not dirty,
+ * and false
for normal incremental updating
+ */
+ public void updateAll(bool force);
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/LegacyActionTools.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/LegacyActionTools.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,746 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 + * Some static utility methods for handling labels on actions. This includes + * mnemonics and accelerators. + *
+ *+ * Clients may neither instantiate this class nor extend. + *
+ * + * @since 3.2 + */ +public final class LegacyActionTools { + + /** + * Table of key codes (key type:String
, value type:
+ * Integer
); null
if not yet initialized.
+ *
+ * @see #findKeyCode
+ */
+ private static Map!(String,Object) keyCodes = null;
+
+ /**
+ * Table of string representations of keys (key type: Integer
,
+ * value type: String
); null
> if not yet
+ * initialized.
+ *
+ * @see #findKeyString
+ */
+ private static Map!(Object,String) keyStrings = null;
+
+ /**
+ * The localized uppercase version of ALT
+ */
+ private static String localizedAlt;
+
+ /**
+ * The localized uppercase version of COMMAND
+ */
+ private static String localizedCommand;
+
+ /**
+ * The localized uppercase version of CTRL
+ */
+ private static String localizedCtrl;
+
+ /**
+ * Table of key codes (key type: String
, value type:
+ * Integer
); null
if not yet initialized. The
+ * key is the localalized name of the key as it appears in menus.
+ *
+ * @see #findLocalizedKeyCode
+ */
+ private static Map!(String,Object) localizedKeyCodes = null;
+
+ /**
+ * The localized uppercase version of SHIFT
+ */
+ private static String localizedShift;
+
+ /**
+ * The constant to use if there is no mnemonic for this location.
+ */
+ public static const char MNEMONIC_NONE = 0;
+
+ /**
+ * Converts an accelerator key code to a string representation.
+ *
+ * @param keyCode
+ * the key code to be translated
+ * @return a string representation of the key code
+ */
+ public static final String convertAccelerator(int keyCode) {
+ String modifier = getModifierString(keyCode);
+ String fullKey;
+ if (modifier.equals("")) { //$NON-NLS-1$
+ fullKey = findKeyString(keyCode);
+ } else {
+ fullKey = modifier ~ "+" ~ findKeyString(keyCode); //$NON-NLS-1$
+ }
+ return fullKey;
+ }
+
+ /**
+ * Parses the given accelerator text, and converts it to an accelerator key
+ * code.
+ *
+ * @param acceleratorText
+ * the accelerator text
+ * @return the DWT key code, or 0 if there is no accelerator
+ */
+ public static final int convertAccelerator(String acceleratorText) {
+ int accelerator = 0;
+ auto tokens = tango.text.Util.delimit( acceleratorText, "+" );
+
+ int keyCode = -1;
+ foreach( idx, token; tokens ){
+// bool hasMoreTokens = stok.hasMoreTokens();
+// while (hasMoreTokens) {
+// String token = stok.nextToken();
+// bool hasMoreTokens = idx+1 < tokens.length;//stok.hasMoreTokens();
+ // Every token except the last must be one of the modifiers
+ // Ctrl, Shift, Alt, or Command
+ if (idx+1 < tokens.length) {
+ int modifier = findModifier(token);
+ if (modifier !is 0) {
+ accelerator |= modifier;
+ } else { // Leave if there are none
+ return 0;
+ }
+ } else {
+ keyCode = findKeyCode(token);
+ }
+ }
+ if (keyCode !is -1) {
+ accelerator |= keyCode;
+ }
+ return accelerator;
+ }
+
+ /**
+ * Parses the given accelerator text, and converts it to an accelerator key
+ * code.
+ *
+ * Support for localized modifiers is for backwards compatibility with 1.0.
+ * Use setAccelerator(int) to set accelerators programatically or the
+ * accelerator
tag in action definitions in plugin.xml.
+ *
+ * @param acceleratorText
+ * the accelerator text localized to the current locale
+ * @return the DWT key code, or 0 if there is no accelerator
+ */
+ static final int convertLocalizedAccelerator(String acceleratorText) {
+ int accelerator = 0;
+ auto tokens = tango.text.Util.delimit( acceleratorText, "+" );
+// StringTokenizer stok = new StringTokenizer(acceleratorText, "+"); //$NON-NLS-1$
+
+ int keyCode = -1;
+
+ foreach( idx, token; tokens ){
+// bool hasMoreTokens = stok.hasMoreTokens();
+// while (hasMoreTokens) {
+// String token = stok.nextToken();
+// hasMoreTokens = stok.hasMoreTokens();
+ // Every token except the last must be one of the modifiers
+ // Ctrl, Shift, Alt, or Command
+ if (idx+1 < tokens.length) {
+ int modifier = findLocalizedModifier(token);
+ if (modifier !is 0) {
+ accelerator |= modifier;
+ } else { // Leave if there are none
+ return 0;
+ }
+ } else {
+ keyCode = findLocalizedKeyCode(token);
+ }
+ }
+ if (keyCode !is -1) {
+ accelerator |= keyCode;
+ }
+ return accelerator;
+ }
+
+ /**
+ * Extracts the accelerator text from the given text. Returns
+ * null
if there is no accelerator text, and the empty string
+ * if there is no text after the accelerator delimeter (tab or '@').
+ *
+ * @param text
+ * the text for the action; may be null
.
+ * @return the accelerator text, or null
+ */
+ public static final String extractAcceleratorText(String text) {
+ if (text is null) {
+ return null;
+ }
+
+ int index = text.lastIndexOf('\t');
+ if (index is -1) {
+ index = text.lastIndexOf('@');
+ }
+ if (index >= 0) {
+ return text.substring(index + 1);
+ }
+ return null;
+ }
+
+ /**
+ * Extracts the mnemonic text from the given string.
+ *
+ * @param text
+ * The text from which the mnemonic should be extracted; may be
+ * null
+ * @return The text of the mnemonic; will be {@link #MNEMONIC_NONE} if there
+ * is no mnemonic;
+ */
+ public static final char extractMnemonic(String text) {
+ if (text is null) {
+ return MNEMONIC_NONE;
+ }
+
+ int index = text.indexOf('&');
+ if (index is -1) {
+ return MNEMONIC_NONE;
+ }
+
+ int textLength = text.length;
+
+ // Ignore '&' at the end of the string.
+ if (index is textLength - 1) {
+ return MNEMONIC_NONE;
+ }
+
+ // Ignore two consecutive ampersands.
+ while (text.charAt(index + 1) is '&') {
+ index = text.indexOf('&', ++index);
+ if (index is textLength - 1) {
+ return MNEMONIC_NONE;
+ }
+ }
+
+ return text.charAt(index + 1);
+ }
+
+ /**
+ * Maps a standard keyboard key name to an DWT key code. Key names are
+ * converted to upper case before comparison. If the key name is a single
+ * letter, for example "S", its character code is returned.
+ * + * The following key names are known (case is ignored): + *
"BACKSPACE"
"TAB"
"RETURN"
"ENTER"
"ESC"
"ESCAPE"
"DELETE"
"SPACE"
"ARROW_UP"
, "ARROW_DOWN"
,
+ * "ARROW_LEFT"
, and "ARROW_RIGHT"
"PAGE_UP"
and "PAGE_DOWN"
"HOME"
"END"
"INSERT"
"F1"
, "F2"
through "F12"
-1
if no match was found
+ * @see DWT
+ */
+ public static final int findKeyCode(String token) {
+ if (keyCodes is null) {
+ initKeyCodes();
+ }
+ token = token.toUpperCase();
+ Integer i = cast(Integer) keyCodes.get(token);
+ if (i !is null) {
+ return i.intValue();
+ }
+ if (token.length is 1) {
+ return token.charAt(0);
+ }
+ return -1;
+ }
+
+ /**
+ * Maps an DWT key code to a standard keyboard key name. The key code is
+ * stripped of modifiers (DWT.CTRL, DWT.ALT, DWT.SHIFT, and DWT.COMMAND). If
+ * the key code is not an DWT code (for example if it a key code for the key
+ * 'S'), a string containing a character representation of the key code is
+ * returned.
+ *
+ * @param keyCode
+ * the key code to be translated
+ * @return the string representation of the key code
+ * @see DWT
+ * @since 2.0
+ */
+ public static final String findKeyString( int keyCode) {
+ if (keyStrings is null) {
+ initKeyStrings();
+ }
+ int i = keyCode & ~(DWT.CTRL | DWT.ALT | DWT.SHIFT | DWT.COMMAND);
+ Integer integer = new Integer(i);
+ String result = keyStrings.get(integer);
+ if (result !is null) {
+ return result;
+ }
+ result = dcharToString( cast(dchar) i );
+ return result;
+ }
+
+ /**
+ * Find the supplied code for a localized key. As #findKeyCode but localized
+ * to the current locale.
+ *
+ * Support for localized modifiers is for backwards compatibility with 1.0.
+ * Use setAccelerator(int) to set accelerators programatically or the
+ * accelerator
tag in action definitions in plugin.xml.
+ *
+ * @param token
+ * the localized key name
+ * @return the DWT key code, -1
if no match was found
+ * @see #findKeyCode
+ */
+ private static final int findLocalizedKeyCode(String token) {
+ if (localizedKeyCodes is null) {
+ initLocalizedKeyCodes();
+ }
+ token = token.toUpperCase();
+ Integer i = cast(Integer) localizedKeyCodes.get(token);
+ if (i !is null) {
+ return i.intValue();
+ }
+ if (token.length is 1) {
+ return token.charAt(0);
+ }
+ return -1;
+ }
+
+ /**
+ * Maps the localized modifier names to a code in the same manner as
+ * #findModifier.
+ *
+ * Support for localized modifiers is for backwards compatibility with 1.0.
+ * Use setAccelerator(int) to set accelerators programatically or the
+ * accelerator
tag in action definitions in plugin.xml.
+ *
+ * @see #findModifier
+ */
+ private static final int findLocalizedModifier(String token) {
+ if (localizedCtrl is null) {
+ initLocalizedModifiers();
+ }
+
+ token = token.toUpperCase();
+ if (token.equals(localizedCtrl)) {
+ return DWT.CTRL;
+ }
+ if (token.equals(localizedShift)) {
+ return DWT.SHIFT;
+ }
+ if (token.equals(localizedAlt)) {
+ return DWT.ALT;
+ }
+ if (token.equals(localizedCommand)) {
+ return DWT.COMMAND;
+ }
+ return 0;
+ }
+
+ /**
+ * Maps standard keyboard modifier key names to the corresponding DWT
+ * modifier bit. The following modifier key names are recognized (case is
+ * ignored): "CTRL"
, "SHIFT"
,
+ * "ALT"
, and "COMMAND"
. The given modifier
+ * key name is converted to upper case before comparison.
+ *
+ * @param token
+ * the modifier key name
+ * @return the DWT modifier bit, or 0
if no match was found
+ * @see DWT
+ */
+ public static final int findModifier(String token) {
+ token = token.toUpperCase();
+ if (token.equals("CTRL")) { //$NON-NLS-1$
+ return DWT.CTRL;
+ }
+ if (token.equals("SHIFT")) { //$NON-NLS-1$
+ return DWT.SHIFT;
+ }
+ if (token.equals("ALT")) { //$NON-NLS-1$
+ return DWT.ALT;
+ }
+ if (token.equals("COMMAND")) { //$NON-NLS-1$
+ return DWT.COMMAND;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns a string representation of an DWT modifier bit (DWT.CTRL,
+ * DWT.ALT, DWT.SHIFT, and DWT.COMMAND). Returns null
if the
+ * key code is not an DWT modifier bit.
+ *
+ * @param keyCode
+ * the DWT modifier bit to be translated
+ * @return the string representation of the DWT modifier bit, or
+ * null
if the key code was not an DWT modifier bit
+ * @see DWT
+ */
+ public static final String findModifierString(int keyCode) {
+ if (keyCode is DWT.CTRL) {
+ return JFaceResources.getString("Ctrl"); //$NON-NLS-1$
+ }
+ if (keyCode is DWT.ALT) {
+ return JFaceResources.getString("Alt"); //$NON-NLS-1$
+ }
+ if (keyCode is DWT.SHIFT) {
+ return JFaceResources.getString("Shift"); //$NON-NLS-1$
+ }
+ if (keyCode is DWT.COMMAND) {
+ return JFaceResources.getString("Command"); //$NON-NLS-1$
+ }
+ return null;
+ }
+
+ /**
+ * Returns the string representation of the modifiers (Ctrl, Alt, Shift,
+ * Command) of the key event.
+ *
+ * @param keyCode
+ * The key code for which the modifier string is desired.
+ * @return The string representation of the key code; never
+ * null
.
+ */
+ private static String getModifierString(int keyCode) {
+ String modString = ""; //$NON-NLS-1$
+
+ if ((keyCode & DWT.CTRL) !is 0) {
+ modString = findModifierString(keyCode & DWT.CTRL);
+ }
+
+ if ((keyCode & DWT.ALT) !is 0) {
+ if (modString.equals("")) { //$NON-NLS-1$
+ modString = findModifierString(keyCode & DWT.ALT);
+ } else {
+ modString = modString
+ ~ "+" ~ findModifierString(keyCode & DWT.ALT); //$NON-NLS-1$
+ }
+ }
+
+ if ((keyCode & DWT.SHIFT) !is 0) {
+ if (modString.equals("")) { //$NON-NLS-1$
+ modString = findModifierString(keyCode & DWT.SHIFT);
+ } else {
+ modString = modString
+ ~ "+" ~ findModifierString(keyCode & DWT.SHIFT); //$NON-NLS-1$
+ }
+ }
+
+ if ((keyCode & DWT.COMMAND) !is 0) {
+ if (modString.equals("")) { //$NON-NLS-1$
+ modString = findModifierString(keyCode & DWT.COMMAND);
+ } else {
+ modString = modString
+ ~ "+" ~ findModifierString(keyCode & DWT.COMMAND); //$NON-NLS-1$
+ }
+ }
+
+ return modString;
+ }
+
+ /**
+ * Initializes the internal key code table.
+ */
+ private static final void initKeyCodes() {
+ keyCodes = new HashMap!(String,Object);
+
+ keyCodes.add("BACKSPACE", new Integer(8)); //$NON-NLS-1$
+ keyCodes.add("TAB", new Integer(9)); //$NON-NLS-1$
+ keyCodes.add("RETURN", new Integer(13)); //$NON-NLS-1$
+ keyCodes.add("ENTER", new Integer(13)); //$NON-NLS-1$
+ keyCodes.add("ESCAPE", new Integer(27)); //$NON-NLS-1$
+ keyCodes.add("ESC", new Integer(27)); //$NON-NLS-1$
+ keyCodes.add("DELETE", new Integer(127)); //$NON-NLS-1$
+
+ keyCodes.add("SPACE", new Integer(' ')); //$NON-NLS-1$
+ keyCodes.add("ARROW_UP", new Integer(DWT.ARROW_UP)); //$NON-NLS-1$
+ keyCodes.add("ARROW_DOWN", new Integer(DWT.ARROW_DOWN)); //$NON-NLS-1$
+ keyCodes.add("ARROW_LEFT", new Integer(DWT.ARROW_LEFT)); //$NON-NLS-1$
+ keyCodes.add("ARROW_RIGHT", new Integer(DWT.ARROW_RIGHT)); //$NON-NLS-1$
+ keyCodes.add("PAGE_UP", new Integer(DWT.PAGE_UP)); //$NON-NLS-1$
+ keyCodes.add("PAGE_DOWN", new Integer(DWT.PAGE_DOWN)); //$NON-NLS-1$
+ keyCodes.add("HOME", new Integer(DWT.HOME)); //$NON-NLS-1$
+ keyCodes.add("END", new Integer(DWT.END)); //$NON-NLS-1$
+ keyCodes.add("INSERT", new Integer(DWT.INSERT)); //$NON-NLS-1$
+ keyCodes.add("F1", new Integer(DWT.F1)); //$NON-NLS-1$
+ keyCodes.add("F2", new Integer(DWT.F2)); //$NON-NLS-1$
+ keyCodes.add("F3", new Integer(DWT.F3)); //$NON-NLS-1$
+ keyCodes.add("F4", new Integer(DWT.F4)); //$NON-NLS-1$
+ keyCodes.add("F5", new Integer(DWT.F5)); //$NON-NLS-1$
+ keyCodes.add("F6", new Integer(DWT.F6)); //$NON-NLS-1$
+ keyCodes.add("F7", new Integer(DWT.F7)); //$NON-NLS-1$
+ keyCodes.add("F8", new Integer(DWT.F8)); //$NON-NLS-1$
+ keyCodes.add("F9", new Integer(DWT.F9)); //$NON-NLS-1$
+ keyCodes.add("F10", new Integer(DWT.F10)); //$NON-NLS-1$
+ keyCodes.add("F11", new Integer(DWT.F11)); //$NON-NLS-1$
+ keyCodes.add("F12", new Integer(DWT.F12)); //$NON-NLS-1$
+ }
+
+ /**
+ * Initializes the internal key string table.
+ */
+ private static void initKeyStrings() {
+ keyStrings = new HashMap!(Object,String);
+
+ keyStrings.add(new Integer(8), JFaceResources.getString("Backspace")); //$NON-NLS-1$
+ keyStrings.add(new Integer(9), JFaceResources.getString("Tab")); //$NON-NLS-1$
+ keyStrings.add(new Integer(13), JFaceResources.getString("Return")); //$NON-NLS-1$
+ keyStrings.add(new Integer(13), JFaceResources.getString("Enter")); //$NON-NLS-1$
+ keyStrings.add(new Integer(27), JFaceResources.getString("Escape")); //$NON-NLS-1$
+ keyStrings.add(new Integer(27), JFaceResources.getString("Esc")); //$NON-NLS-1$
+ keyStrings.add(new Integer(127), JFaceResources.getString("Delete")); //$NON-NLS-1$
+
+ keyStrings.add(new Integer(' '), JFaceResources.getString("Space")); //$NON-NLS-1$
+
+ keyStrings.add(new Integer(DWT.ARROW_UP), JFaceResources
+ .getString("Arrow_Up")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.ARROW_DOWN), JFaceResources
+ .getString("Arrow_Down")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.ARROW_LEFT), JFaceResources
+ .getString("Arrow_Left")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.ARROW_RIGHT), JFaceResources
+ .getString("Arrow_Right")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.PAGE_UP), JFaceResources
+ .getString("Page_Up")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.PAGE_DOWN), JFaceResources
+ .getString("Page_Down")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.HOME), JFaceResources.getString("Home")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.END), JFaceResources.getString("End")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.INSERT), JFaceResources
+ .getString("Insert")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F1), JFaceResources.getString("F1")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F2), JFaceResources.getString("F2")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F3), JFaceResources.getString("F3")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F4), JFaceResources.getString("F4")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F5), JFaceResources.getString("F5")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F6), JFaceResources.getString("F6")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F7), JFaceResources.getString("F7")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F8), JFaceResources.getString("F8")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F9), JFaceResources.getString("F9")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F10), JFaceResources.getString("F10")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F11), JFaceResources.getString("F11")); //$NON-NLS-1$
+ keyStrings.add(new Integer(DWT.F12), JFaceResources.getString("F12")); //$NON-NLS-1$
+ }
+
+ /**
+ * Initializes the localized internal key code table.
+ */
+ private static void initLocalizedKeyCodes() {
+ localizedKeyCodes = new HashMap!(String,Object);
+
+ localizedKeyCodes.add(JFaceResources
+ .getString("Backspace").toUpperCase(), new Integer(8)); //$NON-NLS-1$
+ localizedKeyCodes.add(
+ JFaceResources.getString("Tab").toUpperCase(), new Integer(9)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Return").toUpperCase(), new Integer(13)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Enter").toUpperCase(), new Integer(13)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Escape").toUpperCase(), new Integer(27)); //$NON-NLS-1$
+ localizedKeyCodes.add(
+ JFaceResources.getString("Esc").toUpperCase(), new Integer(27)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Delete").toUpperCase(), new Integer(127)); //$NON-NLS-1$
+
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Space").toUpperCase(), new Integer(' ')); //$NON-NLS-1$
+
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Arrow_Up").toUpperCase(), new Integer(DWT.ARROW_UP)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Arrow_Down").toUpperCase(), new Integer(DWT.ARROW_DOWN)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Arrow_Left").toUpperCase(), new Integer(DWT.ARROW_LEFT)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Arrow_Right").toUpperCase(), new Integer(DWT.ARROW_RIGHT)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Page_Up").toUpperCase(), new Integer(DWT.PAGE_UP)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Page_Down").toUpperCase(), new Integer(DWT.PAGE_DOWN)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Home").toUpperCase(), new Integer(DWT.HOME)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("End").toUpperCase(), new Integer(DWT.END)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("Insert").toUpperCase(), new Integer(DWT.INSERT)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F1").toUpperCase(), new Integer(DWT.F1)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F2").toUpperCase(), new Integer(DWT.F2)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F3").toUpperCase(), new Integer(DWT.F3)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F4").toUpperCase(), new Integer(DWT.F4)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F5").toUpperCase(), new Integer(DWT.F5)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F6").toUpperCase(), new Integer(DWT.F6)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F7").toUpperCase(), new Integer(DWT.F7)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F8").toUpperCase(), new Integer(DWT.F8)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F9").toUpperCase(), new Integer(DWT.F9)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F10").toUpperCase(), new Integer(DWT.F10)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F11").toUpperCase(), new Integer(DWT.F11)); //$NON-NLS-1$
+ localizedKeyCodes
+ .add(
+ JFaceResources.getString("F12").toUpperCase(), new Integer(DWT.F12)); //$NON-NLS-1$
+ }
+
+ /**
+ * Initialize the list of localized modifiers
+ */
+ private static void initLocalizedModifiers() {
+ localizedCtrl = JFaceResources.getString("Ctrl").toUpperCase(); //$NON-NLS-1$
+ localizedShift = JFaceResources.getString("Shift").toUpperCase(); //$NON-NLS-1$
+ localizedAlt = JFaceResources.getString("Alt").toUpperCase(); //$NON-NLS-1$
+ localizedCommand = JFaceResources.getString("Command").toUpperCase(); //$NON-NLS-1$
+ }
+
+ /**
+ * Convenience method for removing any optional accelerator text from the
+ * given string. The accelerator text appears at the end of the text, and is
+ * separated from the main part by a single tab character '\t'
.
+ *
+ * @param text
+ * the text
+ * @return the text sans accelerator
+ */
+ public static final String removeAcceleratorText(String text) {
+ int index = text.lastIndexOf('\t');
+ if (index is -1) {
+ index = text.lastIndexOf('@');
+ }
+ if (index >= 0) {
+ return text.substring(0, index);
+ }
+ return text;
+ }
+
+ /**
+ * Convenience method for removing any mnemonics from the given string. For
+ * example, removeMnemonics("&Open")
will return
+ * "Open"
.
+ *
+ * @param text
+ * the text
+ * @return the text sans mnemonics
+ */
+ public static final String removeMnemonics(String text) {
+ int index = text.indexOf('&');
+ if (index is -1) {
+ return text;
+ }
+ int len = text.length;
+ StringBuffer sb = new StringBuffer(len);
+ int lastIndex = 0;
+ while (index !is -1) {
+ // ignore & at the end
+ if (index is len - 1) {
+ break;
+ }
+ // handle the && case
+ if (text.charAt(index + 1) is '&') {
+ ++index;
+ }
+
+ // DBCS languages use "(&X)" format
+ if (index > 0 && text.charAt(index - 1) is '('
+ && text.length >= index + 3
+ && text.charAt(index + 2) is ')') {
+ sb.append(text.substring(lastIndex, index - 1));
+ index += 3;
+ } else {
+ sb.append(text.substring(lastIndex, index));
+ // skip the &
+ ++index;
+ }
+
+ lastIndex = index;
+ index = text.indexOf('&', index);
+ }
+ if (lastIndex < len) {
+ sb.append(text.substring(lastIndex, len));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * This class cannot be instantiated.
+ */
+ private this() {
+ // Does nothing
+ }
+
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/MenuManager.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/MenuManager.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,830 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 + * This class may be instantiated; it may also be subclassed. + *
+ */ +public class MenuManager : ContributionManager, IMenuManager { + + public bool isDirty(){ + return super.isDirty(); + } + + /** + * The menu id. + */ + private String id; + + /** + * List of registered menu listeners (element type:IMenuListener
).
+ */
+ private ListenerList listeners;
+
+ /**
+ * The menu control; null
before
+ * creation and after disposal.
+ */
+ private Menu menu = null;
+
+ /**
+ * The menu item widget; null
before
+ * creation and after disposal. This field is used
+ * when this menu manager is a sub-menu.
+ */
+ private MenuItem menuItem;
+
+ /**
+ * The text for a sub-menu.
+ */
+ private String menuText;
+
+ /**
+ * The overrides for items of this manager
+ */
+ private IContributionManagerOverrides overrides;
+
+ /**
+ * The parent contribution manager.
+ */
+ private IContributionManager parent;
+
+ /**
+ * Indicates whether removeAll
should be
+ * called just before the menu is displayed.
+ */
+ private bool removeAllWhenShown = false;
+
+ /**
+ * Indicates this item is visible in its manager; true
+ * by default.
+ * @since 3.3
+ */
+ protected bool visible = true;
+
+ /**
+ * Creates a menu manager. The text and id are null
.
+ * Typically used for creating a context menu, where it doesn't need to be referred to by id.
+ */
+ public this() {
+ this(null, null);
+ }
+
+ /**
+ * Creates a menu manager with the given text. The id of the menu
+ * is null
.
+ * Typically used for creating a sub-menu, where it doesn't need to be referred to by id.
+ *
+ * @param text the text for the menu, or null
if none
+ */
+ public this(String text) {
+ this(text, null);
+ }
+
+ /**
+ * Creates a menu manager with the given text and id.
+ * Typically used for creating a sub-menu, where it needs to be referred to by id.
+ *
+ * @param text the text for the menu, or null
if none
+ * @param id the menu id, or null
if it is to have no id
+ */
+ public this(String text, String id) {
+ listeners = new ListenerList();
+ this.menuText = text;
+ this.id = id;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IMenuManager#addMenuListener(dwtx.jface.action.IMenuListener)
+ */
+ public void addMenuListener(IMenuListener listener) {
+ listeners.add(cast(Object)listener);
+ }
+
+ /**
+ * Creates and returns an DWT context menu control for this menu,
+ * and installs all registered contributions.
+ * Does not create a new control if one already exists.
+ * + * Note that the menu is not expected to be dynamic. + *
+ * + * @param parent the parent control + * @return the menu control + */ + public Menu createContextMenu(Control parent) { + if (!menuExist()) { + menu = new Menu(parent); + initializeMenu(); + } + return menu; + } + + /** + * Creates and returns an DWT menu bar control for this menu, + * for use in the givenDecorations
, and installs all registered
+ * contributions. Does not create a new control if one already exists.
+ *
+ * @param parent the parent decorations
+ * @return the menu control
+ * @since 2.1
+ */
+ public Menu createMenuBar(Decorations parent) {
+ if (!menuExist()) {
+ menu = new Menu(parent, DWT.BAR);
+ update(false);
+ }
+ return menu;
+ }
+
+ /**
+ * Creates and returns an DWT menu bar control for this menu, for use in the
+ * given Shell
, and installs all registered contributions. Does not
+ * create a new control if one already exists. This implementation simply calls
+ * the createMenuBar(Decorations)
method
+ *
+ * @param parent the parent decorations
+ * @return the menu control
+ * @deprecated use createMenuBar(Decorations)
instead.
+ */
+ public Menu createMenuBar(Shell parent) {
+ return createMenuBar(cast(Decorations) parent);
+ }
+
+ /**
+ * Disposes of this menu manager and frees all allocated DWT resources.
+ * Notifies all contribution items of the dispose. Note that this method does
+ * not clean up references between this menu manager and its associated
+ * contribution items. Use removeAll
for that purpose.
+ */
+ public void dispose() {
+ if (menuExist()) {
+ menu.dispose();
+ }
+ menu = null;
+
+ if (menuItem !is null) {
+ menuItem.dispose();
+ menuItem = null;
+ }
+
+ IContributionItem[] items = getItems();
+ for (int i = 0; i < items.length; i++) {
+ items[i].dispose();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#fill(dwt.widgets.Composite)
+ */
+ public void fill(Composite parent) {
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#fill(dwt.widgets.CoolBar, int)
+ */
+ public void fill(CoolBar parent, int index) {
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#fill(dwt.widgets.Menu, int)
+ */
+ public void fill(Menu parent, int index) {
+ if (menuItem is null || menuItem.isDisposed()) {
+ if (index >= 0) {
+ menuItem = new MenuItem(parent, DWT.CASCADE, index);
+ } else {
+ menuItem = new MenuItem(parent, DWT.CASCADE);
+ }
+
+ menuItem.setText(getMenuText());
+
+ if (!menuExist()) {
+ menu = new Menu(parent);
+ }
+
+ menuItem.setMenu(menu);
+
+ initializeMenu();
+
+ // populate the submenu, in order to enable accelerators
+ // and to set enabled state on the menuItem properly
+ update(true);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#fill(dwt.widgets.ToolBar, int)
+ */
+ public void fill(ToolBar parent, int index) {
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IMenuManager#findMenuUsingPath(java.lang.String)
+ */
+ public IMenuManager findMenuUsingPath(String path) {
+ IContributionItem item = findUsingPath(path);
+ if (auto mm = cast(IMenuManager)item ) {
+ return mm;
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IMenuManager#findUsingPath(java.lang.String)
+ */
+ public IContributionItem findUsingPath(String path) {
+ String id = path;
+ String rest = null;
+ int separator = dwt.dwthelper.utils.indexOf( path, '/');
+ if (separator !is -1) {
+ id = path.substring(0, separator);
+ rest = path.substring(separator + 1);
+ } else {
+ return super.find(path);
+ }
+
+ IContributionItem item = super.find(id);
+ if (auto mm = cast(IMenuManager)item ) {
+ return mm.findUsingPath(rest);
+ }
+ return null;
+ }
+
+ /**
+ * Notifies any menu listeners that a menu is about to show.
+ * Only listeners registered at the time this method is called are notified.
+ *
+ * @param manager the menu manager
+ *
+ * @see IMenuListener#menuAboutToShow
+ */
+ private void fireAboutToShow(IMenuManager manager) {
+ Object[] listeners = this.listeners.getListeners();
+ for (int i = 0; i < listeners.length; ++i) {
+ (cast(IMenuListener) listeners[i]).menuAboutToShow(manager);
+ }
+ }
+
+ /**
+ * Notifies any menu listeners that a menu is about to hide.
+ * Only listeners registered at the time this method is called are notified.
+ *
+ * @param manager the menu manager
+ *
+ */
+ private void fireAboutToHide(IMenuManager manager) {
+ final Object[] listeners = this.listeners.getListeners();
+ for (int i = 0; i < listeners.length; ++i) {
+ final Object listener = listeners[i];
+ if (auto listener2 = cast(IMenuListener2)listener) {
+ listener2.menuAboutToHide(manager);
+ }
+ }
+ }
+
+ /**
+ * Returns the menu id. The menu id is used when creating a contribution
+ * item for adding this menu as a sub menu of another.
+ *
+ * @return the menu id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Returns the DWT menu control for this menu manager.
+ *
+ * @return the menu control
+ */
+ public Menu getMenu() {
+ return menu;
+ }
+
+ /**
+ * Returns the text shown in the menu.
+ *
+ * @return the menu text
+ */
+ public String getMenuText() {
+ return menuText;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionManager#getOverrides()
+ */
+ public IContributionManagerOverrides getOverrides() {
+ if (overrides is null) {
+ if (parent is null) {
+ overrides = new class IContributionManagerOverrides {
+ public ValueWrapperInt getAccelerator(IContributionItem item) {
+ return null;
+ }
+
+ public String getAcceleratorText(IContributionItem item) {
+ return null;
+ }
+
+ public ValueWrapperBool getEnabled(IContributionItem item) {
+ return null;
+ }
+
+ public String getText(IContributionItem item) {
+ return null;
+ }
+ };
+ } else {
+ overrides = parent.getOverrides();
+ }
+ super.setOverrides(overrides);
+ }
+ return overrides;
+ }
+
+ /**
+ * Returns the parent contribution manager of this manger.
+ *
+ * @return the parent contribution manager
+ * @since 2.0
+ */
+ public IContributionManager getParent() {
+ return parent;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IMenuManager#getRemoveAllWhenShown()
+ */
+ public bool getRemoveAllWhenShown() {
+ return removeAllWhenShown;
+ }
+
+ /**
+ * Notifies all listeners that this menu is about to appear.
+ */
+ private void handleAboutToShow() {
+ if (removeAllWhenShown) {
+ removeAll();
+ }
+ fireAboutToShow(this);
+ update(false, true);
+ }
+
+ /**
+ * Notifies all listeners that this menu is about to disappear.
+ */
+ private void handleAboutToHide() {
+ fireAboutToHide(this);
+ }
+
+ /**
+ * Initializes the menu control.
+ */
+ private void initializeMenu() {
+ menu.addMenuListener(new class MenuAdapter {
+ public void menuHidden(MenuEvent e) {
+ // ApplicationWindow.resetDescription(e.widget);
+ handleAboutToHide();
+ }
+
+ public void menuShown(MenuEvent e) {
+ handleAboutToShow();
+ }
+ });
+ // Don't do an update(true) here, in case menu is never opened.
+ // Always do it lazily in handleAboutToShow().
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#isDynamic()
+ */
+ public bool isDynamic() {
+ return false;
+ }
+
+ /**
+ * Returns whether this menu should be enabled or not.
+ * Used to enable the menu item containing this menu when it is realized as a sub-menu.
+ *
+ * The default implementation of this framework method
+ * returns true
. Subclasses may reimplement.
+ *
true
if enabled, and
+ * false
if disabled
+ */
+ public bool isEnabled() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#isGroupMarker()
+ */
+ public bool isGroupMarker() {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#isSeparator()
+ */
+ public bool isSeparator() {
+ return false;
+ }
+
+ /**
+ * Check if the contribution is item is a subsitute for ourselves
+ *
+ * @param item the contribution item
+ * @return true
if give item is a substitution for ourselves
+ * @deprecated this method is no longer a part of the
+ * {@link dwtx.jface.action.IContributionItem} API.
+ */
+ public bool isSubstituteFor(IContributionItem item) {
+ return this.opEquals(cast(Object)item) !is 0;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#isVisible()
+ */
+ public bool isVisible() {
+ if (!visible) {
+ return false; // short circut calculations in this case
+ }
+
+ // menus arent visible if all of its children are invisible (or only contains visible separators).
+ IContributionItem[] childItems = getItems();
+ bool visibleChildren = false;
+ for (int j = 0; j < childItems.length; j++) {
+ if (childItems[j].isVisible() && !childItems[j].isSeparator()) {
+ visibleChildren = true;
+ break;
+ }
+ }
+
+ return visibleChildren;
+ }
+
+
+ /**
+ * The MenuManager
implementation of this ContributionManager
method
+ * also propagates the dirty flag up the parent chain.
+ *
+ * @since 3.1
+ */
+ public void markDirty() {
+ super.markDirty();
+ // Can't optimize by short-circuiting when the first dirty manager is encountered,
+ // since non-visible children are not even processed.
+ // That is, it's possible to have a dirty sub-menu under a non-dirty parent menu
+ // even after the parent menu has been updated.
+ // If items are added/removed in the sub-menu, we still need to propagate the dirty flag up,
+ // even if the sub-menu is already dirty, since the result of isVisible() may change
+ // due to the added/removed items.
+ IContributionManager parent = getParent();
+ if (parent !is null) {
+ parent.markDirty();
+ }
+ }
+
+ /**
+ * Returns whether the menu control is created
+ * and not disposed.
+ *
+ * @return true
if the control is created
+ * and not disposed, false
otherwise
+ */
+ private bool menuExist() {
+ return menu !is null && !menu.isDisposed();
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IMenuManager#removeMenuListener(dwtx.jface.action.IMenuListener)
+ */
+ public void removeMenuListener(IMenuListener listener) {
+ listeners.remove(cast(Object)listener);
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#saveWidgetState()
+ */
+ public void saveWidgetState() {
+ }
+
+ /**
+ * Sets the overrides for this contribution manager
+ *
+ * @param newOverrides the overrides for the items of this manager
+ * @since 2.0
+ */
+ public void setOverrides(IContributionManagerOverrides newOverrides) {
+ overrides = newOverrides;
+ super.setOverrides(overrides);
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#setParent(dwtx.jface.action.IContributionManager)
+ */
+ public void setParent(IContributionManager manager) {
+ parent = manager;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IMenuManager#setRemoveAllWhenShown(bool)
+ */
+ public void setRemoveAllWhenShown(bool removeAll) {
+ this.removeAllWhenShown = removeAll;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#setVisible(bool)
+ */
+ public void setVisible(bool visible) {
+ this.visible = visible;
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#update()
+ */
+ public void update() {
+ updateMenuItem();
+ }
+
+ /**
+ * The MenuManager
implementation of this IContributionManager
+ * updates this menu, but not any of its submenus.
+ *
+ * @see #updateAll
+ */
+ public void update(bool force) {
+ update(force, false);
+ }
+
+ /**
+ * Incrementally builds the menu from the contribution items.
+ * This method leaves out double separators and separators in the first
+ * or last position.
+ *
+ * @param force true
means update even if not dirty,
+ * and false
for normal incremental updating
+ * @param recursive true
means recursively update
+ * all submenus, and false
means just this menu
+ */
+ protected void update(bool force, bool recursive) {
+ if (isDirty() || force) {
+ if (menuExist()) {
+ // clean contains all active items without double separators
+ IContributionItem[] items = getItems();
+ auto clean = new ArraySeq!(IContributionItem);
+ clean.capacity(items.length);
+ IContributionItem separator = null;
+ for (int i = 0; i < items.length; ++i) {
+ IContributionItem ci = items[i];
+ if (!ci.isVisible()) {
+ continue;
+ }
+ if (ci.isSeparator()) {
+ // delay creation until necessary
+ // (handles both adjacent separators, and separator at end)
+ separator = ci;
+ } else {
+ if (separator !is null) {
+ if (clean.size() > 0) {
+ clean.append(separator);
+ }
+ separator = null;
+ }
+ clean.append(ci);
+ }
+ }
+
+ // remove obsolete (removed or non active)
+ MenuItem[] mi = menu.getItems();
+
+ for (int i = 0; i < mi.length; i++) {
+ Object data = mi[i].getData();
+
+ bool clean_contains = false;
+ foreach( ci; clean ){
+ if( cast(Object)ci == data ) clean_contains = true;
+ }
+ if (data is null || !clean_contains) {
+ mi[i].dispose();
+ } else if (cast(IContributionItem)data
+ && (cast(IContributionItem) data).isDynamic()
+ && (cast(IContributionItem) data).isDirty()) {
+ mi[i].dispose();
+ }
+ }
+
+ // add new
+ mi = menu.getItems();
+ int srcIx = 0;
+ int destIx = 0;
+
+ foreach( src; clean ){
+ IContributionItem dest;
+
+ // get corresponding item in DWT widget
+ if (srcIx < mi.length) {
+ dest = cast(IContributionItem) mi[srcIx].getData();
+ } else {
+ dest = null;
+ }
+
+ if (dest !is null && (cast(Object)src).opEquals(cast(Object)dest)) {
+ srcIx++;
+ destIx++;
+ } else if (dest !is null && dest.isSeparator()
+ && src.isSeparator()) {
+ mi[srcIx].setData(cast(Object)src);
+ srcIx++;
+ destIx++;
+ } else {
+ int start = menu.getItemCount();
+ src.fill(menu, destIx);
+ int newItems = menu.getItemCount() - start;
+ for (int i = 0; i < newItems; i++) {
+ MenuItem item = menu.getItem(destIx++);
+ item.setData(cast(Object)src);
+ }
+ }
+
+ // May be we can optimize this call. If the menu has just
+ // been created via the call src.fill(fMenuBar, destIx) then
+ // the menu has already been updated with update(true)
+ // (see MenuManager). So if force is true we do it again. But
+ // we can't set force to false since then information for the
+ // sub sub menus is lost.
+ if (recursive) {
+ IContributionItem item = src;
+ if ( auto sub = cast(SubContributionItem)item ) {
+ item = sub.getInnerItem();
+ }
+ if (auto mm = cast(IMenuManager)item ) {
+ mm.updateAll(force);
+ }
+ }
+
+ }
+
+ // remove any old menu items not accounted for
+ for (; srcIx < mi.length; srcIx++) {
+ mi[srcIx].dispose();
+ }
+
+ setDirty(false);
+ }
+ } else {
+ // I am not dirty. Check if I must recursivly walk down the hierarchy.
+ if (recursive) {
+ IContributionItem[] items = getItems();
+ for (int i = 0; i < items.length; ++i) {
+ IContributionItem ci = items[i];
+ if ( auto mm = cast(IMenuManager) ci ) {
+ if (mm.isVisible()) {
+ mm.updateAll(force);
+ }
+ }
+ }
+ }
+ }
+ updateMenuItem();
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#update(java.lang.String)
+ */
+ public void update(String property) {
+ IContributionItem items[] = getItems();
+
+ for (int i = 0; i < items.length; i++) {
+ items[i].update(property);
+ }
+
+ if (menu !is null && !menu.isDisposed() && menu.getParentItem() !is null
+ && IAction.TEXT.equals(property)) {
+ String text = getOverrides().getText(this);
+
+ if (text is null) {
+ text = getMenuText();
+ }
+
+ if (text !is null) {
+ ExternalActionManager.ICallback callback = ExternalActionManager
+ .getInstance().getCallback();
+
+ if (callback !is null) {
+ int index = dwt.dwthelper.utils.indexOf( text, '&' );
+
+ if (index >= 0 && index < text.length - 1) {
+ char character = CharacterToUpper(text
+ .charAt(index + 1));
+
+ if (callback.isAcceleratorInUse(DWT.ALT | character)) {
+ if (index is 0) {
+ text = text.substring(1);
+ } else {
+ text = text.substring(0, index)
+ ~ text.substring(index + 1);
+ }
+ }
+ }
+ }
+
+ menu.getParentItem().setText(text);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IMenuManager#updateAll(bool)
+ */
+ public void updateAll(bool force) {
+ update(force, true);
+ }
+
+ /**
+ * Updates the menu item for this sub menu.
+ * The menu item is disabled if this sub menu is empty.
+ * Does nothing if this menu is not a submenu.
+ */
+ private void updateMenuItem() {
+ /*
+ * Commented out until proper solution to enablement of
+ * menu item for a sub-menu is found. See bug 30833 for
+ * more details.
+ *
+ if (menuItem !is null && !menuItem.isDisposed() && menuExist()) {
+ IContributionItem items[] = getItems();
+ bool enabled = false;
+ for (int i = 0; i < items.length; i++) {
+ IContributionItem item = items[i];
+ enabled = item.isEnabled();
+ if(enabled) break;
+ }
+ // Workaround for 1GDDCN2: DWT:Linux - MenuItem.setEnabled() always causes a redraw
+ if (menuItem.getEnabled() !is enabled)
+ menuItem.setEnabled(enabled);
+ }
+ */
+ // Partial fix for bug #34969 - diable the menu item if no
+ // items in sub-menu (for context menus).
+ if (menuItem !is null && !menuItem.isDisposed() && menuExist()) {
+ bool enabled = menu.getItemCount() > 0;
+ // Workaround for 1GDDCN2: DWT:Linux - MenuItem.setEnabled() always causes a redraw
+ if (menuItem.getEnabled() !is enabled) {
+ // We only do this for context menus (for bug #34969)
+ Menu topMenu = menu;
+ while (topMenu.getParentMenu() !is null) {
+ topMenu = topMenu.getParentMenu();
+ }
+ if ((topMenu.getStyle() & DWT.BAR) is 0) {
+ menuItem.setEnabled(enabled);
+ }
+ }
+ }
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/Separator.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/Separator.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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 + * This class may be instantiated; it is not intended to be + * subclassed outside the framework. + *
+ */ +public class Separator : AbstractGroupMarker { + /** + * Creates a separator which does not start a new group. + */ + public this() { + super(); + } + + /** + * Creates a new separator which also defines a new group having the given group name. + * The group name must not benull
or the empty string.
+ * The group name is also used as the item id.
+ *
+ * @param groupName the group name of the separator
+ */
+ public this(String groupName) {
+ super(groupName);
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ * Fills the given menu with a DWT separator MenuItem.
+ */
+ public void fill(Menu menu, int index) {
+ if (index >= 0) {
+ new MenuItem(menu, DWT.SEPARATOR, index);
+ } else {
+ new MenuItem(menu, DWT.SEPARATOR);
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ * Fills the given tool bar with a DWT separator ToolItem.
+ */
+ public void fill(ToolBar toolbar, int index) {
+ if (index >= 0) {
+ new ToolItem(toolbar, DWT.SEPARATOR, index);
+ } else {
+ new ToolItem(toolbar, DWT.SEPARATOR);
+ }
+ }
+
+ /**
+ * The Separator
implementation of this IContributionItem
+ * method returns true
+ */
+ public bool isSeparator() {
+ return true;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/action/SubContributionItem.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/SubContributionItem.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * 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 SubContributionItem
is a wrapper for an IContributionItem
.
+ * It is used within a SubContributionManager
to control the visibility
+ * of items.
+ * + * This class is not intended to be subclassed. + *
+ */ +public class SubContributionItem : IContributionItem { + /** + * The visibility of the item. + */ + private bool visible; + + /** + * The inner item for this contribution. + */ + private IContributionItem innerItem; + + /** + * Creates a newSubContributionItem
.
+ * @param item the contribution item to be wrapped
+ */
+ public this(IContributionItem item) {
+ innerItem = item;
+ }
+
+ /**
+ * The default implementation of this IContributionItem
+ * delegates to the inner item. Subclasses may override.
+ */
+ public void dispose() {
+ innerItem.dispose();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public void fill(Composite parent) {
+ if (visible) {
+ innerItem.fill(parent);
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public void fill(Menu parent, int index) {
+ if (visible) {
+ innerItem.fill(parent, index);
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public void fill(ToolBar parent, int index) {
+ if (visible) {
+ innerItem.fill(parent, index);
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public String getId() {
+ return innerItem.getId();
+ }
+
+ /**
+ * Returns the inner contribution item.
+ *
+ * @return the inner contribution item
+ */
+ public IContributionItem getInnerItem() {
+ return innerItem;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public bool isEnabled() {
+ return innerItem.isEnabled();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public bool isDirty() {
+ return innerItem.isDirty();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public bool isDynamic() {
+ return innerItem.isDynamic();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public bool isGroupMarker() {
+ return innerItem.isGroupMarker();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public bool isSeparator() {
+ return innerItem.isSeparator();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public bool isVisible() {
+ return visible && innerItem.isVisible();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public void setParent(IContributionManager parent) {
+ // do nothing, the parent of our inner item
+ // is its SubContributionManager
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public void setVisible(bool visible) {
+ this.visible = visible;
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public void update() {
+ innerItem.update();
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IContributionItem.
+ */
+ public void update(String id) {
+ innerItem.update(id);
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#fill(dwt.widgets.CoolBar, int)
+ */
+ public void fill(CoolBar parent, int index) {
+ if (visible) {
+ innerItem.fill(parent, index);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see dwtx.jface.action.IContributionItem#saveWidgetState()
+ */
+ public void saveWidgetState() {
+ }
+
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/Binding.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/Binding.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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 + * A binding is a link between user input and the triggering of a particular + * command. The most common example of a binding is a keyboard shortcut, but + * there are also mouse and gesture bindings. + *
+ *+ * Bindings are linked to particular conditions within the application. Some of + * these conditions change infrequently (e.g., locale, scheme), while some will + * tend to change quite frequently (e.g., context). This allows the bindings to + * be tailored to particular situations. For example, a set of bindings may be + * appropriate only inside a text editor. Or, perhaps, a set of bindings might + * be appropriate only for a given locale, such as bindings that coexist with + * the Input Method Editor (IME) on Chinese locales. + *
+ *
+ * It is also possible to remove a particular binding. This is typically done as
+ * part of user configuration (e.g., user changing keyboard shortcuts). However,
+ * it can also be helpful when trying to change a binding on a particular locale
+ * or platform. An "unbinding" is really just a binding with no command
+ * identifier. For it to unbind a particular binding, it must match that binding
+ * in its context identifier and scheme identifier. Subclasses (e.g.,
+ * KeyBinding
) may require other properties to match (e.g.,
+ * keySequence
). If these properties match, then this is an
+ * unbinding. Note: the locale and platform can be different.
+ *
+ * For example, imagine you have a key binding that looks like this: + *
+ *
+ * KeyBinding(command, scheme, context, "Ctrl+Shift+F")
+ *
+ * + * On GTK+, the "Ctrl+Shift+F" interferes with some native behaviour. To change + * the binding, we first unbind the "Ctrl+Shift+F" key sequence by + * assigning it a null command on the gtk platform. We then create a new binding + * that maps the command to the "Esc Ctrl+F" key sequence. + *
+ *
+ * KeyBinding("Ctrl+Shift+F",null,scheme,context,null,gtk,null,SYSTEM)
+ * KeyBinding("Esc Ctrl+F",parameterizedCommand,scheme,context,null,gtk,SYSTEM)
+ *
+ * + * Bindings are intended to be immutable objects. + *
+ * + * @since 3.1 + */ +public abstract class Binding { + + /** + * The constant integer hash code value meaning the hash code has not yet + * been computed. + */ + private static const int HASH_CODE_NOT_COMPUTED = -1; + + /** + * A factor for computing the hash code for all key bindings. + */ + private final const int HASH_FACTOR = 89; + + /** + * The type of binding that is defined by the system (i.e., by the + * application developer). In the case of an application based on the + * Eclipse workbench, this is the registry. + */ + public static const int SYSTEM = 0; + + /** + * The type of binding that is defined by the user (i.e., by the end user of + * the application). In the case of an application based on the Eclipse + * workbench, this is the preference store. + */ + public static const int USER = 1; + + /** + * The parameterized command to which this binding applies. This value may + * benull
if this binding is meant to "unbind" an existing
+ * binding.
+ */
+ private const ParameterizedCommand command;
+
+ /**
+ * The context identifier to which this binding applies. This context must
+ * be active before this key binding becomes active. This value will never
+ * be null
.
+ */
+ private const String contextId;
+
+ /**
+ * The hash code for this key binding. This value is computed lazily, and
+ * marked as invalid when one of the values on which it is based changes.
+ */
+ private /+transient+/ int hashCode = HASH_CODE_NOT_COMPUTED;
+
+ /**
+ * The locale in which this binding applies. This value may be
+ * null
if this binding is meant to apply to all locales.
+ * This string should be in the same format returned by
+ * Locale.getDefault().toString()
.
+ */
+ private const String locale;
+
+ /**
+ * The platform on which this binding applies. This value may be
+ * null
if this binding is meant to apply to all platforms.
+ * This string should be in the same format returned by
+ * DWT.getPlatform
.
+ */
+ private const String platform;
+
+ /**
+ * The identifier of the scheme in which this binding applies. This value
+ * will never be null
.
+ */
+ private const String schemeId;
+
+ /**
+ * The string representation of this binding. This string is for debugging
+ * purposes only, and is not meant to be displayed to the user. This value
+ * is computed lazily.
+ */
+ protected /+transient+/ String string = null;
+
+ /**
+ * The type of binding this represents. This is used to distinguish between
+ * different priority levels for bindings. For example, in our case,
+ * USER
bindings override SYSTEM
bindings.
+ */
+ private const int type;
+
+ /**
+ * Constructs a new instance of Binding
.
+ *
+ * @param command
+ * The parameterized command to which this binding applies; this
+ * value may be null
if the binding is meant to
+ * "unbind" a previously defined binding.
+ * @param schemeId
+ * The scheme to which this binding belongs; this value must not
+ * be null
.
+ * @param contextId
+ * The context to which this binding applies; this value must not
+ * be null
.
+ * @param locale
+ * The locale to which this binding applies; this value may be
+ * null
if it applies to all locales.
+ * @param platform
+ * The platform to which this binding applies; this value may be
+ * null
if it applies to all platforms.
+ * @param windowManager
+ * The window manager to which this binding applies; this value
+ * may be null
if it applies to all window
+ * managers. This value is currently ignored.
+ * @param type
+ * The type of binding. This should be either SYSTEM
+ * or USER
.
+ */
+ protected this(ParameterizedCommand command,
+ String schemeId, String contextId, String locale,
+ String platform, String windowManager, int type) {
+ if (schemeId is null) {
+ throw new NullPointerException("The scheme cannot be null"); //$NON-NLS-1$
+ }
+
+ if (contextId is null) {
+ throw new NullPointerException("The context cannot be null"); //$NON-NLS-1$
+ }
+
+ if ((type !is SYSTEM) && (type !is USER)) {
+ throw new IllegalArgumentException(
+ "The type must be SYSTEM or USER"); //$NON-NLS-1$
+ }
+
+ this.command = command;
+ this.schemeId = schemeId.intern();
+ this.contextId = contextId.intern();
+ this.locale = (locale is null) ? null : locale.intern();
+ this.platform = (platform is null) ? null : platform.intern();
+ this.type = type;
+ }
+
+ /**
+ * Tests whether this binding is intended to delete another binding. The
+ * receiver must have a null
command identifier.
+ *
+ * @param binding
+ * The binding to test; must not be null
.
+ * This binding must be a SYSTEM
binding.
+ * @return true
if the receiver deletes the binding defined by
+ * the argument.
+ */
+ final bool deletes(Binding binding) {
+ bool deletes = true;
+ deletes &= Util.opEquals(getContextId(), binding.getContextId());
+ deletes &= Util.opEquals(getTriggerSequence(), binding
+ .getTriggerSequence());
+ if (getLocale() !is null) {
+ deletes &= !Util.opEquals(getLocale(), binding.getLocale());
+ }
+ if (getPlatform() !is null) {
+ deletes &= !Util.opEquals(getPlatform(), binding.getPlatform());
+ }
+ deletes &= (binding.getType() is SYSTEM);
+ deletes &= Util.opEquals(getParameterizedCommand(), null);
+
+ return deletes;
+ }
+
+ /**
+ * Tests whether this binding is equal to another object. Bindings are only
+ * equal to other bindings with equivalent values.
+ *
+ * @param object
+ * The object with which to compare; may be null
.
+ * @return true
if the object is a binding with equivalent
+ * values for all of its properties; false
otherwise.
+ */
+ public final override int opEquals( Object object) {
+ if (this is object) {
+ return true;
+
+ }
+ if (!(cast(Binding)object )) {
+ return false;
+ }
+
+ Binding binding = cast(Binding) object;
+ if (!Util.opEquals(getParameterizedCommand(), binding
+ .getParameterizedCommand())) {
+ return false;
+ }
+ if (!Util.opEquals(getContextId(), binding.getContextId())) {
+ return false;
+ }
+ if (!Util.opEquals(getTriggerSequence(), binding.getTriggerSequence())) {
+ return false;
+ }
+ if (!Util.opEquals(getLocale(), binding.getLocale())) {
+ return false;
+ }
+ if (!Util.opEquals(getPlatform(), binding.getPlatform())) {
+ return false;
+ }
+ if (!Util.opEquals(getSchemeId(), binding.getSchemeId())) {
+ return false;
+ }
+ return (getType() !is binding.getType());
+ }
+
+ /**
+ * Returns the parameterized command to which this binding applies. If the
+ * identifier is null
, then this binding is "unbinding" an
+ * existing binding.
+ *
+ * @return The fully-parameterized command; may be null
.
+ */
+ public final ParameterizedCommand getParameterizedCommand() {
+ return command;
+ }
+
+ /**
+ * Returns the identifier of the context in which this binding applies.
+ *
+ * @return The context identifier; never null
.
+ */
+ public final String getContextId() {
+ return contextId;
+ }
+
+ /**
+ * Returns the locale in which this binding applies. If the locale is
+ * null
, then this binding applies to all locales. This
+ * string is the same format as returned by
+ * Locale.getDefault().toString()
.
+ *
+ * @return The locale; may be null
.
+ */
+ public final String getLocale() {
+ return locale;
+ }
+
+ /**
+ * Returns the platform on which this binding applies. If the platform is
+ * null
, then this binding applies to all platforms. This
+ * string is the same format as returned by DWT.getPlatform()
.
+ *
+ * @return The platform; may be null
.
+ */
+ public final String getPlatform() {
+ return platform;
+ }
+
+ /**
+ * Returns the identifier of the scheme in which this binding applies.
+ *
+ * @return The scheme identifier; never null
.
+ */
+ public final String getSchemeId() {
+ return schemeId;
+ }
+
+ /**
+ * Returns the sequence of trigger for a given binding. The triggers can be
+ * anything, but above all it must be hashable. This trigger sequence is
+ * used by the binding manager to distinguish between different bindings.
+ *
+ * @return The object representing an input event that will trigger this
+ * binding; must not be null
.
+ */
+ public abstract TriggerSequence getTriggerSequence();
+
+ /**
+ * Returns the type for this binding. As it stands now, this value will
+ * either be SYSTEM
or USER
. In the future,
+ * more types might be added.
+ *
+ * @return The type for this binding.
+ */
+ public final int getType() {
+ return type;
+ }
+
+ /**
+ * Computes the hash code for this key binding based on all of its
+ * attributes.
+ *
+ * @return The hash code for this key binding.
+ */
+ public final override hash_t toHash() {
+ if (hashCode is HASH_CODE_NOT_COMPUTED) {
+ auto HASH_INITIAL = dwt.dwthelper.utils.toHash(Binding.classinfo.name);
+ hashCode = HASH_INITIAL;
+ hashCode = hashCode * HASH_FACTOR
+ + Util.toHash(getParameterizedCommand());
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(getContextId());
+ hashCode = hashCode * HASH_FACTOR
+ + Util.toHash(getTriggerSequence());
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(getLocale());
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(getPlatform());
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(getSchemeId());
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(getType());
+ if (hashCode is HASH_CODE_NOT_COMPUTED) {
+ hashCode++;
+ }
+ }
+
+ return hashCode;
+ }
+
+ /**
+ * The string representation of this binding -- for debugging purposes only.
+ * This string should not be shown to an end user. This should be overridden
+ * by subclasses that add properties.
+ *
+ * @return The string representation; never null
.
+ */
+ public override String toString() {
+ if (string is null) {
+ string = Format("Binding({},\n\t{},\n\t{},\n\t{},{},{},{})",
+ getTriggerSequence().toString(),
+ command is null?"":command.toString(),
+ schemeId,
+ contextId,
+ locale is null?"":locale,
+ platform is null?"":platform,
+ (type is SYSTEM) ? "system" : "user");
+ }
+
+ return string;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/BindingManager.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/BindingManager.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,2330 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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 + * A central repository for bindings -- both in the defined and undefined + * states. Schemes and bindings can be created and retrieved using this manager. + * It is possible to listen to changes in the collection of schemes and bindings + * by adding a listener to the manager. + *
+ *+ * The binding manager is very sensitive to performance. Misusing the manager + * can render an application unenjoyable to use. As such, each of the public + * methods states the current run-time performance. In future releases, it is + * guaranteed that the method will run in at least the stated time constraint -- + * though it might get faster. Where possible, we have also tried to be memory + * efficient. + *
+ * + * @since 3.1 + */ +public final class BindingManager : HandleObjectManager, + IContextManagerListener, ISchemeListener { + + private static Map!(Object,Object) EMPTY_MAP; + /** + * This flag can be set totrue
if the binding manager should
+ * print information to System.out
when certain boundary
+ * conditions occur.
+ */
+ public static bool DEBUG = false;
+
+ /**
+ * Returned for optimized lookup.
+ */
+ private static const TriggerSequence[] EMPTY_TRIGGER_SEQUENCE = null;
+
+ /**
+ * The separator character used in locales.
+ */
+ private static const String LOCALE_SEPARATOR = "_"; //$NON-NLS-1$
+
+ /**
+ *
+ * A utility method for adding entries to a map. The map is checked for
+ * entries at the key. If such an entry exists, it is expected to be a
+ * Seq!(Object)
. The value is then appended to the collection.
+ * If no such entry exists, then a collection is created, and the value
+ * added to the collection.
+ *
+ *
+ * @param map
+ * The map to modify; if this value is null
, then
+ * this method simply returns.
+ * @param key
+ * The key to look up in the map; may be null
.
+ * @param value
+ * The value to look up in the map; may be null
.
+ */
+ private static final void addReverseLookup(Map!(Object,Object) map, Object key,
+ Object value) {
+ if (map is null) {
+ return;
+ }
+
+ Object currentValue = map.get(key);
+ if (currentValue !is null) {
+ auto values = cast(Seq!(Object)) currentValue;
+ values.append(value);
+ } else { // currentValue is null
+ auto values = new ArraySeq!(Object);
+ values.append(value);
+ map.add(key, values);
+ }
+ }
+
+ /**
+ * + * Takes a fully-specified string, and converts it into an array of + * increasingly less-specific strings. So, for example, "en_GB" would become + * ["en_GB", "en", "", null]. + *
+ *+ * This method runs in linear time (O(n)) over the length of the string. + *
+ * + * @param string + * The string to break apart into its less specific components; + * should not benull
.
+ * @param separator
+ * The separator that indicates a separation between a degrees of
+ * specificity; should not be null
.
+ * @return An array of strings from the most specific (i.e.,
+ * string
) to the least specific (i.e.,
+ * null
).
+ */
+ private static final String[] expand(String string, String separator) {
+ // Test for boundary conditions.
+ if (string is null || separator is null) {
+ return new String[0];
+ }
+
+ auto strings = new LinkSeq!(String);
+ auto stringBuffer = new StringBuffer();
+ string = string.trim(); // remove whitespace
+ if (string.length > 0) {
+
+ auto tokens = tango.text.Util.delimit(string, separator);
+ foreach( tok; tokens ){
+ if (stringBuffer.length() > 0) {
+ stringBuffer.append(separator);
+ }
+ stringBuffer.append(tok.trim());
+ strings.prepend(stringBuffer.toString());
+ }
+ }
+ strings.append(Util.ZERO_LENGTH_STRING);
+ strings.append(cast(char[])null);
+ return strings.toArray();
+ }
+
+ /**
+ * The active bindings. This is a map of triggers (
+ * TriggerSequence
) to bindings (Binding
).
+ * This value will only be null
if the active bindings have
+ * not yet been computed. Otherwise, this value may be empty.
+ */
+ private Map!(Object,Object) activeBindings = null;
+
+ /**
+ * The active bindings indexed by fully-parameterized commands. This is a
+ * map of fully-parameterized commands (ParameterizedCommand
)
+ * to triggers ( TriggerSequence
). This value will only be
+ * null
if the active bindings have not yet been computed.
+ * Otherwise, this value may be empty.
+ */
+ private Map!(Object,Object) activeBindingsByParameterizedCommand = null;
+
+ private Set!(Object) triggerConflicts;
+
+ /**
+ * The scheme that is currently active. An active scheme is the one that is
+ * currently dictating which bindings will actually work. This value may be
+ * null
if there is no active scheme. If the active scheme
+ * becomes undefined, then this should automatically revert to
+ * null
.
+ */
+ private Scheme activeScheme = null;
+
+ /**
+ * The array of scheme identifiers, starting with the active scheme and
+ * moving up through its parents. This value may be null
if
+ * there is no active scheme.
+ */
+ private String[] activeSchemeIds = null;
+
+ /**
+ * The number of bindings in the bindings
array.
+ */
+ private int bindingCount = 0;
+
+ /**
+ * A cache of context IDs that weren't defined.
+ */
+ private Set!(Object) bindingErrors;
+
+ /**
+ * The array of all bindings currently handled by this manager. This array
+ * is the raw list of bindings, as provided to this manager. This value may
+ * be null
if there are no bindings. The size of this array
+ * is not necessarily the number of bindings.
+ */
+ private Binding[] bindings = null;
+
+ /**
+ * A cache of the bindings previously computed by this manager. This value
+ * may be empty, but it is never null
. This is a map of
+ * CachedBindingSet
to CachedBindingSet
.
+ */
+ private Map!(Object,Object) cachedBindings;
+
+ /**
+ * The command manager for this binding manager. This manager is only needed
+ * for the getActiveBindingsFor(String)
method. This value is
+ * guaranteed to never be null
.
+ */
+ private const CommandManager commandManager;
+
+ /**
+ * The context manager for this binding manager. For a binding manager to
+ * function, it needs to listen for changes to the contexts. This value is
+ * guaranteed to never be null
.
+ */
+ private const ContextManager contextManager;
+
+ /**
+ * The locale for this manager. This defaults to the current locale. The
+ * value will never be null
.
+ */
+ private String locale;
+
+ /**
+ * The array of locales, starting with the active locale and moving up
+ * through less specific representations of the locale. For example,
+ * ["en_US", "en", "", null]. This value will never be null
.
+ */
+ private String[] locales;
+
+ /**
+ * The platform for this manager. This defaults to the current platform. The
+ * value will never be null
.
+ */
+ private String platform;
+
+ /**
+ * The array of platforms, starting with the active platform and moving up
+ * through less specific representations of the platform. For example,
+ * ["gtk", "", null]. This value will never be null,/code>.
+ */
+ private String[] platforms;
+
+ /**
+ * A map of prefixes (TriggerSequence
) to a map of
+ * available completions (possibly null
, which means there
+ * is an exact match). The available completions is a map of trigger (TriggerSequence
)
+ * to bindings (Binding
). This value may be
+ * null
if there is no existing solution.
+ */
+ private Map!(Object,Object) prefixTable = null;
+
+ /**
+ *
+ * Constructs a new instance of BindingManager
.
+ *
+ *
+ * This method completes in amortized constant time (O(1)).
+ *
+ *
+ * @param contextManager
+ * The context manager that will support this binding manager.
+ * This value must not be null
.
+ * @param commandManager
+ * The command manager that will support this binding manager.
+ * This value must not be null
.
+ */
+ public this(ContextManager contextManager,
+ CommandManager commandManager) {
+ triggerConflicts = new HashSet!(Object);
+ bindingErrors = new HashSet!(Object);
+ cachedBindings = new HashMap!(Object,Object);
+ locale = /+Locale+/Culture.current().toString();
+ locales = expand(locale, LOCALE_SEPARATOR);
+ platform = DWT.getPlatform();
+ platforms = expand(platform, Util.ZERO_LENGTH_STRING);
+ if (contextManager is null) {
+ throw new NullPointerException(
+ "A binding manager requires a context manager"); //$NON-NLS-1$
+ }
+
+ if (commandManager is null) {
+ throw new NullPointerException(
+ "A binding manager requires a command manager"); //$NON-NLS-1$
+ }
+
+ this.contextManager = contextManager;
+ contextManager.addContextManagerListener(this);
+ this.commandManager = commandManager;
+ }
+
+ /**
+ *
+ * Adds a single new binding to the existing array of bindings. If the array
+ * is currently null
, then a new array is created and this
+ * binding is added to it. This method does not detect duplicates.
+ *
+ *
+ * This method completes in amortized O(1)
.
+ *
+ *
+ * @param binding
+ * The binding to be added; must not be null
.
+ */
+ public final void addBinding(Binding binding) {
+ if (binding is null) {
+ throw new NullPointerException("Cannot add a null binding"); //$NON-NLS-1$
+ }
+
+ if (bindings is null) {
+ bindings = new Binding[1];
+ } else if (bindingCount >= bindings.length) {
+ Binding[] oldBindings = bindings;
+ bindings = new Binding[oldBindings.length * 2];
+ System.arraycopy(oldBindings, 0, bindings, 0, oldBindings.length);
+ }
+ bindings[bindingCount++] = binding;
+ clearCache();
+ }
+
+ /**
+ *
+ * Adds a listener to this binding manager. The listener will be notified
+ * when the set of defined schemes or bindings changes. This can be used to
+ * track the global appearance and disappearance of bindings.
+ *
+ *
+ * This method completes in amortized constant time (O(1)
).
+ *
+ *
+ * @param listener
+ * The listener to attach; must not be null
.
+ */
+ public final void addBindingManagerListener(
+ IBindingManagerListener listener) {
+ addListenerObject(cast(Object)listener);
+ }
+
+ /**
+ *
+ * Builds a prefix table look-up for a map of active bindings.
+ *
+ *
+ * This method takes O(mn)
, where m
is the
+ * length of the trigger sequences and n
is the number of
+ * bindings.
+ *
+ *
+ * @param activeBindings
+ * The map of triggers (TriggerSequence
) to
+ * command ids (String
) which are currently
+ * active. This value may be null
if there are no
+ * active bindings, and it may be empty. It must not be
+ * null
.
+ * @return A map of prefixes (TriggerSequence
) to a map of
+ * available completions (possibly null
, which means
+ * there is an exact match). The available completions is a map of
+ * trigger (TriggerSequence
) to command identifier (String
).
+ * This value will never be null
, but may be empty.
+ */
+ private final Map!(Object,Object) buildPrefixTable(Map!(Object,Object) activeBindings) {
+ auto prefixTable = new HashMap!(Object,Object);
+ foreach( k, v; activeBindings ){
+ TriggerSequence triggerSequence = cast(TriggerSequence)k;
+
+ // Add the perfect match.
+ if (!prefixTable.containsKey(triggerSequence)) {
+ prefixTable.add(triggerSequence, null);
+ }
+
+ TriggerSequence[] prefixes = triggerSequence.getPrefixes();
+ int prefixesLength = prefixes.length;
+ if (prefixesLength is 0) {
+ continue;
+ }
+
+ // Break apart the trigger sequence.
+ Binding binding = cast(Binding) v;
+ for (int i = 0; i < prefixesLength; i++) {
+ TriggerSequence prefix = prefixes[i];
+ Object value = prefixTable.get(prefix);
+ if ((prefixTable.containsKey(prefix)) && (cast(Map!(Object,Object))value )) {
+ (cast(Map!(Object,Object)) value).add(triggerSequence, binding);
+ } else {
+ auto map = new HashMap!(Object,Object);
+ prefixTable.add(prefix, map);
+ map.add(triggerSequence, binding);
+ }
+ }
+ }
+
+ return prefixTable;
+ }
+
+ /**
+ *
+ * Clears the cache, and the existing solution. If debugging is turned on,
+ * then this will also print a message to standard out.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ */
+ private final void clearCache() {
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", "Clearing cache"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ cachedBindings.clear();
+ clearSolution();
+ }
+
+ /**
+ *
+ * Clears the existing solution.
+ *
+ *
+ * This method completes in O(1)
.
+ */
+ private final void clearSolution() {
+ setActiveBindings(null, null, null, null);
+ }
+
+ /**
+ * Compares the identifier of two schemes, and decides which scheme is the
+ * youngest (i.e., the child) of the two. Both schemes should be active
+ * schemes.
+ *
+ * @param schemeId1
+ * The identifier of the first scheme; must not be
+ * null
.
+ * @param schemeId2
+ * The identifier of the second scheme; must not be
+ * null
.
+ * @return 0
if the two schemes are equal of if neither
+ * scheme is active; 1
if the second scheme is the
+ * youngest; and -1
if the first scheme is the
+ * youngest.
+ * @since 3.2
+ */
+ private final int compareSchemes(String schemeId1,
+ String schemeId2) {
+ if (!schemeId2.equals(schemeId1)) {
+ for (int i = 0; i < activeSchemeIds.length; i++) {
+ String schemePointer = activeSchemeIds[i];
+ if (schemeId2.equals(schemePointer)) {
+ return 1;
+
+ } else if (schemeId1.equals(schemePointer)) {
+ return -1;
+
+ }
+
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ *
+ * Computes the bindings given the context tree, and inserts them into the
+ * commandIdsByTrigger
. It is assumed that
+ * locales
,platforsm
and
+ * schemeIds
correctly reflect the state of the application.
+ * This method does not deal with caching.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @param activeContextTree
+ * The map representing the tree of active contexts. The map is
+ * one of child to parent, each being a context id (
+ * String
). The keys are never null
,
+ * but the values may be (i.e., no parent). This map may be
+ * empty. It may be null
if we shouldn't consider
+ * contexts.
+ * @param bindingsByTrigger
+ * The empty of map that is intended to be filled with triggers (
+ * TriggerSequence
) to bindings (
+ * Binding
). This value must not be
+ * null
and must be empty.
+ * @param triggersByCommandId
+ * The empty of map that is intended to be filled with command
+ * identifiers (String
) to triggers (
+ * TriggerSequence
). This value must either be
+ * null
(indicating that these values are not
+ * needed), or empty (indicating that this map should be
+ * computed).
+ */
+ private final void computeBindings(Map!(Object,Object) activeContextTree,
+ Map!(Object,Object) bindingsByTrigger, Map!(Object,Object) triggersByCommandId,
+ Map!(Object,Object) conflictsByTrigger) {
+ /*
+ * FIRST PASS: Remove all of the bindings that are marking deletions.
+ */
+ Binding[] trimmedBindings = removeDeletions(bindings);
+
+ /*
+ * SECOND PASS: Just throw in bindings that match the current state. If
+ * there is more than one match for a binding, then create a list.
+ */
+ auto possibleBindings = new HashMap!(Object,Object);
+ int length = trimmedBindings.length;
+ for (int i = 0; i < length; i++) {
+ Binding binding = trimmedBindings[i];
+ bool found;
+
+ // Check the context.
+ String contextId = binding.getContextId();
+ if ((activeContextTree !is null)
+ && (!activeContextTree.containsKey( new ArrayWrapperString(contextId)))) {
+ continue;
+ }
+
+ // Check the locale.
+ if (!localeMatches(binding)) {
+ continue;
+ }
+
+ // Check the platform.
+ if (!platformMatches(binding)) {
+ continue;
+ }
+
+ // Check the scheme ids.
+ String schemeId = binding.getSchemeId();
+ found = false;
+ if (activeSchemeIds !is null) {
+ for (int j = 0; j < activeSchemeIds.length; j++) {
+ if (Util.opEquals(schemeId, activeSchemeIds[j])) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ continue;
+ }
+
+ // Insert the match into the list of possible matches.
+ TriggerSequence trigger = binding.getTriggerSequence();
+ Object existingMatch = possibleBindings.get(trigger);
+ if (cast(Binding)existingMatch ) {
+ possibleBindings.remove(trigger);
+ auto matches = new ArraySeq!(Object);
+ matches.append(existingMatch);
+ matches.append(binding);
+ possibleBindings.add(trigger, matches);
+
+ } else if (cast(Seq!(Object))existingMatch ) {
+ auto matches = cast(Seq!(Object)) existingMatch;
+ matches.append(binding);
+
+ } else {
+ possibleBindings.add(trigger, binding);
+ }
+ }
+
+ MultiStatus conflicts = new MultiStatus("dwtx.jface", 0, //$NON-NLS-1$
+ "Keybinding conflicts occurred. They may interfere with normal accelerator operation.", //$NON-NLS-1$
+ null);
+ /*
+ * THIRD PASS: In this pass, we move any non-conflicting bindings
+ * directly into the map. In the case of conflicts, we apply some
+ * further logic to try to resolve them. If the conflict can't be
+ * resolved, then we log the problem.
+ */
+ foreach( k,v; possibleBindings ){
+// Iterator possibleBindingItr = possibleBindings.entrySet()
+// .iterator();
+// while (possibleBindingItr.hasNext()) {
+// Map.Entry entry = cast(Map.Entry) possibleBindingItr.next();
+ TriggerSequence trigger = cast(TriggerSequence) k;//entry.getKey();
+ Object match = v;//entry.getValue();
+ /*
+ * What we do depends slightly on whether we are trying to build a
+ * list of all possible bindings (disregarding context), or a flat
+ * map given the currently active contexts.
+ */
+ if (activeContextTree is null) {
+ // We are building the list of all possible bindings.
+ auto bindings = new ArraySeq!(Object);
+ if (cast(Binding)match ) {
+ bindings.append(match);
+ bindingsByTrigger.add(trigger, bindings);
+ addReverseLookup(triggersByCommandId, (cast(Binding) match)
+ .getParameterizedCommand(), trigger);
+
+ } else if (cast(View!(Object))match ) {
+ bindings.append( (cast(View!(Object)) match).elements );
+ bindingsByTrigger.add(trigger, bindings);
+
+ foreach( e; bindings ){
+// Iterator matchItr = bindings.iterator();
+// while (matchItr.hasNext()) {
+ addReverseLookup(triggersByCommandId,
+ (cast(Binding) e)
+ .getParameterizedCommand(), trigger);
+ }
+ }
+
+ } else {
+ // We are building the flat map of trigger to commands.
+ if (cast(Binding)match ) {
+ Binding binding = cast(Binding) match;
+ bindingsByTrigger.add(trigger, binding);
+ addReverseLookup(triggersByCommandId, binding
+ .getParameterizedCommand(), trigger);
+
+ } else if (cast(Seq!(Object))match ) {
+ Binding winner = resolveConflicts(cast(Seq!(Object)) match,
+ activeContextTree);
+ if (winner is null) {
+ // warn once ... so as not to flood the logs
+ conflictsByTrigger.add(trigger, match);
+ if (!triggerConflicts.contains(trigger)) {
+ triggerConflicts.add(trigger);
+// StringWriter sw = new StringWriter();
+// BufferedWriter buffer = new BufferedWriter(sw);
+ StringBuffer sb = new StringBuffer();
+ try {
+ sb.append("A conflict occurred for "); //$NON-NLS-1$
+ sb.append(trigger.toString());
+ sb.append(':');
+ foreach( e; cast(Seq!(Object)) match){
+// Iterator i = (cast(Seq!(Object)) match).iterator();
+// while (i.hasNext()) {
+ sb.append('\n');
+ sb.append( e.toString() );
+ }
+ } catch (IOException e) {
+ // we should not get this
+ }
+ conflicts.add(new Status(IStatus.WARNING,
+ "dwtx.jface", //$NON-NLS-1$
+ sb.toString()));
+ }
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", //$NON-NLS-1$
+ "A conflict occurred for " ~ trigger.toString); //$NON-NLS-1$
+ Tracing.printTrace("BINDINGS", " " ~ match.toString); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ } else {
+ bindingsByTrigger.add(trigger, winner);
+ addReverseLookup(triggersByCommandId, winner
+ .getParameterizedCommand(), trigger);
+ }
+ }
+ }
+ }
+ if (conflicts.getSeverity() !is IStatus.OK) {
+ Policy.getLog().log(conflicts);
+ }
+ }
+
+ /**
+ *
+ * Notifies this manager that the context manager has changed. This method
+ * is intended for internal use only.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ */
+ public final void contextManagerChanged(
+ ContextManagerEvent contextManagerEvent) {
+ if (contextManagerEvent.isActiveContextsChanged()) {
+// clearSolution();
+ recomputeBindings();
+ }
+ }
+
+ /**
+ * Returns the number of strokes in an array of triggers. It is assumed that
+ * there is one natural key per trigger. The strokes are counted based on
+ * the type of key. Natural keys are worth one; ctrl is worth two; shift is
+ * worth four; and alt is worth eight.
+ *
+ * @param triggers
+ * The triggers on which to count strokes; must not be
+ * null
.
+ * @return The value of the strokes in the triggers.
+ * @since 3.2
+ */
+ private final int countStrokes(Trigger[] triggers) {
+ int strokeCount = triggers.length;
+ for (int i = 0; i < triggers.length; i++) {
+ Trigger trigger = triggers[i];
+ if (cast(KeyStroke)trigger ) {
+ KeyStroke keyStroke = cast(KeyStroke) trigger;
+ int modifierKeys = keyStroke.getModifierKeys();
+ IKeyLookup lookup = KeyLookupFactory.getDefault();
+ if ((modifierKeys & lookup.getAlt()) !is 0) {
+ strokeCount += 8;
+ }
+ if ((modifierKeys & lookup.getCtrl()) !is 0) {
+ strokeCount += 2;
+ }
+ if ((modifierKeys & lookup.getShift()) !is 0) {
+ strokeCount += 4;
+ }
+ if ((modifierKeys & lookup.getCommand()) !is 0) {
+ strokeCount += 2;
+ }
+ } else {
+ strokeCount += 99;
+ }
+ }
+
+ return strokeCount;
+ }
+
+ /**
+ *
+ * Creates a tree of context identifiers, representing the hierarchical
+ * structure of the given contexts. The tree is structured as a mapping from
+ * child to parent.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the height of the context tree.
+ *
+ *
+ * @param contextIds
+ * The set of context identifiers to be converted into a tree;
+ * must not be null
.
+ * @return The tree of contexts to use; may be empty, but never
+ * null
. The keys and values are both strings.
+ */
+ private final Map!(Object,Object) createContextTreeFor(Set!(Object) contextIds) {
+ auto contextTree = new HashMap!(Object,Object);
+
+ foreach( e; contextIds ){
+ auto childContextId = (cast(ArrayWrapperString)e).array;
+ while (childContextId !is null) {
+ // Check if we've already got the part of the tree from here up.
+ if (contextTree.containsKey(/+childContextId+/e)) {
+ break;
+ }
+
+ // Retrieve the context.
+ Context childContext = contextManager
+ .getContext(childContextId);
+
+ // Add the child-parent pair to the tree.
+ try {
+ String parentContextId = childContext.getParentId();
+ contextTree.add(new ArrayWrapperString(childContextId), new ArrayWrapperString(parentContextId));
+ childContextId = parentContextId;
+ } catch (NotDefinedException e) {
+ break; // stop ascending
+ }
+ }
+ }
+
+ return contextTree;
+ }
+
+ /**
+ *
+ * Creates a tree of context identifiers, representing the hierarchical
+ * structure of the given contexts. The tree is structured as a mapping from
+ * child to parent. In this tree, the key binding specific filtering of
+ * contexts will have taken place.
+ *
+ *
+ * This method completes in O(n^2)
, where n
+ * is the height of the context tree.
+ *
+ *
+ * @param contextIds
+ * The set of context identifiers to be converted into a tree;
+ * must not be null
.
+ * @return The tree of contexts to use; may be empty, but never
+ * null
. The keys and values are both strings.
+ */
+ private final Map!(Object,Object) createFilteredContextTreeFor(Set!(Object) contextIds) {
+ // Check to see whether a dialog or window is active.
+ bool dialog = false;
+ bool window = false;
+ foreach( e; contextIds ){
+ String contextId = (cast(ArrayWrapperString) e).array;
+ if (IContextIds.CONTEXT_ID_DIALOG.equals(contextId)) {
+ dialog = true;
+ continue;
+ }
+ if (IContextIds.CONTEXT_ID_WINDOW.equals(contextId)) {
+ window = true;
+ continue;
+ }
+ }
+
+ /*
+ * Remove all context identifiers for contexts whose parents are dialog
+ * or window, and the corresponding dialog or window context is not
+ * active.
+ */
+ foreach( e; contextIds.dup ){
+ String contextId = (cast(ArrayWrapperString) e).array;
+ Context context = contextManager.getContext(contextId);
+ try {
+ String parentId = context.getParentId();
+ while (parentId !is null) {
+ if (IContextIds.CONTEXT_ID_DIALOG.equals(parentId)) {
+ if (!dialog) {
+ contextIds.remove(e);
+// contextIdItr.remove();
+ }
+ break;
+ }
+ if (IContextIds.CONTEXT_ID_WINDOW.equals(parentId)) {
+ if (!window) {
+ contextIds.remove(e);
+// contextIdItr.remove();
+ }
+ break;
+ }
+ if (IContextIds.CONTEXT_ID_DIALOG_AND_WINDOW
+ .equals(parentId)) {
+ if ((!window) && (!dialog)) {
+ contextIds.remove(e);
+// contextIdItr.remove();
+ }
+ break;
+ }
+
+ context = contextManager.getContext(parentId);
+ parentId = context.getParentId();
+ }
+ } catch (NotDefinedException e) {
+ // since this context was part of an undefined hierarchy,
+ // I'm going to yank it out as a bad bet
+ contextIds.remove(e);
+// contextIdItr.remove();
+
+ // This is a logging optimization, only log the error once.
+ if (context is null || !bindingErrors.contains(new ArrayWrapperString(context.getId()))) {
+ if (context !is null) {
+ bindingErrors.add(new ArrayWrapperString(context.getId()));
+ }
+
+ // now log like you've never logged before!
+ Policy.getLog().log(new Status( IStatus.ERROR, Policy.JFACE, IStatus.OK,
+ "Undefined context while filtering dialog/window contexts", //$NON-NLS-1$
+ e));
+ }
+ }
+ }
+
+ return createContextTreeFor(contextIds);
+ }
+
+ /**
+ *
+ * Notifies all of the listeners to this manager that the defined or active
+ * schemes of bindings have changed.
+ *
+ *
+ * The time this method takes to complete is dependent on external
+ * listeners.
+ *
+ *
+ * @param event
+ * The event to send to all of the listeners; must not be
+ * null
.
+ */
+ private final void fireBindingManagerChanged(BindingManagerEvent event) {
+ if (event is null) {
+ throw new NullPointerException();
+ }
+
+ Object[] listeners = getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ IBindingManagerListener listener = cast(IBindingManagerListener) listeners[i];
+ listener.bindingManagerChanged(event);
+ }
+ }
+
+ /**
+ *
+ * Returns the active bindings. The caller must not modify the returned map.
+ *
+ *
+ * This method completes in O(1)
. If the active bindings are
+ * not yet computed, then this completes in O(nn)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @return The map of triggers (TriggerSequence
) to
+ * bindings (Binding
) which are currently active.
+ * This value may be null
if there are no active
+ * bindings, and it may be empty.
+ */
+ private final Map!(Object,Object) getActiveBindings() {
+ if (activeBindings is null) {
+ recomputeBindings();
+ }
+
+ return activeBindings;
+ }
+
+ /**
+ *
+ * Returns the active bindings indexed by command identifier. The caller
+ * must not modify the returned map.
+ *
+ *
+ * This method completes in O(1)
. If the active bindings are
+ * not yet computed, then this completes in O(nn)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @return The map of fully-parameterized commands (ParameterizedCommand
)
+ * to triggers (TriggerSequence
) which are
+ * currently active. This value may be null
if there
+ * are no active bindings, and it may be empty.
+ */
+ private final Map!(Object,Object) getActiveBindingsByParameterizedCommand() {
+ if (activeBindingsByParameterizedCommand is null) {
+ recomputeBindings();
+ }
+
+ return activeBindingsByParameterizedCommand;
+ }
+
+ /**
+ *
+ * Computes the bindings for the current state of the application, but
+ * disregarding the current contexts. This can be useful when trying to
+ * display all the possible bindings.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @return A map of trigger (TriggerSequence
) to bindings (
+ * Seq!(Object)
containing Binding
).
+ * This map may be empty, but it is never null
.
+ */
+ public final Map!(Object,Object) getActiveBindingsDisregardingContext() {
+ if (bindings is null) {
+ // Not yet initialized. This is happening too early. Do nothing.
+ if( EMPTY_MAP is null ) EMPTY_MAP = new HashMap!(Object,Object);
+ return EMPTY_MAP;
+ }
+
+ // Build a cached binding set for that state.
+ CachedBindingSet bindingCache = new CachedBindingSet(null,
+ locales, platforms, activeSchemeIds);
+
+ /*
+ * Check if the cached binding set already exists. If so, simply set the
+ * active bindings and return.
+ */
+ CachedBindingSet existingCache = cast(CachedBindingSet) cachedBindings
+ .get(bindingCache);
+ if (existingCache is null) {
+ existingCache = bindingCache;
+ cachedBindings.add(existingCache, existingCache);
+ }
+ auto commandIdsByTrigger = existingCache.getBindingsByTrigger();
+ if (commandIdsByTrigger !is null) {
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ return commandIdsByTrigger;
+ }
+
+ // There is no cached entry for this.
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ // Compute the active bindings.
+ commandIdsByTrigger = new HashMap!(Object,Object);
+ auto triggersByParameterizedCommand = new HashMap!(Object,Object);
+ auto conflictsByTrigger = new HashMap!(Object,Object);
+ computeBindings(null, commandIdsByTrigger,
+ triggersByParameterizedCommand, conflictsByTrigger);
+ existingCache.setBindingsByTrigger(commandIdsByTrigger);
+ existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
+ existingCache.setConflictsByTrigger(conflictsByTrigger);
+ return /+Collections.unmodifiableMap(+/commandIdsByTrigger;
+ }
+
+ /**
+ *
+ * Computes the bindings for the current state of the application, but
+ * disregarding the current contexts. This can be useful when trying to
+ * display all the possible bindings.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @return A map of trigger (TriggerSequence
) to bindings (
+ * Seq!(Object)
containing Binding
).
+ * This map may be empty, but it is never null
.
+ * @since 3.2
+ */
+ private final Map!(Object,Object) getActiveBindingsDisregardingContextByParameterizedCommand() {
+ if (bindings is null) {
+ // Not yet initialized. This is happening too early. Do nothing.
+ if( EMPTY_MAP is null ) EMPTY_MAP = new HashMap!(Object,Object);
+ return EMPTY_MAP;
+ }
+
+ // Build a cached binding set for that state.
+ CachedBindingSet bindingCache = new CachedBindingSet(null,
+ locales, platforms, activeSchemeIds);
+
+ /*
+ * Check if the cached binding set already exists. If so, simply set the
+ * active bindings and return.
+ */
+ CachedBindingSet existingCache = cast(CachedBindingSet) cachedBindings
+ .get(bindingCache);
+ if (existingCache is null) {
+ existingCache = bindingCache;
+ cachedBindings.add(existingCache, existingCache);
+ }
+ auto triggersByParameterizedCommand = existingCache
+ .getTriggersByCommandId();
+ if (triggersByParameterizedCommand !is null) {
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ return /+Collections.unmodifiableMap(+/triggersByParameterizedCommand;
+ }
+
+ // There is no cached entry for this.
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ // Compute the active bindings.
+ auto commandIdsByTrigger = new HashMap!(Object,Object);
+ auto conflictsByTrigger = new HashMap!(Object,Object);
+ triggersByParameterizedCommand = new HashMap!(Object,Object);
+ computeBindings(null, commandIdsByTrigger,
+ triggersByParameterizedCommand, conflictsByTrigger);
+ existingCache.setBindingsByTrigger(commandIdsByTrigger);
+ existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
+ existingCache.setConflictsByTrigger(conflictsByTrigger);
+
+ return /+Collections.unmodifiableMap(+/triggersByParameterizedCommand;
+ }
+
+ /**
+ *
+ * Computes the bindings for the current state of the application, but
+ * disregarding the current contexts. This can be useful when trying to
+ * display all the possible bindings.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @return All of the active bindings (Binding
), not sorted
+ * in any fashion. This collection may be empty, but it is never
+ * null
.
+ */
+ public final View!(Object) getActiveBindingsDisregardingContextFlat() {
+ auto mergedBindings = new ArraySeq!(Object);
+
+ foreach( k,v; getActiveBindingsDisregardingContext() ){
+ auto bindingCollection = cast(View!(Object))v;
+ if ((bindingCollection !is null) && (!bindingCollection.drained())) {
+ foreach( e; bindingCollection ){
+ mergedBindings.append(e);
+ }
+ }
+ }
+
+ return mergedBindings;
+ }
+
+ /**
+ *
+ * Returns the active bindings for a particular command identifier, but
+ * discounting the current contexts. This method operates in O(n) time over
+ * the number of bindings.
+ *
+ *
+ * This method completes in O(1)
. If the active bindings are
+ * not yet computed, then this completes in O(nn)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @param parameterizedCommand
+ * The fully-parameterized command whose bindings are requested.
+ * This argument may be null
.
+ * @return The array of active triggers (TriggerSequence
)
+ * for a particular command identifier. This value is guaranteed to
+ * never be null
, but it may be empty.
+ * @since 3.2
+ */
+ public final TriggerSequence[] getActiveBindingsDisregardingContextFor(
+ ParameterizedCommand parameterizedCommand) {
+ Object object = getActiveBindingsDisregardingContextByParameterizedCommand()
+ .get(parameterizedCommand);
+ if (auto collection = cast(Seq!(Object))object ) {
+ return arraycast!(TriggerSequence)(collection.toArray());
+ }
+
+ return EMPTY_TRIGGER_SEQUENCE;
+ }
+
+ /**
+ *
+ * Returns the active bindings for a particular command identifier. This
+ * method operates in O(n) time over the number of bindings.
+ *
+ *
+ * This method completes in O(1)
. If the active bindings are
+ * not yet computed, then this completes in O(nn)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @param parameterizedCommand
+ * The fully-parameterized command whose bindings are requested.
+ * This argument may be null
.
+ * @return The array of active triggers (TriggerSequence
)
+ * for a particular command identifier. This value is guaranteed to
+ * never be null
, but it may be empty.
+ */
+ public final TriggerSequence[] getActiveBindingsFor(
+ ParameterizedCommand parameterizedCommand) {
+ Object object = getActiveBindingsByParameterizedCommand().get(
+ parameterizedCommand);
+ if ( auto collection = cast(Seq!(Object))object ) {
+ return arraycast!(TriggerSequence)(collection.toArray());
+ }
+
+ return EMPTY_TRIGGER_SEQUENCE;
+ }
+
+ /**
+ *
+ * Returns the active bindings for a particular command identifier. This
+ * method operates in O(n) time over the number of bindings.
+ *
+ *
+ * This method completes in O(1)
. If the active bindings are
+ * not yet computed, then this completes in O(nn)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @param commandId
+ * The identifier of the command whose bindings are requested.
+ * This argument may be null
. It is assumed that
+ * the command has no parameters.
+ * @return The array of active triggers (TriggerSequence
)
+ * for a particular command identifier. This value is guaranteed not
+ * to be null
, but it may be empty.
+ */
+ public final TriggerSequence[] getActiveBindingsFor(String commandId) {
+ ParameterizedCommand parameterizedCommand = new ParameterizedCommand(
+ commandManager.getCommand(commandId), null);
+ Object object = getActiveBindingsByParameterizedCommand().get(
+ parameterizedCommand);
+ if ( auto collection = cast(Seq!(Object))object ) {
+ return arraycast!(TriggerSequence)(collection.toArray());
+ }
+
+ return EMPTY_TRIGGER_SEQUENCE;
+ }
+
+ /**
+ * A variation on {@link BindingManager#getActiveBindingsFor(String)} that
+ * returns an array of bindings, rather than trigger sequences. This method
+ * is needed for doing "best" calculations on the active bindings.
+ *
+ * @param commandId
+ * The identifier of the command for which the active bindings
+ * should be retrieved; must not be null
.
+ * @return The active bindings for the given command; this value may be
+ * null
if there are no active bindings.
+ * @since 3.2
+ */
+ private final Binding[] getActiveBindingsFor1(String commandId) {
+ TriggerSequence[] triggers = getActiveBindingsFor(commandId);
+ if (triggers.length is 0) {
+ return null;
+ }
+
+ auto activeBindings = getActiveBindings();
+ if (activeBindings !is null) {
+ Binding[] bindings = new Binding[triggers.length];
+ for (int i = 0; i < triggers.length; i++) {
+ TriggerSequence triggerSequence = triggers[i];
+ Object object = activeBindings.get(triggerSequence);
+ Binding binding = cast(Binding) object;
+ bindings[i] = binding;
+ }
+ return bindings;
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ * Gets the currently active scheme.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ *
+ * @return The active scheme; may be null
if there is no
+ * active scheme. If a scheme is returned, it is guaranteed to be
+ * defined.
+ */
+ public final Scheme getActiveScheme() {
+ return activeScheme;
+ }
+
+ /**
+ * Gets the best active binding for a command. The best binding is the one
+ * that would be most appropriate to show in a menu. Bindings which belong
+ * to a child scheme are given preference over those in a parent scheme.
+ * Bindings which belong to a particular locale or platform are given
+ * preference over those that do not. The rest of the calculaton is based
+ * most on various concepts of "length", as well as giving some modifier
+ * keys preference (e.g., Alt
is less likely to appear than
+ * Ctrl
).
+ *
+ * @param commandId
+ * The identifier of the command for which the best active
+ * binding should be retrieved; must not be null
.
+ * @return The trigger sequence for the best binding; may be
+ * null
if no bindings are active for the given
+ * command.
+ * @since 3.2
+ */
+ public final TriggerSequence getBestActiveBindingFor(String commandId) {
+ Binding[] bindings = getActiveBindingsFor1(commandId);
+ if ((bindings is null) || (bindings.length is 0)) {
+ return null;
+ }
+
+ Binding bestBinding = bindings[0];
+ int compareTo;
+ for (int i = 1; i < bindings.length; i++) {
+ Binding currentBinding = bindings[i];
+
+ // Bindings in a child scheme are always given preference.
+ String bestSchemeId = bestBinding.getSchemeId();
+ String currentSchemeId = currentBinding.getSchemeId();
+ compareTo = compareSchemes(bestSchemeId, currentSchemeId);
+ if (compareTo > 0) {
+ bestBinding = currentBinding;
+ }
+ if (compareTo !is 0) {
+ continue;
+ }
+
+ /*
+ * Bindings with a locale are given preference over those that do
+ * not.
+ */
+ String bestLocale = bestBinding.getLocale();
+ String currentLocale = currentBinding.getLocale();
+ if ((bestLocale is null) && (currentLocale !is null)) {
+ bestBinding = currentBinding;
+ }
+ if (!(Util.opEquals(bestLocale, currentLocale))) {
+ continue;
+ }
+
+ /*
+ * Bindings with a platform are given preference over those that do
+ * not.
+ */
+ String bestPlatform = bestBinding.getPlatform();
+ String currentPlatform = currentBinding.getPlatform();
+ if ((bestPlatform is null) && (currentPlatform !is null)) {
+ bestBinding = currentBinding;
+ }
+ if (!(Util.opEquals(bestPlatform, currentPlatform))) {
+ continue;
+ }
+
+ /*
+ * Check to see which has the least number of triggers in the
+ * trigger sequence.
+ */
+ TriggerSequence bestTriggerSequence = bestBinding
+ .getTriggerSequence();
+ TriggerSequence currentTriggerSequence = currentBinding
+ .getTriggerSequence();
+ Trigger[] bestTriggers = bestTriggerSequence.getTriggers();
+ Trigger[] currentTriggers = currentTriggerSequence
+ .getTriggers();
+ compareTo = bestTriggers.length - currentTriggers.length;
+ if (compareTo > 0) {
+ bestBinding = currentBinding;
+ }
+ if (compareTo !is 0) {
+ continue;
+ }
+
+ /*
+ * Compare the number of keys pressed in each trigger sequence. Some
+ * types of keys count less than others (i.e., some types of
+ * modifiers keys are less likely to be chosen).
+ */
+ compareTo = countStrokes(bestTriggers)
+ - countStrokes(currentTriggers);
+ if (compareTo > 0) {
+ bestBinding = currentBinding;
+ }
+ if (compareTo !is 0) {
+ continue;
+ }
+
+ // If this is still a tie, then just chose the shortest text.
+ compareTo = bestTriggerSequence.format().length
+ - currentTriggerSequence.format().length;
+ if (compareTo > 0) {
+ bestBinding = currentBinding;
+ }
+ }
+
+ return bestBinding.getTriggerSequence();
+ }
+
+ /**
+ * Gets the formatted string representing the best active binding for a
+ * command. The best binding is the one that would be most appropriate to
+ * show in a menu. Bindings which belong to a child scheme are given
+ * preference over those in a parent scheme. The rest of the calculaton is
+ * based most on various concepts of "length", as well as giving some
+ * modifier keys preference (e.g., Alt
is less likely to
+ * appear than Ctrl
).
+ *
+ * @param commandId
+ * The identifier of the command for which the best active
+ * binding should be retrieved; must not be null
.
+ * @return The formatted string for the best binding; may be
+ * null
if no bindings are active for the given
+ * command.
+ * @since 3.2
+ */
+ public final String getBestActiveBindingFormattedFor(String commandId) {
+ TriggerSequence binding = getBestActiveBindingFor(commandId);
+ if (binding !is null) {
+ return binding.format();
+ }
+
+ return null;
+ }
+
+ /**
+ *
+ * Returns the set of all bindings managed by this class.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ *
+ * @return The array of all bindings. This value may be null
+ * and it may be empty.
+ */
+ public final Binding[] getBindings() {
+ if (bindings is null) {
+ return null;
+ }
+
+ Binding[] returnValue = new Binding[bindingCount];
+ System.arraycopy(bindings, 0, returnValue, 0, bindingCount);
+ return returnValue;
+ }
+
+ /**
+ *
+ * Returns the array of schemes that are defined.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ *
+ * @return The array of defined schemes; this value may be empty or
+ * null
.
+ */
+ public final Scheme[] getDefinedSchemes() {
+ return arraycast!(Scheme)(definedHandleObjects.toArray());
+ }
+
+ /**
+ *
+ * Returns the active locale for this binding manager. The locale is in the
+ * same format as Locale.getDefault().toString()
.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ *
+ * @return The active locale; never null
.
+ */
+ public final String getLocale() {
+ return locale;
+ }
+
+ /**
+ *
+ * Returns all of the possible bindings that start with the given trigger
+ * (but are not equal to the given trigger).
+ *
+ *
+ * This method completes in O(1)
. If the bindings aren't
+ * currently computed, then this completes in O(n)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @param trigger
+ * The prefix to look for; must not be null
.
+ * @return A map of triggers (TriggerSequence
) to bindings (Binding
).
+ * This map may be empty, but it is never null
.
+ */
+ public final Map!(Object,Object) getPartialMatches(TriggerSequence trigger) {
+ auto partialMatches = cast(Map!(Object,Object)) getPrefixTable().get(trigger);
+ if (partialMatches is null) {
+ if( EMPTY_MAP is null ) EMPTY_MAP = new HashMap!(Object,Object);
+ return EMPTY_MAP;
+ }
+
+ return partialMatches;
+ }
+
+ /**
+ *
+ * Returns the command identifier for the active binding matching this
+ * trigger, if any.
+ *
+ *
+ * This method completes in O(1)
. If the bindings aren't
+ * currently computed, then this completes in O(n)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @param trigger
+ * The trigger to match; may be null
.
+ * @return The binding that matches, if any; null
otherwise.
+ */
+ public final Binding getPerfectMatch(TriggerSequence trigger) {
+ return cast(Binding) getActiveBindings().get(trigger);
+ }
+
+ /**
+ *
+ * Returns the active platform for this binding manager. The platform is in
+ * the same format as DWT.getPlatform()
.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ *
+ * @return The active platform; never null
.
+ */
+ public final String getPlatform() {
+ return platform;
+ }
+
+ /**
+ *
+ * Returns the prefix table. The caller must not modify the returned map.
+ *
+ *
+ * This method completes in O(1)
. If the active bindings are
+ * not yet computed, then this completes in O(n)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @return A map of prefixes (TriggerSequence
) to a map of
+ * available completions (possibly null
, which means
+ * there is an exact match). The available completions is a map of
+ * trigger (TriggerSequence
) to binding (Binding
).
+ * This value will never be null
but may be empty.
+ */
+ private final Map!(Object,Object) getPrefixTable() {
+ if (prefixTable is null) {
+ recomputeBindings();
+ }
+
+ return prefixTable;
+ }
+
+ /**
+ *
+ * Gets the scheme with the given identifier. If the scheme does not already
+ * exist, then a new (undefined) scheme is created with that identifier.
+ * This guarantees that schemes will remain unique.
+ *
+ *
+ * This method completes in amortized O(1)
.
+ *
+ *
+ * @param schemeId
+ * The identifier for the scheme to retrieve; must not be
+ * null
.
+ * @return A scheme with the given identifier.
+ */
+ public final Scheme getScheme(String schemeId) {
+ checkId(schemeId);
+
+ Scheme scheme = cast(Scheme) handleObjectsById.get(schemeId);
+ if (scheme is null) {
+ scheme = new Scheme(schemeId);
+ handleObjectsById.add(schemeId, scheme);
+ scheme.addSchemeListener(this);
+ }
+
+ return scheme;
+ }
+
+ /**
+ *
+ * Ascends all of the parents of the scheme until no more parents are found.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the height of the context tree.
+ *
+ *
+ * @param schemeId
+ * The id of the scheme for which the parents should be found;
+ * may be null
.
+ * @return The array of scheme ids (String
) starting with
+ * schemeId
and then ascending through its ancestors.
+ */
+ private final String[] getSchemeIds(String schemeId) {
+ auto strings = new ArraySeq!(Object);
+ while (schemeId !is null) {
+ strings.append( stringcast(schemeId));
+ try {
+ schemeId = getScheme(schemeId).getParentId();
+ } catch (NotDefinedException e) {
+ Policy.getLog().log( new Status(
+ IStatus.ERROR, Policy.JFACE, IStatus.OK,
+ "Failed ascending scheme parents", //$NON-NLS-1$
+ e));
+ return null;
+ }
+ }
+
+ return stringcast(strings.toArray());
+ }
+
+ /**
+ *
+ * Returns whether the given trigger sequence is a partial match for the
+ * given sequence.
+ *
+ *
+ * This method completes in O(1)
. If the bindings aren't
+ * currently computed, then this completes in O(n)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @param trigger
+ * The sequence which should be the prefix for some binding;
+ * should not be null
.
+ * @return true
if the trigger can be found in the active
+ * bindings; false
otherwise.
+ */
+ public final bool isPartialMatch(TriggerSequence trigger) {
+ return (getPrefixTable().get(trigger) !is null);
+ }
+
+ /**
+ *
+ * Returns whether the given trigger sequence is a perfect match for the
+ * given sequence.
+ *
+ *
+ * This method completes in O(1)
. If the bindings aren't
+ * currently computed, then this completes in O(n)
, where
+ * n
is the number of bindings.
+ *
+ *
+ * @param trigger
+ * The sequence which should match exactly; should not be
+ * null
.
+ * @return true
if the trigger can be found in the active
+ * bindings; false
otherwise.
+ */
+ public final bool isPerfectMatch(TriggerSequence trigger) {
+ return getActiveBindings().containsKey(trigger);
+ }
+
+ /**
+ *
+ * Tests whether the locale for the binding matches one of the active
+ * locales.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of active locales.
+ *
+ *
+ * @param binding
+ * The binding with which to test; must not be null
.
+ * @return true
if the binding's locale matches;
+ * false
otherwise.
+ */
+ private final bool localeMatches(Binding binding) {
+ bool matches = false;
+
+ String locale = binding.getLocale();
+ if (locale is null) {
+ return true; // shortcut a common case
+ }
+
+ for (int i = 0; i < locales.length; i++) {
+ if (Util.opEquals(locales[i], locale)) {
+ matches = true;
+ break;
+ }
+ }
+
+ return matches;
+ }
+
+ /**
+ *
+ * Tests whether the platform for the binding matches one of the active
+ * platforms.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of active platforms.
+ *
+ *
+ * @param binding
+ * The binding with which to test; must not be null
.
+ * @return true
if the binding's platform matches;
+ * false
otherwise.
+ */
+ private final bool platformMatches(Binding binding) {
+ bool matches = false;
+
+ String platform = binding.getPlatform();
+ if (platform is null) {
+ return true; // shortcut a common case
+ }
+
+ for (int i = 0; i < platforms.length; i++) {
+ if (Util.opEquals(platforms[i], platform)) {
+ matches = true;
+ break;
+ }
+ }
+
+ return matches;
+ }
+
+ /**
+ *
+ * This recomputes the bindings based on changes to the state of the world.
+ * This computation can be triggered by changes to contexts, the active
+ * scheme, the locale, or the platform. This method tries to use the cache
+ * of pre-computed bindings, if possible. When this method completes,
+ * activeBindings
will be set to the current set of bindings
+ * and cachedBindings
will contain an instance of
+ * CachedBindingSet
representing these bindings.
+ *
+ *
+ * This method completes in O(n+pn)
, where n
+ * is the number of bindings, and p
is the average number of
+ * triggers in a trigger sequence.
+ *
+ */
+ private final void recomputeBindings() {
+ if (bindings is null) {
+ // Not yet initialized. This is happening too early. Do nothing.
+ if( EMPTY_MAP is null ) EMPTY_MAP = new HashMap!(Object,Object);
+ setActiveBindings(EMPTY_MAP, EMPTY_MAP,
+ EMPTY_MAP, EMPTY_MAP);
+ return;
+ }
+
+ // Figure out the current state.
+ auto activeContextIds = new HashSet!(Object);
+ foreach( e; contextManager.getActiveContextIds()){
+ activeContextIds.add(stringcast(e));
+ }
+ auto activeContextTree = createFilteredContextTreeFor(activeContextIds);
+
+ // Build a cached binding set for that state.
+ CachedBindingSet bindingCache = new CachedBindingSet(
+ activeContextTree, locales, platforms, activeSchemeIds);
+
+ /*
+ * Check if the cached binding set already exists. If so, simply set the
+ * active bindings and return.
+ */
+ CachedBindingSet existingCache = cast(CachedBindingSet) cachedBindings
+ .get(bindingCache);
+ if (existingCache is null) {
+ existingCache = bindingCache;
+ cachedBindings.add(existingCache, existingCache);
+ }
+ auto commandIdsByTrigger = existingCache.getBindingsByTrigger();
+ if (commandIdsByTrigger !is null) {
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ setActiveBindings(commandIdsByTrigger, existingCache
+ .getTriggersByCommandId(), existingCache.getPrefixTable(),
+ existingCache.getConflictsByTrigger());
+ return;
+ }
+
+ // There is no cached entry for this.
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ // Compute the active bindings.
+ commandIdsByTrigger = new HashMap!(Object,Object);
+ auto triggersByParameterizedCommand = new HashMap!(Object,Object);
+ auto conflictsByTrigger = new HashMap!(Object,Object);
+ computeBindings(activeContextTree, commandIdsByTrigger,
+ triggersByParameterizedCommand, conflictsByTrigger);
+ existingCache.setBindingsByTrigger(commandIdsByTrigger);
+ existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
+ existingCache.setConflictsByTrigger(conflictsByTrigger);
+ setActiveBindings(commandIdsByTrigger, triggersByParameterizedCommand,
+ buildPrefixTable(commandIdsByTrigger),
+ conflictsByTrigger);
+ existingCache.setPrefixTable(prefixTable);
+ }
+
+ /**
+ *
+ * Remove the specific binding by identity. Does nothing if the binding is
+ * not in the manager.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @param binding
+ * The binding to be removed; must not be null
.
+ * @since 3.2
+ */
+ public final void removeBinding(Binding binding) {
+ if (bindings is null || bindings.length < 1) {
+ return;
+ }
+
+ Binding[] newBindings = new Binding[bindings.length];
+ bool bindingsChanged = false;
+ int index = 0;
+ for (int i = 0; i < bindingCount; i++) {
+ Binding b = bindings[i];
+ if (b is binding) {
+ bindingsChanged = true;
+ } else {
+ newBindings[index++] = b;
+ }
+ }
+
+ if (bindingsChanged) {
+ this.bindings = newBindings;
+ bindingCount = index;
+ clearCache();
+ }
+ }
+
+ /**
+ *
+ * Removes a listener from this binding manager.
+ *
+ *
+ * This method completes in amortized O(1)
.
+ *
+ *
+ * @param listener
+ * The listener to be removed; must not be null
.
+ */
+ public final void removeBindingManagerListener(
+ IBindingManagerListener listener) {
+ removeListenerObject(cast(Object)listener);
+ }
+
+ /**
+ *
+ * Removes any binding that matches the given values -- regardless of
+ * command identifier.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @param sequence
+ * The sequence to match; may be null
.
+ * @param schemeId
+ * The scheme id to match; may be null
.
+ * @param contextId
+ * The context id to match; may be null
.
+ * @param locale
+ * The locale to match; may be null
.
+ * @param platform
+ * The platform to match; may be null
.
+ * @param windowManager
+ * The window manager to match; may be null
. TODO
+ * Currently ignored.
+ * @param type
+ * The type to look for.
+ *
+ */
+ public final void removeBindings(TriggerSequence sequence,
+ String schemeId, String contextId, String locale,
+ String platform, String windowManager, int type) {
+ if ((bindings is null) || (bindingCount < 1)) {
+ return;
+ }
+
+ Binding[] newBindings = new Binding[bindings.length];
+ bool bindingsChanged = false;
+ int index = 0;
+ for (int i = 0; i < bindingCount; i++) {
+ Binding binding = bindings[i];
+ bool equals = true;
+ equals &= Util.opEquals(sequence, binding.getTriggerSequence());
+ equals &= Util.opEquals(schemeId, binding.getSchemeId());
+ equals &= Util.opEquals(contextId, binding.getContextId());
+ equals &= Util.opEquals(locale, binding.getLocale());
+ equals &= Util.opEquals(platform, binding.getPlatform());
+ equals &= (type is binding.getType());
+ if (equals) {
+ bindingsChanged = true;
+ } else {
+ newBindings[index++] = binding;
+ }
+ }
+
+ if (bindingsChanged) {
+ this.bindings = newBindings;
+ bindingCount = index;
+ clearCache();
+ }
+ }
+
+ /**
+ *
+ * Attempts to remove deletion markers from the collection of bindings.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @param bindings
+ * The bindings from which the deleted items should be removed.
+ * This array should not be null
, but may be
+ * empty.
+ * @return The array of bindings with the deletions removed; never
+ * null
, but may be empty. Contains only instances
+ * of Binding
.
+ */
+ private final Binding[] removeDeletions(Binding[] bindings) {
+ auto deletions = new HashMap!(Object,Object);
+ Binding[] bindingsCopy = new Binding[bindingCount];
+ System.arraycopy(bindings, 0, bindingsCopy, 0, bindingCount);
+ int deletedCount = 0;
+
+ // Extract the deletions.
+ for (int i = 0; i < bindingCount; i++) {
+ Binding binding = bindingsCopy[i];
+ if ((binding.getParameterizedCommand() is null)
+ && (localeMatches(binding)) && (platformMatches(binding))) {
+ TriggerSequence sequence = binding.getTriggerSequence();
+ Object currentValue = deletions.get(sequence);
+ if (cast(Binding)currentValue ) {
+ auto collection = new ArraySeq!(Object);
+ collection.append(currentValue);
+ collection.append(binding);
+ deletions.add(sequence, collection);
+ } else if ( auto collection = cast(Seq!(Object))currentValue ) {
+ collection.append(binding);
+ } else {
+ deletions.add(sequence, binding);
+ }
+ bindingsCopy[i] = null;
+ deletedCount++;
+ }
+ }
+
+ if (DEBUG) {
+ Tracing.printTrace("BINDINGS", Format("There are {} deletion markers", deletions.size()) //$NON-NLS-1$ //$NON-NLS-2$
+ ); //$NON-NLS-1$
+ }
+
+ // Remove the deleted items.
+ for (int i = 0; i < bindingCount; i++) {
+ Binding binding = bindingsCopy[i];
+ if (binding !is null) {
+ Object deletion = deletions.get(binding
+ .getTriggerSequence());
+ if (cast(Binding)deletion ) {
+ if ((cast(Binding) deletion).deletes(binding)) {
+ bindingsCopy[i] = null;
+ deletedCount++;
+ }
+
+ } else if (cast(Seq!(Object))deletion ) {
+ Seq!(Object) collection = cast(Seq!(Object)) deletion;
+ foreach( e; collection){
+// Iterator iterator = collection.iterator();
+// while (iterator.hasNext()) {
+ Object deletionBinding = e;//iterator.next();
+ if (cast(Binding)deletionBinding ) {
+ if ((cast(Binding) deletionBinding).deletes(binding)) {
+ bindingsCopy[i] = null;
+ deletedCount++;
+ break;
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ // Compact the array.
+ Binding[] returnValue = new Binding[bindingCount - deletedCount];
+ int index = 0;
+ for (int i = 0; i < bindingCount; i++) {
+ Binding binding = bindingsCopy[i];
+ if (binding !is null) {
+ returnValue[index++] = binding;
+ }
+ }
+
+ return returnValue;
+ }
+
+ /**
+ *
+ * Attempts to resolve the conflicts for the given bindings.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @param bindings
+ * The bindings which all match the same trigger sequence; must
+ * not be null
, and should contain at least two
+ * items. This collection should only contain instances of
+ * Binding
(i.e., no null
values).
+ * @param activeContextTree
+ * The tree of contexts to be used for all of the comparison. All
+ * of the keys should be active context identifiers (i.e., never
+ * null
). The values will be their parents (i.e.,
+ * possibly null
). Both keys and values are
+ * context identifiers (String
). This map should
+ * never be empty, and must never be null
.
+ * @return The binding which best matches the current state. If there is a
+ * tie, then return null
.
+ */
+ private final Binding resolveConflicts(View!(Object) bindings,
+ Map!(Object,Object) activeContextTree) {
+ /*
+ * This flag is used to indicate when the bestMatch binding conflicts
+ * with another binding. We keep the best match binding so that we know
+ * if we find a better binding. However, if we don't find a better
+ * binding, then we known to return null.
+ */
+ bool conflict = false;
+
+// Iterator bindingItr = bindings.iterator();
+ Binding bestMatch;
+ bool first = true;
+ foreach( b; bindings ){
+ if( first ){
+ first = false;
+ bestMatch = cast(Binding) b;//bindingItr.next();
+ continue;
+ }
+
+ /*
+ * Iterate over each binding and compare it with the best match. If a
+ * better match is found, then replace the best match and set the
+ * conflict flag to false. If a conflict is found, then leave the best
+ * match and set the conflict flag. Otherwise, just continue.
+ */
+// while (bindingItr.hasNext()) {
+ Binding current = cast(Binding) b;//bindingItr.next();
+
+ /*
+ * SCHEME: Test whether the current is in a child scheme. Bindings
+ * defined in a child scheme will always take priority over bindings
+ * defined in a parent scheme.
+ */
+ String currentSchemeId = current.getSchemeId();
+ String bestSchemeId = bestMatch.getSchemeId();
+ int compareTo = compareSchemes(bestSchemeId, currentSchemeId);
+ if (compareTo > 0) {
+ bestMatch = current;
+ conflict = false;
+ }
+ if (compareTo !is 0) {
+ continue;
+ }
+
+ /*
+ * CONTEXTS: Check for context superiority. Bindings defined in a
+ * child context will take priority over bindings defined in a
+ * parent context -- assuming that the schemes lead to a conflict.
+ */
+ String currentContext = current.getContextId();
+ String bestContext = bestMatch.getContextId();
+ if (!currentContext.equals(bestContext)) {
+ bool goToNextBinding = false;
+
+ // Ascend the current's context tree.
+ String contextPointer = currentContext;
+ while (contextPointer !is null) {
+ if (contextPointer.equals(bestContext)) {
+ // the current wins
+ bestMatch = current;
+ conflict = false;
+ goToNextBinding = true;
+ break;
+ }
+ contextPointer = stringcast(activeContextTree
+ .get(stringcast(contextPointer)));
+ }
+
+ // Ascend the best match's context tree.
+ contextPointer = bestContext;
+ while (contextPointer !is null) {
+ if (contextPointer.equals(currentContext)) {
+ // the best wins
+ goToNextBinding = true;
+ break;
+ }
+ contextPointer = stringcast( activeContextTree
+ .get(stringcast(contextPointer)));
+ }
+
+ if (goToNextBinding) {
+ continue;
+ }
+ }
+
+ /*
+ * TYPE: Test for type superiority.
+ */
+ if (current.getType() > bestMatch.getType()) {
+ bestMatch = current;
+ conflict = false;
+ continue;
+ } else if (bestMatch.getType() > current.getType()) {
+ continue;
+ }
+
+ // We could not resolve the conflict between these two.
+ conflict = true;
+ }
+
+ // If the best match represents a conflict, then return null.
+ if (conflict) {
+ return null;
+ }
+
+ // Otherwise, we have a winner....
+ return bestMatch;
+ }
+
+ /**
+ *
+ * Notifies this manager that a scheme has changed. This method is intended
+ * for internal use only.
+ *
+ *
+ * This method calls out to listeners, and so the time it takes to complete
+ * is dependent on third-party code.
+ *
+ *
+ * @param schemeEvent
+ * An event describing the change in the scheme.
+ */
+ public final void schemeChanged(SchemeEvent schemeEvent) {
+ if (schemeEvent.isDefinedChanged()) {
+ Scheme scheme = schemeEvent.getScheme();
+ bool schemeIdAdded = scheme.isDefined();
+ bool activeSchemeChanged = false;
+ if (schemeIdAdded) {
+ definedHandleObjects.add(scheme);
+ } else {
+ definedHandleObjects.remove(scheme);
+
+ if (activeScheme is scheme) {
+ activeScheme = null;
+ activeSchemeIds = null;
+ activeSchemeChanged = true;
+
+ // Clear the binding solution.
+ clearSolution();
+ }
+ }
+
+ if (isListenerAttached()) {
+ fireBindingManagerChanged(new BindingManagerEvent(this, false,
+ null, activeSchemeChanged, scheme, schemeIdAdded,
+ false, false));
+ }
+ }
+ }
+
+ /**
+ * Sets the active bindings and the prefix table. This ensures that the two
+ * values change at the same time, and that any listeners are notified
+ * appropriately.
+ *
+ * @param activeBindings
+ * This is a map of triggers ( TriggerSequence
)
+ * to bindings (Binding
). This value will only
+ * be null
if the active bindings have not yet
+ * been computed. Otherwise, this value may be empty.
+ * @param activeBindingsByCommandId
+ * This is a map of fully-parameterized commands (ParameterizedCommand
)
+ * to triggers ( TriggerSequence
). This value
+ * will only be null
if the active bindings have
+ * not yet been computed. Otherwise, this value may be empty.
+ * @param prefixTable
+ * A map of prefixes (TriggerSequence
) to a map
+ * of available completions (possibly null
, which
+ * means there is an exact match). The available completions is a
+ * map of trigger (TriggerSequence
) to binding (Binding
).
+ * This value may be null
if there is no existing
+ * solution.
+ */
+ private final void setActiveBindings(Map!(Object,Object) activeBindings,
+ Map!(Object,Object) activeBindingsByCommandId, Map!(Object,Object) prefixTable,
+ Map!(Object,Object) conflicts) {
+ this.activeBindings = activeBindings;
+ Map!(Object,Object) previousBindingsByParameterizedCommand = this.activeBindingsByParameterizedCommand;
+ this.activeBindingsByParameterizedCommand = activeBindingsByCommandId;
+ this.prefixTable = prefixTable;
+ InternalPolicy.currentConflicts = conflicts;
+
+ fireBindingManagerChanged(new BindingManagerEvent(this, true,
+ previousBindingsByParameterizedCommand, false, null, false,
+ false, false));
+ }
+
+ /**
+ *
+ * Selects one of the schemes as the active scheme. This scheme must be
+ * defined.
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the height of the context tree.
+ *
+ *
+ * @param scheme
+ * The scheme to become active; must not be null
.
+ * @throws NotDefinedException
+ * If the given scheme is currently undefined.
+ */
+ public final void setActiveScheme(Scheme scheme) {
+ if (scheme is null) {
+ throw new NullPointerException("Cannot activate a null scheme"); //$NON-NLS-1$
+ }
+
+ if ((scheme is null) || (!scheme.isDefined())) {
+ throw new NotDefinedException(
+ "Cannot activate an undefined scheme. " //$NON-NLS-1$
+ ~ scheme.getId());
+ }
+
+ if (Util.opEquals(activeScheme, scheme)) {
+ return;
+ }
+
+ activeScheme = scheme;
+ activeSchemeIds = getSchemeIds(activeScheme.getId());
+ clearSolution();
+ fireBindingManagerChanged(new BindingManagerEvent(this, false, null,
+ true, null, false, false, false));
+ }
+
+ /**
+ *
+ * Changes the set of bindings for this binding manager. Changing the set of
+ * bindings all at once ensures that: (1) duplicates are removed; and (2)
+ * avoids unnecessary intermediate computations. This method clears the
+ * existing bindings, but does not trigger a recomputation (other method
+ * calls are required to do that).
+ *
+ *
+ * This method completes in O(n)
, where n
is
+ * the number of bindings.
+ *
+ *
+ * @param bindings
+ * The new array of bindings; may be null
. This
+ * set is copied into a local data structure.
+ */
+ public final void setBindings(Binding[] bindings) {
+ if (Arrays.equals(this.bindings, bindings)) {
+ return; // nothing has changed
+ }
+
+ if ((bindings is null) || (bindings.length is 0)) {
+ this.bindings = null;
+ bindingCount = 0;
+ } else {
+ int bindingsLength = bindings.length;
+ this.bindings = new Binding[bindingsLength];
+ System.arraycopy(bindings, 0, this.bindings, 0, bindingsLength);
+ bindingCount = bindingsLength;
+ }
+ clearCache();
+ }
+
+ /**
+ *
+ * Changes the locale for this binding manager. The locale can be used to
+ * provide locale-specific bindings. If the locale is different than the
+ * current locale, this will force a recomputation of the bindings. The
+ * locale is in the same format as
+ * Locale.getDefault().toString()
.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ *
+ * @param locale
+ * The new locale; must not be null
.
+ * @see Locale#getDefault()
+ */
+ public final void setLocale(String locale) {
+ if (locale is null) {
+ throw new NullPointerException("The locale cannot be null"); //$NON-NLS-1$
+ }
+
+ if (!Util.opEquals(this.locale, locale)) {
+ this.locale = locale;
+ this.locales = expand(locale, LOCALE_SEPARATOR);
+ clearSolution();
+ fireBindingManagerChanged(new BindingManagerEvent(this, false,
+ null, false, null, false, true, false));
+ }
+ }
+
+ /**
+ *
+ * Changes the platform for this binding manager. The platform can be used
+ * to provide platform-specific bindings. If the platform is different than
+ * the current platform, then this will force a recomputation of the
+ * bindings. The locale is in the same format as
+ * DWT.getPlatform()
.
+ *
+ *
+ * This method completes in O(1)
.
+ *
+ *
+ * @param platform
+ * The new platform; must not be null
.
+ * @see dwt.DWT#getPlatform()
+ */
+ public final void setPlatform(String platform) {
+ if (platform is null) {
+ throw new NullPointerException("The platform cannot be null"); //$NON-NLS-1$
+ }
+
+ if (!Util.opEquals(this.platform, platform)) {
+ this.platform = platform;
+ this.platforms = expand(platform, Util.ZERO_LENGTH_STRING);
+ clearSolution();
+ fireBindingManagerChanged(new BindingManagerEvent(this, false,
+ null, false, null, false, false, true));
+ }
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/BindingManagerEvent.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/BindingManagerEvent.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ *******************************************************************************/
+
+module dwtx.jface.bindings.BindingManagerEvent;
+
+import dwtx.jface.bindings.BindingManager;
+import dwtx.jface.bindings.Scheme;
+import dwtx.jface.bindings.TriggerSequence;
+
+// import tango.util.collection.Seq!(Object);
+import tango.util.collection.model.Map;
+import tango.util.collection.model.Seq;
+
+import dwtx.core.commands.ParameterizedCommand;
+import dwtx.core.commands.common.AbstractBitSetEvent;
+import dwtx.jface.util.Util;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An instance of this class describes changes to an instance of
+ * BindingManager
.
+ *
+ * This class is not intended to be extended by clients.
+ *
+ *
+ * @since 3.1
+ * @see IBindingManagerListener#bindingManagerChanged(BindingManagerEvent)
+ */
+public final class BindingManagerEvent : AbstractBitSetEvent {
+
+ /**
+ * The bit used to represent whether the map of active bindings has changed.
+ */
+ private static const int CHANGED_ACTIVE_BINDINGS = 1;
+
+ /**
+ * The bit used to represent whether the active scheme has changed.
+ */
+ private static const int CHANGED_ACTIVE_SCHEME = 1 << 1;
+
+ /**
+ * The bit used to represent whether the active locale has changed.
+ */
+ private static const int CHANGED_LOCALE = 1 << 2;
+
+ /**
+ * The bit used to represent whether the active platform has changed.
+ */
+ private static const int CHANGED_PLATFORM = 1 << 3;
+
+ /**
+ * The bit used to represent whether the scheme's defined state has changed.
+ */
+ private static const int CHANGED_SCHEME_DEFINED = 1 << 4;
+
+ /**
+ * The binding manager that has changed; this value is never
+ * null
.
+ */
+ private const BindingManager manager;
+
+ /**
+ * The map of triggers (Seq!(Object)
of
+ * TriggerSequence
) by parameterized command (ParameterizedCommand
)
+ * before the change occurred. This map may be empty and it may be
+ * null
.
+ */
+ private const Map!(Object,Object) previousTriggersByParameterizedCommand;
+
+ /**
+ * The scheme that became defined or undefined. This value may be
+ * null
if no scheme changed its defined state.
+ */
+ private const Scheme scheme;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param manager
+ * the instance of the binding manager that changed; must not be
+ * null
.
+ * @param activeBindingsChanged
+ * Whether the active bindings have changed.
+ * @param previousTriggersByParameterizedCommand
+ * The map of triggers (TriggerSequence
) by
+ * fully-parameterized command (ParameterizedCommand
)
+ * before the change occured. This map may be null
+ * or empty.
+ * @param activeSchemeChanged
+ * true, iff the active scheme changed.
+ * @param scheme
+ * The scheme that became defined or undefined; null
+ * if no scheme changed state.
+ * @param schemeDefined
+ * true
if the given scheme became defined;
+ * false
otherwise.
+ * @param localeChanged
+ * true
iff the active locale changed
+ * @param platformChanged
+ * true
iff the active platform changed
+ */
+ public this(BindingManager manager,
+ bool activeBindingsChanged,
+ Map!(Object,Object) previousTriggersByParameterizedCommand,
+ bool activeSchemeChanged, Scheme scheme,
+ bool schemeDefined, bool localeChanged,
+ bool platformChanged) {
+ if (manager is null) {
+ throw new NullPointerException(
+ "A binding manager event needs a binding manager"); //$NON-NLS-1$
+ }
+ this.manager = manager;
+
+ if (schemeDefined && (scheme is null)) {
+ throw new NullPointerException(
+ "If a scheme changed defined state, then there should be a scheme identifier"); //$NON-NLS-1$
+ }
+ this.scheme = scheme;
+
+ this.previousTriggersByParameterizedCommand = previousTriggersByParameterizedCommand;
+
+ if (activeBindingsChanged) {
+ changedValues |= CHANGED_ACTIVE_BINDINGS;
+ }
+ if (activeSchemeChanged) {
+ changedValues |= CHANGED_ACTIVE_SCHEME;
+ }
+ if (localeChanged) {
+ changedValues |= CHANGED_LOCALE;
+ }
+ if (platformChanged) {
+ changedValues |= CHANGED_PLATFORM;
+ }
+ if (schemeDefined) {
+ changedValues |= CHANGED_SCHEME_DEFINED;
+ }
+ }
+
+ /**
+ * Returns the instance of the manager that changed.
+ *
+ * @return the instance of the manager that changed. Guaranteed not to be
+ * null
.
+ */
+ public final BindingManager getManager() {
+ return manager;
+ }
+
+ /**
+ * Returns the scheme that changed.
+ *
+ * @return The changed scheme
+ */
+ public final Scheme getScheme() {
+ return scheme;
+ }
+
+ /**
+ * Returns whether the active bindings have changed.
+ *
+ * @return true
if the active bindings have changed;
+ * false
otherwise.
+ */
+ public final bool isActiveBindingsChanged() {
+ return ((changedValues & CHANGED_ACTIVE_BINDINGS) !is 0);
+ }
+
+ /**
+ * Computes whether the active bindings have changed for a given command
+ * identifier.
+ *
+ * @param parameterizedCommand
+ * The fully-parameterized command whose bindings might have
+ * changed; must not be null
.
+ * @return true
if the active bindings have changed for the
+ * given command identifier; false
otherwise.
+ */
+ public final bool isActiveBindingsChangedFor(
+ ParameterizedCommand parameterizedCommand) {
+ TriggerSequence[] currentBindings = manager
+ .getActiveBindingsFor(parameterizedCommand);
+ TriggerSequence[] previousBindings;
+ if (previousTriggersByParameterizedCommand !is null) {
+ Seq!(Object) previousBindingCollection = cast(Seq!(Object)) previousTriggersByParameterizedCommand
+ .get(parameterizedCommand);
+ if (previousBindingCollection is null) {
+ previousBindings = null;
+ } else {
+ previousBindings = cast(TriggerSequence[])previousBindingCollection.toArray();
+ }
+ } else {
+ previousBindings = null;
+ }
+
+ return !Util.opEquals(currentBindings, previousBindings);
+ }
+
+ /**
+ * Returns whether or not the active scheme changed.
+ *
+ * @return true, iff the active scheme property changed.
+ */
+ public bool isActiveSchemeChanged() {
+ return ((changedValues & CHANGED_ACTIVE_SCHEME) !is 0);
+ }
+
+ /**
+ * Returns whether the locale has changed
+ *
+ * @return true
if the locale changed; false
+ * otherwise.
+ */
+ public bool isLocaleChanged() {
+ return ((changedValues & CHANGED_LOCALE) !is 0);
+ }
+
+ /**
+ * Returns whether the platform has changed
+ *
+ * @return true
if the platform changed; false
+ * otherwise.
+ */
+ public bool isPlatformChanged() {
+ return ((changedValues & CHANGED_PLATFORM) !is 0);
+ }
+
+ /**
+ * Returns whether the list of defined scheme identifiers has changed.
+ *
+ * @return true
if the list of scheme identifiers has
+ * changed; false
otherwise.
+ */
+ public final bool isSchemeChanged() {
+ return (scheme !is null);
+ }
+
+ /**
+ * Returns whether or not the scheme became defined
+ *
+ * @return true
if the scheme became defined.
+ */
+ public final bool isSchemeDefined() {
+ return (((changedValues & CHANGED_SCHEME_DEFINED) !is 0) && (scheme !is null));
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/CachedBindingSet.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/CachedBindingSet.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,372 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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
+ *******************************************************************************/
+module dwtx.jface.bindings.CachedBindingSet;
+
+import dwtx.jface.bindings.TriggerSequence;
+import dwtx.jface.bindings.Binding;
+
+import tango.util.collection.model.Map;
+
+import dwtx.jface.util.Util;
+
+import dwt.dwthelper.utils;
+
+/**
+ *
+ * A resolution of bindings for a given state. To see if we already have a
+ * cached binding set, just create one of these binding sets and then look it up
+ * in a map. If it is not already there, then add it and set the cached binding
+ * resolution.
+ *
+ *
+ * @since 3.1
+ */
+final class CachedBindingSet {
+
+ /**
+ * A factor for computing the hash code for all cached binding sets.
+ */
+ private const static int HASH_FACTOR = 89;
+
+ /**
+ *
+ * A representation of the tree of active contexts at the time this cached
+ * binding set was computed. It is a map of context id (String
)
+ * to context id (String
). Each key represents one of the
+ * active contexts or one of its ancestors, while each value represents its
+ * parent. This is a way of perserving information about what the hierarchy
+ * looked like.
+ *
+ *
+ * This value will be null
if the contexts were disregarded
+ * in the computation. It may also be empty. All of the keys are guaranteed
+ * to be non- null
, but the values can be null
+ * (i.e., no parent).
+ *
+ */
+ private const Map!(Object,Object) activeContextTree;
+
+ /**
+ * The map representing the resolved state of the bindings. This is a map of
+ * a trigger (TriggerSequence
) to binding (Binding
).
+ * This value may be null
if it has not yet been initialized.
+ */
+ private Map!(Object,Object) bindingsByTrigger = null;
+
+ /**
+ * A map of triggers to collections of bindings. If this binding set
+ * contains conflicts, they are logged here.
+ *
+ * @since 3.3
+ */
+ private Map!(Object,Object) conflictsByTrigger = null;
+
+ /**
+ * The hash code for this object. This value is computed lazily, and marked
+ * as invalid when one of the values on which it is based changes.
+ */
+ private /+transient+/ int hashCode;
+
+ /**
+ * Whether hashCode
still contains a valid value.
+ */
+ private /+transient+/ bool hashCodeComputed = false;
+
+ /**
+ *
+ * The list of locales that were active at the time this binding set was
+ * computed. This list starts with the most specific representation of the
+ * locale, and moves to more general representations. For example, this
+ * array might look like ["en_US", "en", "", null].
+ *
+ *
+ * This value will never be null
, and it will never be
+ * empty. It must contain at least one element, but its elements can be
+ * null
.
+ *
+ */
+ private const String[] locales;
+
+ /**
+ *
+ * The list of platforms that were active at the time this binding set was
+ * computed. This list starts with the most specific representation of the
+ * platform, and moves to more general representations. For example, this
+ * array might look like ["gtk", "", null].
+ *
+ *
+ * This value will never be null
, and it will never be
+ * empty. It must contain at least one element, but its elements can be
+ * null
.
+ *
+ */
+ private const String[] platforms;
+
+ /**
+ * A map of prefixes (TriggerSequence
) to a map of
+ * available completions (possibly null
, which means there
+ * is an exact match). The available completions is a map of trigger (TriggerSequence
)
+ * to command identifier (String
). This value is
+ * null
if it has not yet been initialized.
+ */
+ private Map!(Object,Object) prefixTable = null;
+
+ /**
+ *
+ * The list of schemes that were active at the time this binding set was
+ * computed. This list starts with the active scheme, and then continues
+ * with all of its ancestors -- in order. For example, this might look like
+ * ["emacs", "default"].
+ *
+ *
+ * This value will never be null
, and it will never be
+ * empty. It must contain at least one element. Its elements cannot be
+ * null
.
+ *
+ */
+ private const String[] schemeIds;
+
+ /**
+ * The map representing the resolved state of the bindings. This is a map of
+ * a command id (String
) to triggers (Collection
+ * of TriggerSequence
). This value may be null
+ * if it has not yet been initialized.
+ */
+ private Map!(Object,Object) triggersByCommandId = null;
+
+ /**
+ * Constructs a new instance of CachedBindingSet
.
+ *
+ * @param activeContextTree
+ * The set of context identifiers that were active when this
+ * binding set was calculated; may be empty. If it is
+ * null
, then the contexts were disregarded in
+ * the computation. This is a map of context id (
+ * String
) to parent context id (
+ * String
). This is a way of caching the look of
+ * the context tree at the time the binding set was computed.
+ * @param locales
+ * The locales that were active when this binding set was
+ * calculated. The first element is the currently active locale,
+ * and it is followed by increasingly more general locales. This
+ * must not be null
and must contain at least one
+ * element. The elements can be null
, though.
+ * @param platforms
+ * The platform that were active when this binding set was
+ * calculated. The first element is the currently active
+ * platform, and it is followed by increasingly more general
+ * platforms. This must not be null
and must
+ * contain at least one element. The elements can be
+ * null
, though.
+ * @param schemeIds
+ * The scheme that was active when this binding set was
+ * calculated, followed by its ancestors. This may be
+ * null
null
.
+ */
+ this(Map!(Object,Object) activeContextTree, String[] locales,
+ String[] platforms, String[] schemeIds) {
+ if (locales is null) {
+ throw new NullPointerException("The locales cannot be null."); //$NON-NLS-1$
+ }
+
+ if (locales.length is 0) {
+ throw new NullPointerException("The locales cannot be empty."); //$NON-NLS-1$
+ }
+
+ if (platforms is null) {
+ throw new NullPointerException("The platforms cannot be null."); //$NON-NLS-1$
+ }
+
+ if (platforms.length is 0) {
+ throw new NullPointerException("The platforms cannot be empty."); //$NON-NLS-1$
+ }
+
+ this.activeContextTree = activeContextTree;
+ this.locales = locales;
+ this.platforms = platforms;
+ this.schemeIds = schemeIds;
+ }
+
+ /**
+ * Compares this binding set with another object. The objects will be equal
+ * if they are both instance of CachedBindingSet
and have
+ * equivalent values for all of their properties.
+ *
+ * @param object
+ * The object with which to compare; may be null
.
+ * @return true
if they are both instances of
+ * CachedBindingSet
and have the same values for all
+ * of their properties; false
otherwise.
+ */
+ public final override int opEquals(Object object) {
+ if (!(cast(CachedBindingSet)object )) {
+ return false;
+ }
+
+ CachedBindingSet other = cast(CachedBindingSet) object;
+
+ if (!Util.opEquals(cast(Object)activeContextTree, cast(Object)other.activeContextTree)) {
+ return false;
+ }
+ if (!Util.opEquals(locales, other.locales)) {
+ return false;
+ }
+ if (!Util.opEquals(platforms, other.platforms)) {
+ return false;
+ }
+ return Util.opEquals(schemeIds, other.schemeIds);
+ }
+
+ /**
+ * Returns the map of command identifiers indexed by trigger sequence.
+ *
+ * @return A map of triggers (TriggerSequence
) to bindings (Binding
).
+ * This value may be null
if this was not yet
+ * initialized.
+ */
+ final Map!(Object,Object) getBindingsByTrigger() {
+ return bindingsByTrigger;
+ }
+
+ /**
+ * Returns a map of conflicts for this set of contexts.
+ *
+ * @return A map of trigger to a collection of Bindings. May be
+ * null
.
+ * @since 3.3
+ */
+ final Map!(Object,Object) getConflictsByTrigger() {
+ return conflictsByTrigger;
+ }
+
+ /**
+ * Returns the map of prefixes to a map of trigger sequence to command
+ * identifiers.
+ *
+ * @return A map of prefixes (TriggerSequence
) to a map of
+ * available completions (possibly null
, which means
+ * there is an exact match). The available completions is a map of
+ * trigger (TriggerSequence
) to command identifier (String
).
+ * This value may be null
if it has not yet been
+ * initialized.
+ */
+ final Map!(Object,Object) getPrefixTable() {
+ return prefixTable;
+ }
+
+ /**
+ * Returns the map of triggers indexed by command identifiers.
+ *
+ * @return A map of command identifiers (String
) to
+ * triggers (Collection
of
+ * TriggerSequence
). This value may be
+ * null
if this was not yet initialized.
+ */
+ final Map!(Object,Object) getTriggersByCommandId() {
+ return triggersByCommandId;
+ }
+
+ /**
+ * Computes the hash code for this cached binding set. The hash code is
+ * based only on the immutable values. This allows the set to be created and
+ * checked for in a hashed collection before doing any
+ * computation.
+ *
+ * @return The hash code for this cached binding set.
+ */
+ public final override hash_t toHash() {
+ if (!hashCodeComputed) {
+
+ auto HASH_INITIAL = dwt.dwthelper.utils.toHash(CachedBindingSet.classinfo.name );
+ hashCode = HASH_INITIAL;
+ hashCode = hashCode * HASH_FACTOR
+ + Util.toHash(cast(Object)activeContextTree);
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(locales);
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(platforms);
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(schemeIds);
+ hashCodeComputed = true;
+ }
+
+ return hashCode;
+ }
+
+ /**
+ * Sets the map of command identifiers indexed by trigger.
+ *
+ * @param commandIdsByTrigger
+ * The map to set; must not be null
. This is a
+ * map of triggers (TriggerSequence
) to binding (Binding
).
+ */
+ final void setBindingsByTrigger(Map!(Object,Object) commandIdsByTrigger) {
+ if (commandIdsByTrigger is null) {
+ throw new NullPointerException(
+ "Cannot set a null binding resolution"); //$NON-NLS-1$
+ }
+
+ this.bindingsByTrigger = commandIdsByTrigger;
+ }
+
+ /**
+ * Sets the map of conflicting bindings by trigger.
+ *
+ * @param conflicts
+ * The map to set; must not be null
.
+ * @since 3.3
+ */
+ final void setConflictsByTrigger(Map!(Object,Object) conflicts) {
+ if (conflicts is null) {
+ throw new NullPointerException(
+ "Cannot set a null binding conflicts"); //$NON-NLS-1$
+ }
+ conflictsByTrigger = conflicts;
+ }
+
+ /**
+ * Sets the map of prefixes to a map of trigger sequence to command
+ * identifiers.
+ *
+ * @param prefixTable
+ * A map of prefixes (TriggerSequence
) to a map
+ * of available completions (possibly null
, which
+ * means there is an exact match). The available completions is a
+ * map of trigger (TriggerSequence
) to command
+ * identifier (String
). Must not be
+ * null
.
+ */
+ final void setPrefixTable(Map!(Object,Object) prefixTable) {
+ if (prefixTable is null) {
+ throw new NullPointerException("Cannot set a null prefix table"); //$NON-NLS-1$
+ }
+
+ this.prefixTable = prefixTable;
+ }
+
+ /**
+ * Sets the map of triggers indexed by command identifiers.
+ *
+ * @param triggersByCommandId
+ * The map to set; must not be null
. This is a
+ * map of command identifiers (String
) to
+ * triggers (Collection
of
+ * TriggerSequence
).
+ */
+ final void setTriggersByCommandId(Map!(Object,Object) triggersByCommandId) {
+ if (triggersByCommandId is null) {
+ throw new NullPointerException(
+ "Cannot set a null binding resolution"); //$NON-NLS-1$
+ }
+
+ this.triggersByCommandId = triggersByCommandId;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/IBindingManagerListener.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/IBindingManagerListener.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * An instance of BindingManagerListener
can be used by clients to
+ * receive notification of changes to an instance of
+ * BindingManager
.
+ *
+ * This interface may be implemented by clients. + *
+ * + * @since 3.1 + * @see BindingManager#addBindingManagerListener(IBindingManagerListener) + * @see dwtx.jface.bindings.BindingManager#addBindingManagerListener(IBindingManagerListener) + * @see BindingManagerEvent + */ +public interface IBindingManagerListener { + + /** + * Notifies that attributes inside an instance ofBindingManager
have changed.
+ * Specific details are described in the BindingManagerEvent
. Changes in the
+ * binding manager can cause the set of defined or active schemes or bindings to change.
+ *
+ * @param event
+ * the binding manager event. Guaranteed not to be null
.
+ */
+ void bindingManagerChanged(BindingManagerEvent event);
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/ISchemeListener.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/ISchemeListener.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * An instance of ISchemeListener
can be used by clients to
+ * receive notification of changes to one or more instances of
+ * IScheme
.
+ *
+ * This interface may be implemented by clients. + *
+ * + * @since 3.1 + * @see Scheme#addSchemeListener(ISchemeListener) + * @see Scheme#removeSchemeListener(ISchemeListener) + * @see SchemeEvent + */ +public interface ISchemeListener { + + /** + * Notifies that one or more attributes of an instance of + *IScheme
have changed. Specific details are described in
+ * the SchemeEvent
.
+ *
+ * @param schemeEvent
+ * the scheme event. Guaranteed not to be null
.
+ */
+ void schemeChanged(SchemeEvent schemeEvent);
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/Scheme.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/Scheme.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * An instance of IScheme
is a handle representing a binding
+ * scheme as defined by the extension point dwtx.ui.bindings
.
+ * The identifier of the handle is the identifier of the scheme being represented.
+ *
+ * An instance of IScheme
can be obtained from an instance of
+ * ICommandManager
for any identifier, whether or not a scheme
+ * with that identifier is defined in the plugin registry.
+ *
+ * The handle-based nature of this API allows it to work well with runtime
+ * plugin activation and deactivation. If a scheme is defined, that means that
+ * its corresponding plug-in is active. If the plug-in is then deactivated, the
+ * scheme will still exist but it will be undefined. An attempt to use an
+ * undefined scheme will result in a NotDefinedException
+ * being thrown.
+ *
+ * This class is not intended to be extended by clients. + *
+ * + * @since 3.1 + * @see ISchemeListener + * @see dwtx.core.commands.CommandManager + */ +public final class Scheme : NamedHandleObject, Comparable { + + /** + * The collection of all objects listening to changes on this scheme. This + * value isnull
if there are no listeners.
+ */
+ private Set!(ISchemeListener) listeners = null;
+
+ /**
+ * The parent identifier for this scheme. This is the identifier of the
+ * scheme from which this scheme inherits some of its bindings. This value
+ * can be null
if the scheme has no parent.
+ */
+ private String parentId = null;
+
+ /**
+ * Constructs a new instance of Scheme
with an identifier.
+ *
+ * @param id
+ * The identifier to create; must not be null
.
+ */
+ this(String id) {
+ super(id);
+ }
+
+ /**
+ * Registers an instance of ISchemeListener
to listen for
+ * changes to attributes of this instance.
+ *
+ * @param schemeListener
+ * the instance of ISchemeListener
to register.
+ * Must not be null
. If an attempt is made to
+ * register an instance of ISchemeListener
which
+ * is already registered with this instance, no operation is
+ * performed.
+ */
+ public final void addSchemeListener(ISchemeListener schemeListener) {
+ if (schemeListener is null) {
+ throw new NullPointerException("Can't add a null scheme listener."); //$NON-NLS-1$
+ }
+
+ if (listeners is null) {
+ listeners = new HashSet!(ISchemeListener);
+ }
+
+ listeners.add(schemeListener);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public final int compareTo(Object object) {
+ Scheme scheme = cast(Scheme) object;
+ int compareTo = Util.compare(this.id, scheme.id);
+ if (compareTo is 0) {
+ compareTo = Util.compare(this.name, scheme.name);
+ if (compareTo is 0) {
+ compareTo = Util.compare(this.parentId, scheme.parentId);
+ if (compareTo is 0) {
+ compareTo = Util.compare(this.description,
+ scheme.description);
+ if (compareTo is 0) {
+ compareTo = Util.compare(this.defined, scheme.defined);
+ }
+ }
+ }
+ }
+
+ return compareTo;
+ }
+
+ /**
+ *
+ * Defines this scheme by giving it a name, and possibly a description and a
+ * parent identifier as well. The defined property for the scheme automatically
+ * becomes true
.
+ *
+ * Notification is sent to all listeners that something has changed. + *
+ * + * @param name + * The name of this scheme; must not benull
.
+ * @param description
+ * The description for this scheme; may be null
.
+ * @param parentId
+ * The parent identifier for this scheme; may be
+ * null
.
+ */
+ public final void define(String name, String description,
+ String parentId) {
+ if (name is null) {
+ throw new NullPointerException(
+ "The name of a scheme cannot be null"); //$NON-NLS-1$
+ }
+
+ bool definedChanged = !this.defined;
+ this.defined = true;
+
+ bool nameChanged = !Util.opEquals(this.name, name);
+ this.name = name;
+
+ bool descriptionChanged = !Util.opEquals(this.description,
+ description);
+ this.description = description;
+
+ bool parentIdChanged = !Util.opEquals(this.parentId, parentId);
+ this.parentId = parentId;
+
+ fireSchemeChanged(new SchemeEvent(this, definedChanged, nameChanged,
+ descriptionChanged, parentIdChanged));
+ }
+
+ /**
+ * Notifies all listeners that this scheme has changed. This sends the given
+ * event to all of the listeners, if any.
+ *
+ * @param event
+ * The event to send to the listeners; must not be
+ * null
.
+ */
+ private final void fireSchemeChanged(SchemeEvent event) {
+ if (event is null) {
+ throw new NullPointerException(
+ "Cannot send a null event to listeners."); //$NON-NLS-1$
+ }
+
+ if (listeners is null) {
+ return;
+ }
+ foreach( e; listeners ){
+ ISchemeListener listener = cast(ISchemeListener)e;
+ listener.schemeChanged(event);
+ }
+ }
+
+ /**
+ * + * Returns the identifier of the parent of the scheme represented by this + * handle. + *
+ *+ * Notification is sent to all registered listeners if this attribute + * changes. + *
+ * + * @return the identifier of the parent of the scheme represented by this + * handle. May benull
.
+ * @throws NotDefinedException
+ * if the scheme represented by this handle is not defined.
+ */
+ public final String getParentId() {
+ if (!defined) {
+ throw new NotDefinedException(
+ "Cannot get the parent identifier from an undefined scheme. " //$NON-NLS-1$
+ ~ id);
+ }
+
+ return parentId;
+ }
+
+ /**
+ * Unregisters an instance of ISchemeListener
listening for
+ * changes to attributes of this instance.
+ *
+ * @param schemeListener
+ * the instance of ISchemeListener
to unregister.
+ * Must not be null
. If an attempt is made to
+ * unregister an instance of ISchemeListener
which
+ * is not already registered with this instance, no operation is
+ * performed.
+ */
+ public final void removeSchemeListener(ISchemeListener schemeListener) {
+ if (schemeListener is null) {
+ throw new NullPointerException("Cannot remove a null listener."); //$NON-NLS-1$
+ }
+
+ if (listeners is null) {
+ return;
+ }
+
+ listeners.remove(schemeListener);
+
+ if (listeners.drained()) {
+ listeners = null;
+ }
+ }
+
+ /**
+ * The string representation of this command -- for debugging purposes only.
+ * This string should not be shown to an end user.
+ *
+ * @return The string representation; never null
.
+ */
+ public final String toString() {
+ if (string is null) {
+ string = Format("Scheme({},{},{},{},{})",id,name,description,parentId,defined);
+ }
+ return string;
+ }
+
+ /**
+ * Makes this scheme become undefined. This has the side effect of changing
+ * the name, description and parent identifier to null
.
+ * Notification is sent to all listeners.
+ */
+ public final void undefine() {
+ string = null;
+
+ bool definedChanged = defined;
+ defined = false;
+
+ bool nameChanged = name !is null;
+ name = null;
+
+ bool descriptionChanged = description !is null;
+ description = null;
+
+ bool parentIdChanged = parentId !is null;
+ parentId = null;
+
+ fireSchemeChanged(new SchemeEvent(this, definedChanged, nameChanged,
+ descriptionChanged, parentIdChanged));
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/SchemeEvent.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/SchemeEvent.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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 IScheme
.
+ * + * This class is not intended to be extended by clients. + *
+ * + * @since 3.1 + * @see ISchemeListener#schemeChanged(SchemeEvent) + */ +public final class SchemeEvent : AbstractNamedHandleEvent { + + /** + * The bit used to represent whether the scheme has changed its parent. + */ + private static const int CHANGED_PARENT_ID = LAST_USED_BIT << 1; + + /** + * The scheme that has changed; this value is nevernull
.
+ */
+ private const Scheme scheme;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param scheme
+ * the instance of the interface that changed; must not be
+ * null
.
+ * @param definedChanged
+ * true, iff the defined property changed.
+ * @param nameChanged
+ * true, iff the name property changed.
+ * @param descriptionChanged
+ * true
if the description property changed;
+ * false
otherwise.
+ * @param parentIdChanged
+ * true, iff the parentId property changed.
+ */
+ public this(Scheme scheme, bool definedChanged,
+ bool nameChanged, bool descriptionChanged,
+ bool parentIdChanged) {
+ super(definedChanged, descriptionChanged, nameChanged);
+
+ if (scheme is null) {
+ throw new NullPointerException();
+ }
+ this.scheme = scheme;
+
+ if (parentIdChanged) {
+ changedValues |= CHANGED_PARENT_ID;
+ }
+ }
+
+ /**
+ * Returns the instance of the scheme that changed.
+ *
+ * @return the instance of the scheme that changed. Guaranteed not to be
+ * null
.
+ */
+ public final Scheme getScheme() {
+ return scheme;
+ }
+
+ /**
+ * Returns whether or not the parentId property changed.
+ *
+ * @return true, iff the parentId property changed.
+ */
+ public final bool isParentIdChanged() {
+ return ((changedValues & CHANGED_PARENT_ID) !is 0);
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/Trigger.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/Trigger.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 + * The abstract class for any object that can be used as a trigger for a binding. + * This ensures that trigger conform to certain minimum requirements. Namely, triggers + * need to be hashable. + *
+ * + * @since 3.1 + */ +public abstract class Trigger : Comparable { + + /** + * Tests whether this object is equal to another object. A handle object is + * only equal to another trigger with the same properties. + * + * @param object + * The object with which to compare; may benull
.
+ * @return true
if the objects are equal; false
+ * otherwise.
+ */
+ public override abstract int opEquals(Object object);
+
+ /**
+ * Computes the hash code for this object.
+ *
+ * @return The hash code for this object.
+ */
+ public override abstract hash_t toHash();
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/TriggerSequence.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/TriggerSequence.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * A sequence of one or more triggers. None of these triggers may be
+ * null
.
+ *
HASH_CODE_NOT_COMPUTED
iff the hash code has not
+ * yet been computed.
+ */
+ protected /+transient+/ int hashCode = HASH_CODE_NOT_COMPUTED;
+
+ /**
+ * The list of trigger in this sequence. This value is never
+ * null
, and never contains null
elements.
+ */
+ protected const Trigger[] triggers;
+
+ /**
+ * Constructs a new instance of TriggerSequence
.
+ *
+ * @param triggers
+ * The triggers contained within this sequence; must not be
+ * null
or contain null
elements.
+ * May be empty.
+ */
+ public this(Trigger[] triggers) {
+ if (triggers is null) {
+ throw new NullPointerException("The triggers cannot be null"); //$NON-NLS-1$
+ }
+
+ for (int i = 0; i < triggers.length; i++) {
+ if (triggers[i] is null) {
+ throw new IllegalArgumentException(
+ "All triggers in a trigger sequence must be an instance of Trigger"); //$NON-NLS-1$
+ }
+ }
+
+ int triggerLength = triggers.length;
+ this.triggers = new Trigger[triggerLength];
+ System.arraycopy(triggers, 0, this.triggers, 0, triggerLength);
+ }
+
+ /**
+ * Returns whether or not this key sequence ends with the given key
+ * sequence.
+ *
+ * @param triggerSequence
+ * a trigger sequence. Must not be null
.
+ * @param equals
+ * whether or not an identical trigger sequence should be
+ * considered as a possible match.
+ * @return true
, iff the given trigger sequence ends with
+ * this trigger sequence.
+ */
+ public final bool endsWith(TriggerSequence triggerSequence,
+ bool equals) {
+ if (triggerSequence is null) {
+ throw new NullPointerException(
+ "Cannot end with a null trigger sequence"); //$NON-NLS-1$
+ }
+
+ return Util.endsWith(triggers, triggerSequence.triggers, equals);
+ }
+
+ public final override int opEquals(Object object) {
+ // Check if they're the same.
+ if (object is this) {
+ return true;
+ }
+
+ // Check if they're the same type.
+ if (!(cast(TriggerSequence)object )) {
+ return false;
+ }
+
+ TriggerSequence triggerSequence = cast(TriggerSequence) object;
+ return Util.opEquals(triggers, triggerSequence.triggers);
+ }
+
+ /**
+ * Formats this trigger sequence into the current default look.
+ *
+ * @return A string representation for this trigger sequence using the
+ * default look; never null
.
+ */
+ public abstract String format();
+
+ /**
+ *
+ * Returns a list of prefixes for the current sequence. A prefix is any
+ * leading subsequence in a TriggerSequence
. A prefix is
+ * also an instance of TriggerSequence
.
+ *
+ * For example, consider a trigger sequence that consists of four triggers: + * A, B, C and D. The prefixes would be "", "A", "A B", and "A B C". The + * list of prefixes must always be the same as the size of the trigger list. + *
+ * + * @return The array of possible prefixes for this sequence. This array must + * not benull
, but may be empty. It must only
+ * contains instances of TriggerSequence
.
+ */
+ public abstract TriggerSequence[] getPrefixes();
+
+ /**
+ * Returns the list of triggers.
+ *
+ * @return The triggers; never null
and guaranteed to only
+ * contain instances of Trigger
.
+ */
+ public final Trigger[] getTriggers() {
+ int triggerLength = triggers.length;
+ Trigger[] triggerCopy = new Trigger[triggerLength];
+ System.arraycopy(triggers, 0, triggerCopy, 0, triggerLength);
+ return triggerCopy;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ public final override hash_t toHash() {
+ if (hashCode is HASH_CODE_NOT_COMPUTED) {
+ auto HASH_INITIAL = dwt.dwthelper.utils.toHash( TriggerSequence.classinfo.name );
+ hashCode = HASH_INITIAL;
+ hashCode = hashCode * HASH_FACTOR + Util.toHash(triggers);
+ if (hashCode is HASH_CODE_NOT_COMPUTED) {
+ hashCode++;
+ }
+ }
+
+ return hashCode;
+ }
+
+ /**
+ * Returns whether or not this trigger sequence is empty.
+ *
+ * @return true
, iff the trigger sequence is empty.
+ */
+ public final bool isEmpty() {
+ return (triggers.length is 0);
+ }
+
+ /**
+ * Returns whether or not this trigger sequence starts with the given
+ * trigger sequence.
+ *
+ * @param triggerSequence
+ * a trigger sequence. Must not be null
.
+ * @param equals
+ * whether or not an identical trigger sequence should be
+ * considered as a possible match.
+ * @return true
, iff the given trigger sequence starts with
+ * this key sequence.
+ */
+ public final bool startsWith(TriggerSequence triggerSequence,
+ bool equals) {
+ if (triggerSequence is null) {
+ throw new NullPointerException(
+ "A trigger sequence cannot start with null"); //$NON-NLS-1$
+ }
+
+ return Util.startsWith(triggers, triggerSequence.triggers, equals);
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/IKeyLookup.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/IKeyLookup.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,462 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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 + * A facilitiy for converting the formal representation for key strokes + * (i.e., used in persistence) into real key stroke instances. + *
+ * + * @since 3.1 + */ +public interface IKeyLookup { + /** + * The formal name of the 'Alt' key. + */ + public static const String ALT_NAME = "ALT"; //$NON-NLS-1$ + + /** + * The formal name of the 'Arrow Down' key. + */ + public static const String ARROW_DOWN_NAME = "ARROW_DOWN"; //$NON-NLS-1$ + + /** + * The formal name of the 'Arrow Left' key. + */ + public static const String ARROW_LEFT_NAME = "ARROW_LEFT"; //$NON-NLS-1$ + + /** + * The formal name of the 'Arrow Right' key. + */ + public static const String ARROW_RIGHT_NAME = "ARROW_RIGHT"; //$NON-NLS-1$ + + /** + * The formal name of the 'Arrow Up' key. + */ + public static const String ARROW_UP_NAME = "ARROW_UP"; //$NON-NLS-1$ + + /** + * An alternate name for the backspace key. + */ + public static const String BACKSPACE_NAME = "BACKSPACE"; //$NON-NLS-1$ + + /** + * The formal name for the 'Break' key. + */ + public static const String BREAK_NAME = "BREAK"; //$NON-NLS-1$ + + /** + * The formal name of the backspace key. + */ + public static const String BS_NAME = "BS"; //$NON-NLS-1$ + + /** + * The formal name for the 'Caps Lock' key. + */ + public static const String CAPS_LOCK_NAME = "CAPS_LOCK"; //$NON-NLS-1$ + + /** + * The formal name of the 'Command' key. + */ + public static const String COMMAND_NAME = "COMMAND"; //$NON-NLS-1$ + + /** + * The formal name of the carriage return (U+000D) + */ + public static const String CR_NAME = "CR"; //$NON-NLS-1$ + + /** + * The formal name of the 'Ctrl' key. + */ + public static const String CTRL_NAME = "CTRL"; //$NON-NLS-1$ + + /** + * The formal name of the delete (U+007F) key + */ + public static const String DEL_NAME = "DEL"; //$NON-NLS-1$ + + /** + * An alternative name for the delete key. + */ + public static const String DELETE_NAME = "DELETE"; //$NON-NLS-1$ + + /** + * The formal name of the 'End' key. + */ + public static const String END_NAME = "END"; //$NON-NLS-1$ + + /** + * An alternative name for the enter key. + */ + public static const String ENTER_NAME = "ENTER"; //$NON-NLS-1$ + + /** + * The formal name of the escape (U+001B) key. + */ + public static const String ESC_NAME = "ESC"; //$NON-NLS-1$ + + /** + * An alternative name for the escape key. + */ + public static const String ESCAPE_NAME = "ESCAPE"; //$NON-NLS-1$ + + /** + * The formal name of the 'F1' key. + */ + public static const String F1_NAME = "F1"; //$NON-NLS-1$ + + /** + * The formal name of the 'F10' key. + */ + public static const String F10_NAME = "F10"; //$NON-NLS-1$ + + /** + * The formal name of the 'F11' key. + */ + public static const String F11_NAME = "F11"; //$NON-NLS-1$ + + /** + * The formal name of the 'F12' key. + */ + public static const String F12_NAME = "F12"; //$NON-NLS-1$ + + /** + * The formal name of the 'F13' key. + */ + public static const String F13_NAME = "F13"; //$NON-NLS-1$ + + /** + * The formal name of the 'F14' key. + */ + public static const String F14_NAME = "F14"; //$NON-NLS-1$ + + /** + * The formal name of the 'F15' key. + */ + public static const String F15_NAME = "F15"; //$NON-NLS-1$ + + /** + * The formal name of the 'F2' key. + */ + public static const String F2_NAME = "F2"; //$NON-NLS-1$ + + /** + * The formal name of the 'F3' key. + */ + public static const String F3_NAME = "F3"; //$NON-NLS-1$ + + /** + * The formal name of the 'F4' key. + */ + public static const String F4_NAME = "F4"; //$NON-NLS-1$ + + /** + * The formal name of the 'F5' key. + */ + public static const String F5_NAME = "F5"; //$NON-NLS-1$ + + /** + * The formal name of the 'F6' key. + */ + public static const String F6_NAME = "F6"; //$NON-NLS-1$ + + /** + * The formal name of the 'F7' key. + */ + public static const String F7_NAME = "F7"; //$NON-NLS-1$ + + /** + * The formal name of the 'F8' key. + */ + public static const String F8_NAME = "F8"; //$NON-NLS-1$ + + /** + * The formal name of the 'F9' key. + */ + public static const String F9_NAME = "F9"; //$NON-NLS-1$ + + /** + * The formal name of the form feed (U+000C) key. + */ + public static const String FF_NAME = "FF"; //$NON-NLS-1$ + + /** + * The formal name of the 'Home' key. + */ + public static const String HOME_NAME = "HOME"; //$NON-NLS-1$ + + /** + * The formal name of the 'Insert' key. + */ + public static const String INSERT_NAME = "INSERT"; //$NON-NLS-1$ + + /** + * The formal name of the line feed (U+000A) key. + */ + public static const String LF_NAME = "LF"; //$NON-NLS-1$ + + /** + * The formal name of the 'M1' key. + */ + public static const String M1_NAME = "M1"; //$NON-NLS-1$ + + /** + * The formal name of the 'M2' key. + */ + public static const String M2_NAME = "M2"; //$NON-NLS-1$ + + /** + * The formal name of the 'M3' key. + */ + public static const String M3_NAME = "M3"; //$NON-NLS-1$ + + /** + * The formal name of the 'M4' key. + */ + public static const String M4_NAME = "M4"; //$NON-NLS-1$ + + /** + * The formal name of the null (U+0000) key. + */ + public static const String NUL_NAME = "NUL"; //$NON-NLS-1$ + + /** + * The formal name of the 'NumLock' key. + */ + public static const String NUM_LOCK_NAME = "NUM_LOCK"; //$NON-NLS-1$ + + /** + * The formal name of the '0' key on the numpad. + */ + public static const String NUMPAD_0_NAME = "NUMPAD_0"; //$NON-NLS-1$ + + /** + * The formal name of the '1' key on the numpad. + */ + public static const String NUMPAD_1_NAME = "NUMPAD_1"; //$NON-NLS-1$ + + /** + * The formal name of the '2' key on the numpad. + */ + public static const String NUMPAD_2_NAME = "NUMPAD_2"; //$NON-NLS-1$ + + /** + * The formal name of the '3' key on the numpad. + */ + public static const String NUMPAD_3_NAME = "NUMPAD_3"; //$NON-NLS-1$ + + /** + * The formal name of the '4' key on the numpad. + */ + public static const String NUMPAD_4_NAME = "NUMPAD_4"; //$NON-NLS-1$ + + /** + * The formal name of the '5' key on the numpad. + */ + public static const String NUMPAD_5_NAME = "NUMPAD_5"; //$NON-NLS-1$ + + /** + * The formal name of the '6' key on the numpad. + */ + public static const String NUMPAD_6_NAME = "NUMPAD_6"; //$NON-NLS-1$ + + /** + * The formal name of the '7' key on the numpad. + */ + public static const String NUMPAD_7_NAME = "NUMPAD_7"; //$NON-NLS-1$ + + /** + * The formal name of the '8' key on the numpad. + */ + public static const String NUMPAD_8_NAME = "NUMPAD_8"; //$NON-NLS-1$ + + /** + * The formal name of the '9' key on the numpad. + */ + public static const String NUMPAD_9_NAME = "NUMPAD_9"; //$NON-NLS-1$ + + /** + * The formal name of the 'Add' key on the numpad. + */ + public static const String NUMPAD_ADD_NAME = "NUMPAD_ADD"; //$NON-NLS-1$ + + /** + * The formal name of the 'Decimal' key on the numpad. + */ + public static const String NUMPAD_DECIMAL_NAME = "NUMPAD_DECIMAL"; //$NON-NLS-1$ + + /** + * The formal name of the 'Divide' key on the numpad. + */ + public static const String NUMPAD_DIVIDE_NAME = "NUMPAD_DIVIDE"; //$NON-NLS-1$ + + /** + * The formal name of the 'Enter' key on the numpad. + */ + public static const String NUMPAD_ENTER_NAME = "NUMPAD_ENTER"; //$NON-NLS-1$ + + /** + * The formal name of the '=' key on the numpad. + */ + public static const String NUMPAD_EQUAL_NAME = "NUMPAD_EQUAL"; //$NON-NLS-1$ + + /** + * The formal name of the 'Multiply' key on the numpad. + */ + public static const String NUMPAD_MULTIPLY_NAME = "NUMPAD_MULTIPLY"; //$NON-NLS-1$ + + /** + * The formal name of the 'Subtract' key on the numpad. + */ + public static const String NUMPAD_SUBTRACT_NAME = "NUMPAD_SUBTRACT"; //$NON-NLS-1$ + + /** + * The formal name of the 'Page Down' key. + */ + public static const String PAGE_DOWN_NAME = "PAGE_DOWN"; //$NON-NLS-1$ + + /** + * The formal name of the 'Page Up' key. + */ + public static const String PAGE_UP_NAME = "PAGE_UP"; //$NON-NLS-1$ + + /** + * The formal name for the 'Pause' key. + */ + public static const String PAUSE_NAME = "PAUSE"; //$NON-NLS-1$ + + /** + * The formal name for the 'Print Screen' key. + */ + public static const String PRINT_SCREEN_NAME = "PRINT_SCREEN"; //$NON-NLS-1$ + + /** + * An alternative name for the enter key. + */ + public static const String RETURN_NAME = "RETURN"; //$NON-NLS-1$ + + /** + * The formal name for the 'Scroll Lock' key. + */ + public static const String SCROLL_LOCK_NAME = "SCROLL_LOCK"; //$NON-NLS-1$ + + /** + * The formal name of the 'Shift' key. + */ + public static const String SHIFT_NAME = "SHIFT"; //$NON-NLS-1$ + + /** + * The formal name of the space (U+0020) key. + */ + public static const String SPACE_NAME = "SPACE"; //$NON-NLS-1$ + + /** + * The formal name of the tab (U+0009) key. + */ + public static const String TAB_NAME = "TAB"; //$NON-NLS-1$ + + /** + * The formal name of the vertical tab (U+000B) key. + */ + public static const String VT_NAME = "VT"; //$NON-NLS-1$ + + /** + * Looks up a single natural key by its formal name, and returns the integer + * representation for this natural key + * + * @param name + * The formal name of the natural key to look-up; must not be + *null
.
+ * @return The integer representation of this key. If the natural key cannot
+ * be found, then this method returns 0
.
+ */
+ public int formalKeyLookup(String name);
+
+ /**
+ * Looks up a single natural key by its formal name, and returns the integer
+ * representation for this natural key
+ *
+ * @param name
+ * The formal name of the natural key to look-up; must not be
+ * null
.
+ * @return The integer representation of this key. If the natural key cannot
+ * be found, then this method returns 0
.
+ */
+ public Integer formalKeyLookupInteger(String name);
+
+ /**
+ * Looks up a single modifier key by its formal name, and returns the integer
+ * representation for this modifier key
+ *
+ * @param name
+ * The formal name of the modifier key to look-up; must not be
+ * null
.
+ * @return The integer representation of this key. If the modifier key
+ * cannot be found, then this method returns 0
.
+ */
+ public int formalModifierLookup(String name);
+
+ /**
+ * Looks up a key value, and returns the formal string representation for
+ * that key
+ *
+ * @param key
+ * The key to look-up.
+ * @return The formal string representation of this key. If this key cannot
+ * be found, then it is simply the character corresponding to that
+ * integer value.
+ */
+ public String formalNameLookup(int key);
+
+ /**
+ * Returns the integer representation of the ALT key.
+ *
+ * @return The ALT key
+ */
+ public int getAlt();
+
+ /**
+ * Returns the integer representation of the COMMAND key.
+ *
+ * @return The COMMAND key
+ */
+ public int getCommand();
+
+ /**
+ * Returns the integer representation of the CTRL key.
+ *
+ * @return The CTRL key
+ */
+ public int getCtrl();
+
+ /**
+ * Returns the integer representation of the SHIFT key.
+ *
+ * @return The SHIFT key
+ */
+ public int getShift();
+
+ /**
+ * Returns whether the given key is a modifier key.
+ *
+ * @param key
+ * The integer value of the key to check.
+ * @return true
if the key is one of the modifier keys;
+ * false
otherwise.
+ */
+ public bool isModifierKey(int key);
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/KeyBinding.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/KeyBinding.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 + * A keyboard shortcut. This is a binding between some keyboard input and the + * triggering of a command. This object is immutable. + *
+ * + * @since 3.1 + */ +public final class KeyBinding : Binding { + + /** + * The key sequence which triggers this binding. This sequence is never + *null
.
+ */
+ private const KeySequence keySequence;
+
+ /**
+ * Constructs a new instance of KeyBinding
.
+ *
+ * @param keySequence
+ * The key sequence which should trigger this binding. This value
+ * must not be null
. It also must be a complete,
+ * non-empty key sequence.
+ * @param command
+ * The parameterized command to which this binding applies; this
+ * value may be null
if the binding is meant to
+ * "unbind" a previously defined binding.
+ * @param schemeId
+ * The scheme to which this binding belongs; this value must not
+ * be null
.
+ * @param contextId
+ * The context to which this binding applies; this value must not
+ * be null
.
+ * @param locale
+ * The locale to which this binding applies; this value may be
+ * null
if it applies to all locales.
+ * @param platform
+ * The platform to which this binding applies; this value may be
+ * null
if it applies to all platforms.
+ * @param windowManager
+ * The window manager to which this binding applies; this value
+ * may be null
if it applies to all window
+ * managers. This value is currently ignored.
+ * @param type
+ * The type of binding. This should be either SYSTEM
+ * or USER
.
+ */
+ public this(KeySequence keySequence,
+ ParameterizedCommand command, String schemeId,
+ String contextId, String locale, String platform,
+ String windowManager, int type) {
+ super(command, schemeId, contextId, locale, platform, windowManager,
+ type);
+
+ if (keySequence is null) {
+ throw new NullPointerException("The key sequence cannot be null"); //$NON-NLS-1$
+ }
+
+ if (!keySequence.isComplete()) {
+ throw new IllegalArgumentException(
+ "Cannot bind to an incomplete key sequence"); //$NON-NLS-1$
+ }
+
+ if (keySequence.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Cannot bind to an empty key sequence"); //$NON-NLS-1$
+ }
+
+ this.keySequence = keySequence;
+ }
+
+ /**
+ * Returns the key sequence which triggers this binding. The key sequence
+ * will not be null
, empty or incomplete.
+ *
+ * @return The key sequence; never null
.
+ */
+ public final KeySequence getKeySequence() {
+ return keySequence;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.Binding#getTriggerSequence()
+ */
+ public TriggerSequence getTriggerSequence() {
+ return getKeySequence();
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/KeyLookupFactory.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/KeyLookupFactory.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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
+ * A factory class for ILookup
instances. This factory can be
+ * used to retrieve instances of look-ups defined by this package. It also
+ * allows you to define your own look-up for use in the classes.
+ *
KeyStroke
in
+ * converting string representations to instances.
+ */
+ private static IKeyLookup defaultLookup;
+
+ private static void check_staticthis(){
+ if( SWT_KEY_LOOKUP is null ){
+ synchronized{
+ if( SWT_KEY_LOOKUP is null ){
+ SWT_KEY_LOOKUP = new SWTKeyLookup();
+ defaultLookup = SWT_KEY_LOOKUP;
+ }
+ }
+ }
+ }
+
+ /**
+ * Provides an instance of SWTKeyLookup
.
+ *
+ * @return The DWT look-up table for key stroke format information; never
+ * null
.
+ */
+ public static final IKeyLookup getSWTKeyLookup() {
+ check_staticthis();
+ return SWT_KEY_LOOKUP;
+ }
+
+ /**
+ * An accessor for the current default look-up.
+ *
+ * @return The default look-up; never null
.
+ */
+ public static final IKeyLookup getDefault() {
+ check_staticthis();
+ return defaultLookup;
+ }
+
+ /**
+ * Sets the default look-up.
+ *
+ * @param defaultLookup
+ * the default look-up. Must not be null
.
+ */
+ public static final void setDefault(IKeyLookup defaultLookup) {
+ check_staticthis();
+ if (defaultLookup is null) {
+ throw new NullPointerException("The look-up must not be null"); //$NON-NLS-1$
+ }
+
+ KeyLookupFactory.defaultLookup = defaultLookup;
+ }
+
+ /**
+ * This class should not be instantiated.
+ */
+ private this() {
+ // Not to be constructred.
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/KeySequence.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/KeySequence.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,294 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * A KeySequence
is defined as a list of zero or more
+ * KeyStrokes
, with the stipulation that all
+ * KeyStroke
objects must be complete, save for the last one,
+ * whose completeness is optional. A KeySequence
is said to be
+ * complete if all of its KeyStroke
objects are complete.
+ *
+ * All KeySequence
objects have a formal string representation
+ * available via the toString()
method. There are a number of
+ * methods to get instances of KeySequence
objects, including one
+ * which can parse this formal string representation.
+ *
+ * All KeySequence
objects, via the format()
+ * method, provide a version of their formal string representation translated by
+ * platform and locale, suitable for display to a user.
+ *
+ * KeySequence
objects are immutable. Clients are not permitted
+ * to extend this class.
+ *
KeyStroke
objects allowed during
+ * parsing of the formal string representation.
+ */
+ public const static String KEY_STROKE_DELIMITERS = KEY_STROKE_DELIMITER
+ ~ "\b\r\u007F\u001B\f\n\0\t\u000B"; //$NON-NLS-1$
+
+ /**
+ * Gets an instance of KeySequence
.
+ *
+ * @return a key sequence. This key sequence will have no key strokes.
+ * Guaranteed not to be null
.
+ */
+ public static final KeySequence getInstance() {
+ return EMPTY_KEY_SEQUENCE;
+ }
+
+ /**
+ * Creates an instance of KeySequence
given a key sequence
+ * and a key stroke.
+ *
+ * @param keySequence
+ * a key sequence. Must not be null
.
+ * @param keyStroke
+ * a key stroke. Must not be null
.
+ * @return a key sequence that is equal to the given key sequence with the
+ * given key stroke appended to the end. Guaranteed not to be
+ * null
.
+ */
+ public static final KeySequence getInstance(KeySequence keySequence,
+ KeyStroke keyStroke) {
+ if (keySequence is null || keyStroke is null) {
+ throw new NullPointerException();
+ }
+
+ KeyStroke[] oldKeyStrokes = keySequence.getKeyStrokes();
+ int oldKeyStrokeLength = oldKeyStrokes.length;
+ KeyStroke[] newKeyStrokes = new KeyStroke[oldKeyStrokeLength + 1];
+ System.arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0,
+ oldKeyStrokeLength);
+ newKeyStrokes[oldKeyStrokeLength] = keyStroke;
+ return new KeySequence(newKeyStrokes);
+ }
+
+ /**
+ * Creates an instance of KeySequence
given a single key
+ * stroke.
+ *
+ * @param keyStroke
+ * a single key stroke. Must not be null
.
+ * @return a key sequence. Guaranteed not to be null
.
+ */
+ public static final KeySequence getInstance(KeyStroke keyStroke) {
+ return new KeySequence([ keyStroke ]);
+ }
+
+ /**
+ * Creates an instance of KeySequence
given an array of key
+ * strokes.
+ *
+ * @param keyStrokes
+ * the array of key strokes. This array may be empty, but it must
+ * not be null
. This array must not contain
+ * null
elements.
+ * @return a key sequence. Guaranteed not to be null
.
+ */
+ public static final KeySequence getInstance(KeyStroke[] keyStrokes) {
+ return new KeySequence(keyStrokes);
+ }
+
+ /**
+ * Creates an instance of KeySequence
given a list of key
+ * strokes.
+ *
+ * @param keyStrokes
+ * the list of key strokes. This list may be empty, but it must
+ * not be null
. If this list is not empty, it
+ * must only contain instances of KeyStroke
.
+ * @return a key sequence. Guaranteed not to be null
.
+ */
+ public static final KeySequence getInstance(SeqView!(KeyStroke) keyStrokes) {
+ return new KeySequence(keyStrokes.toArray());
+ }
+
+ /**
+ * Creates an instance of KeySequence
by parsing a given
+ * formal string representation.
+ *
+ * @param string
+ * the formal string representation to parse.
+ * @return a key sequence. Guaranteed not to be null
.
+ * @throws ParseException
+ * if the given formal string representation could not be parsed
+ * to a valid key sequence.
+ */
+ public static final KeySequence getInstance(String string) {
+ if (string is null) {
+ throw new NullPointerException();
+ }
+
+ auto keyStrokes = new ArraySeq!(KeyStroke);
+
+ try {
+ auto tokens = tango.text.Util.delimit( string, KEY_STROKE_DELIMITERS );
+ KeyStroke[] keyStrokeArray = new KeyStroke[ tokens.length ];
+ foreach( idx, tok; tokens ){
+ keyStrokeArray[idx] = KeyStroke.getInstance(tok);
+ }
+ return new KeySequence(keyStrokeArray);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(
+ "Could not construct key sequence with these key strokes: " //$NON-NLS-1$
+ /+~ keyStrokes+/);
+ } catch (NullPointerException e) {
+ throw new ParseException(
+ "Could not construct key sequence with these key strokes: " //$NON-NLS-1$
+ /+~ keyStrokes+/);
+ }
+ }
+
+ /**
+ * Constructs an instance of KeySequence
given a list of key
+ * strokes.
+ *
+ * @param keyStrokes
+ * the list of key strokes. This list may be empty, but it must
+ * not be null
. If this list is not empty, it
+ * must only contain instances of KeyStroke
.
+ */
+ protected this(KeyStroke[] keyStrokes) {
+ super(keyStrokes);
+
+ for (int i = 0; i < triggers.length - 1; i++) {
+ KeyStroke keyStroke = cast(KeyStroke) triggers[i];
+
+ if (!keyStroke.isComplete()) {
+ throw new IllegalArgumentException(null);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#compareTo(java.lang.Object)
+ */
+ public final int compareTo(Object object) {
+ KeySequence castedObject = cast(KeySequence) object;
+ return Util.compare( arraycast!(Comparable)(triggers), arraycast!(Comparable)(castedObject.triggers));
+ }
+
+ /**
+ * Formats this key sequence into the current default look.
+ *
+ * @return A string representation for this key sequence using the default
+ * look; never null
.
+ */
+ public final String format() {
+ return KeyFormatterFactory.getDefault().format(this);
+ }
+
+ /**
+ * Returns the list of key strokes for this key sequence.
+ *
+ * @return the list of key strokes keys. This list may be empty, but is
+ * guaranteed not to be null
. If this list is not
+ * empty, it is guaranteed to only contain instances of
+ * KeyStroke
.
+ */
+ public final KeyStroke[] getKeyStrokes() {
+ int triggerLength = triggers.length;
+ KeyStroke[] keyStrokes = new KeyStroke[triggerLength];
+ System.arraycopy(triggers, 0, keyStrokes, 0, triggerLength);
+ return keyStrokes;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.TriggerSequence#getPrefixes()
+ */
+ public final TriggerSequence[] getPrefixes() {
+ int numberOfPrefixes = triggers.length;
+ TriggerSequence[] prefixes = new TriggerSequence[numberOfPrefixes];
+ prefixes[0] = KeySequence.getInstance();
+ for (int i = 0; i < numberOfPrefixes - 1; i++) {
+ final KeyStroke[] prefixKeyStrokes = new KeyStroke[i + 1];
+ System.arraycopy(triggers, 0, prefixKeyStrokes, 0, i + 1);
+ prefixes[i + 1] = KeySequence.getInstance(prefixKeyStrokes);
+ }
+
+ return prefixes;
+ }
+
+ /**
+ * Returns whether or not this key sequence is complete. Key sequences are
+ * complete iff all of their key strokes are complete.
+ *
+ * @return true
, iff the key sequence is complete.
+ */
+ public final bool isComplete() {
+ int triggersLength = triggers.length;
+ for (int i = 0; i < triggersLength; i++) {
+ if (!(cast(KeyStroke) triggers[i]).isComplete()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the formal string representation for this key sequence.
+ *
+ * @return The formal string representation for this key sequence.
+ * Guaranteed not to be null
.
+ * @see java.lang.Object#toString()
+ */
+ public final String toString() {
+ return KeyFormatterFactory.getFormalKeyFormatter().format(this);
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/KeySequenceText.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/KeySequenceText.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,964 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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 + * A wrapper around the DWT text widget that traps literal key presses and + * converts them into key sequences for display. There are two types of key + * strokes that are displayed: complete and incomplete. A complete key stroke is + * one with a natural key, while an incomplete one has no natural key. + * Incomplete key strokes are only displayed until they are made complete or + * their component key presses are released. + *
+ * + * @since 3.1 + */ +public final class KeySequenceText { + + /** + * A key listener that traps incoming events and displays them in the + * wrapped text field. It has no effect on traversal operations. + */ + private class KeyTrapListener : Listener { + /** + * The index at which insertion should occur. This is used if there is a + * replacement occurring in the middle of the stroke, and the first key + * stroke was incomplete. + */ + private int insertionIndex = -1; + + /** + * Resets the insertion index to point nowhere. In other words, it is + * set to-1
.
+ */
+ void clearInsertionIndex() {
+ insertionIndex = -1;
+ }
+
+ /**
+ * Deletes the current selection. If there is no selection, then it
+ * deletes the last key stroke.
+ *
+ * @param keyStrokes
+ * The key strokes from which to delete. This list must not
+ * be null
, and must represent a valid key
+ * sequence.
+ */
+ private final KeyStroke[] deleteKeyStroke(KeyStroke[] keyStrokes) {
+ clearInsertionIndex();
+
+ if (hasSelection()) {
+ /*
+ * Delete the current selection -- disallowing incomplete
+ * strokes in the middle of the sequence.
+ */
+ KeyStroke[][] deletedKeyStrokes = new KeyStroke[][](1);
+ deleteSelection(keyStrokes, false, deletedKeyStrokes);
+ return deletedKeyStrokes[0];
+ }
+
+ // Remove the last key stroke.
+ if (keyStrokes.length > 0) {
+ int newKeyStrokesLength = keyStrokes.length - 1;
+ KeyStroke[] newKeyStrokes = new KeyStroke[newKeyStrokesLength];
+ System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+ newKeyStrokesLength);
+ return newKeyStrokes;
+ }
+
+ return keyStrokes;
+ }
+
+ /**
+ * Handles the key pressed and released events on the wrapped text
+ * widget. This makes sure to either add the pressed key to the
+ * temporary key stroke, or complete the current temporary key stroke
+ * and prompt for the next. In the case of a key release, this makes
+ * sure that the temporary stroke is correctly displayed --
+ * corresponding with modifier keys that may have been released.
+ *
+ * @param event
+ * The triggering event; must not be null
.
+ */
+ public void handleEvent(Event event) {
+ KeyStroke[] keyStrokes = getKeySequence().getKeyStrokes();
+
+ // Dispatch the event to the correct handler.
+ if (event.type is DWT.KeyDown) {
+ keyStrokes = handleKeyDown(event, keyStrokes);
+ } else if (event.type is DWT.KeyUp) {
+ keyStrokes = handleKeyUp(event, keyStrokes);
+ }
+
+ // Update the underlying widget.
+ setKeySequence(KeySequence.getInstance(keyStrokes));
+
+ // Prevent the event from reaching the widget.
+ event.doit = false;
+ }
+
+ /**
+ * Handles the case where the key event is an DWT.KeyDown
+ * event. This either causes a deletion (if it is an unmodified
+ * backspace key stroke), or an insertion (if it is any other key).
+ *
+ * @param event
+ * The trigger key down event; must not be null
.
+ * @param keyStrokes
+ * The current list of key strokes. This valud must not be
+ * null
, and it must represent a valid key
+ * sequence.
+ */
+ private KeyStroke[] handleKeyDown(Event event, KeyStroke[] keyStrokes) {
+ // Is it an unmodified backspace character?
+ if ((event.character is DWT.BS) && (event.stateMask is 0)) {
+ return deleteKeyStroke(keyStrokes);
+ }
+
+ return insertKeyStroke(event, keyStrokes);
+ }
+
+ /**
+ * Handles the case where the key event is an DWT.KeyUp
+ * event. This resets the insertion index. If there is an incomplete
+ * stroke, then that incomplete stroke is modified to match the keys
+ * that are still held. If no keys are held, then the incomplete stroke
+ * is removed.
+ *
+ * @param event
+ * The triggering event; must not be null
+ * @param keyStrokes
+ * The key strokes that are part of the current key sequence;
+ * these key strokes are guaranteed to represent a valid key
+ * sequence. This value must not be null
.
+ */
+ private final KeyStroke[] handleKeyUp(Event event,
+ KeyStroke[] keyStrokes) {
+ if (hasIncompleteStroke()) {
+ /*
+ * Figure out the DWT integer representation of the remaining
+ * values.
+ */
+ Event mockEvent = new Event();
+ if ((event.keyCode & DWT.MODIFIER_MASK) !is 0) {
+ // This key up is a modifier key being released.
+ mockEvent.stateMask = event.stateMask - event.keyCode;
+ } else {
+ /*
+ * This key up is the other end of a key down that was
+ * trapped by the operating system or window manager.
+ */
+ mockEvent.stateMask = event.stateMask;
+ }
+
+ /*
+ * Get a reasonable facsimile of the stroke that is still
+ * pressed.
+ */
+ int key = SWTKeySupport
+ .convertEventToUnmodifiedAccelerator(mockEvent);
+ KeyStroke remainingStroke = SWTKeySupport
+ .convertAcceleratorToKeyStroke(key);
+ int keyStrokesLength = keyStrokes.length;
+ KeyStroke[] newKeyStrokes;
+ if ((keyStrokesLength > 0)
+ && (remainingStroke.getModifierKeys() !is 0)) {
+ newKeyStrokes = new KeyStroke[keyStrokesLength];
+ System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+ keyStrokesLength - 1);
+ newKeyStrokes[keyStrokesLength - 1] = remainingStroke;
+
+ } else if (keyStrokesLength > 0) {
+ newKeyStrokes = new KeyStroke[keyStrokesLength - 1];
+ System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+ keyStrokesLength - 1);
+
+ } else if (remainingStroke.getModifierKeys() !is 0) {
+ newKeyStrokes = new KeyStroke[keyStrokesLength + 1];
+ System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+ keyStrokesLength);
+ newKeyStrokes[keyStrokesLength] = remainingStroke;
+
+ } else {
+ newKeyStrokes = keyStrokes;
+
+ }
+
+ return newKeyStrokes;
+ }
+
+ return keyStrokes;
+ }
+
+ /**
+ * + * Handles the case where a key down event is leading to a key stroke + * being inserted. The current selection is deleted, and an invalid + * remanents of the stroke are also removed. The insertion is carried + * out at the cursor position. + *
+ *+ * If only a natural key is selected (as part of a larger key stroke), + * then it is possible for the user to press a natural key to replace + * the old natural key. In this situation, pressing any modifier keys + * will replace the whole thing. + *
+ *
+ * If the insertion point is not at the end of the sequence, then
+ * incomplete strokes will not be immediately inserted. Only when the
+ * sequence is completed is the stroke inserted. This is a requirement
+ * as the widget must always represent a valid key sequence. The
+ * insertion point is tracked using insertionIndex
,
+ * which is an index into the key stroke array.
+ *
null
.
+ * @param keyStrokes
+ * The key strokes into which the current stroke should be
+ * inserted. This value must not be null
, and
+ * must represent a valid key sequence.
+ */
+ private final KeyStroke[] insertKeyStroke(Event event,
+ KeyStroke[] keyStrokes) {
+ // Compute the key stroke to insert.
+ int key = SWTKeySupport.convertEventToUnmodifiedAccelerator(event);
+ KeyStroke stroke = SWTKeySupport.convertAcceleratorToKeyStroke(key);
+
+ /*
+ * Only insert the stroke if it is *not ScrollLock. Let's not get
+ * silly
+ */
+ if ((DWT.NUM_LOCK is stroke.getNaturalKey())
+ || (DWT.CAPS_LOCK is stroke.getNaturalKey())
+ || (DWT.SCROLL_LOCK is stroke.getNaturalKey())) {
+ return keyStrokes;
+ }
+
+ if (insertionIndex !is -1) {
+ // There is a previous replacement still going on.
+ if (stroke.isComplete()) {
+ keyStrokes = insertStrokeAt(keyStrokes, stroke,
+ insertionIndex);
+ clearInsertionIndex();
+ }
+
+ } else if (hasSelection()) {
+ // There is a selection that needs to be replaced.
+ KeyStroke[][] deletedKeyStrokes = new KeyStroke[][](1);
+ insertionIndex = deleteSelection(keyStrokes, stroke
+ .isComplete(), deletedKeyStrokes);
+ keyStrokes = deletedKeyStrokes[0];
+ if ((stroke.isComplete())
+ || (insertionIndex >= keyStrokes.length)) {
+ keyStrokes = insertStrokeAt(keyStrokes, stroke,
+ insertionIndex);
+ clearInsertionIndex();
+ }
+
+ } else {
+ // No selection, so remove the incomplete stroke, if any
+ if ((hasIncompleteStroke()) && (keyStrokes.length > 0)) {
+ KeyStroke[] newKeyStrokes = new KeyStroke[keyStrokes.length - 1];
+ System.arraycopy(keyStrokes, 0, newKeyStrokes, 0,
+ keyStrokes.length - 1);
+ keyStrokes = newKeyStrokes;
+ }
+
+ // And then add the new stroke.
+ if ((keyStrokes.length is 0)
+ || (insertionIndex >= keyStrokes.length)
+ || (isCursorInLastPosition())) {
+ keyStrokes = insertStrokeAt(keyStrokes, stroke,
+ keyStrokes.length);
+ clearInsertionIndex();
+ } else {
+ /*
+ * I'm just getting the insertionIndex here. No actual
+ * deletion should occur.
+ */
+ KeyStroke[][] deletedKeyStrokes = new KeyStroke[][](1);
+ insertionIndex = deleteSelection(keyStrokes, stroke
+ .isComplete(), deletedKeyStrokes);
+ keyStrokes = deletedKeyStrokes[0];
+ if (stroke.isComplete()) {
+ keyStrokes = insertStrokeAt(keyStrokes, stroke,
+ insertionIndex);
+ clearInsertionIndex();
+ }
+ }
+
+ }
+
+ return keyStrokes;
+ }
+ }
+
+ /**
+ * A traversal listener that blocks all traversal except for tabs and arrow
+ * keys.
+ */
+ private class TraversalFilter : Listener {
+ /**
+ * Handles the traverse event on the text field wrapped by this class.
+ * It swallows all traverse events example for tab and arrow key
+ * navigation. The other forms of navigation can be reached by tabbing
+ * off of the control.
+ *
+ * @param event
+ * The trigger event; must not be null
.
+ */
+ public void handleEvent(Event event) {
+ switch (event.detail) {
+ case DWT.TRAVERSE_ESCAPE:
+ case DWT.TRAVERSE_MNEMONIC:
+ case DWT.TRAVERSE_NONE:
+ case DWT.TRAVERSE_PAGE_NEXT:
+ case DWT.TRAVERSE_PAGE_PREVIOUS:
+ case DWT.TRAVERSE_RETURN:
+ event.type = DWT.None;
+ event.doit = false;
+ break;
+
+ case DWT.TRAVERSE_TAB_NEXT:
+ case DWT.TRAVERSE_TAB_PREVIOUS:
+ // Check if modifiers other than just 'Shift' were
+ // down.
+ if ((event.stateMask & (DWT.MODIFIER_MASK ^ DWT.SHIFT)) !is 0) {
+ // Modifiers other than shift were down.
+ event.type = DWT.None;
+ event.doit = false;
+ break;
+ }
+
+ // fall through -- either no modifiers, or just shift.
+
+ case DWT.TRAVERSE_ARROW_NEXT:
+ case DWT.TRAVERSE_ARROW_PREVIOUS:
+ default:
+ // Let the traversal happen, but clear the incomplete
+ // stroke
+ if (hasIncompleteStroke()) {
+ KeyStroke[] oldKeyStrokes = getKeySequence()
+ .getKeyStrokes();
+ int newKeyStrokesLength = oldKeyStrokes.length - 1;
+ if (newKeyStrokesLength >= 1) {
+ KeyStroke[] newKeyStrokes = new KeyStroke[newKeyStrokesLength];
+ System.arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0,
+ newKeyStrokesLength);
+ setKeySequence(KeySequence.getInstance(newKeyStrokes));
+ } else {
+ setKeySequence(KeySequence.getInstance());
+ }
+ }
+ }
+
+ }
+ }
+
+ /**
+ * The manager resposible for installing and removing the traversal filter
+ * when the key sequence entry widget gains and loses focus.
+ */
+ private class TraversalFilterManager : FocusListener {
+ /** The managed filter. We only need one instance. */
+ private TraversalFilter filter;
+
+ private bool filtering = false;
+
+ this(){
+ filter = new TraversalFilter();
+ }
+
+ /**
+ * Attaches the global traversal filter.
+ *
+ * @param event
+ * Ignored.
+ */
+ public void focusGained(FocusEvent event) {
+ Display.getCurrent().addFilter(DWT.Traverse, filter);
+ filtering = true;
+ }
+
+ /**
+ * Detaches the global traversal filter.
+ *
+ * @param event
+ * Ignored.
+ */
+ public void focusLost(FocusEvent event) {
+ Display.getCurrent().removeFilter(DWT.Traverse, filter);
+ filtering = false;
+ }
+
+ /**
+ * Remove the traverse filter if we close without focusOut.
+ */
+ public void dispose() {
+ if (filtering) {
+ Display.getCurrent().removeFilter(DWT.Traverse, filter);
+ }
+ }
+ }
+
+ /**
+ * A modification listener that makes sure that external events to this
+ * class (i.e., direct modification of the underlying text) do not break
+ * this class' view of the world.
+ */
+ private class UpdateSequenceListener : ModifyListener {
+ /**
+ * Handles the modify event on the underlying text widget.
+ *
+ * @param event
+ * The triggering event; ignored.
+ */
+ public void modifyText(ModifyEvent event) {
+ try {
+ // The original sequence.
+ KeySequence originalSequence = getKeySequence();
+
+ // The new sequence drawn from the text.
+ String contents = getText();
+ KeySequence newSequence = KeySequence.getInstance(contents);
+
+ // Check to see if they're the same.
+ if (!originalSequence.opEquals(newSequence)) {
+ setKeySequence(newSequence);
+ }
+
+ } catch (ParseException e) {
+ // Abort any cut/paste-driven modifications
+ setKeySequence(getKeySequence());
+ }
+ }
+ }
+
+ static this(){
+ HashSet!(KeyStroke) trappedKeys = new HashSet!(KeyStroke);
+ trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(DWT.TAB));
+ trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(DWT.TAB
+ | DWT.SHIFT));
+ trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(DWT.BS));
+ //List trappedKeyList = new ArrayList(trappedKeys);
+ TRAPPED_KEYS = trappedKeys;// Collections.unmodifiableList(trappedKeyList);
+ }
+
+ /** An empty string instance for use in clearing text values. */
+ private static const String EMPTY_STRING = ""; //$NON-NLS-1$
+
+ /**
+ * The special integer value for the maximum number of strokes indicating
+ * that an infinite number should be allowed.
+ */
+ public static const int INFINITE = -1;
+
+ /**
+ * The name of the property representing the current key sequence in this
+ * key sequence widget.
+ *
+ * @since 3.2
+ */
+ public static const String P_KEY_SEQUENCE = "dwtx.jface.bindings.keys.KeySequenceText.KeySequence"; //$NON-NLS-1$
+
+ /**
+ * The keys trapped by this widget. This list is guaranteed to be roughly
+ * accurate. Perfection is not possible, as DWT does not export traversal
+ * keys as constants.
+ */
+ public static const View!(KeyStroke) TRAPPED_KEYS;
+
+ /**
+ * The key filter attached to the underlying widget that traps key events.
+ */
+ private const KeyTrapListener keyFilter;
+
+ /**
+ * The text of the key sequence -- containing only the complete key strokes.
+ */
+ private KeySequence keySequence;
+
+ /**
+ * Those listening to changes to the key sequence in this widget. This value
+ * may be null
if there are no listeners.
+ */
+ private ArraySeq!(IPropertyChangeListener) listeners = null;
+
+ /** The maximum number of key strokes permitted in the sequence. */
+ private int maxStrokes = INFINITE;
+
+ /** The text widget that is wrapped for this class. */
+ private const Text text;
+
+ /**
+ * The listener that makes sure that the text widget remains up-to-date with
+ * regards to external modification of the text (e.g., cut & pasting).
+ */
+ private const UpdateSequenceListener updateSequenceListener;
+
+ /**
+ * Constructs an instance of KeySequenceTextField
with the
+ * text field to use. If the platform is carbon (MacOS X), then the font is
+ * set to be the same font used to display accelerators in the menus.
+ *
+ * @param wrappedText
+ * The text widget to wrap; must not be null
.
+ */
+ public this(Text wrappedText) {
+ keyFilter = new KeyTrapListener();
+ keySequence = KeySequence.getInstance();
+ updateSequenceListener = new UpdateSequenceListener();
+
+ text = wrappedText;
+
+ // Set the font if the platform is carbon.
+ if ("carbon".equals(DWT.getPlatform())) { //$NON-NLS-1$
+ // Don't worry about this font name here; it is the official menu
+ // font and point size on the Mac.
+ Font font = new Font(cast(Device)text.getDisplay(),
+ "Lucida Grande", 13, DWT.NORMAL); //$NON-NLS-1$
+ text.setFont(font);
+ text.addDisposeListener(new class DisposeListener {
+ Font font_;
+ this(){
+ font_=font;
+ }
+ public void widgetDisposed(DisposeEvent e) {
+ font_.dispose();
+ }
+ });
+ }
+
+ // Add the key listener.
+ text.addListener(DWT.KeyUp, keyFilter);
+ text.addListener(DWT.KeyDown, keyFilter);
+
+ TraversalFilterManager traversalFilterManager = new TraversalFilterManager();
+ text.addFocusListener(traversalFilterManager);
+ text.addDisposeListener(new class DisposeListener {
+ TraversalFilterManager traversalFilterManager_;
+ this(){
+ traversalFilterManager_=traversalFilterManager;
+ }
+ public void widgetDisposed(DisposeEvent e) {
+ traversalFilterManager_.dispose();
+ }
+ });
+
+ // Add an internal modify listener.
+ text.addModifyListener(updateSequenceListener);
+ }
+
+ /**
+ * Adds a property change listener to this key sequence widget. It will be
+ * notified when the key sequence changes.
+ *
+ * @param listener
+ * The listener to be notified when changes occur; must not be
+ * null
.
+ * @since 3.2
+ */
+ public final void addPropertyChangeListener(
+ IPropertyChangeListener listener) {
+ if (listener is null) {
+ return;
+ }
+
+ if (listeners is null) {
+ listeners = new ArraySeq!(IPropertyChangeListener);
+ }
+
+ listeners.append(listener);
+ }
+
+ /**
+ * Clears the text field and resets all the internal values.
+ */
+ public void clear() {
+ KeySequence oldKeySequence = keySequence;
+ keySequence = KeySequence.getInstance();
+ text.setText(EMPTY_STRING);
+ firePropertyChangeEvent(oldKeySequence);
+ }
+
+ /**
+ * Removes the key strokes from the list corresponding the selection. If
+ * allowIncomplete
, then invalid key sequences will be
+ * allowed (i.e., those with incomplete strokes in the non-terminal
+ * position). Otherwise, incomplete strokes will be removed. This modifies
+ * keyStrokes
in place, and has no effect on the text widget
+ * this class wraps.
+ *
+ * @param keyStrokes
+ * The list of key strokes from which the selection should be
+ * removed; must not be null
.
+ * @param allowIncomplete
+ * Whether incomplete strokes should be allowed to exist in the
+ * list after the deletion.
+ * @return The index at which a subsequent insert should occur. This index
+ * only has meaning to the insertStrokeAt
method.
+ */
+ private final int deleteSelection(KeyStroke[] keyStrokes,
+ bool allowIncomplete, KeyStroke[][] deletedKeyStrokes) {
+ // Get the current selection.
+ Point selection = text.getSelection();
+ int start = selection.x;
+ int end = selection.y;
+
+ /*
+ * Using the key sequence format method, discover the point at which
+ * adding key strokes passes or equals the start of the selection. In
+ * other words, find the first stroke that is part of the selection.
+ * Keep track of the text range under which the stroke appears (i.e.,
+ * startTextIndex->string.length() is the first selected stroke).
+ */
+ String string;
+ auto currentStrokes = new ArraySeq!(KeyStroke);
+ int startTextIndex = 0; // keeps track of the start of the stroke
+ int keyStrokesLength = keyStrokes.length;
+ int i;
+ for (i = 0; (i < keyStrokesLength) && (string.length < start); i++) {
+ startTextIndex = string.length;
+ currentStrokes.append(keyStrokes[i]);
+ string = KeySequence.getInstance(currentStrokes).format();
+ }
+
+ /*
+ * If string.length() is start, then the cursor is positioned between
+ * strokes (i.e., selection is outside of a stroke).
+ */
+ int startStrokeIndex;
+ if (string.length is start) {
+ startStrokeIndex = currentStrokes.size();
+ } else {
+ startStrokeIndex = currentStrokes.size() - 1;
+ }
+
+ /*
+ * Check to see if the cursor is only positioned, rather than actually
+ * selecting something. We only need to compute the end if there is a
+ * selection.
+ */
+ int endStrokeIndex;
+ if (start is end) {
+ return startStrokeIndex;
+ }
+
+ for (; (i < keyStrokesLength) && (string.length < end); i++) {
+ currentStrokes.append(keyStrokes[i]);
+ string = KeySequence.getInstance(currentStrokes).format();
+ }
+ endStrokeIndex = currentStrokes.size() - 1;
+ if (endStrokeIndex < 0) {
+ endStrokeIndex = 0;
+ }
+
+ /*
+ * Remove the strokes that are touched by the selection. Keep track of
+ * the first stroke removed.
+ */
+ int newLength = endStrokeIndex - startStrokeIndex + 1;
+ deletedKeyStrokes[0] = new KeyStroke[newLength];
+ KeyStroke startStroke = keyStrokes[startStrokeIndex];
+ System.arraycopy(keyStrokes, 0, keyStrokes, 0, newLength);
+
+ /*
+ * Allow the first stroke removed to be replaced by an incomplete
+ * stroke.
+ */
+ if (allowIncomplete) {
+ int modifierKeys = startStroke.getModifierKeys();
+ KeyStroke incompleteStroke = KeyStroke.getInstance(modifierKeys,
+ KeyStroke.NO_KEY);
+ int incompleteStrokeLength = incompleteStroke.format().length;
+ if ((startTextIndex + incompleteStrokeLength) <= start) {
+ KeyStroke[] added = new KeyStroke[newLength + 1];
+ System.arraycopy(deletedKeyStrokes[0], 0, added, 0,
+ startStrokeIndex);
+ added[startStrokeIndex] = incompleteStroke;
+ System.arraycopy(deletedKeyStrokes[0], startStrokeIndex, added,
+ startStrokeIndex + 1, newLength);
+ deletedKeyStrokes[0] = added;
+ }
+ }
+
+ return startStrokeIndex;
+ }
+
+ /**
+ * Fires a property change event to all of the listeners.
+ *
+ * @param oldKeySequence
+ * The old key sequence; must not be null
.
+ * @since 3.2
+ */
+ protected final void firePropertyChangeEvent(
+ KeySequence oldKeySequence) {
+ if (listeners !is null) {
+ PropertyChangeEvent event = new PropertyChangeEvent(this,
+ P_KEY_SEQUENCE, oldKeySequence, getKeySequence());
+ foreach( listener; listeners ){
+ listener.propertyChange(event);
+ }
+ }
+ }
+
+ /**
+ * An accessor for the KeySequence
that corresponds to the
+ * current state of the text field. This includes incomplete strokes.
+ *
+ * @return The key sequence representation; never null
.
+ */
+ public KeySequence getKeySequence() {
+ return keySequence;
+ }
+
+ /**
+ * An accessor for the underlying text widget's contents.
+ *
+ * @return The text contents of this entry; never null
.
+ */
+ private String getText() {
+ return text.getText();
+ }
+
+ /**
+ * Tests whether the current key sequence has a stroke with no natural key.
+ *
+ * @return true
is there is an incomplete stroke;
+ * false
otherwise.
+ */
+ private bool hasIncompleteStroke() {
+ return !keySequence.isComplete();
+ }
+
+ /**
+ * Tests whether the current text widget has some text selection.
+ *
+ * @return true
if the number of selected characters it
+ * greater than zero; false
otherwise.
+ */
+ private bool hasSelection() {
+ return (text.getSelectionCount() > 0);
+ }
+
+ /**
+ * Inserts the key stroke at the current insertion point. This does a
+ * regular delete and insert, as if the key had been pressed.
+ *
+ * @param stroke
+ * The key stroke to insert; must not be null
.
+ */
+ public void insert(KeyStroke stroke) {
+ if (!stroke.isComplete()) {
+ return;
+ }
+
+ // Copy the key strokes in the current key sequence.
+ KeySequence keySequence = getKeySequence();
+ KeyStroke[] oldKeyStrokes = keySequence.getKeyStrokes();
+ KeyStroke[] newKeyStrokes;
+ if ((hasIncompleteStroke()) && (!keySequence.isEmpty())) {
+ int newKeyStrokesLength = oldKeyStrokes.length - 1;
+ newKeyStrokes = new KeyStroke[newKeyStrokesLength];
+ System.arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0,
+ newKeyStrokesLength);
+ } else {
+ newKeyStrokes = oldKeyStrokes;
+ }
+
+ KeyStroke[][] deletedKeyStrokes = new KeyStroke[][](1);
+ int index = deleteSelection(newKeyStrokes, false, deletedKeyStrokes);
+ if (index is -1) {
+ index = 0;
+ }
+
+ KeyStroke[] keyStrokes = insertStrokeAt(newKeyStrokes, stroke, index);
+ keyFilter.clearInsertionIndex();
+ setKeySequence(KeySequence.getInstance(keyStrokes));
+ }
+
+ /**
+ * Inserts the stroke at the given index in the list of strokes. If the
+ * stroke currently at that index is incomplete, then it tries to merge the
+ * two strokes. If merging is a complete failure (unlikely), then it will
+ * simply overwrite the incomplete stroke. If the stroke at the index is
+ * complete, then it simply inserts the stroke independently.
+ *
+ * @param keyStrokes
+ * The list of key strokes in which the key stroke should be
+ * appended; must not be null
.
+ * @param stroke
+ * The stroke to insert; should not be null
.
+ * @param index
+ * The index at which to insert; must be a valid index into the
+ * list of key strokes.
+ */
+ private final KeyStroke[] insertStrokeAt(KeyStroke[] keyStrokes,
+ KeyStroke stroke, int index) {
+ int keyStrokesLength = keyStrokes.length;
+ KeyStroke currentStroke = (index >= keyStrokesLength) ? null
+ : keyStrokes[index];
+ if ((currentStroke !is null) && (!currentStroke.isComplete())) {
+ int modifierKeys = currentStroke.getModifierKeys();
+ int naturalKey = stroke.getNaturalKey();
+ modifierKeys |= stroke.getModifierKeys();
+ keyStrokes[index] = KeyStroke.getInstance(modifierKeys, naturalKey);
+ return keyStrokes;
+ }
+
+ KeyStroke[] newKeyStrokes = new KeyStroke[keyStrokesLength + 1];
+ System.arraycopy(keyStrokes, 0, newKeyStrokes, 0, index);
+ newKeyStrokes[index] = stroke;
+ if (index < keyStrokesLength) {
+ System.arraycopy(keyStrokes, index, newKeyStrokes, index + 1,
+ keyStrokesLength-index);
+ }
+ return newKeyStrokes;
+ }
+
+ /**
+ * Tests whether the cursor is in the last position. This means that the
+ * selection extends to the last position.
+ *
+ * @return true
if the selection extends to the last
+ * position; false
otherwise.
+ */
+ private bool isCursorInLastPosition() {
+ return (text.getSelection().y >= getText().length);
+ }
+
+ /**
+ * Removes the given listener from this key sequence widget.
+ *
+ * @param listener
+ * The listener to be removed; must not be null
.
+ * @since 3.2
+ */
+ public final void removePropertyChangeListener(
+ IPropertyChangeListener listener) {
+ if ((listener is null) || (listeners is null)) {
+ return;
+ }
+
+ listeners.remove(listener);
+ }
+
+ /**
+ * + * A mutator for the key sequence stored within this widget. The text and + * caret position are updated. + *
+ *+ * All sequences are limited to maxStrokes number of strokes in length. If + * there are already that number of strokes, then it does not show + * incomplete strokes, and does not keep track of them. + *
+ * + * @param newKeySequence + * The new key sequence for this widget; may benull
+ * if none.
+ */
+ public void setKeySequence(KeySequence newKeySequence) {
+ KeySequence oldKeySequence = keySequence;
+ keySequence = newKeySequence;
+
+ // Trim any extra strokes.
+ if (maxStrokes !is INFINITE) {
+ KeyStroke[] oldKeyStrokes = keySequence.getKeyStrokes();
+ if (maxStrokes < oldKeyStrokes.length) {
+ KeyStroke[] newKeyStrokes = new KeyStroke[maxStrokes];
+ System
+ .arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0,
+ maxStrokes);
+ keySequence = KeySequence.getInstance(newKeyStrokes);
+ }
+ }
+
+ // Check to see if the text has changed.
+ String currentString = getText();
+ String newString = keySequence.format();
+ if (!currentString.equals(newString)) {
+ // We need to update the text
+ text.removeModifyListener(updateSequenceListener);
+ text.setText(keySequence.format());
+ text.addModifyListener(updateSequenceListener);
+ text.setSelection(getText().length);
+ }
+
+ firePropertyChangeEvent(oldKeySequence);
+ }
+
+ /**
+ * Returns the maximum number of strokes that are permitted in this widget
+ * at one time.
+ *
+ * @return The maximum number of strokes; will be a positive integer or
+ * INFINITE
.
+ */
+ public int getKeyStrokeLimit() {
+ return maxStrokes;
+ }
+
+ /**
+ * A mutator for the maximum number of strokes that are permitted in this
+ * widget at one time.
+ *
+ * @param keyStrokeLimit
+ * The maximum number of strokes; must be a positive integer or
+ * INFINITE
.
+ */
+ public void setKeyStrokeLimit(int keyStrokeLimit) {
+ if (keyStrokeLimit > 0 || keyStrokeLimit is INFINITE) {
+ this.maxStrokes = keyStrokeLimit;
+ } else {
+ throw new IllegalArgumentException(null);
+ }
+
+ // Make sure we are obeying the new limit.
+ setKeySequence(getKeySequence());
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/KeyStroke.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/KeyStroke.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,283 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * A KeyStroke
is defined as an optional set of modifier keys
+ * followed optionally by a natural key. A KeyStroke
is said to
+ * be complete if it contains a natural key. A natural key is any Unicode
+ * character (e.g., "backspace", etc.), any character belonging to a natural
+ * language (e.g., "A", "1", "[", etc.), or any special control character
+ * specific to computers (e.g., "F10", "PageUp", etc.).
+ *
+ * All KeyStroke
objects have a formal string representation
+ * available via the toString()
method. There are a number of
+ * methods to get instances of KeyStroke
objects, including one
+ * which can parse this formal string representation.
+ *
+ * All KeyStroke
objects, via the format()
method,
+ * provide a version of their formal string representation translated by
+ * platform and locale, suitable for display to a user.
+ *
+ * KeyStroke
objects are immutable. Clients are not permitted to
+ * extend this class.
+ *
Key
objects allowed during
+ * parsing of the formal string representation.
+ */
+ public static const String KEY_DELIMITERS = KEY_DELIMITER;
+
+ /**
+ * The representation for no key.
+ */
+ public static const int NO_KEY = 0;
+
+ /**
+ * Creates an instance of KeyStroke
given a natural key.
+ *
+ * @param naturalKey
+ * the natural key. The format of this integer is defined by
+ * whichever widget toolkit you are using; NO_KEY
+ * always means no natural key.
+ * @return a key stroke. This key stroke will have no modifier keys.
+ * Guaranteed not to be null
.
+ * @see SWTKeySupport
+ */
+ public static final KeyStroke getInstance(int naturalKey) {
+ return new KeyStroke(NO_KEY, naturalKey);
+ }
+
+ /**
+ * Creates an instance of KeyStroke
given a set of modifier keys
+ * and a natural key.
+ *
+ * @param modifierKeys
+ * the modifier keys. The format of this integer is defined by
+ * whichever widget toolkit you are using; NO_KEY
+ * always means no modifier keys.
+ * @param naturalKey
+ * the natural key. The format of this integer is defined by
+ * whichever widget toolkit you are using; NO_KEY
+ * always means no natural key.
+ * @return a key stroke. Guaranteed not to be null
.
+ * @see SWTKeySupport
+ */
+ public static final KeyStroke getInstance(int modifierKeys,
+ int naturalKey) {
+ return new KeyStroke(modifierKeys, naturalKey);
+ }
+
+ /**
+ * Creates an instance of KeyStroke
by parsing a given a formal
+ * string representation.
+ *
+ * @param string
+ * the formal string representation to parse.
+ * @return a key stroke. Guaranteed not to be null
.
+ * @throws ParseException
+ * if the given formal string representation could not be parsed
+ * to a valid key stroke.
+ */
+ public static final KeyStroke getInstance(String string) {
+ if (string is null) {
+ throw new NullPointerException("Cannot parse a null string"); //$NON-NLS-1$
+ }
+
+ IKeyLookup lookup = KeyLookupFactory.getDefault();
+ int modifierKeys = NO_KEY;
+ int naturalKey = NO_KEY;
+ int i = 0;
+
+ auto tokens = tango.text.Util.delimit( string, KEY_DELIMITERS );
+ foreach( idx, token; tokens ){
+
+ if (i % 2 is 0) {
+ if ( idx+1 < tokens.length ) {
+ token = token.toUpperCase();
+ int modifierKey = lookup.formalModifierLookup(token);
+ if (modifierKey is NO_KEY) {
+ throw new ParseException(
+ "Cannot create key stroke with duplicate or non-existent modifier key: " //$NON-NLS-1$
+ ~ token);
+ }
+
+ modifierKeys |= modifierKey;
+
+ } else if (token.length is 1) {
+ naturalKey = token.charAt(0);
+
+ } else {
+ token = token.toUpperCase();
+ naturalKey = lookup.formalKeyLookup(token);
+ }
+ }
+
+ i++;
+ }
+
+ return new KeyStroke(modifierKeys, naturalKey);
+ }
+
+ /**
+ * An integer representation of the modifier keys; NO_KEY
+ * means that there is no modifier key.
+ */
+ private const int modifierKeys;
+
+ /**
+ * The natural key for this key stroke. This value is NO_KEY
+ * if the key stroke is incomplete (i.e., has no natural key).
+ */
+ private const int naturalKey;
+
+ /**
+ * Constructs an instance of KeyStroke
given a set of
+ * modifier keys and a natural key.
+ *
+ * @param modifierKeys
+ * the modifier keys. The format of this integer is defined by
+ * whichever widget toolkit you are using; NO_KEY
+ * always means no modifier keys.
+ * @param naturalKey
+ * the natural key. The format of this integer is defined by
+ * whichever widget toolkit you are using; NO_KEY
+ * always means no natural key.
+ * @see SWTKeySupport
+ */
+ private this(int modifierKeys, int naturalKey) {
+ this.modifierKeys = modifierKeys;
+ this.naturalKey = naturalKey;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public final int compareTo(Object object) {
+ KeyStroke keyStroke = cast(KeyStroke) object;
+ int compareTo = Util.compare(modifierKeys, keyStroke.modifierKeys);
+
+ if (compareTo is 0) {
+ compareTo = Util.compare(naturalKey, keyStroke.naturalKey);
+ }
+
+ return compareTo;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public final override int opEquals(Object object) {
+ if (!(cast(KeyStroke)object )) {
+ return false;
+ }
+
+ KeyStroke keyStroke = cast(KeyStroke) object;
+ if (modifierKeys !is keyStroke.modifierKeys) {
+ return false;
+ }
+
+ return (naturalKey is keyStroke.naturalKey);
+ }
+
+ /**
+ * Formats this key stroke into the current default look.
+ *
+ * @return A string representation for this key stroke using the default
+ * look; never null
.
+ */
+ public final String format() {
+ return KeyFormatterFactory.getDefault().format(this);
+ }
+
+ /**
+ * Returns the modifier keys for this key stroke.
+ *
+ * @return the bit mask of modifier keys; NO_KEY
means that
+ * there is no modifier key.
+ */
+ public final int getModifierKeys() {
+ return modifierKeys;
+ }
+
+ /**
+ * Returns the natural key for this key stroke.
+ *
+ * @return The natural key for this key stroke. This value is
+ * NO_KEY
if the key stroke is incomplete (i.e., has
+ * no natural key).
+ */
+ public final int getNaturalKey() {
+ return naturalKey;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ public final override hash_t toHash() {
+ return modifierKeys << 4 + naturalKey;
+ }
+
+ /**
+ * Returns whether or not this key stroke is complete. Key strokes are
+ * complete iff they have a natural key which is not NO_KEY
.
+ *
+ * @return true
, iff the key stroke is complete.
+ */
+ public final bool isComplete() {
+ return (naturalKey !is NO_KEY);
+ }
+
+ /**
+ * Returns the formal string representation for this key stroke.
+ *
+ * @return The formal string representation for this key stroke. Guaranteed
+ * not to be null
.
+ * @see java.lang.Object#toString()
+ */
+ public final override String toString() {
+ return KeyFormatterFactory.getFormalKeyFormatter().format(this);
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/ParseException.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/ParseException.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * An exception indicating problems while parsing formal string representations
+ * of either KeyStroke
or KeySequence
objects.
+ *
+ * ParseException
objects are immutable. Clients are not
+ * permitted to extend this class.
+ *
ParseException
with the specified detail
+ * message.
+ *
+ * @param s
+ * the detail message.
+ */
+ public this(String s) {
+ super(s);
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/SWTKeyLookup.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/SWTKeyLookup.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,379 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 + * A look-up table for the formal grammar for keys, and the integer values they + * represent. This look-up table is hard-coded to use DWT representations. By + * replacing this class (and + * {@link dwtx.jface.bindings.keys.SWTKeySupport}), you can remove the + * dependency on DWT. + *
+ * + * @since 3.1 + * @see dwtx.jface.bindings.keys.KeyLookupFactory + */ +public final class SWTKeyLookup : IKeyLookup { + + /** + * The look-up table for modifier keys. This is a map of formal name (String
)
+ * to integer value (Integer
).
+ */
+ private const Map!(String,Integer) modifierKeyTable;
+
+ /**
+ * The look-up table for formal names. This is a map of integer value (Integer
)
+ * to formal name (String
).
+ */
+ private const Map!(Integer,String) nameTable;
+
+ /**
+ * The look-up table for natural keys. This is a map of formal name (String
)
+ * to integer value (Integer
).
+ */
+ private const Map!(String,Integer) naturalKeyTable;
+
+ /**
+ * Constructs a new look-up class. This should only be done by the look-up
+ * factory.
+ *
+ * @see KeyLookupFactory
+ */
+ this() {
+ modifierKeyTable = new HashMap!(String,Integer);
+ nameTable = new HashMap!(Integer,String);
+ naturalKeyTable = new HashMap!(String,Integer);
+ Integer alt = new Integer(DWT.ALT);
+ Integer command = new Integer(DWT.COMMAND);
+ Integer ctrl = new Integer(DWT.CTRL);
+ Integer shift = new Integer(DWT.SHIFT);
+ modifierKeyTable.add(ALT_NAME, alt);
+ nameTable.add(alt, ALT_NAME);
+ modifierKeyTable.add(COMMAND_NAME, command);
+ nameTable.add(command, COMMAND_NAME);
+ modifierKeyTable.add(CTRL_NAME, ctrl);
+ nameTable.add(ctrl, CTRL_NAME);
+ modifierKeyTable.add(SHIFT_NAME, shift);
+ nameTable.add(shift, SHIFT_NAME);
+ modifierKeyTable.add(M1_NAME,
+ "carbon".equals(DWT.getPlatform()) ? command : ctrl); //$NON-NLS-1$
+ modifierKeyTable.add(M2_NAME, shift);
+ modifierKeyTable.add(M3_NAME, alt);
+ modifierKeyTable.add(M4_NAME, "carbon".equals(DWT.getPlatform()) ? ctrl //$NON-NLS-1$
+ : command);
+
+ Integer arrowDown = new Integer(DWT.ARROW_DOWN);
+ naturalKeyTable.add(ARROW_DOWN_NAME, arrowDown);
+ nameTable.add(arrowDown, ARROW_DOWN_NAME);
+ Integer arrowLeft = new Integer(DWT.ARROW_LEFT);
+ naturalKeyTable.add(ARROW_LEFT_NAME, arrowLeft);
+ nameTable.add(arrowLeft, ARROW_LEFT_NAME);
+ Integer arrowRight = new Integer(DWT.ARROW_RIGHT);
+ naturalKeyTable.add(ARROW_RIGHT_NAME, arrowRight);
+ nameTable.add(arrowRight, ARROW_RIGHT_NAME);
+ Integer arrowUp = new Integer(DWT.ARROW_UP);
+ naturalKeyTable.add(ARROW_UP_NAME, arrowUp);
+ nameTable.add(arrowUp, ARROW_UP_NAME);
+ Integer breakKey = new Integer(DWT.BREAK);
+ naturalKeyTable.add(BREAK_NAME, breakKey);
+ nameTable.add(breakKey, BREAK_NAME);
+ Integer bs = new Integer(DWT.BS);
+ naturalKeyTable.add(BS_NAME, bs);
+ nameTable.add(bs, BS_NAME);
+ naturalKeyTable.add(BACKSPACE_NAME, bs);
+ Integer capsLock = new Integer(DWT.CAPS_LOCK);
+ naturalKeyTable.add(CAPS_LOCK_NAME, capsLock);
+ nameTable.add(capsLock, CAPS_LOCK_NAME);
+ Integer cr = new Integer(DWT.CR);
+ naturalKeyTable.add(CR_NAME, cr);
+ nameTable.add(cr, CR_NAME);
+ naturalKeyTable.add(ENTER_NAME, cr);
+ naturalKeyTable.add(RETURN_NAME, cr);
+ Integer del = new Integer(DWT.DEL);
+ naturalKeyTable.add(DEL_NAME, del);
+ nameTable.add(del, DEL_NAME);
+ naturalKeyTable.add(DELETE_NAME, del);
+ Integer end = new Integer(DWT.END);
+ naturalKeyTable.add(END_NAME, end);
+ nameTable.add(end, END_NAME);
+ Integer esc = new Integer(DWT.ESC);
+ naturalKeyTable.add(ESC_NAME, esc);
+ nameTable.add(esc, ESC_NAME);
+ naturalKeyTable.add(ESCAPE_NAME, esc);
+ Integer f1 = new Integer(DWT.F1);
+ naturalKeyTable.add(F1_NAME, f1);
+ nameTable.add(f1, F1_NAME);
+ Integer f2 = new Integer(DWT.F2);
+ naturalKeyTable.add(F2_NAME, new Integer(DWT.F2));
+ nameTable.add(f2, F2_NAME);
+ Integer f3 = new Integer(DWT.F3);
+ naturalKeyTable.add(F3_NAME, new Integer(DWT.F3));
+ nameTable.add(f3, F3_NAME);
+ Integer f4 = new Integer(DWT.F4);
+ naturalKeyTable.add(F4_NAME, new Integer(DWT.F4));
+ nameTable.add(f4, F4_NAME);
+ Integer f5 = new Integer(DWT.F5);
+ naturalKeyTable.add(F5_NAME, new Integer(DWT.F5));
+ nameTable.add(f5, F5_NAME);
+ Integer f6 = new Integer(DWT.F6);
+ naturalKeyTable.add(F6_NAME, new Integer(DWT.F6));
+ nameTable.add(f6, F6_NAME);
+ Integer f7 = new Integer(DWT.F7);
+ naturalKeyTable.add(F7_NAME, new Integer(DWT.F7));
+ nameTable.add(f7, F7_NAME);
+ Integer f8 = new Integer(DWT.F8);
+ naturalKeyTable.add(F8_NAME, new Integer(DWT.F8));
+ nameTable.add(f8, F8_NAME);
+ Integer f9 = new Integer(DWT.F9);
+ naturalKeyTable.add(F9_NAME, new Integer(DWT.F9));
+ nameTable.add(f9, F9_NAME);
+ Integer f10 = new Integer(DWT.F10);
+ naturalKeyTable.add(F10_NAME, new Integer(DWT.F10));
+ nameTable.add(f10, F10_NAME);
+ Integer f11 = new Integer(DWT.F11);
+ naturalKeyTable.add(F11_NAME, new Integer(DWT.F11));
+ nameTable.add(f11, F11_NAME);
+ Integer f12 = new Integer(DWT.F12);
+ naturalKeyTable.add(F12_NAME, new Integer(DWT.F12));
+ nameTable.add(f12, F12_NAME);
+ Integer f13 = new Integer(DWT.F13);
+ naturalKeyTable.add(F13_NAME, new Integer(DWT.F13));
+ nameTable.add(f13, F13_NAME);
+ Integer f14 = new Integer(DWT.F14);
+ naturalKeyTable.add(F14_NAME, new Integer(DWT.F14));
+ nameTable.add(f14, F14_NAME);
+ Integer f15 = new Integer(DWT.F15);
+ naturalKeyTable.add(F15_NAME, new Integer(DWT.F15));
+ nameTable.add(f15, F15_NAME);
+ Integer ff = new Integer(12); // ASCII 0x0C
+ naturalKeyTable.add(FF_NAME, ff);
+ nameTable.add(ff, FF_NAME);
+ Integer home = new Integer(DWT.HOME);
+ naturalKeyTable.add(HOME_NAME, home);
+ nameTable.add(home, HOME_NAME);
+ Integer insert = new Integer(DWT.INSERT);
+ naturalKeyTable.add(INSERT_NAME, insert);
+ nameTable.add(insert, INSERT_NAME);
+ Integer lf = new Integer(DWT.LF);
+ naturalKeyTable.add(LF_NAME, lf);
+ nameTable.add(lf, LF_NAME);
+ Integer nul = new Integer(DWT.NULL);
+ naturalKeyTable.add(NUL_NAME, nul);
+ nameTable.add(nul, NUL_NAME);
+ Integer numLock = new Integer(DWT.NUM_LOCK);
+ naturalKeyTable.add(NUM_LOCK_NAME, numLock);
+ nameTable.add(numLock, NUM_LOCK_NAME);
+ Integer keypad0 = new Integer(DWT.KEYPAD_0);
+ naturalKeyTable.add(NUMPAD_0_NAME, keypad0);
+ nameTable.add(keypad0, NUMPAD_0_NAME);
+ Integer keypad1 = new Integer(DWT.KEYPAD_1);
+ naturalKeyTable.add(NUMPAD_1_NAME, keypad1);
+ nameTable.add(keypad1, NUMPAD_1_NAME);
+ Integer keypad2 = new Integer(DWT.KEYPAD_2);
+ naturalKeyTable.add(NUMPAD_2_NAME, keypad2);
+ nameTable.add(keypad2, NUMPAD_2_NAME);
+ Integer keypad3 = new Integer(DWT.KEYPAD_3);
+ naturalKeyTable.add(NUMPAD_3_NAME, keypad3);
+ nameTable.add(keypad3, NUMPAD_3_NAME);
+ Integer keypad4 = new Integer(DWT.KEYPAD_4);
+ naturalKeyTable.add(NUMPAD_4_NAME, keypad4);
+ nameTable.add(keypad4, NUMPAD_4_NAME);
+ Integer keypad5 = new Integer(DWT.KEYPAD_5);
+ naturalKeyTable.add(NUMPAD_5_NAME, keypad5);
+ nameTable.add(keypad5, NUMPAD_5_NAME);
+ Integer keypad6 = new Integer(DWT.KEYPAD_6);
+ naturalKeyTable.add(NUMPAD_6_NAME, keypad6);
+ nameTable.add(keypad6, NUMPAD_6_NAME);
+ Integer keypad7 = new Integer(DWT.KEYPAD_7);
+ naturalKeyTable.add(NUMPAD_7_NAME, keypad7);
+ nameTable.add(keypad7, NUMPAD_7_NAME);
+ Integer keypad8 = new Integer(DWT.KEYPAD_8);
+ naturalKeyTable.add(NUMPAD_8_NAME, keypad8);
+ nameTable.add(keypad8, NUMPAD_8_NAME);
+ Integer keypad9 = new Integer(DWT.KEYPAD_9);
+ naturalKeyTable.add(NUMPAD_9_NAME, keypad9);
+ nameTable.add(keypad9, NUMPAD_9_NAME);
+ Integer keypadAdd = new Integer(DWT.KEYPAD_ADD);
+ naturalKeyTable.add(NUMPAD_ADD_NAME, keypadAdd);
+ nameTable.add(keypadAdd, NUMPAD_ADD_NAME);
+ Integer keypadDecimal = new Integer(DWT.KEYPAD_DECIMAL);
+ naturalKeyTable.add(NUMPAD_DECIMAL_NAME, keypadDecimal);
+ nameTable.add(keypadDecimal, NUMPAD_DECIMAL_NAME);
+ Integer keypadDivide = new Integer(DWT.KEYPAD_DIVIDE);
+ naturalKeyTable.add(NUMPAD_DIVIDE_NAME, keypadDivide);
+ nameTable.add(keypadDivide, NUMPAD_DIVIDE_NAME);
+ Integer keypadCr = new Integer(DWT.KEYPAD_CR);
+ naturalKeyTable.add(NUMPAD_ENTER_NAME, keypadCr);
+ nameTable.add(keypadCr, NUMPAD_ENTER_NAME);
+ Integer keypadEqual = new Integer(DWT.KEYPAD_EQUAL);
+ naturalKeyTable.add(NUMPAD_EQUAL_NAME, keypadEqual);
+ nameTable.add(keypadEqual, NUMPAD_EQUAL_NAME);
+ Integer keypadMultiply = new Integer(DWT.KEYPAD_MULTIPLY);
+ naturalKeyTable.add(NUMPAD_MULTIPLY_NAME, keypadMultiply);
+ nameTable.add(keypadMultiply, NUMPAD_MULTIPLY_NAME);
+ Integer keypadSubtract = new Integer(DWT.KEYPAD_SUBTRACT);
+ naturalKeyTable.add(NUMPAD_SUBTRACT_NAME, keypadSubtract);
+ nameTable.add(keypadSubtract, NUMPAD_SUBTRACT_NAME);
+ Integer pageDown = new Integer(DWT.PAGE_DOWN);
+ naturalKeyTable.add(PAGE_DOWN_NAME, pageDown);
+ nameTable.add(pageDown, PAGE_DOWN_NAME);
+ Integer pageUp = new Integer(DWT.PAGE_UP);
+ naturalKeyTable.add(PAGE_UP_NAME, pageUp);
+ nameTable.add(pageUp, PAGE_UP_NAME);
+ Integer pause = new Integer(DWT.PAUSE);
+ naturalKeyTable.add(PAUSE_NAME, pause);
+ nameTable.add(pause, PAUSE_NAME);
+ Integer printScreen = new Integer(DWT.PRINT_SCREEN);
+ naturalKeyTable.add(PRINT_SCREEN_NAME, printScreen);
+ nameTable.add(printScreen, PRINT_SCREEN_NAME);
+ Integer scrollLock = new Integer(DWT.SCROLL_LOCK);
+ naturalKeyTable.add(SCROLL_LOCK_NAME, scrollLock);
+ nameTable.add(scrollLock, SCROLL_LOCK_NAME);
+ Integer space = new Integer(' ');
+ naturalKeyTable.add(SPACE_NAME, space);
+ nameTable.add(space, SPACE_NAME);
+ Integer tab = new Integer(DWT.TAB);
+ naturalKeyTable.add(TAB_NAME, tab);
+ nameTable.add(tab, TAB_NAME);
+ Integer vt = new Integer(11); // ASCII 0x0B
+ naturalKeyTable.add(VT_NAME, vt);
+ nameTable.add(vt, VT_NAME);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#formalKeyLookup(java.lang.String)
+ *
+ */
+ public final int formalKeyLookup(String name) {
+ Object value = naturalKeyTable.get(name);
+ if (cast(Integer)value ) {
+ return (cast(Integer) value).intValue();
+ }
+
+ if (name.length > 0) {
+ throw new IllegalArgumentException("Unrecognized formal key name: " //$NON-NLS-1$
+ ~ name);
+ }
+
+ return name.charAt(0);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#formalKeyLookupInteger(java.lang.String)
+ *
+ */
+ public final Integer formalKeyLookupInteger(String name) {
+ Object value = naturalKeyTable.get(name);
+ if (cast(Integer)value ) {
+ return cast(Integer) value;
+ }
+
+ return new Integer(name.charAt(0));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#formalModifierLookup(java.lang.String)
+ *
+ */
+ public final int formalModifierLookup(String name) {
+ Object value = modifierKeyTable.get(name);
+ if (cast(Integer)value ) {
+ return (cast(Integer) value).intValue();
+ }
+
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#formalNameLookup(int)
+ *
+ */
+ public final String formalNameLookup(int key) {
+ Integer keyObject = new Integer(key);
+ if (nameTable.containsKey(keyObject) ) {
+ return nameTable.get(keyObject);
+ }
+
+ return dcharToString( cast(dchar) key );
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#getAlt()
+ *
+ */
+ public final int getAlt() {
+ return DWT.ALT;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#getCommand()
+ *
+ */
+ public final int getCommand() {
+ return DWT.COMMAND;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#getCtrl()
+ *
+ */
+ public final int getCtrl() {
+ return DWT.CTRL;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#getShift()
+ *
+ */
+ public final int getShift() {
+ return DWT.SHIFT;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.IKeyLookup#isModifierKey(int)
+ *
+ */
+ public final bool isModifierKey(int key) {
+ return ((key & DWT.MODIFIER_MASK) !is 0);
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/SWTKeySupport.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/SWTKeySupport.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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 + * A utility class for converting DWT events into key strokes. + *
+ * + * @since 3.1 + */ +public final class SWTKeySupport { + + /** + * A formatter that displays key sequences in a style native to the + * platform. + */ + private static const IKeyFormatter NATIVE_FORMATTER; + + static this(){ + NATIVE_FORMATTER = new NativeKeyFormatter(); + } + + /** + * Given an DWT accelerator value, provide the corresponding key stroke. + * + * @param accelerator + * The accelerator to convert; should be a valid DWT accelerator + * value. + * @return The equivalent key stroke; nevernull
.
+ */
+ public static final KeyStroke convertAcceleratorToKeyStroke(int accelerator) {
+ int modifierKeys = accelerator & DWT.MODIFIER_MASK;
+ int naturalKey;
+ if (accelerator is modifierKeys) {
+ naturalKey = KeyStroke.NO_KEY;
+ } else {
+ naturalKey = accelerator - modifierKeys;
+ }
+
+ return KeyStroke.getInstance(modifierKeys, naturalKey);
+ }
+
+ /**
+ * + * Converts the given event into an DWT accelerator value -- considering the + * modified character with the shift modifier. This is the third accelerator + * value that should be checked when processing incoming key events. + *
+ *+ * For example, on a standard US keyboard, "Ctrl+Shift+5" would be viewed as + * "Ctrl+Shift+%". + *
+ * + * @param event + * The event to be converted; must not benull
.
+ * @return The combination of the state mask and the unmodified character.
+ */
+ public static final int convertEventToModifiedAccelerator(Event event) {
+ int modifiers = event.stateMask & DWT.MODIFIER_MASK;
+ char character = topKey(event);
+ return modifiers + toUpperCase(character);
+ }
+
+ /**
+ * + * Converts the given event into an DWT accelerator value -- considering the + * unmodified character with all modifier keys. This is the first + * accelerator value that should be checked when processing incoming key + * events. However, all alphabetic characters are considered as their + * uppercase equivalents. + *
+ *+ * For example, on a standard US keyboard, "Ctrl+Shift+5" would be viewed as + * "Ctrl+Shift+5". + *
+ * + * @param event + * The event to be converted; must not benull
.
+ * @return The combination of the state mask and the unmodified character.
+ */
+ public static final int convertEventToUnmodifiedAccelerator(
+ Event event) {
+ return convertEventToUnmodifiedAccelerator(event.stateMask,
+ event.keyCode);
+ }
+
+ /**
+ * + * Converts the given state mask and key code into an DWT accelerator value -- + * considering the unmodified character with all modifier keys. All + * alphabetic characters are considered as their uppercase equivalents. + *
+ *+ * For example, on a standard US keyboard, "Ctrl+Shift+5" would be viewed as + * "Ctrl+Shift+5". + *
+ * + * @param stateMask + * The integer mask of modifiers keys depressed when this was + * pressed. + * @param keyCode + * The key that was pressed, before being modified. + * @return The combination of the state mask and the unmodified character. + */ + private static final int convertEventToUnmodifiedAccelerator( + int stateMask, int keyCode) { + int modifiers = stateMask & DWT.MODIFIER_MASK; + int character = keyCode; + return modifiers + toUpperCase(character); + } + + /** + *+ * Converts the given event into an DWT accelerator value -- considering the + * unmodified character with all modifier keys. This is the first + * accelerator value that should be checked. However, all alphabetic + * characters are considered as their uppercase equivalents. + *
+ *+ * For example, on a standard US keyboard, "Ctrl+Shift+5" would be viewed as + * "Ctrl+%". + *
+ * + * @param event + * The event to be converted; must not benull
.
+ * @return The combination of the state mask and the unmodified character.
+ */
+ public static final int convertEventToUnmodifiedAccelerator(
+ KeyEvent event) {
+ return convertEventToUnmodifiedAccelerator(event.stateMask,
+ event.keyCode);
+ }
+
+ /**
+ * Converts the given event into an DWT accelerator value -- considering the
+ * modified character without the shift modifier. This is the second
+ * accelerator value that should be checked when processing incoming key
+ * events. Key strokes with alphabetic natural keys are run through
+ * convertEventToUnmodifiedAccelerator
.
+ *
+ * @param event
+ * The event to be converted; must not be null
.
+ * @return The combination of the state mask without shift, and the modified
+ * character.
+ */
+ public static final int convertEventToUnshiftedModifiedAccelerator(
+ Event event) {
+ // Disregard alphabetic key strokes.
+ if (CharacterIsLetter(cast(dchar) event.keyCode)) {
+ return convertEventToUnmodifiedAccelerator(event);
+ }
+
+ int modifiers = event.stateMask & (DWT.MODIFIER_MASK ^ DWT.SHIFT);
+ char character = topKey(event);
+ return modifiers + toUpperCase(character);
+ }
+
+ /**
+ * Given a key stroke, this method provides the equivalent DWT accelerator
+ * value. The functional inverse of
+ * convertAcceleratorToKeyStroke
.
+ *
+ * @param keyStroke
+ * The key stroke to convert; must not be null
.
+ * @return The DWT accelerator value
+ */
+ public static final int convertKeyStrokeToAccelerator(
+ KeyStroke keyStroke) {
+ return keyStroke.getModifierKeys() + keyStroke.getNaturalKey();
+ }
+
+ /**
+ * Provides an instance of IKeyFormatter
appropriate for the
+ * current instance.
+ *
+ * @return an instance of IKeyFormatter
appropriate for the
+ * current instance; never null
.
+ */
+ public static IKeyFormatter getKeyFormatterForPlatform() {
+ return NATIVE_FORMATTER;
+ }
+
+ /**
+ * Makes sure that a fully-modified character is converted to the normal
+ * form. This means that "Ctrl+" key strokes must reverse the modification
+ * caused by control-escaping. Also, all lower case letters are converted to
+ * uppercase.
+ *
+ * @param event
+ * The event from which the fully-modified character should be
+ * pulled.
+ * @return The modified character, uppercase and without control-escaping.
+ */
+ private static final char topKey(Event event) {
+ char character = event.character;
+ bool ctrlDown = (event.stateMask & DWT.CTRL) !is 0;
+
+ if (ctrlDown && event.character !is event.keyCode
+ && event.character < 0x20
+ && (event.keyCode & DWT.KEYCODE_BIT) is 0) {
+ character += 0x40;
+ }
+
+ return character;
+ }
+
+ /**
+ * Makes the given character uppercase if it is a letter.
+ *
+ * @param keyCode
+ * The character to convert.
+ * @return The uppercase equivalent, if any; otherwise, the character
+ * itself.
+ */
+ private static final int toUpperCase(int keyCode) {
+ // Will this key code be truncated?
+ if (keyCode > 0xFFFF) {
+ return keyCode;
+ }
+
+ // Downcast in safety. Only make characters uppercase.
+ char character = cast(char) keyCode;
+ return CharacterIsLetter(character) ? CharacterToUpper(character)
+ : keyCode;
+ }
+
+ /**
+ * This class should never be instantiated.
+ */
+ protected this() {
+ // This class should never be instantiated.
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/formatting/AbstractKeyFormatter.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/formatting/AbstractKeyFormatter.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * An abstract implementation of a key formatter that provides a lot of common
+ * key formatting functionality. It is recommended that implementations of
+ * IKeyFormatter
subclass from here, rather than implementing
+ * IKeyFormatter
directly.
+ *
sortModifierKeys(int)
.
+ */
+ protected static const int[] NO_MODIFIER_KEYS = null;
+
+ /**
+ * The bundle in which to look up the internationalized text for all of the
+ * individual keys in the system. This is the platform-agnostic version of
+ * the internationalized strings. Some platforms (namely Carbon) provide
+ * special Unicode characters and glyphs for some keys.
+ */
+ private static const ResourceBundle RESOURCE_BUNDLE;
+
+ /**
+ * The keys in the resource bundle. This is used to avoid missing resource
+ * exceptions when they aren't necessary.
+ */
+ private static const Set!(String) resourceBundleKeys;
+
+ static this() {
+ RESOURCE_BUNDLE = ResourceBundle.getBundle(AbstractKeyFormatter.classinfo.name);
+ resourceBundleKeys = new HashSet!(String);
+ foreach( key; RESOURCE_BUNDLE.getKeys()){
+ resourceBundleKeys.add(key);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keysKeyFormatter#format(dwtx.jface.bindings.keys.KeySequence)
+ */
+ public String format(int key) {
+ IKeyLookup lookup = KeyLookupFactory.getDefault();
+ String name = lookup.formalNameLookup(key);
+
+ if (resourceBundleKeys.contains(name)) {
+ return Util.translateString(RESOURCE_BUNDLE, name, name);
+ }
+
+ return name;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.KeyFormatter#format(dwtx.jface.bindings.keys.KeySequence)
+ */
+ public String format(KeySequence keySequence) {
+ StringBuffer stringBuffer = new StringBuffer();
+
+ KeyStroke[] keyStrokes = keySequence.getKeyStrokes();
+ int keyStrokesLength = keyStrokes.length;
+ for (int i = 0; i < keyStrokesLength; i++) {
+ stringBuffer.append(format(keyStrokes[i]));
+
+ if (i + 1 < keyStrokesLength) {
+ stringBuffer.append(getKeyStrokeDelimiter());
+ }
+ }
+
+ return stringBuffer.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.KeyFormatter#formatKeyStroke(dwtx.jface.bindings.keys.KeyStroke)
+ */
+ public String format(KeyStroke keyStroke) {
+ String keyDelimiter = getKeyDelimiter();
+
+ // Format the modifier keys, in sorted order.
+ int modifierKeys = keyStroke.getModifierKeys();
+ int[] sortedModifierKeys = sortModifierKeys(modifierKeys);
+ StringBuffer stringBuffer = new StringBuffer();
+ if (sortedModifierKeys !is null) {
+ for (int i = 0; i < sortedModifierKeys.length; i++) {
+ int modifierKey = sortedModifierKeys[i];
+ if (modifierKey !is KeyStroke.NO_KEY) {
+ stringBuffer.append(format(modifierKey));
+ stringBuffer.append(keyDelimiter);
+ }
+ }
+ }
+
+ // Format the natural key, if any.
+ int naturalKey = keyStroke.getNaturalKey();
+ if (naturalKey !is 0) {
+ stringBuffer.append(format(naturalKey));
+ }
+
+ return stringBuffer.toString();
+
+ }
+
+ /**
+ * An accessor for the delimiter you wish to use between keys. This is used
+ * by the default format implementations to determine the key delimiter.
+ *
+ * @return The delimiter to use between keys; should not be
+ * null
.
+ */
+ protected abstract String getKeyDelimiter();
+
+ /**
+ * An accessor for the delimiter you wish to use between key strokes. This
+ * used by the default format implementations to determine the key stroke
+ * delimiter.
+ *
+ * @return The delimiter to use between key strokes; should not be
+ * null
.
+ */
+ protected abstract String getKeyStrokeDelimiter();
+
+ /**
+ * Separates the modifier keys from each other, and then places them in an
+ * array in some sorted order. The sort order is dependent on the type of
+ * formatter.
+ *
+ * @param modifierKeys
+ * The modifier keys from the key stroke.
+ * @return An array of modifier key values -- separated and sorted in some
+ * order. Any values in this array that are
+ * KeyStroke.NO_KEY
should be ignored.
+ */
+ protected abstract int[] sortModifierKeys(int modifierKeys);
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/formatting/EmacsKeyFormatter.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/formatting/EmacsKeyFormatter.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 + * A key formatter providing the Emacs-style accelerators using single letters + * to represent the modifier keys. + *
+ * + * @since 3.1 + */ +public final class EmacsKeyFormatter : AbstractKeyFormatter { + + /** + * The resource bundle used byformat()
to translate formal
+ * string representations by locale.
+ */
+ private const static ResourceBundle RESOURCE_BUNDLE;
+
+ static this(){
+ RESOURCE_BUNDLE = ResourceBundle.getBundle(EmacsKeyFormatter.classinfo.name);
+ }
+ /**
+ * Formats an individual key into a human readable format. This converts the
+ * key into a format similar to Xemacs.
+ *
+ * @param key
+ * The key to format; must not be null
.
+ * @return The key formatted as a string; should not be null
.
+ */
+ public String format(int key) {
+ IKeyLookup lookup = KeyLookupFactory.getDefault();
+ if (lookup.isModifierKey(key)) {
+ String formattedName = Util.translateString(RESOURCE_BUNDLE, lookup
+ .formalNameLookup(key), null);
+ if (formattedName !is null) {
+ return formattedName;
+ }
+ }
+
+ return super.format(key).toLowerCase();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#getKeyDelimiter()
+ */
+ protected String getKeyDelimiter() {
+ return Util.translateString(RESOURCE_BUNDLE, KEY_DELIMITER_KEY,
+ KeyStroke.KEY_DELIMITER);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#getKeyStrokeDelimiter()
+ */
+ protected String getKeyStrokeDelimiter() {
+ return Util.translateString(RESOURCE_BUNDLE, KEY_STROKE_DELIMITER_KEY,
+ KeySequence.KEY_STROKE_DELIMITER);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#sortModifierKeys(int)
+ */
+ protected int[] sortModifierKeys(int modifierKeys) {
+ IKeyLookup lookup = KeyLookupFactory.getDefault();
+ int[] sortedKeys = new int[4];
+ int index = 0;
+
+ if ((modifierKeys & lookup.getAlt()) !is 0) {
+ sortedKeys[index++] = lookup.getAlt();
+ }
+ if ((modifierKeys & lookup.getCommand()) !is 0) {
+ sortedKeys[index++] = lookup.getCommand();
+ }
+ if ((modifierKeys & lookup.getCtrl()) !is 0) {
+ sortedKeys[index++] = lookup.getCtrl();
+ }
+ if ((modifierKeys & lookup.getShift()) !is 0) {
+ sortedKeys[index++] = lookup.getShift();
+ }
+
+ return sortedKeys;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/formatting/FormalKeyFormatter.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/formatting/FormalKeyFormatter.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 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 + * Formats the keys in the internal key sequence grammar. This is used for + * persistence, and is not really intended for display to the user. + *
+ * + * @since 3.1 + */ +public final class FormalKeyFormatter : AbstractKeyFormatter { + + /* + * (non-Javadoc) + * + * @see dwtx.jface.bindings.keys.KeyFormatter#format(dwtx.ui.keys.KeySequence) + */ + public String format(int key) { + IKeyLookup lookup = KeyLookupFactory.getDefault(); + return lookup.formalNameLookup(key); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#getKeyDelimiter() + */ + protected String getKeyDelimiter() { + return KeyStroke.KEY_DELIMITER; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#getKeyStrokeDelimiter() + */ + protected String getKeyStrokeDelimiter() { + return KeySequence.KEY_STROKE_DELIMITER; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#sortModifierKeys(int) + */ + protected int[] sortModifierKeys(int modifierKeys) { + IKeyLookup lookup = KeyLookupFactory.getDefault(); + int[] sortedKeys = new int[4]; + int index = 0; + + if ((modifierKeys & lookup.getAlt()) !is 0) { + sortedKeys[index++] = lookup.getAlt(); + } + if ((modifierKeys & lookup.getCommand()) !is 0) { + sortedKeys[index++] = lookup.getCommand(); + } + if ((modifierKeys & lookup.getCtrl()) !is 0) { + sortedKeys[index++] = lookup.getCtrl(); + } + if ((modifierKeys & lookup.getShift()) !is 0) { + sortedKeys[index++] = lookup.getShift(); + } + + return sortedKeys; + } +} diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/formatting/IKeyFormatter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/bindings/keys/formatting/IKeyFormatter.d Tue Apr 01 08:00:31 2008 +0200 @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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+ * Any formatter capable of taking a key sequence or a key stroke and converting + * it into a string. These formatters are used to produce the strings that the + * user sees in the keys preference page and the menus, as well as the strings + * that are used for persistent storage. + *
+ * + * @since 3.1 + */ +public interface IKeyFormatter { + + /** + * Formats an individual key into a human readable format. This uses an + * internationalization resource bundle to look up the key. This does not do + * any platform-specific formatting (e.g., Carbon's command character). + * + * @param key + * The key to format. + * @return The key formatted as a string; should not benull
.
+ */
+ String format(int key);
+
+ /**
+ * Format the given key sequence into a string. The manner of the conversion
+ * is dependent on the formatter. It is required that unequal key sequences
+ * return unequal strings.
+ *
+ * @param keySequence
+ * The key sequence to convert; must not be null
.
+ * @return A string representation of the key sequence; must not be
+ * null
.
+ */
+ String format(KeySequence keySequence);
+
+ /**
+ * Format the given key strokes into a string. The manner of the conversion
+ * is dependent on the formatter. It is required that unequal key strokes
+ * return unequal strings.
+ *
+ * @param keyStroke
+ * The key stroke to convert; must not be null
.
+ * @return A string representation of the key stroke; must not be
+ * null
+ */
+ String format(KeyStroke keyStroke);
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/formatting/KeyFormatterFactory.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/formatting/KeyFormatterFactory.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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
+ * A cache for formatters. It keeps a few instances of pre-defined instances of
+ * IKeyFormatter
available for use. It also allows the default
+ * formatter to be changed.
+ *
null
.
+ */
+ public static final IKeyFormatter getDefault() {
+ return defaultKeyFormatter;
+ }
+
+ /**
+ * Provides an instance of EmacsKeyFormatter
.
+ *
+ * @return The Xemacs formatter; never null
.
+ */
+ public static final IKeyFormatter getEmacsKeyFormatter() {
+ return EMACS_KEY_FORMATTER;
+ }
+
+ /**
+ * Provides an instance of FormalKeyFormatter
.
+ *
+ * @return The formal formatter; never null
.
+ */
+ public static final IKeyFormatter getFormalKeyFormatter() {
+ return FORMAL_KEY_FORMATTER;
+ }
+
+ /**
+ * Sets the default key formatter.
+ *
+ * @param defaultKeyFormatter
+ * the default key formatter. Must not be null
.
+ */
+ public static final void setDefault(IKeyFormatter defaultKeyFormatter) {
+ if (defaultKeyFormatter is null) {
+ throw new NullPointerException();
+ }
+
+ KeyFormatterFactory.defaultKeyFormatter = defaultKeyFormatter;
+ }
+
+ /**
+ * This class should not be instantiated.
+ */
+ private this() {
+ // Not to be constructred.
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/bindings/keys/formatting/NativeKeyFormatter.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/bindings/keys/formatting/NativeKeyFormatter.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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 + * Formats the key sequences and key strokes into the native human-readable + * format. This is typically what you would see on the menus for the given + * platform and locale. + *
+ * + * @since 3.1 + */ +public final class NativeKeyFormatter : AbstractKeyFormatter { + + /** + * The key into the internationalization resource bundle for the delimiter + * to use between keys (on the Carbon platform). + */ + private const static String CARBON_KEY_DELIMITER_KEY = "CARBON_KEY_DELIMITER"; //$NON-NLS-1$ + + /** + * A look-up table for the string representations of various carbon keys. + */ + private const static HashMap!(String,String) CARBON_KEY_LOOK_UP; + + /** + * The resource bundle used byformat()
to translate formal
+ * string representations by locale.
+ */
+ private const static ResourceBundle RESOURCE_BUNDLE;
+
+ /**
+ * The key into the internationalization resource bundle for the delimiter
+ * to use between key strokes (on the Win32 platform).
+ */
+ private const static String WIN32_KEY_STROKE_DELIMITER_KEY = "WIN32_KEY_STROKE_DELIMITER"; //$NON-NLS-1$
+
+ static this() {
+ CARBON_KEY_LOOK_UP = new HashMap!(String,String);
+ RESOURCE_BUNDLE = ResourceBundle.getBundle(NativeKeyFormatter.classinfo.name);
+
+ String carbonBackspace = "\u232B"; //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.BS_NAME, carbonBackspace);
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.BACKSPACE_NAME, carbonBackspace);
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.CR_NAME, "\u21A9"); //$NON-NLS-1$
+ String carbonDelete = "\u2326"; //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.DEL_NAME, carbonDelete);
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.DELETE_NAME, carbonDelete);
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.SPACE_NAME, "\u2423"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.ALT_NAME, "\u2325"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.COMMAND_NAME, "\u2318"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.CTRL_NAME, "\u2303"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.SHIFT_NAME, "\u21E7"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.ARROW_DOWN_NAME, "\u2193"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.ARROW_LEFT_NAME, "\u2190"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.ARROW_RIGHT_NAME, "\u2192"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.ARROW_UP_NAME, "\u2191"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.END_NAME, "\u2198"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.NUMPAD_ENTER_NAME, "\u2324"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.HOME_NAME, "\u2196"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.PAGE_DOWN_NAME, "\u21DF"); //$NON-NLS-1$
+ CARBON_KEY_LOOK_UP.add(IKeyLookup.PAGE_UP_NAME, "\u21DE"); //$NON-NLS-1$
+ }
+
+ /**
+ * Formats an individual key into a human readable format. This uses an
+ * internationalization resource bundle to look up the key. This does the
+ * platform-specific formatting for Carbon.
+ *
+ * @param key
+ * The key to format.
+ * @return The key formatted as a string; should not be null
.
+ */
+ public final String format(int key) {
+ IKeyLookup lookup = KeyLookupFactory.getDefault();
+ String name = lookup.formalNameLookup(key);
+
+ // TODO consider platform-specific resource bundles
+ if ("carbon".equals(DWT.getPlatform())) { //$NON-NLS-1$
+ String formattedName = cast(String) CARBON_KEY_LOOK_UP.get(name);
+ if (formattedName !is null) {
+ return formattedName;
+ }
+ }
+
+ return super.format(key);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#getKeyDelimiter()
+ */
+ protected String getKeyDelimiter() {
+ // We must do the look up every time, as our locale might change.
+ if ("carbon".equals(DWT.getPlatform())) { //$NON-NLS-1$
+ return Util.translateString(RESOURCE_BUNDLE,
+ CARBON_KEY_DELIMITER_KEY, Util.ZERO_LENGTH_STRING);
+ }
+
+ return Util.translateString(RESOURCE_BUNDLE, KEY_DELIMITER_KEY,
+ KeyStroke.KEY_DELIMITER);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#getKeyStrokeDelimiter()
+ */
+ protected String getKeyStrokeDelimiter() {
+ // We must do the look up every time, as our locale might change.
+ if ("win32".equals(DWT.getPlatform())) { //$NON-NLS-1$
+ return Util.translateString(RESOURCE_BUNDLE,
+ WIN32_KEY_STROKE_DELIMITER_KEY,
+ KeySequence.KEY_STROKE_DELIMITER);
+ }
+
+ return Util.translateString(RESOURCE_BUNDLE, KEY_STROKE_DELIMITER_KEY,
+ KeySequence.KEY_STROKE_DELIMITER);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.bindings.keys.AbstractKeyFormatter#sortModifierKeys(int)
+ */
+ protected int[] sortModifierKeys(int modifierKeys) {
+ IKeyLookup lookup = KeyLookupFactory.getDefault();
+ String platform = DWT.getPlatform();
+ int[] sortedKeys = new int[4];
+ int index = 0;
+
+ if ("win32".equals(platform) || "wpf".equals(platform)) { //$NON-NLS-1$ //$NON-NLS-2$
+ if ((modifierKeys & lookup.getCtrl()) !is 0) {
+ sortedKeys[index++] = lookup.getCtrl();
+ }
+ if ((modifierKeys & lookup.getAlt()) !is 0) {
+ sortedKeys[index++] = lookup.getAlt();
+ }
+ if ((modifierKeys & lookup.getShift()) !is 0) {
+ sortedKeys[index++] = lookup.getShift();
+ }
+
+ } else if ("gtk".equals(platform) || "motif".equals(platform)) { //$NON-NLS-1$ //$NON-NLS-2$
+ if ((modifierKeys & lookup.getShift()) !is 0) {
+ sortedKeys[index++] = lookup.getShift();
+ }
+ if ((modifierKeys & lookup.getCtrl()) !is 0) {
+ sortedKeys[index++] = lookup.getCtrl();
+ }
+ if ((modifierKeys & lookup.getAlt()) !is 0) {
+ sortedKeys[index++] = lookup.getAlt();
+ }
+
+ } else if ("carbon".equals(platform)) { //$NON-NLS-1$
+ if ((modifierKeys & lookup.getShift()) !is 0) {
+ sortedKeys[index++] = lookup.getShift();
+ }
+ if ((modifierKeys & lookup.getCtrl()) !is 0) {
+ sortedKeys[index++] = lookup.getCtrl();
+ }
+ if ((modifierKeys & lookup.getAlt()) !is 0) {
+ sortedKeys[index++] = lookup.getAlt();
+ }
+ if ((modifierKeys & lookup.getCommand()) !is 0) {
+ sortedKeys[index++] = lookup.getCommand();
+ }
+
+ }
+
+ return sortedKeys;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/dialogs/PopupDialog.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/dialogs/PopupDialog.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,1244 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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
+ * Stefan Xenos, IBM - bug 156790: Adopt GridLayoutFactory within JFace
+ * Port to the D programming language:
+ * Frank Benoit + * Because the dialog is short-lived, most of the configuration of the dialog is + * done in the constructor. Set methods are only provided for those values that + * are expected to be dynamically computed based on a particular instance's + * internal state. + *
+ * Clients are expected to override the creation of the main dialog area, and
+ * may optionally override the creation of the title area and info area in order
+ * to add content. In general, however, the creation of stylistic features, such
+ * as the dialog menu, separator styles, and fonts, is kept private so that all
+ * popup dialogs will have a similar appearance.
+ *
+ * @since 3.2
+ */
+public class PopupDialog : Window {
+
+ /**
+ *
+ */
+ private static const GridDataFactory LAYOUTDATA_GRAB_BOTH;
+
+ /**
+ * The dialog settings key name for stored dialog x location.
+ */
+ private static const String DIALOG_ORIGIN_X = "DIALOG_X_ORIGIN"; //$NON-NLS-1$
+
+ /**
+ * The dialog settings key name for stored dialog y location.
+ */
+ private static const String DIALOG_ORIGIN_Y = "DIALOG_Y_ORIGIN"; //$NON-NLS-1$
+
+ /**
+ * The dialog settings key name for stored dialog width.
+ */
+ private static const String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
+
+ /**
+ * The dialog settings key name for stored dialog height.
+ */
+ private static const String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
+
+ /**
+ * The dialog settings key name for remembering if the persisted bounds
+ * should be accessed.
+ */
+ private static const String DIALOG_USE_PERSISTED_BOUNDS = "DIALOG_USE_PERSISTED_BOUNDS"; //$NON-NLS-1$
+
+ /**
+ * Move action for the dialog.
+ */
+ private class MoveAction : Action {
+
+ this() {
+ super(JFaceResources.getString("PopupDialog.move"), //$NON-NLS-1$
+ IAction.AS_PUSH_BUTTON);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.action.IAction#run()
+ */
+ public void run() {
+ performTrackerAction(DWT.NONE);
+ }
+
+ }
+
+ /**
+ * Resize action for the dialog.
+ */
+ private class ResizeAction : Action {
+
+ this() {
+ super(JFaceResources.getString("PopupDialog.resize"), //$NON-NLS-1$
+ IAction.AS_PUSH_BUTTON);
+ }
+
+ /*
+ * @see dwtx.jface.action.Action#run()
+ */
+ public void run() {
+ performTrackerAction(DWT.RESIZE);
+ }
+ }
+
+ /**
+ *
+ * Remember bounds action for the dialog.
+ */
+ private class PersistBoundsAction : Action {
+
+ this() {
+ super(JFaceResources.getString("PopupDialog.persistBounds"), //$NON-NLS-1$
+ IAction.AS_CHECK_BOX);
+ setChecked(persistBounds);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.action.IAction#run()
+ */
+ public void run() {
+ persistBounds = isChecked();
+ }
+ }
+
+ /**
+ * Shell style appropriate for a simple hover popup that cannot get focus.
+ */
+ public const static int HOVER_SHELLSTYLE = DWT.NO_FOCUS | DWT.ON_TOP
+ | DWT.NO_TRIM;
+
+ /**
+ * Shell style appropriate for an info popup that can get focus.
+ */
+ public const static int INFOPOPUP_SHELLSTYLE = DWT.NO_TRIM;
+
+ /**
+ * Shell style appropriate for a resizable info popup that can get focus.
+ */
+ public const static int INFOPOPUPRESIZE_SHELLSTYLE = DWT.RESIZE;
+
+ /**
+ * Margin width (in pixels) to be used in layouts inside popup dialogs
+ * (value is 0).
+ */
+ public const static int POPUP_MARGINWIDTH = 0;
+
+ /**
+ * Margin height (in pixels) to be used in layouts inside popup dialogs
+ * (value is 0).
+ */
+ public const static int POPUP_MARGINHEIGHT = 0;
+
+ /**
+ * Vertical spacing (in pixels) between cells in the layouts inside popup
+ * dialogs (value is 1).
+ */
+ public const static int POPUP_VERTICALSPACING = 1;
+
+ /**
+ * Vertical spacing (in pixels) between cells in the layouts inside popup
+ * dialogs (value is 1).
+ */
+ public const static int POPUP_HORIZONTALSPACING = 1;
+
+ /**
+ *
+ */
+ private static const GridLayoutFactory POPUP_LAYOUT_FACTORY;
+
+ static this(){
+ LAYOUTDATA_GRAB_BOTH = GridDataFactory.fillDefaults().grab(true,true);
+ POPUP_LAYOUT_FACTORY = GridLayoutFactory
+ .fillDefaults().margins(POPUP_MARGINWIDTH, POPUP_MARGINHEIGHT)
+ .spacing(POPUP_HORIZONTALSPACING, POPUP_VERTICALSPACING);
+ }
+ /**
+ * Border thickness in pixels.
+ */
+ private static const int BORDER_THICKNESS = 1;
+
+ /**
+ * The dialog's toolbar for the move and resize capabilities.
+ */
+ private ToolBar toolBar = null;
+
+ /**
+ * The dialog's menu manager.
+ */
+ private MenuManager menuManager = null;
+
+ /**
+ * The control representing the main dialog area.
+ */
+ private Control dialogArea;
+
+ /**
+ * Labels that contain title and info text. Cached so they can be updated
+ * dynamically if possible.
+ */
+ private Label titleLabel, infoLabel;
+
+ /**
+ * Separator controls. Cached so they can be excluded from color changes.
+ */
+ private Control titleSeparator, infoSeparator;
+
+ /**
+ * The images for the dialog menu.
+ */
+ private Image menuImage, disabledMenuImage = null;
+
+ /**
+ * Font to be used for the info area text. Computed based on the dialog's
+ * font.
+ */
+ private Font infoFont;
+
+ /**
+ * Font to be used for the title area text. Computed based on the dialog's
+ * font.
+ */
+ private Font titleFont;
+
+ /**
+ * Flags indicating whether we are listening for shell deactivate events,
+ * either those or our parent's. Used to prevent closure when a menu command
+ * is chosen or a secondary popup is launched.
+ */
+ private bool listenToDeactivate;
+
+ private bool listenToParentDeactivate;
+
+ private Listener parentDeactivateListener;
+
+ /**
+ * Flag indicating whether focus should be taken when the dialog is opened.
+ */
+ private bool takeFocusOnOpen = false;
+
+ /**
+ * Flag specifying whether a menu should be shown that allows the user to
+ * move and resize.
+ */
+ private bool showDialogMenu_ = false;
+
+ /**
+ * Flag specifying whether a menu action allowing the user to choose whether
+ * the dialog bounds should be persisted is to be shown.
+ */
+ private bool showPersistAction = false;
+
+ /**
+ * Flag specifying whether the bounds of the popup should be persisted. This
+ * flag is updated by a menu if the menu is shown.
+ */
+ private bool persistBounds = false;
+
+ /**
+ * Text to be shown in an optional title area (on top).
+ */
+ private String titleText;
+
+ /**
+ * Text to be shown in an optional info area (at the bottom).
+ */
+ private String infoText;
+
+ /**
+ * Constructs a new instance of PopupDialog
.
+ *
+ * @param parent
+ * The parent shell.
+ * @param shellStyle
+ * The shell style.
+ * @param takeFocusOnOpen
+ * A bool indicating whether focus should be taken by this
+ * popup when it opens.
+ * @param persistBounds
+ * A bool indicating whether the bounds should be persisted
+ * upon close of the dialog. The bounds can only be persisted if
+ * the dialog settings for persisting the bounds are also
+ * specified. If a menu action will be provided that allows the
+ * user to control this feature, then the last known value of the
+ * user's setting will be used instead of this flag.
+ * @param showDialogMenu
+ * A bool indicating whether a menu for moving and resizing
+ * the popup should be provided.
+ * @param showPersistAction
+ * A bool indicating whether an action allowing the user to
+ * control the persisting of the dialog bounds should be shown in
+ * the dialog menu. This parameter has no effect if
+ * showDialogMenu
is false
.
+ * @param titleText
+ * Text to be shown in an upper title area, or null
+ * if there is no title.
+ * @param infoText
+ * Text to be shown in a lower info area, or null
+ * if there is no info area.
+ *
+ * @see PopupDialog#getDialogSettings()
+ */
+ public this(Shell parent, int shellStyle, bool takeFocusOnOpen,
+ bool persistBounds, bool showDialogMenu_,
+ bool showPersistAction, String titleText, String infoText) {
+ super(parent);
+ setShellStyle(shellStyle);
+ this.takeFocusOnOpen = takeFocusOnOpen;
+ this.showDialogMenu_ = showDialogMenu_;
+ this.showPersistAction = showPersistAction;
+ this.titleText = titleText;
+ this.infoText = infoText;
+
+ setBlockOnOpen(false);
+
+ this.persistBounds = persistBounds;
+ initializeWidgetState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.window.Window#configureShell(Shell)
+ */
+ protected void configureShell(Shell shell) {
+ Display display = shell.getDisplay();
+ shell.setBackground(display.getSystemColor(DWT.COLOR_BLACK));
+
+ int border = ((getShellStyle() & DWT.NO_TRIM) is 0) ? 0
+ : BORDER_THICKNESS;
+ GridLayoutFactory.fillDefaults().margins(border, border).spacing(5,5).applyTo(shell);
+
+ shell.addListener(DWT.Deactivate, new class Listener {
+ public void handleEvent(Event event) {
+ /*
+ * Close if we are deactivating and have no child shells. If we
+ * have child shells, we are deactivating due to their opening.
+ * On X, we receive this when a menu child (such as the system
+ * menu) of the shell opens, but I have not found a way to
+ * distinguish that case here. Hence bug #113577 still exists.
+ */
+ if (listenToDeactivate && event.widget is getShell()
+ && getShell().getShells().length is 0) {
+ close();
+ } else {
+ /* We typically ignore deactivates to work around platform-specific
+ * event ordering. Now that we've ignored whatever we were supposed to,
+ * start listening to deactivates. Example issues can be found in
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=123392
+ */
+ listenToDeactivate = true;
+ }
+ }
+ });
+ // Set this true whenever we activate. It may have been turned
+ // off by a menu or secondary popup showing.
+ shell.addListener(DWT.Activate, new class Listener {
+ public void handleEvent(Event event) {
+ // ignore this event if we have launched a child
+ if (event.widget is getShell()
+ && getShell().getShells().length is 0) {
+ listenToDeactivate = true;
+ // Typically we start listening for parent deactivate after
+ // we are activated, except on the Mac, where the deactivate
+ // is received after activate.
+ // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=100668
+ listenToParentDeactivate = !"carbon".equals(DWT.getPlatform()); //$NON-NLS-1$
+ }
+ }
+ });
+
+ if ((getShellStyle() & DWT.ON_TOP) !is 0 && shell.getParent() !is null) {
+ parentDeactivateListener= new class Listener {
+ public void handleEvent(Event event) {
+ if (listenToParentDeactivate) {
+ close();
+ } else {
+ // Our first deactivate, now start listening on the Mac.
+ listenToParentDeactivate = listenToDeactivate;
+ }
+ }
+ };
+ shell.getParent().addListener(DWT.Deactivate, parentDeactivateListener);
+ }
+
+ shell.addDisposeListener(new class DisposeListener {
+ public void widgetDisposed(DisposeEvent event) {
+ handleDispose();
+ }
+ });
+ }
+
+ /**
+ * The PopupDialog
implementation of this Window
+ * method creates and lays out the top level composite for the dialog. It
+ * then calls the createTitleMenuArea
,
+ * createDialogArea
, and createInfoTextArea
+ * methods to create an optional title and menu area on the top, a dialog
+ * area in the center, and an optional info text area at the bottom.
+ * Overriding createDialogArea
and (optionally)
+ * createTitleMenuArea
and createTitleMenuArea
+ * are recommended rather than overriding this method.
+ *
+ * @param parent
+ * the composite used to parent the contents.
+ *
+ * @return the control representing the contents.
+ */
+ protected Control createContents(Composite parent) {
+ Composite composite = new Composite(parent, DWT.NONE);
+ POPUP_LAYOUT_FACTORY.applyTo(composite);
+ LAYOUTDATA_GRAB_BOTH.applyTo(composite);
+
+ // Title area
+ if (hasTitleArea()) {
+ createTitleMenuArea(composite);
+ titleSeparator = createHorizontalSeparator(composite);
+ }
+ // Content
+ dialogArea = createDialogArea(composite);
+ // Create a grid data layout data if one was not provided.
+ // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=118025
+ if (dialogArea.getLayoutData() is null) {
+ LAYOUTDATA_GRAB_BOTH.applyTo(dialogArea);
+ }
+
+ // Info field
+ if (hasInfoArea()) {
+ infoSeparator = createHorizontalSeparator(composite);
+ createInfoTextArea(composite);
+ }
+
+ applyColors(composite);
+ applyFonts(composite);
+ return composite;
+ }
+
+ /**
+ * Creates and returns the contents of the dialog (the area below the title
+ * area and above the info text area.
+ *
+ * The PopupDialog
implementation of this framework method
+ * creates and returns a new Composite
with standard margins
+ * and spacing.
+ *
+ * The returned control's layout data must be an instance of
+ * GridData
. This method must not modify the parent's
+ * layout.
+ *
+ * Subclasses must override this method but may call super
as
+ * in the following example:
+ *
+ *
+ * Composite composite = (Composite) super.createDialogArea(parent); + * //add controls to composite as necessary + * return composite; + *+ * + * @param parent + * the parent composite to contain the dialog area + * @return the dialog area control + */ + protected Control createDialogArea(Composite parent) { + Composite composite = new Composite(parent, DWT.NONE); + POPUP_LAYOUT_FACTORY.applyTo(composite); + LAYOUTDATA_GRAB_BOTH.applyTo(composite); + return composite; + } + + /** + * Returns the control that should get initial focus. Subclasses may + * override this method. + * + * @return the Control that should receive focus when the popup opens. + */ + protected Control getFocusControl() { + return dialogArea; + } + + /** + * Sets the tab order for the popup. Clients should override to introduce + * specific tab ordering. + * + * @param composite + * the composite in which all content, including the title area + * and info area, was created. This composite's parent is the + * shell. + */ + protected void setTabOrder(Composite composite) { + // default is to do nothing + } + + /** + * Returns a bool indicating whether the popup should have a title area + * at the top of the dialog. Subclasses may override. Default behavior is to + * have a title area if there is to be a menu or title text. + * + * @return
true
if a title area should be created,
+ * false
if it should not.
+ */
+ protected bool hasTitleArea() {
+ return titleText !is null || showDialogMenu_;
+ }
+
+ /**
+ * Returns a bool indicating whether the popup should have an info area
+ * at the bottom of the dialog. Subclasses may override. Default behavior is
+ * to have an info area if info text was provided at the time of creation.
+ *
+ * @return true
if a title area should be created,
+ * false
if it should not.
+ */
+ protected bool hasInfoArea() {
+ return infoText !is null;
+ }
+
+ /**
+ * Creates the title and menu area. Subclasses typically need not override
+ * this method, but instead should use the constructor parameters
+ * showDialogMenu
and showPersistAction
to
+ * indicate whether a menu should be shown, and
+ * createTitleControl
to to customize the presentation of the
+ * title.
+ *
+ *
+ * If this method is overridden, the returned control's layout data must be
+ * an instance of GridData
. This method must not modify the
+ * parent's layout.
+ *
+ * @param parent
+ * The parent composite.
+ * @return The Control representing the title and menu area.
+ */
+ protected Control createTitleMenuArea(Composite parent) {
+
+ Composite titleAreaComposite = new Composite(parent, DWT.NONE);
+ POPUP_LAYOUT_FACTORY.copy().numColumns(2).applyTo(titleAreaComposite);
+ GridDataFactory.fillDefaults()
+ .align_(DWT.FILL, DWT.CENTER).grab(true, false)
+ .applyTo(titleAreaComposite);
+
+ createTitleControl(titleAreaComposite);
+
+ if (showDialogMenu_) {
+ createDialogMenu(titleAreaComposite);
+ }
+ return titleAreaComposite;
+ }
+
+ /**
+ * Creates the control to be used to represent the dialog's title text.
+ * Subclasses may override if a different control is desired for
+ * representing the title text, or if something different than the title
+ * should be displayed in location where the title text typically is shown.
+ *
+ *
+ * If this method is overridden, the returned control's layout data must be
+ * an instance of GridData
. This method must not modify the
+ * parent's layout.
+ *
+ * @param parent
+ * The parent composite.
+ * @return The Control representing the title area.
+ */
+ protected Control createTitleControl(Composite parent) {
+ titleLabel = new Label(parent, DWT.NONE);
+
+ GridDataFactory.fillDefaults()
+ .align_(DWT.FILL, DWT.CENTER)
+ .grab(true, false)
+ .span(showDialogMenu_ ? 1 : 2, 1)
+ .applyTo(titleLabel);
+
+ Font font = titleLabel.getFont();
+ FontData[] fontDatas = font.getFontData();
+ for (int i = 0; i < fontDatas.length; i++) {
+ fontDatas[i].setStyle(DWT.BOLD);
+ }
+ titleFont = new Font(titleLabel.getDisplay(), fontDatas);
+ titleLabel.setFont(titleFont);
+
+ if (titleText !is null) {
+ titleLabel.setText(titleText);
+ }
+ return titleLabel;
+ }
+
+ /**
+ * Creates the optional info text area. This method is only called if the
+ * hasInfoArea()
method returns true. Subclasses typically
+ * need not override this method, but may do so.
+ *
+ *
+ * If this method is overridden, the returned control's layout data must be
+ * an instance of
+ * This method is reimplemented for special configuration of PopupDialogs.
+ * It never blocks on open, immediately returning
+ * This method is extended to save the dialog bounds and initialize widget
+ * state so that the widgets can be recreated if the dialog is reopened.
+ * This method may be extended (GridData
. This method must not modify the
+ * parent's layout.
+ *
+ *
+ * @param parent
+ * The parent composite.
+ * @return The control representing the info text area.
+ *
+ * @see PopupDialog#hasInfoArea()
+ * @see PopupDialog#createTitleControl(Composite)
+ */
+ protected Control createInfoTextArea(Composite parent) {
+ // Status label
+ infoLabel = new Label(parent, DWT.RIGHT);
+ infoLabel.setText(infoText);
+ Font font = infoLabel.getFont();
+ FontData[] fontDatas = font.getFontData();
+ for (int i = 0; i < fontDatas.length; i++) {
+ fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+ }
+ infoFont = new Font(infoLabel.getDisplay(), fontDatas);
+ infoLabel.setFont(infoFont);
+ GridDataFactory.fillDefaults().grab(true, false).align_(DWT.FILL, DWT.BEGINNING)
+ .applyTo(infoLabel);
+ infoLabel.setForeground(parent.getDisplay().getSystemColor(
+ DWT.COLOR_WIDGET_DARK_SHADOW));
+ return infoLabel;
+ }
+
+ /**
+ * Create a horizontal separator for the given parent.
+ *
+ * @param parent
+ * The parent composite.
+ * @return The Control representing the horizontal separator.
+ */
+ private Control createHorizontalSeparator(Composite parent) {
+ Label separator = new Label(parent, DWT.SEPARATOR | DWT.HORIZONTAL
+ | DWT.LINE_DOT);
+ GridDataFactory.fillDefaults().align_(DWT.FILL, DWT.CENTER).grab(true, false).applyTo(separator);
+ return separator;
+ }
+
+ /**
+ * Create the dialog's menu for the move and resize actions.
+ *
+ * @param parent
+ * The parent composite.
+ */
+ private void createDialogMenu(Composite parent) {
+
+ toolBar = new ToolBar(parent, DWT.FLAT);
+ ToolItem viewMenuButton = new ToolItem(toolBar, DWT.PUSH, 0);
+
+ GridDataFactory.fillDefaults().align_(DWT.END, DWT.CENTER).applyTo(toolBar);
+
+ menuImage = ImageDescriptor.createFromFile(PopupDialog.classinfo,
+ "images/popup_menu.gif").createImage();//$NON-NLS-1$
+ disabledMenuImage = ImageDescriptor.createFromFile(PopupDialog.classinfo,
+ "images/popup_menu_disabled.gif").createImage();//$NON-NLS-1$
+ viewMenuButton.setImage(menuImage);
+ viewMenuButton.setDisabledImage(disabledMenuImage);
+ viewMenuButton.setToolTipText(JFaceResources
+ .getString("PopupDialog.menuTooltip")); //$NON-NLS-1$
+ viewMenuButton.addSelectionListener(new class SelectionAdapter {
+ public void widgetSelected(SelectionEvent e) {
+ showDialogMenu();
+ }
+ });
+ viewMenuButton.addDisposeListener(new class DisposeListener {
+ public void widgetDisposed(DisposeEvent e) {
+ menuImage.dispose();
+ menuImage = null;
+ disabledMenuImage.dispose();
+ disabledMenuImage = null;
+ }
+ });
+ // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=177183
+ toolBar.addMouseListener(new class MouseAdapter {
+ public void mouseDown(MouseEvent e) {
+ showDialogMenu();
+ }
+ });
+ }
+
+ /**
+ * Fill the dialog's menu. Subclasses may extend or override.
+ *
+ * @param dialogMenu
+ * The dialog's menu.
+ */
+ protected void fillDialogMenu(IMenuManager dialogMenu) {
+ dialogMenu.add(new GroupMarker("SystemMenuStart")); //$NON-NLS-1$
+ dialogMenu.add(new MoveAction());
+ dialogMenu.add(new ResizeAction());
+ if (showPersistAction) {
+ dialogMenu.add(new PersistBoundsAction());
+ }
+ dialogMenu.add(new Separator("SystemMenuEnd")); //$NON-NLS-1$
+ }
+
+ /**
+ * Perform the requested tracker action (resize or move).
+ *
+ * @param style
+ * The track style (resize or move).
+ */
+ private void performTrackerAction(int style) {
+ Shell shell = getShell();
+ if (shell is null || shell.isDisposed()) {
+ return;
+ }
+
+ Tracker tracker = new Tracker(shell.getDisplay(), style);
+ tracker.setStippled(true);
+ Rectangle[] r = [ shell.getBounds() ];
+ tracker.setRectangles(r);
+
+ // Ignore any deactivate events caused by opening the tracker.
+ // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=120656
+ bool oldListenToDeactivate = listenToDeactivate;
+ listenToDeactivate = false;
+ if (tracker.open()) {
+ if (shell !is null && !shell.isDisposed()) {
+ shell.setBounds(tracker.getRectangles()[0]);
+ }
+ }
+ listenToDeactivate = oldListenToDeactivate;
+ }
+
+ /**
+ * Show the dialog's menu. This message has no effect if the receiver was
+ * not configured to show a menu. Clients may call this method in order to
+ * trigger the menu via keystrokes or other gestures. Subclasses typically
+ * do not override method.
+ */
+ protected void showDialogMenu() {
+ if (!showDialogMenu_) {
+ return;
+ }
+
+ if (menuManager is null) {
+ menuManager = new MenuManager();
+ fillDialogMenu(menuManager);
+ }
+ // Setting this flag works around a problem that remains on X only,
+ // whereby activating the menu deactivates our shell.
+ listenToDeactivate = !"gtk".equals(DWT.getPlatform()); //$NON-NLS-1$
+
+ Menu menu = menuManager.createContextMenu(getShell());
+ Rectangle bounds = toolBar.getBounds();
+ Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
+ topLeft = getShell().toDisplay(topLeft);
+ menu.setLocation(topLeft.x, topLeft.y);
+ menu.setVisible(true);
+ }
+
+ /**
+ * Set the text to be shown in the popup's info area. This message has no
+ * effect if there was no info text supplied when the dialog first opened.
+ * Subclasses may override this method.
+ *
+ * @param text
+ * the text to be shown when the info area is displayed.
+ *
+ */
+ protected void setInfoText(String text) {
+ infoText = text;
+ if (infoLabel !is null) {
+ infoLabel.setText(text);
+ }
+ }
+
+ /**
+ * Set the text to be shown in the popup's title area. This message has no
+ * effect if there was no title label specified when the dialog was
+ * originally opened. Subclasses may override this method.
+ *
+ * @param text
+ * the text to be shown when the title area is displayed.
+ *
+ */
+ protected void setTitleText(String text) {
+ titleText = text;
+ if (titleLabel !is null) {
+ titleLabel.setText(text);
+ }
+ }
+
+ /**
+ * Return a bool indicating whether this dialog will persist its bounds.
+ * This value is initially set in the dialog's constructor, but can be
+ * modified if the persist bounds action is shown on the menu and the user
+ * has changed its value. Subclasses may override this method.
+ *
+ * @return OK
if the
+ * open is successful, or CANCEL
if it is not. It provides
+ * framework hooks that allow subclasses to set the focus and tab order, and
+ * avoids the use of shell.open()
in cases where the focus
+ * should not be given to the shell initially.
+ *
+ * @return the return code
+ *
+ * @see dwtx.jface.window.Window#open()
+ */
+ public int open() {
+
+ Shell shell = getShell();
+ if (shell is null || shell.isDisposed()) {
+ shell = null;
+ // create the window
+ create();
+ shell = getShell();
+ }
+
+ // provide a hook for adjusting the bounds. This is only
+ // necessary when there is content driven sizing that must be
+ // adjusted each time the dialog is opened.
+ adjustBounds();
+
+ // limit the shell size to the display size
+ constrainShellSize();
+
+ // set up the tab order for the dialog
+ setTabOrder(cast(Composite) getContents());
+
+ // initialize flags for listening to deactivate
+ listenToDeactivate = false;
+ listenToParentDeactivate = false;
+
+ // open the window
+ if (takeFocusOnOpen) {
+ shell.open();
+ getFocusControl().setFocus();
+ } else {
+ shell.setVisible(true);
+ }
+
+ return OK;
+
+ }
+
+ /**
+ * Closes this window, disposes its shell, and removes this window from its
+ * window manager (if it has one).
+ * super.close
must be called).
+ * true
if the window is (or was already) closed, and
+ * false
if it is still open
+ */
+ public bool close() {
+ // If already closed, there is nothing to do.
+ // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=127505
+ if (getShell() is null || getShell().isDisposed()) {
+ return true;
+ }
+
+ saveDialogBounds(getShell());
+ // Widgets are about to be disposed, so null out any state
+ // related to them that was not handled in dispose listeners.
+ // We do this before disposal so that any received activate or
+ // deactivate events are duly ignored.
+ initializeWidgetState();
+
+ if (parentDeactivateListener !is null) {
+ getShell().getParent().removeListener(DWT.Deactivate, parentDeactivateListener);
+ parentDeactivateListener = null;
+ }
+
+ return super.close();
+ }
+
+ /**
+ * Gets the dialog settings that should be used for remembering the bounds
+ * of the dialog. Subclasses should override this method when they wish to
+ * persist the bounds of the dialog.
+ *
+ * @return settings the dialog settings used to store the dialog's location
+ * and/or size, or null
if the dialog's bounds should
+ * never be stored.
+ */
+ protected IDialogSettings getDialogSettings() {
+ return null;
+ }
+
+ /**
+ * Saves the bounds of the shell in the appropriate dialog settings. The
+ * bounds are recorded relative to the parent shell, if there is one, or
+ * display coordinates if there is no parent shell. Subclasses typically
+ * need not override this method, but may extend it (calling
+ * super.saveDialogBounds
if additional bounds information
+ * should be stored. Clients may also call this method to persist the bounds
+ * at times other than closing the dialog.
+ *
+ * @param shell
+ * The shell whose bounds are to be stored
+ */
+ protected void saveDialogBounds(Shell shell) {
+ IDialogSettings settings = getDialogSettings();
+ if (settings !is null) {
+ Point shellLocation = shell.getLocation();
+ Point shellSize = shell.getSize();
+ Shell parent = getParentShell();
+ if (parent !is null) {
+ Point parentLocation = parent.getLocation();
+ shellLocation.x -= parentLocation.x;
+ shellLocation.y -= parentLocation.y;
+ }
+ if (persistBounds) {
+ String prefix = this.classinfo.name;
+ settings.put(prefix ~ DIALOG_ORIGIN_X, shellLocation.x);
+ settings.put(prefix ~ DIALOG_ORIGIN_Y, shellLocation.y);
+ settings.put(prefix ~ DIALOG_WIDTH, shellSize.x);
+ settings.put(prefix ~ DIALOG_HEIGHT, shellSize.y);
+ }
+ if (showPersistAction && showDialogMenu_) {
+ settings.put(
+ this.classinfo.name ~ DIALOG_USE_PERSISTED_BOUNDS,
+ persistBounds);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.window.Window#getInitialSize()
+ */
+ protected Point getInitialSize() {
+ Point result = super.getInitialSize();
+ if (persistBounds) {
+ IDialogSettings settings = getDialogSettings();
+ if (settings !is null) {
+ try {
+ int width = settings.getInt(this.classinfo.name
+ ~ DIALOG_WIDTH);
+ int height = settings.getInt(this.classinfo.name
+ ~ DIALOG_HEIGHT);
+ result = new Point(width, height);
+
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ // No attempt is made to constrain the bounds. The default
+ // constraining behavior in Window will be used.
+ return result;
+ }
+
+ /**
+ * Adjust the bounds of the popup as necessary prior to opening the dialog.
+ * Default is to do nothing, which honors any bounds set directly by clients
+ * or those that have been saved in the dialog settings. Subclasses should
+ * override this method when there are bounds computations that must be
+ * checked each time the dialog is opened.
+ */
+ protected void adjustBounds() {
+ }
+
+ /**
+ * (non-Javadoc)
+ *
+ * @see dwtx.jface.window.Window#getInitialLocation(dwt.graphics.Point)
+ */
+ protected Point getInitialLocation(Point initialSize) {
+ Point result = super.getInitialLocation(initialSize);
+ if (persistBounds) {
+ IDialogSettings settings = getDialogSettings();
+ if (settings !is null) {
+ try {
+ int x = settings.getInt(this.classinfo.name
+ ~ DIALOG_ORIGIN_X);
+ int y = settings.getInt(this.classinfo.name
+ ~ DIALOG_ORIGIN_Y);
+ result = new Point(x, y);
+ // The coordinates were stored relative to the parent shell.
+ // Convert to display coordinates.
+ Shell parent = getParentShell();
+ if (parent !is null) {
+ Point parentLocation = parent.getLocation();
+ result.x += parentLocation.x;
+ result.y += parentLocation.y;
+ }
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ // No attempt is made to constrain the bounds. The default
+ // constraining behavior in Window will be used.
+ return result;
+ }
+
+ /**
+ * Apply any desired color to the specified composite and its children.
+ *
+ * @param composite
+ * the contents composite
+ */
+ private void applyColors(Composite composite) {
+ applyForegroundColor(getShell().getDisplay().getSystemColor(
+ DWT.COLOR_INFO_FOREGROUND), composite,
+ getForegroundColorExclusions());
+ applyBackgroundColor(getShell().getDisplay().getSystemColor(
+ DWT.COLOR_INFO_BACKGROUND), composite,
+ getBackgroundColorExclusions());
+ }
+
+ /**
+ * Apply any desired fonts to the specified composite and its children.
+ *
+ * @param composite
+ * the contents composite
+ */
+ private void applyFonts(Composite composite) {
+ Dialog.applyDialogFont(composite);
+
+ }
+
+ /**
+ * Set the specified foreground color for the specified control and all of
+ * its children, except for those specified in the list of exclusions.
+ *
+ * @param color
+ * the color to use as the foreground color
+ * @param control
+ * the control whose color is to be changed
+ * @param exclusions
+ * a list of controls who are to be excluded from getting their
+ * color assigned
+ */
+ private void applyForegroundColor(Color color, Control control,
+ SeqView!(Control) exclusions) {
+ if (!exclusions.contains(control)) {
+ control.setForeground(color);
+ }
+ if ( auto comp = cast(Composite)control ) {
+ Control[] children = comp.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ applyForegroundColor(color, children[i], exclusions);
+ }
+ }
+ }
+
+ /**
+ * Set the specified background color for the specified control and all of
+ * its children.
+ *
+ * @param color
+ * the color to use as the background color
+ * @param control
+ * the control whose color is to be changed
+ * @param exclusions
+ * a list of controls who are to be excluded from getting their
+ * color assigned
+ */
+ private void applyBackgroundColor(Color color, Control control,
+ SeqView!(Control) exclusions) {
+ if (!exclusions.contains(control)) {
+ control.setBackground(color);
+ }
+ if (auto comp = cast(Composite)control ) {
+ Control[] children = comp.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ applyBackgroundColor(color, children[i], exclusions);
+ }
+ }
+ }
+
+ /**
+ * Set the specified foreground color for the specified control and all of
+ * its children. Subclasses may override this method, but typically do not.
+ * If a subclass wishes to exclude a particular control in its contents from
+ * getting the specified foreground color, it may instead override
+ * PopupDialog.getForegroundColorExclusions
.
+ *
+ * @param color
+ * the color to use as the background color
+ * @param control
+ * the control whose color is to be changed
+ * @see PopupDialog#getBackgroundColorExclusions()
+ */
+ protected void applyForegroundColor(Color color, Control control) {
+ applyForegroundColor(color, control, getForegroundColorExclusions());
+ }
+
+ /**
+ * Set the specified background color for the specified control and all of
+ * its children. Subclasses may override this method, but typically do not.
+ * If a subclass wishes to exclude a particular control in its contents from
+ * getting the specified background color, it may instead override
+ * PopupDialog.getBackgroundColorExclusions
.
+ *
+ * @param color
+ * the color to use as the background color
+ * @param control
+ * the control whose color is to be changed
+ * @see PopupDialog#getBackgroundColorExclusions()
+ */
+ protected void applyBackgroundColor(Color color, Control control) {
+ applyBackgroundColor(color, control, getBackgroundColorExclusions());
+ }
+
+ /**
+ * Return a list of controls which should never have their foreground color
+ * reset. Subclasses may extend this method (should always call
+ * super.getForegroundColorExclusions
to aggregate the list.
+ *
+ *
+ * @return the List of controls
+ */
+ protected SeqView!(Control) getForegroundColorExclusions() {
+ auto list = new ArraySeq!(Control);
+ list.capacity(3);
+ if (infoLabel !is null) {
+ list.append(infoLabel);
+ }
+ if (titleSeparator !is null) {
+ list.append(titleSeparator);
+ }
+ if (infoSeparator !is null) {
+ list.append(infoSeparator);
+ }
+ return list;
+ }
+
+ /**
+ * Return a list of controls which should never have their background color
+ * reset. Subclasses may extend this method (should always call
+ * super.getBackgroundColorExclusions
to aggregate the list.
+ *
+ * @return the List of controls
+ */
+ protected SeqView!(Control) getBackgroundColorExclusions() {
+ auto list = new ArraySeq!(Control);
+ list.capacity(2);
+ if (titleSeparator !is null) {
+ list.append(titleSeparator);
+ }
+ if (infoSeparator !is null) {
+ list.append(infoSeparator);
+ }
+ return list;
+ }
+
+ /**
+ * Initialize any state related to the widgetry that should be set up each
+ * time widgets are created.
+ */
+ private void initializeWidgetState() {
+ menuManager = null;
+ dialogArea = null;
+ titleLabel = null;
+ titleSeparator = null;
+ infoSeparator = null;
+ infoLabel = null;
+ toolBar = null;
+
+ // If the menu item for persisting bounds is displayed, use the stored
+ // value to determine whether any persisted bounds should be honored at
+ // all.
+ if (showDialogMenu_ && showPersistAction) {
+ IDialogSettings settings = getDialogSettings();
+ if (settings !is null) {
+ persistBounds = settings.getBoolean(this.classinfo.name
+ ~ DIALOG_USE_PERSISTED_BOUNDS);
+ }
+ }
+
+ }
+
+ /**
+ * The dialog is being disposed. Dispose of any resources allocated.
+ *
+ */
+ private void handleDispose() {
+ if (infoFont !is null && !infoFont.isDisposed()) {
+ infoFont.dispose();
+ }
+ infoFont = null;
+ if (titleFont !is null && !titleFont.isDisposed()) {
+ titleFont.dispose();
+ }
+ titleFont = null;
+ }
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/dialogs/StatusDialog.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/dialogs/StatusDialog.d Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,306 @@
+/*******************************************************************************
+ * 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 null
. null
will
+ * set the empty text and no image.
+ */
+ public void setErrorStatus(IStatus status) {
+ if (status !is null && !status.isOK()) {
+ String message = status.getMessage();
+ if (message !is null && message.length > 0) {
+ setText(message);
+ // unqualified call of setImage is too ambiguous for
+ // Foundation 1.0 compiler
+ // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=140576
+ this.outer.setImage(findImage(status));
+ setBackground(JFaceColors.getErrorBackground(getDisplay()));
+ return;
+ }
+ }
+ setText(""); //$NON-NLS-1$
+ // unqualified call of setImage is too ambiguous for Foundation 1.0
+ // compiler
+ // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=140576
+ this.outer.setImage(null);
+ setBackground(fNormalMsgAreaBackground);
+ }
+ }
+
+ /**
+ * Creates an instance of a status dialog.
+ *
+ * @param parent
+ * the parent Shell of the dialog
+ */
+ public this(Shell parent) {
+ super(parent);
+ fLastStatus = new Status(IStatus.OK, Policy.JFACE, IStatus.OK,
+ Util.ZERO_LENGTH_STRING, null);
+ }
+
+ /**
+ * Specifies whether status line appears to the left of the buttons
+ * (default) or above them.
+ *
+ * @param aboveButtons
+ * if true
status line is placed above buttons; if
+ * false
to the right
+ */
+ public void setStatusLineAboveButtons(bool aboveButtons) {
+ fStatusLineAboveButtons = aboveButtons;
+ }
+
+ /**
+ * Update the dialog's status line to reflect the given status. It is safe
+ * to call this method before the dialog has been opened.
+ *
+ * @param status
+ * the status to set
+ */
+ protected void updateStatus(IStatus status) {
+ fLastStatus = status;
+ if (fStatusLine !is null && !fStatusLine.isDisposed()) {
+ updateButtonsEnableState(status);
+ fStatusLine.setErrorStatus(status);
+ }
+ }
+
+ /**
+ * Returns the last status.
+ *
+ * @return IStatus
+ */
+ public IStatus getStatus() {
+ return fLastStatus;
+ }
+
+ /**
+ * Updates the status of the ok button to reflect the given status.
+ * Subclasses may override this method to update additional buttons.
+ *
+ * @param status
+ * the status.
+ */
+ protected void updateButtonsEnableState(IStatus status) {
+ if (fOkButton !is null && !fOkButton.isDisposed()) {
+ fOkButton.setEnabled(!status.matches(IStatus.ERROR));
+ }
+ }
+
+ /*
+ * @see Window#create(Shell)
+ */
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+ if (fTitle !is null) {
+ shell.setText(fTitle);
+ }
+ }
+
+ /*
+ * @see Window#create()
+ */
+ public void create() {
+ super.create();
+ if (fLastStatus !is null) {
+ // policy: dialogs are not allowed to come up with an error message
+ if (fLastStatus.matches(IStatus.ERROR)) {
+ // remove the message
+ fLastStatus = new Status(IStatus.ERROR,
+ fLastStatus.getPlugin(), fLastStatus.getCode(),
+ "", fLastStatus.getException()); //$NON-NLS-1$
+ }
+ updateStatus(fLastStatus);
+ }
+ }
+
+ /*
+ * @see Dialog#createButtonsForButtonBar(Composite)
+ */
+ protected void createButtonsForButtonBar(Composite parent) {
+ fOkButton = createButton(parent, IDialogConstants.OK_ID,
+ IDialogConstants.OK_LABEL, true);
+ createButton(parent, IDialogConstants.CANCEL_ID,
+ IDialogConstants.CANCEL_LABEL, false);
+ }
+
+ /*
+ * @see Dialog#createButtonBar(Composite)
+ */
+ protected Control createButtonBar(Composite parent) {
+ Composite composite = new Composite(parent, DWT.NULL);
+ GridLayout layout = new GridLayout();
+
+ if (fStatusLineAboveButtons) {
+ layout.numColumns = 1;
+ } else {
+ layout.numColumns = 2;
+ }
+
+ layout.marginHeight = 0;
+ layout.marginLeft = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+ layout.marginWidth = 0;
+ composite.setLayout(layout);
+ composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ if (!fStatusLineAboveButtons && isHelpAvailable()) {
+ createHelpControl(composite);
+ }
+ fStatusLine = new MessageLine(composite);
+ fStatusLine.setAlignment(DWT.LEFT);
+ GridData statusData = new GridData(GridData.FILL_HORIZONTAL);
+ fStatusLine.setErrorStatus(null);
+ if (fStatusLineAboveButtons && isHelpAvailable()) {
+ statusData.horizontalSpan = 2;
+ createHelpControl(composite);
+ }
+ fStatusLine.setLayoutData(statusData);
+ applyDialogFont(composite);
+
+ /*
+ * Create the rest of the button bar, but tell it not to create a help
+ * button (we've already created it).
+ */
+ bool helpAvailable = isHelpAvailable();
+ setHelpAvailable(false);
+ super.createButtonBar(composite);
+ setHelpAvailable(helpAvailable);
+ return composite;
+ }
+
+ /**
+ * Sets the title for this dialog.
+ *
+ * @param title
+ * the title.
+ */
+ public void setTitle(String title) {
+ fTitle = title !is null ? title : ""; //$NON-NLS-1$
+ Shell shell = getShell();
+ if ((shell !is null) && !shell.isDisposed()) {
+ shell.setText(fTitle);
+ }
+ }
+
+ /**
+ * Sets the image for this dialog.
+ *
+ * @param image
+ * the image.
+ */
+ public void setImage(Image image) {
+ fImage = image;
+ Shell shell = getShell();
+ if ((shell !is null) && !shell.isDisposed()) {
+ shell.setImage(fImage);
+ }
+ }
+
+}
diff -r db8940420ed8 -r e0f0aaf75edd dwtx/jface/util/Util.d
--- a/dwtx/jface/util/Util.d Mon Mar 31 02:00:41 2008 +0200
+++ b/dwtx/jface/util/Util.d Tue Apr 01 08:00:31 2008 +0200
@@ -179,6 +179,9 @@
return 0;
}
}
+ public static final int compare(String left, String right) {
+ return left < right;
+ }
// /**
// * Compares two lists -- account for null
. The lists must
@@ -272,6 +275,9 @@
return left is null ? right is null : ((right !is null) && left
.opEquals(right));
}
+ public static final bool opEquals(String left, String right) {
+ return left == right;
+ }
/**
* Tests whether two arrays of objects are equal to each other. The arrays
@@ -316,6 +322,22 @@
return true;
}
+ public static final bool opEquals(String[] leftArray, String[] rightArray) {
+ if (leftArray.length !is rightArray.length) {
+ return false;
+ }
+
+ for (int i = 0; i < leftArray.length; i++) {
+ String left = leftArray[i];
+ String right = rightArray[i];
+ if (left != right) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Provides a hash code based on the given integer value.
*
@@ -365,6 +387,18 @@
return hashCode;
}
+ public static final hash_t toHash(String str) {
+ return dwt.dwthelper.utils.toHash(str);
+ }
+ public static final hash_t toHash(String[] objects) {
+ int hashCode = 89;
+ for (int i = 0; i < objects.length; i++) {
+ auto object = objects[i];
+ hashCode = hashCode * 31 + toHash(object);
+ }
+
+ return hashCode;
+ }
/**
* Checks whether the second array is a subsequence of the first array, and