# HG changeset patch # User Frank Benoit # Date 1207191025 -7200 # Node ID ca63e2bea4bf461c09617545a56a659d3c211a91 # Parent eb6b3e6de86988fc5ce8685877d6b01775fe67d4 CoolBarManager diff -r eb6b3e6de869 -r ca63e2bea4bf dwtx/jface/action/CoolBarManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/action/CoolBarManager.d Thu Apr 03 04:50:25 2008 +0200 @@ -0,0 +1,1097 @@ +/******************************************************************************* + * Copyright (c) 2003, 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.CoolBarManager; + +import dwtx.jface.action.ToolBarContributionItem; +import dwtx.jface.action.Separator; + +import dwtx.jface.action.ContributionManager; +import dwtx.jface.action.IContributionManager; +import dwtx.jface.action.IContributionItem; +import dwtx.jface.action.ICoolBarManager; +import dwtx.jface.action.IToolBarManager; +import dwtx.jface.action.IMenuManager; +import dwtx.jface.action.MenuManager; + +import tango.util.collection.ArraySeq; +import tango.util.collection.HashMap; +import tango.util.collection.model.Iterator; +// import java.util.HashMap; +// import java.util.Iterator; +// import java.util.List; +// import java.util.ListIterator; + +import dwt.DWT; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.CoolBar; +import dwt.widgets.CoolItem; +import dwt.widgets.Menu; +import dwtx.core.runtime.Assert; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Integer; +import tango.io.Stdout; + +/** + * A cool bar manager is a contribution manager which realizes itself and its + * items in a cool bar control. + *

+ * This class may be instantiated; it may also be subclassed. + *

+ * + * @since 3.0 + */ +public class CoolBarManager : ContributionManager, + ICoolBarManager { + + class ListIterator { + + ArraySeq!(Object) c; + int i; + + this( ArraySeq!(Object) c ){ + this.c = c; + } + + Object next(){ + return c.get(++i); + } + Object previous(){ + return c.get(--i); + } + + bool hasNext(){ + return i+1 < c.size(); + } + bool hasPrevious(){ + return i > 0; + } + + void remove(){ + c.removeAt(i); + if( i is c.size() ) i--; + } + int nextIndex(){ + return i+1; + } + } + + /** + * A separator created by the end user. + */ + public final static String USER_SEPARATOR = "UserSeparator"; //$NON-NLS-1$ + + /** + * The original creation order of the contribution items. + */ + private ArraySeq!(Object) cbItemsCreationOrder; + + /** + * MenuManager for cool bar pop-up menu, or null if none. + */ + private MenuManager contextMenuManager = null; + + /** + * The cool bar control; null before creation and after + * disposal. + */ + private CoolBar coolBar = null; + + /** + * The cool bar items style; DWT.NONE by default. + */ + private int itemStyle = DWT.NONE; + + /** + * Creates a new cool bar manager with the default style. Equivalent to + * CoolBarManager(DWT.NONE). + */ + public this() { + cbItemsCreationOrder = new ArraySeq!(Object); + // do nothing + } + + /** + * Creates a cool bar manager for an existing cool bar control. This + * manager becomes responsible for the control, and will dispose of it when + * the manager is disposed. + * + * @param coolBar + * the cool bar control + */ + public this(CoolBar coolBar) { + this(); + Assert.isNotNull(coolBar); + this.coolBar = coolBar; + itemStyle = coolBar.getStyle(); + } + + /** + * Creates a cool bar manager with the given DWT style. Calling createControl + * will create the cool bar control. + * + * @param style + * the cool bar item style; see + * {@link dwt.widgets.CoolBar CoolBar}for for valid + * style bits + */ + public this(int style) { + cbItemsCreationOrder = new ArraySeq!(Object); + itemStyle = style; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.ICoolBarManager#add(dwtx.jface.action.IToolBarManager) + */ + public void add(IToolBarManager toolBarManager) { + Assert.isNotNull(cast(Object)toolBarManager); + super.add(new ToolBarContributionItem(toolBarManager)); + } + + /** + * Collapses consecutive separators and removes a separator from the + * beginning and end of the list. + * + * @param contributionList + * the list of contributions; must not be null. + * @return The contribution list provided with extraneous separators + * removed; this value is never null, but may be + * empty. + */ + private ArraySeq!(Object) adjustContributionList(ArraySeq!(Object) contributionList) { + IContributionItem item; + // Fist remove a separator if it is the first element of the list + if (contributionList.size() !is 0) { + item = cast(IContributionItem) contributionList.get(0); + if (item.isSeparator()) { + contributionList.removeAt(0); + } + + auto iterator = new ListIterator( contributionList ); + // collapse consecutive separators + while (iterator.hasNext()) { + item = cast(IContributionItem) iterator.next(); + if (item.isSeparator()) { + while (iterator.hasNext()) { + item = cast(IContributionItem) iterator.next(); + if (item.isSeparator()) { + iterator.remove(); + } else { + break; + } + } + + } + } + // Now check last element to see if there is a separator + item = cast(IContributionItem) contributionList.get(contributionList + .size() - 1); + if (item.isSeparator()) { + contributionList.removeAt(contributionList.size() - 1); + } + } + return contributionList; + + } + + /* (non-Javadoc) + * @see dwtx.jface.action.ContributionManager#checkDuplication(dwtx.jface.action.IContributionItem) + */ + protected bool allowItem(IContributionItem itemToAdd) { + /* We will allow as many null entries as they like, though there should + * be none. + */ + if (itemToAdd is null) { + return true; + } + + /* Null identifiers can be expected in generic contribution items. + */ + String firstId = itemToAdd.getId(); + if (firstId is null) { + return true; + } + + // Cycle through the current list looking for duplicates. + IContributionItem[] currentItems = getItems(); + for (int i = 0; i < currentItems.length; i++) { + IContributionItem currentItem = currentItems[i]; + + // We ignore null entries. + if (currentItem is null) { + continue; + } + + String secondId = currentItem.getId(); + if (firstId.equals(secondId)) { + if (Policy.TRACE_TOOLBAR) { + Stdout.formatln("Trying to add a duplicate item."); //$NON-NLS-1$ + ExceptionPrintStackTrace(new Exception(null), Stdout ); + Stdout.formatln("DONE --------------------------"); //$NON-NLS-1$ + } + return false; + } + } + + return true; + } + + /** + * Positions the list iterator to the end of all the separators. Calling + * next() the iterator should return the immediate object + * following the last separator. + * + * @param iterator + * the list iterator. + */ + private void collapseSeparators(ListIterator iterator) { + + while (iterator.hasNext()) { + IContributionItem item = cast(IContributionItem) iterator.next(); + if (!item.isSeparator()) { + iterator.previous(); + return; + } + } + } + + /** + * Returns whether the cool bar control has been created and not yet + * disposed. + * + * @return true if the control has been created and not yet + * disposed, false otherwise + */ + private bool coolBarExist() { + return coolBar !is null && !coolBar.isDisposed(); + } + + /** + * Creates and returns this manager's cool bar control. Does not create a + * new control if one already exists. + * + * @param parent + * the parent control + * @return the cool bar control + */ + public CoolBar createControl(Composite parent) { + Assert.isNotNull(parent); + if (!coolBarExist()) { + coolBar = new CoolBar(parent, itemStyle); + coolBar.setMenu(getContextMenuControl()); + coolBar.setLocked(false); + update(false); + } + return coolBar; + } + + /** + * Disposes of this cool bar 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 cool bar manager and its + * associated contribution items. Use removeAll for that + * purpose. + */ + public void dispose() { + if (coolBarExist()) { + IContributionItem[] items = getItems(); + for (int i = 0; i < items.length; i++) { + // Disposes of the contribution item. + // If Contribution Item is a toolbar then it will dispose of + // all the nested + // contribution items. + items[i].dispose(); + } + coolBar.dispose(); + coolBar = null; + } + // If a context menu existed then dispose of it. + if (contextMenuManager !is null) { + contextMenuManager.dispose(); + contextMenuManager = null; + } + + } + + /** + * Disposes the given cool item. + * + * @param item + * the cool item to dispose + */ + private void dispose(CoolItem item) { + if ((item !is null) && !item.isDisposed()) { + + item.setData(null); + Control control = item.getControl(); + // if the control is already disposed, setting the coolitem + // control to null will cause an DWT exception, workaround + // for 19630 + if ((control !is null) && !control.isDisposed()) { + item.setControl(null); + } + item.dispose(); + } + } + + /** + * Finds the cool item associated with the given contribution item. + * + * @param item + * the contribution item + * @return the associated cool item, or null if not found + */ + private CoolItem findCoolItem(IContributionItem item) { + CoolItem[] coolItems = (coolBar is null) ? null : coolBar.getItems(); + return findCoolItem(coolItems, item); + } + + private CoolItem findCoolItem(CoolItem[] items, IContributionItem item) { + if (items is null) { + return null; + } + + for (int i = 0; i < items.length; i++) { + CoolItem coolItem = items[i]; + IContributionItem data = cast(IContributionItem) coolItem.getData(); + if (data !is null && (cast(Object)data).opEquals(cast(Object)item)) { + return coolItem; + } + } + return null; + } + + /** + * Return a consistent set of wrap indices. The return value will always + * include at least one entry and the first entry will always be zero. + * CoolBar.getWrapIndices() is inconsistent in whether or not it returns an + * index for the first row. + * + * @param wraps + * the wrap indicies from the cool bar widget + * @return the adjusted wrap indicies. + */ + private int[] getAdjustedWrapIndices(int[] wraps) { + int[] adjustedWrapIndices; + if (wraps.length is 0) { + adjustedWrapIndices = [ 0 ]; + } else { + if (wraps[0] !is 0) { + adjustedWrapIndices = new int[wraps.length + 1]; + adjustedWrapIndices[0] = 0; + for (int i = 0; i < wraps.length; i++) { + adjustedWrapIndices[i + 1] = wraps[i]; + } + } else { + adjustedWrapIndices = wraps; + } + } + return adjustedWrapIndices; + } + + /** + * Returns the control of the Menu Manager. If the menu manager does not + * have a control then one is created. + * + * @return menu control associated with manager, or null if none + */ + private Menu getContextMenuControl() { + if ((contextMenuManager !is null) && (coolBar !is null)) { + Menu menuWidget = contextMenuManager.getMenu(); + if ((menuWidget is null) || (menuWidget.isDisposed())) { + menuWidget = contextMenuManager.createContextMenu(coolBar); + } + return menuWidget; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.ICoolBarManager#isLayoutLocked() + */ + public IMenuManager getContextMenuManager() { + return contextMenuManager; + } + + /** + * Returns the cool bar control for this manager. + * + * @return the cool bar control, or null if none + */ + public CoolBar getControl() { + return coolBar; + } + + /** + * Returns an array list of all the contribution items in the manager. + * + * @return an array list of contribution items. + */ + private ArraySeq!(Object) getItemList() { + IContributionItem[] cbItems = getItems(); + ArraySeq!(Object) list = new ArraySeq!(Object); + list.capacity(cbItems.length); + for (int i = 0; i < cbItems.length; i++) { + list.append( cast(Object) cbItems[i]); + } + return list; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.ICoolBarManager#isLayoutLocked() + */ + public bool getLockLayout() { + if (!coolBarExist()) { + return false; + } + return coolBar.getLocked(); + } + + /** + * Returns the number of rows that should be displayed visually. + * + * @param items + * the array of contributin items + * @return the number of rows + */ + private int getNumRows(IContributionItem[] items) { + int numRows = 1; + bool separatorFound = false; + for (int i = 0; i < items.length; i++) { + if (items[i].isSeparator()) { + separatorFound = true; + } + if ((separatorFound) && (items[i].isVisible()) + && (!items[i].isGroupMarker()) && (!items[i].isSeparator())) { + numRows++; + separatorFound = false; + } + } + return numRows; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.ICoolBarManager#getStyle() + */ + public int getStyle() { + return itemStyle; + } + + /** + * Subclasses may extend this ContributionManager method, + * but must call super.itemAdded. + * + * @see dwtx.jface.action.ContributionManager#itemAdded(dwtx.jface.action.IContributionItem) + */ + protected void itemAdded(IContributionItem item) { + Assert.isNotNull(cast(Object)item); + super.itemAdded(item); + int insertedAt = indexOf(item); + bool replaced = false; + final int size = cbItemsCreationOrder.size(); + for (int i = 0; i < size; i++) { + IContributionItem created = cast(IContributionItem) cbItemsCreationOrder + .get(i); + if (created.getId() !is null && created.getId().equals(item.getId())) { + cbItemsCreationOrder.replaceAt(i, cast(Object) item); + replaced = true; + break; + } + } + + if (!replaced) { + cbItemsCreationOrder.addAt(Math.min(Math.max(insertedAt, 0), + cbItemsCreationOrder.size()), cast(Object) item); + } + } + + /** + * Subclasses may extend this ContributionManager method, + * but must call super.itemRemoved. + * + * @see dwtx.jface.action.ContributionManager#itemRemoved(dwtx.jface.action.IContributionItem) + */ + protected void itemRemoved(IContributionItem item) { + Assert.isNotNull(cast(Object)item); + super.itemRemoved(item); + CoolItem coolItem = findCoolItem(item); + if (coolItem !is null) { + coolItem.setData(null); + } + } + + /** + * Positions the list iterator to the starting of the next row. By calling + * next on the returned iterator, it will return the first element of the + * next row. + * + * @param iterator + * the list iterator of contribution items + * @param ignoreCurrentItem + * Whether the current item in the iterator should be considered + * (as well as subsequent items). + */ + private void nextRow(ListIterator iterator, bool ignoreCurrentItem) { + + IContributionItem currentElement = null; + if (!ignoreCurrentItem && iterator.hasPrevious()) { + currentElement = cast(IContributionItem) iterator.previous(); + iterator.next(); + } + + if ((currentElement !is null) && (currentElement.isSeparator())) { + collapseSeparators(iterator); + return; + } + + //Find next separator + while (iterator.hasNext()) { + IContributionItem item = cast(IContributionItem) iterator.next(); + if (item.isSeparator()) { + // we we find a separator, collapse any consecutive + // separators + // and return + collapseSeparators(iterator); + return; + } + } + } + + /* + * Used for debuging. Prints all the items in the internal structures. + */ + // private void printContributions(ArrayList contributionList) { + // int index = 0; + // System.out.println("----------------------------------\n"); //$NON-NLS-1$ + // for (Iterator i = contributionList.iterator(); i.hasNext(); index++) { + // IContributionItem item = (IContributionItem) i.next(); + // if (item.isSeparator()) { + // System.out.println("Separator"); //$NON-NLS-1$ + // } else { + // System.out.println(index + ". Item id: " + item.getId() //$NON-NLS-1$ + // + " - is Visible: " //$NON-NLS-1$ + // + item.isVisible()); + // } + // } + // } + /** + * Synchronizes the visual order of the cool items in the control with this + * manager's internal data structures. This method should be called before + * requesting the order of the contribution items to ensure that the order + * is accurate. + *

+ * Note that update() and refresh() are + * converses: update() changes the visual order to match the + * internal structures, and refresh changes the internal + * structures to match the visual order. + *

+ */ + public void refresh() { + if (!coolBarExist()) { + return; + } + + // Retreives the list of contribution items as an array list + auto contributionList = getItemList(); + + // Check the size of the list + if (contributionList.size() is 0) { + return; + } + + // The list of all the cool items in their visual order + CoolItem[] coolItems = coolBar.getItems(); + // The wrap indicies of the coolbar + int[] wrapIndicies = getAdjustedWrapIndices(coolBar.getWrapIndices()); + + int row = 0; + int coolItemIndex = 0; + + // Traverse through all cool items in the coolbar add them to a new + // data structure + // in the correct order + auto displayedItems = new ArraySeq!(Object); + displayedItems.capacity(coolBar.getItemCount()); + for (int i = 0; i < coolItems.length; i++) { + CoolItem coolItem = coolItems[i]; + if ( auto cbItem = cast(IContributionItem) coolItem.getData() ) { + displayedItems.addAt(Math.min(i, displayedItems.size()), cast(Object) cbItem); + } + } + + // Add separators to the displayed Items data structure + int offset = 0; + for (int i = 1; i < wrapIndicies.length; i++) { + int insertAt = wrapIndicies[i] + offset; + displayedItems.addAt(insertAt, new Separator(USER_SEPARATOR)); + offset++; + } + + // Determine which rows are invisible + auto existingVisibleRows = new ArraySeq!(Object); + existingVisibleRows.capacity(4); + ListIterator rowIterator = new ListIterator( contributionList ); + collapseSeparators(rowIterator); + int numRow = 0; + while (rowIterator.hasNext()) { + // Scan row + while (rowIterator.hasNext()) { + IContributionItem cbItem = cast(IContributionItem) rowIterator + .next(); + if (displayedItems.contains(cast(Object) cbItem)) { + existingVisibleRows.append(new Integer(numRow)); + break; + } + if (cbItem.isSeparator()) { + break; + } + } + nextRow(rowIterator, false); + numRow++; + } + + auto existingRows = new ListIterator( existingVisibleRows ); + // Adjust row number to the first visible + if (existingRows.hasNext()) { + row = (cast(Integer) existingRows.next()).intValue(); + } + + auto itemLocation = new HashMap!(Object,Object); + foreach( key; displayedItems ){ + IContributionItem item = cast(IContributionItem)key; + if (item.isSeparator()) { + if (existingRows.hasNext()) { + Integer value = cast(Integer) existingRows.next(); + row = value.intValue(); + } else { + row++; + } + } else { + itemLocation.add(cast(Object)item, new Integer(row)); + } + + } + + // Insert the contribution items in their correct location + foreach( key; displayedItems ){ + IContributionItem cbItem = cast(IContributionItem) key; + if (cbItem.isSeparator()) { + coolItemIndex = 0; + } else { + relocate(cbItem, coolItemIndex, contributionList, itemLocation); + cbItem.saveWidgetState(); + coolItemIndex++; + } + } + + if (contributionList.size() !is 0) { + contributionList = adjustContributionList(contributionList); + auto array = arraycast!(IContributionItem)( contributionList.toArray() ); + internalSetItems(array); + } + + } + + /** + * Relocates the given contribution item to the specified index. + * + * @param cbItem + * the conribution item to relocate + * @param index + * the index to locate this item + * @param contributionList + * the current list of conrtributions + * @param itemLocation + */ + private void relocate(IContributionItem cbItem, int index, + ArraySeq!(Object) contributionList, HashMap!(Object,Object) itemLocation) { + + if (!(cast(Integer)itemLocation.get(cast(Object)cbItem) )) { + return; + } + int targetRow = (cast(Integer) itemLocation.get(cast(Object)cbItem)).intValue(); + + int cbInternalIndex = -1; int idx; + foreach( el; contributionList ){ + if( el == cast(Object)cbItem){ + cbInternalIndex = idx; + break; + } + idx++; + } + + // by default add to end of list + int insertAt = contributionList.size(); + // Find the row to place this item in. + auto iterator = new ListIterator( contributionList ); + // bypass any separators at the begining + collapseSeparators(iterator); + int currentRow = -1; + while (iterator.hasNext()) { + + currentRow++; + if (currentRow is targetRow) { + // We found the row to insert the item + int virtualIndex = 0; + insertAt = iterator.nextIndex(); + // first check the position of the current element (item) + // then get the next element + while (iterator.hasNext()) { + IContributionItem item = cast(IContributionItem) iterator + .next(); + Integer itemRow = cast(Integer) itemLocation.get( cast(Object) item); + if (item.isSeparator()) { + break; + } + // if the item has an associate widget + if ((itemRow !is null) && (itemRow.intValue() is targetRow)) { + // if the next element is the index we are looking for + // then break + if (virtualIndex >= index) { + break; + } + virtualIndex++; + + } + insertAt++; + } + // If we don't need to move it then we return + if (cbInternalIndex is insertAt) { + return; + } + break; + } + nextRow(iterator, true); + } + contributionList.remove(cast(Object) cbItem); + + // Adjust insertAt index + if (cbInternalIndex < insertAt) { + insertAt--; + } + + // if we didn't find the row then add a new row + if (currentRow !is targetRow) { + contributionList.append(new Separator(USER_SEPARATOR)); + insertAt = contributionList.size(); + } + insertAt = Math.min(insertAt, contributionList.size()); + contributionList.addAt(insertAt, cast(Object) cbItem); + + } + + /** + * Restores the canonical order of this cool bar manager. The canonical + * order is the order in which the contribution items where added. + */ + public void resetItemOrder() { + auto iterator = new ListIterator( cbItemsCreationOrder ); + while( iterator.hasNext() ){ + IContributionItem item = cast(IContributionItem) iterator.next(); + // if its a user separator then do not include in original order. + if ((item.getId() !is null) && (item.getId().equals(USER_SEPARATOR))) { + iterator.remove(); + } + } + setItems( arraycast!(IContributionItem)(cbItemsCreationOrder.toArray)); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.ICoolBarManager#setContextMenuManager(dwtx.jface.action.IMenuManager) + */ + public void setContextMenuManager(IMenuManager contextMenuManager) { + this.contextMenuManager = cast(MenuManager) contextMenuManager; + if (coolBar !is null) { + coolBar.setMenu(getContextMenuControl()); + } + } + + /** + * Replaces the current items with the given items. + * Forces an update. + * + * @param newItems the items with which to replace the current items + */ + public void setItems(IContributionItem[] newItems) { + // dispose of all the cool items on the cool bar manager + if (coolBar !is null) { + CoolItem[] coolItems = coolBar.getItems(); + for (int i = 0; i < coolItems.length; i++) { + dispose(coolItems[i]); + } + } + // Set the internal structure to this order + internalSetItems(newItems); + // Force and update + update(true); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.ICoolBarManager#lockLayout(bool) + */ + public void setLockLayout(bool value) { + if (!coolBarExist()) { + return; + } + coolBar.setLocked(value); + } + + /** + * Subclasses may extend this IContributionManager method, + * but must call super.update. + * + * @see dwtx.jface.action.IContributionManager#update(bool) + */ + public void update(bool force) { + if ((!isDirty() && !force) || (!coolBarExist())) { + return; + } + + bool relock = false; + bool changed = false; + + try { + coolBar.setRedraw(false); + + // Refresh the widget data with the internal data structure. + refresh(); + + if (coolBar.getLocked()) { + coolBar.setLocked(false); + relock = true; + } + + /* + * Make a list of items including only those items that are + * visible. Separators should stay because they mark line breaks in + * a cool bar. + */ + IContributionItem[] items = getItems(); + auto visibleItems = new ArraySeq!(Object); + visibleItems.capacity(items.length); + for (int i = 0; i < items.length; i++) { + IContributionItem item = items[i]; + if (item.isVisible()) { + visibleItems.append(cast(Object)item); + } + } + + /* + * Make a list of CoolItem widgets in the cool bar for which there + * is no current visible contribution item. These are the widgets + * to be disposed. Dynamic items are also removed. + */ + CoolItem[] coolItems = coolBar.getItems(); + auto coolItemsToRemove = new ArraySeq!(Object); + coolItemsToRemove.capacity(coolItems.length); + for (int i = 0; i < coolItems.length; i++) { + Object data = coolItems[i].getData(); + if ((data is null) + || (!visibleItems.contains(data)) + || ((cast(IContributionItem)data ) && (cast(IContributionItem) data) + .isDynamic())) { + coolItemsToRemove.append(coolItems[i]); + } + } + + // Dispose of any items in the list to be removed. + for (int i = coolItemsToRemove.size() - 1; i >= 0; i--) { + CoolItem coolItem = cast(CoolItem) coolItemsToRemove.get(i); + if (!coolItem.isDisposed()) { + Control control = coolItem.getControl(); + if (control !is null) { + coolItem.setControl(null); + control.dispose(); + } + coolItem.dispose(); + } + } + + // Add any new items by telling them to fill. + coolItems = coolBar.getItems(); + IContributionItem sourceItem; + IContributionItem destinationItem; + int sourceIndex = 0; + int destinationIndex = 0; + auto visibleItemItr = new ListIterator( visibleItems ); + while (visibleItemItr.hasNext()) { + sourceItem = cast(IContributionItem) visibleItemItr.next(); + + // Retrieve the corresponding contribution item from DWT's + // data. + if (sourceIndex < coolItems.length) { + destinationItem = cast(IContributionItem) coolItems[sourceIndex] + .getData(); + } else { + destinationItem = null; + } + + // The items match is they are equal or both separators. + if (destinationItem !is null) { + if ((cast(Object)sourceItem).opEquals(cast(Object)destinationItem)) { + sourceIndex++; + destinationIndex++; + sourceItem.update(); + continue; + + } else if ((destinationItem.isSeparator()) + && (sourceItem.isSeparator())) { + coolItems[sourceIndex].setData(cast(Object)sourceItem); + sourceIndex++; + destinationIndex++; + sourceItem.update(); + continue; + + } + } + + // Otherwise, a new item has to be added. + int start = coolBar.getItemCount(); + sourceItem.fill(coolBar, destinationIndex); + int newItems = coolBar.getItemCount() - start; + for (int i = 0; i < newItems; i++) { + coolBar.getItem(destinationIndex++).setData(cast(Object)sourceItem); + } + changed = true; + } + + // Remove any old widgets not accounted for. + for (int i = coolItems.length - 1; i >= sourceIndex; i--) { + final CoolItem item = coolItems[i]; + if (!item.isDisposed()) { + Control control = item.getControl(); + if (control !is null) { + item.setControl(null); + control.dispose(); + } + item.dispose(); + changed = true; + } + } + + // Update wrap indices. + updateWrapIndices(); + + // Update the sizes. + for (int i = 0; i < items.length; i++) { + IContributionItem item = items[i]; + item.update(SIZE); + } + + // if the coolBar was previously locked then lock it + if (relock) { + coolBar.setLocked(true); + } + + if (changed) { + updateTabOrder(); + } + + // We are no longer dirty. + setDirty(false); + } finally { + coolBar.setRedraw(true); + } + } + + /** + * Sets the tab order of the coolbar to the visual order of its items. + */ + /* package */void updateTabOrder() { + if (coolBar !is null) { + CoolItem[] items = coolBar.getItems(); + if (items !is null) { + auto children = new ArraySeq!(Object); + children.capacity(items.length); + for (int i = 0; i < items.length; i++) { + if ((items[i].getControl() !is null) + && (!items[i].getControl().isDisposed())) { + children.append(items[i].getControl()); + } + } + // Convert array + Control[] childrenArray; + childrenArray = arraycast!(Control)(children.toArray()); + + if (childrenArray !is null) { + coolBar.setTabList(childrenArray); + } + + } + } + } + + /** + * Updates the indices at which the cool bar should wrap. + */ + private void updateWrapIndices() { + final IContributionItem[] items = getItems(); + final int numRows = getNumRows(items) - 1; + + // Generate the list of wrap indices. + final int[] wrapIndices = new int[numRows]; + bool foundSeparator = false; + int j = 0; + CoolItem[] coolItems = (coolBar is null) ? null : coolBar.getItems(); + + for (int i = 0; i < items.length; i++) { + IContributionItem item = items[i]; + CoolItem coolItem = findCoolItem(coolItems, item); + if (item.isSeparator()) { + foundSeparator = true; + } + if ((!item.isSeparator()) && (!item.isGroupMarker()) + && (item.isVisible()) && (coolItem !is null) + && (foundSeparator)) { + wrapIndices[j] = coolBar.indexOf(coolItem); + j++; + foundSeparator = false; + } + } + + /* + * Check to see if these new wrap indices are different than the old + * ones. + */ + final int[] oldIndices = coolBar.getWrapIndices(); + bool shouldUpdate = false; + if (oldIndices.length is wrapIndices.length) { + for (int i = 0; i < oldIndices.length; i++) { + if (oldIndices[i] !is wrapIndices[i]) { + shouldUpdate = true; + break; + } + } + } else { + shouldUpdate = true; + } + + if (shouldUpdate) { + coolBar.setWrapIndices(wrapIndices); + } + } +} diff -r eb6b3e6de869 -r ca63e2bea4bf dwtx/jface/action/ICoolBarManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/action/ICoolBarManager.d Thu Apr 03 04:50:25 2008 +0200 @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2003, 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.ICoolBarManager; + +import dwtx.jface.action.IContributionManager; +import dwtx.jface.action.IToolBarManager; +import dwtx.jface.action.IMenuManager; + +import dwt.widgets.CoolBar; + +import dwt.dwthelper.utils; + +/** + * The ICoolBarManager interface provides protocol for managing + * contributions to a cool bar. A cool bar manager delegates responsibility for + * creating child controls to its contribution items by calling + * {@link IContributionItem#fill(CoolBar, int)}. + *

+ * This interface is internal to the framework; it should not be implemented + * outside the framework. This package provides a concrete cool bar manager + * implementation, {@link CoolBarManager}, which + * clients may instantiate or subclass. + *

+ * + * @see ToolBarContributionItem + * @since 3.0 + */ +public interface ICoolBarManager : IContributionManager { + + /** + * Property name of a cool item's size (value "size"). + *

+ * The cool bar manager uses this property to tell its cool items to update + * their size. + *

+ * + * @see IContributionItem#update(String) @issue consider declaring this + * constant elsewhere + */ + public static const String SIZE = "size"; //$NON-NLS-1$ + + /** + * A convenience method to add a tool bar as a contribution item to this + * cool bar manager. Equivalent to add(new ToolBarContributionManager(toolBarManager)). + * + * @param toolBarManager + * the tool bar manager to be added + * @see ToolBarContributionItem + */ + public void add(IToolBarManager toolBarManager); + + /** + * Returns the context menu manager used by this cool bar manager. This + * context menu manager is used by the cool bar manager except for cool + * items that provide their own. + * + * @return the context menu manager, or null if none + * @see #setContextMenuManager + */ + public IMenuManager getContextMenuManager(); + + /** + * Returns whether the layout of the underlying cool bar widget is locked. + * + * @return true if cool bar layout is locked, false + * otherwise + */ + public bool getLockLayout(); + + /** + * Returns the style of the underlying cool bar widget. + * + * @return the style of the cool bar + */ + public int getStyle(); + + /** + * Sets the context menu of this cool bar manager to the given menu + * manager. + * + * @param menuManager + * the context menu manager, or null if none + * @see #getContextMenuManager + */ + public void setContextMenuManager(IMenuManager menuManager); + + /** + * Locks or unlocks the layout of the underlying cool bar widget. Once the + * cool bar is locked, cool items cannot be repositioned by the user. + *

+ * Note that items can be added or removed programmatically even while the + * cool bar is locked. + *

+ * + * @param value + * true to lock the cool bar, false + * to unlock + */ + public void setLockLayout(bool value); + +} diff -r eb6b3e6de869 -r ca63e2bea4bf dwtx/jface/action/IToolBarManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/action/IToolBarManager.d Thu Apr 03 04:50:25 2008 +0200 @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.IToolBarManager; + +import dwtx.jface.action.IContributionManager; + +/** + * The IToolBarManager interface provides protocol for managing + * contributions to a tool bar. It extends IContributionManager + * but does not declare any new members; it exists only to increase the + * readability of code using tool bars. + *

+ * This package also provides a concrete tool bar manager implementation, + * {@link ToolBarManager ToolBarManager}. + *

+ */ +public interface IToolBarManager : IContributionManager { +} diff -r eb6b3e6de869 -r ca63e2bea4bf dwtx/jface/action/ToolBarContributionItem.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/action/ToolBarContributionItem.d Thu Apr 03 04:50:25 2008 +0200 @@ -0,0 +1,691 @@ +/******************************************************************************* + * 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.ToolBarContributionItem; + +import dwtx.jface.action.IContributionItem; +import dwtx.jface.action.ContributionItem; +import dwtx.jface.action.MenuManager; +import dwtx.jface.action.IToolBarManager; +import dwtx.jface.action.ToolBarManager; +import dwtx.jface.action.ActionContributionItem; +import dwtx.jface.action.SubContributionItem; +import dwtx.jface.action.ICoolBarManager; +import dwtx.jface.action.Separator; +import dwtx.jface.action.IContributionManager; + +import tango.util.collection.ArraySeq; +// import java.util.Iterator; + +import dwt.DWT; +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.CoolBar; +import dwt.widgets.CoolItem; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwt.widgets.Menu; +import dwt.widgets.ToolBar; +import dwt.widgets.ToolItem; +import dwtx.core.runtime.Assert; +import dwtx.jface.internal.provisional.action.IToolBarContributionItem; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; +import tango.io.Stdout; + +/** + * The ToolBarContributionItem class provides a wrapper for tool + * bar managers when used in cool bar managers. It extends ContributionItem + * but and provides some additional methods to customize the size of the cool + * item and to retrieve the underlying tool bar manager. + *

+ * This class may be instantiated; it is not intended to be subclassed. + *

+ * + * @since 3.0 + */ +public class ToolBarContributionItem : ContributionItem, IToolBarContributionItem { + + public IContributionManager getParent() { + return super.getParent(); + } + + /** + * A constant used by setMinimumItemsToShow and getMinimumItemsToShow + * to indicate that all tool items should be shown in the cool item. + */ + public static const int SHOW_ALL_ITEMS = -1; + + /** + * The pull down menu used to list all hidden tool items if the current + * size is less than the preffered size. + */ + private MenuManager chevronMenuManager = null; + + /** + * The widget created for this item; null before creation + * and after disposal. + */ + private CoolItem coolItem = null; + + /** + * Current height of cool item + */ + private int currentHeight = -1; + + /** + * Current width of cool item. + */ + private int currentWidth = -1; + + /** + * A flag indicating that this item has been disposed. This prevents future + * method invocations from doing things they shouldn't. + */ + private bool disposed = false; + + /** + * Mininum number of tool items to show in the cool item widget. + */ + private int minimumItemsToShow = SHOW_ALL_ITEMS; + + /** + * The tool bar manager used to manage the tool items contained in the cool + * item widget. + */ + private ToolBarManager toolBarManager = null; + + /** + * Enable/disable chevron support. + */ + private bool useChevron = true; + + /** + * Convenience method equivalent to ToolBarContributionItem(new ToolBarManager(), null). + */ + public this() { + this(new ToolBarManager(), null); + } + + /** + * Convenience method equivalent to ToolBarContributionItem(toolBarManager, null). + * + * @param toolBarManager + * the tool bar manager + */ + public this(IToolBarManager toolBarManager) { + this(toolBarManager, null); + } + + /** + * Creates a tool bar contribution item. + * + * @param toolBarManager + * the tool bar manager to wrap + * @param id + * the contribution item id, or null if none + */ + public this(IToolBarManager toolBarManager, String id) { + super(id); + Assert.isTrue( null !is cast(ToolBarManager)toolBarManager ); + this.toolBarManager = cast(ToolBarManager) toolBarManager; + } + + /** + * Checks whether this contribution item has been disposed. If it has, and + * the tracing options are active, then it prints some debugging + * information. + * + * @return true if the item is disposed; false + * otherwise. + * + */ + private final bool checkDisposed() { + if (disposed) { + if (Policy.TRACE_TOOLBAR) { + Stdout.formatln("Method invocation on a disposed tool bar contribution item."); //$NON-NLS-1$ + ExceptionPrintStackTrace( new Exception(null), Stdout ); + } + + return true; + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.IContributionItem#dispose() + */ + public void dispose() { + // Dispose of the ToolBar and all its contributions + if (toolBarManager !is null) { + toolBarManager.dispose(); + toolBarManager = null; + } + + /* + * We need to dispose the cool item or we might be left holding a cool + * item with a disposed control. + */ + if ((coolItem !is null) && (!coolItem.isDisposed())) { + coolItem.dispose(); + coolItem = null; + } + + // Mark this item as disposed. + disposed = true; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.IContributionItem#fill(dwt.widgets.CoolBar, + * int) + */ + public void fill(CoolBar coolBar, int index) { + if (checkDisposed()) { + return; + } + + if (coolItem is null && coolBar !is null) { + ToolBar oldToolBar = toolBarManager.getControl(); + ToolBar toolBar = toolBarManager.createControl(coolBar); + if ((oldToolBar !is null) && (oldToolBar.opEquals(toolBar))) { + // We are using an old tool bar, so we need to update. + toolBarManager.update(true); + } + + // Do not create a coolItem if the toolbar is empty + if (toolBar.getItemCount() < 1) { + return; + } + int flags = DWT.DROP_DOWN; + if (index >= 0) { + coolItem = new CoolItem(coolBar, flags, index); + } else { + coolItem = new CoolItem(coolBar, flags); + } + // sets the back reference + coolItem.setData(this); + // Add the toolbar to the CoolItem widget + coolItem.setControl(toolBar); + + // Handle Context Menu + // ToolBarManager.createControl can actually return a pre-existing control. + // Only add the listener if the toolbar was newly created (bug 62097). + if (oldToolBar !is toolBar) { + toolBar.addListener(DWT.MenuDetect, new class Listener { + + public void handleEvent(Event event) { + // if the toolbar does not have its own context menu then + // handle the event + if (toolBarManager.getContextMenuManager() is null) { + handleContextMenu(event); + } + } + }); + } + + // Handle for chevron clicking + if (getUseChevron()) { + // Chevron Support + coolItem.addSelectionListener(new class SelectionAdapter { + + public void widgetSelected(SelectionEvent event) { + if (event.detail is DWT.ARROW) { + handleChevron(event); + } + } + }); + } + + // Handle for disposal + coolItem.addDisposeListener(new class DisposeListener { + + public void widgetDisposed(DisposeEvent event) { + handleWidgetDispose(event); + } + }); + + // Sets the size of the coolItem + updateSize(true); + } + } + + /** + * Returns a consistent set of wrap indices. The return value will always + * include at least one entry and the first entry will always be zero. + * CoolBar.getWrapIndices() is inconsistent in whether or not it returns an + * index for the first row. + */ + private int[] getAdjustedWrapIndices(int[] wraps) { + int[] adjustedWrapIndices; + if (wraps.length is 0) { + adjustedWrapIndices = [ 0 ]; + } else { + if (wraps[0] !is 0) { + adjustedWrapIndices = new int[wraps.length + 1]; + adjustedWrapIndices[0] = 0; + for (int i = 0; i < wraps.length; i++) { + adjustedWrapIndices[i + 1] = wraps[i]; + } + } else { + adjustedWrapIndices = wraps; + } + } + return adjustedWrapIndices; + } + + /** + * Returns the current height of the corresponding cool item. + * + * @return the current height + */ + public int getCurrentHeight() { + if (checkDisposed()) { + return -1; + } + return currentHeight; + } + + /** + * Returns the current width of the corresponding cool item. + * + * @return the current size + */ + public int getCurrentWidth() { + if (checkDisposed()) { + return -1; + } + return currentWidth; + } + + /** + * Returns the minimum number of tool items to show in the cool item. + * + * @return the minimum number of tool items to show, or SHOW_ALL_ITEMS + * if a value was not set + * @see #setMinimumItemsToShow(int) + */ + public int getMinimumItemsToShow() { + if (checkDisposed()) { + return -1; + } + return minimumItemsToShow; + } + + /** + * Returns the internal tool bar manager of the contribution item. + * + * @return the tool bar manager, or null if one is not + * defined. + * @see IToolBarManager + */ + public IToolBarManager getToolBarManager() { + if (checkDisposed()) { + return null; + } + return toolBarManager; + } + + /** + * Returns whether chevron support is enabled. + * + * @return true if chevron support is enabled, false + * otherwise + */ + public bool getUseChevron() { + if (checkDisposed()) { + return false; + } + return useChevron; + } + + /** + * Create and display the chevron menu. + */ + private void handleChevron(SelectionEvent event) { + CoolItem item = cast(CoolItem) event.widget; + Control control = item.getControl(); + if (!(cast(ToolBar)control ) ) { + return; + } + CoolBar coolBar = item.getParent(); + ToolBar toolBar = cast(ToolBar) control; + Rectangle toolBarBounds = toolBar.getBounds(); + ToolItem[] items = toolBar.getItems(); + auto hidden = new ArraySeq!(Object); + for (int i = 0; i < items.length; ++i) { + Rectangle itemBounds = items[i].getBounds(); + if (!((itemBounds.x + itemBounds.width <= toolBarBounds.width) && (itemBounds.y + + itemBounds.height <= toolBarBounds.height))) { + hidden.append(items[i]); + } + } + + // Create a pop-up menu with items for each of the hidden buttons. + if (chevronMenuManager !is null) { + chevronMenuManager.dispose(); + } + chevronMenuManager = new MenuManager(); + foreach( it; hidden ){ + ToolItem toolItem = cast(ToolItem) it; + IContributionItem data = cast(IContributionItem) toolItem.getData(); + if (cast(ActionContributionItem)data ) { + ActionContributionItem contribution = new ActionContributionItem( + (cast(ActionContributionItem) data).getAction()); + chevronMenuManager.add(contribution); + } else if (cast(SubContributionItem)data ) { + IContributionItem innerData = (cast(SubContributionItem) data) + .getInnerItem(); + if (cast(ActionContributionItem)innerData ) { + ActionContributionItem contribution = new ActionContributionItem( + (cast(ActionContributionItem) innerData).getAction()); + chevronMenuManager.add(contribution); + } + } else if (data.isSeparator()) { + chevronMenuManager.add(new Separator()); + } + } + Menu popup = chevronMenuManager.createContextMenu(coolBar); + Point chevronPosition = coolBar.toDisplay(event.x, event.y); + popup.setLocation(chevronPosition.x, chevronPosition.y); + popup.setVisible(true); + } + + /** + * Handles the event when the toobar item does not have its own context + * menu. + * + * @param event + * the event object + */ + private void handleContextMenu(Event event) { + ToolBar toolBar = toolBarManager.getControl(); + // If parent has a menu then use that one + Menu parentMenu = toolBar.getParent().getMenu(); + if ((parentMenu !is null) && (!parentMenu.isDisposed())) { + toolBar.setMenu(parentMenu); + // Hook listener to remove menu once it has disapeared + parentMenu.addListener(DWT.Hide, new class Listener { + + public void handleEvent(Event innerEvent) { + ToolBar innerToolBar = toolBarManager.getControl(); + if (innerToolBar !is null) { + innerToolBar.setMenu(null); + Menu innerParentMenu = innerToolBar.getParent() + .getMenu(); + if (innerParentMenu !is null) { + innerParentMenu.removeListener(DWT.Hide, this); + } + } + } + }); + } + } + + /** + * Handles the disposal of the widget. + * + * @param event + * the event object + */ + private void handleWidgetDispose(DisposeEvent event) { + coolItem = null; + } + + /** + * A contribution item is visible iff its internal state is visible or + * the tool bar manager contains something other than group markers and + * separators. + * + * @return true if the tool bar manager contains something + * other than group marks and separators, and the internal state is + * set to be visible. + */ + public bool isVisible() { + if (checkDisposed()) { + return false; + } + + bool visibleItem = false; + if (toolBarManager !is null) { + IContributionItem[] contributionItems = toolBarManager.getItems(); + for (int i = 0; i < contributionItems.length; i++) { + IContributionItem contributionItem = contributionItems[i]; + if ((!contributionItem.isGroupMarker()) + && (!contributionItem.isSeparator())) { + visibleItem = true; + break; + } + } + } + + return (visibleItem || super.isVisible()); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.IContributionItem#saveWidgetState() + */ + public void saveWidgetState() { + if (checkDisposed()) { + return; + } + if (coolItem is null) { + return; + } + + //1. Save current size + CoolBar coolBar = coolItem.getParent(); + bool isLastOnRow = false; + int lastIndex = coolBar.getItemCount() - 1; + int coolItemIndex = coolBar.indexOf(coolItem); + int[] wrapIndicies = getAdjustedWrapIndices(coolBar.getWrapIndices()); + // Traverse through all wrap indicies backwards + for (int row = wrapIndicies.length - 1; row >= 0; row--) { + if (wrapIndicies[row] <= coolItemIndex) { + + int nextRow = row + 1; + int nextRowStartIndex; + if (nextRow > (wrapIndicies.length - 1)) { + nextRowStartIndex = lastIndex + 1; + } else { + nextRowStartIndex = wrapIndicies[nextRow]; + } + + // Check to see if its the last item on the row + if (coolItemIndex is (nextRowStartIndex - 1)) { + isLastOnRow = true; + } + break; + } + } + + // Save the preferred size as actual size for the last item on a row + int nCurrentWidth; + if (isLastOnRow) { + nCurrentWidth = coolItem.getPreferredSize().x; + } else { + nCurrentWidth = coolItem.getSize().x; + } + setCurrentWidth(nCurrentWidth); + setCurrentHeight(coolItem.getSize().y); + } + + /** + * Sets the current height of the cool item. Update(SIZE) should be called + * to adjust the widget. + * + * @param currentHeight + * the current height to set + */ + public void setCurrentHeight(int currentHeight) { + if (checkDisposed()) { + return; + } + this.currentHeight = currentHeight; + } + + /** + * Sets the current width of the cool item. Update(SIZE) should be called + * to adjust the widget. + * + * @param currentWidth + * the current width to set + */ + public void setCurrentWidth(int currentWidth) { + if (checkDisposed()) { + return; + } + this.currentWidth = currentWidth; + } + + /** + * Sets the minimum number of tool items to show in the cool item. If this + * number is less than the total tool items, a chevron will appear and the + * hidden tool items appear in a drop down menu. By default, all the tool + * items are shown in the cool item. + * + * @param minimumItemsToShow + * the minimum number of tool items to show. + * @see #getMinimumItemsToShow() + * @see #setUseChevron(bool) + */ + public void setMinimumItemsToShow(int minimumItemsToShow) { + if (checkDisposed()) { + return; + } + this.minimumItemsToShow = minimumItemsToShow; + } + + /** + * Enables or disables chevron support for the cool item. By default, + * chevron support is enabled. + * + * @param value + * true to enable chevron support, false + * otherwise. + */ + public void setUseChevron(bool value) { + if (checkDisposed()) { + return; + } + useChevron = value; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.action.IContributionItem#update(java.lang.String) + */ + public void update(String propertyName) { + if (checkDisposed()) { + return; + } + if (coolItem !is null) { + IToolBarManager manager = getToolBarManager(); + if (manager !is null) { + manager.update(true); + } + + if ((propertyName is null) + || propertyName.equals(ICoolBarManager.SIZE)) { + updateSize(true); + } + } + } + + /** + * Updates the cool items' preferred, minimum, and current size. The + * preferred size is calculated based on the tool bar size and extra trim. + * + * @param changeCurrentSize + * true if the current size should be changed to + * the preferred size, false to not change the + * current size + */ + private void updateSize(bool changeCurrentSize) { + if (checkDisposed()) { + return; + } + // cannot set size if coolItem is null + if (coolItem is null || coolItem.isDisposed()) { + return; + } + bool locked = false; + CoolBar coolBar = coolItem.getParent(); + try { + // Fix odd behaviour with locked tool bars + if (coolBar !is null) { + if (coolBar.getLocked()) { + coolBar.setLocked(false); + locked = true; + } + } + ToolBar toolBar = cast(ToolBar) coolItem.getControl(); + if ((toolBar is null) || (toolBar.isDisposed()) + || (toolBar.getItemCount() <= 0)) { + // if the toolbar does not contain any items then dispose of + // coolItem + coolItem.setData(null); + Control control = coolItem.getControl(); + if ((control !is null) && !control.isDisposed()) { + control.dispose(); + coolItem.setControl(null); + } + if (!coolItem.isDisposed()) { + coolItem.dispose(); + } + } else { + // If the toolbar item exists then adjust the size of the cool + // item + Point toolBarSize = toolBar.computeSize(DWT.DEFAULT, + DWT.DEFAULT); + // Set the preffered size to the size of the toolbar plus trim + Point preferredSize = coolItem.computeSize(toolBarSize.x, + toolBarSize.y); + coolItem.setPreferredSize(preferredSize); + // note setMinimumSize must be called before setSize, see PR + // 15565 + // Set minimum size + if (getMinimumItemsToShow() !is SHOW_ALL_ITEMS) { + int toolItemWidth = toolBar.getItems()[0].getWidth(); + int minimumWidth = toolItemWidth * getMinimumItemsToShow(); + coolItem.setMinimumSize(minimumWidth, toolBarSize.y); + } else { + coolItem.setMinimumSize(toolBarSize.x, toolBarSize.y); + } + if (changeCurrentSize) { + // Set current size to preferred size + coolItem.setSize(preferredSize); + } + } + } finally { + // If the cool bar was locked, then set it back to locked + if ((locked) && (coolBar !is null)) { + coolBar.setLocked(true); + } + } + } + +} diff -r eb6b3e6de869 -r ca63e2bea4bf dwtx/jface/action/ToolBarManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/action/ToolBarManager.d Thu Apr 03 04:50:25 2008 +0200 @@ -0,0 +1,420 @@ +/******************************************************************************* + * 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.ToolBarManager; + +import dwtx.jface.action.ContributionManager; +import dwtx.jface.action.IToolBarManager; +import dwtx.jface.action.MenuManager; +import dwtx.jface.action.IContributionItem; + +import tango.util.collection.ArraySeq; +// import java.util.Iterator; + +import dwt.DWT; +import dwt.accessibility.ACC; +import dwt.accessibility.AccessibleAdapter; +import dwt.accessibility.AccessibleEvent; +import dwt.accessibility.AccessibleListener; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Menu; +import dwt.widgets.ToolBar; +import dwt.widgets.ToolItem; + +import dwt.dwthelper.utils; + +/** + * A tool bar manager is a contribution manager which realizes itself and its + * items in a tool bar control. + *

+ * This class may be instantiated; it may also be subclassed if a more + * sophisticated layout is required. + *

+ */ +public class ToolBarManager : ContributionManager, + IToolBarManager { + + /** + * The tool bar items style; DWT.NONE by default. + */ + private int itemStyle = DWT.NONE; + + /** + * The tool bat control; null before creation and after + * disposal. + */ + private ToolBar toolBar = null; + + /** + * The menu manager to the context menu associated with the toolbar. + * + * @since 3.0 + */ + private MenuManager contextMenuManager = null; + + /** + * Creates a new tool bar manager with the default DWT button style. Use the + * {@link #createControl(Composite)} method to create the tool bar control. + * + */ + public this() { + //Do nothing if there are no parameters + } + + /** + * Creates a tool bar manager with the given DWT button style. Use the + * createControl method to create the tool bar control. + * + * @param style + * the tool bar item style + * @see dwt.widgets.ToolBar for valid style bits + */ + public this(int style) { + itemStyle = style; + } + + /** + * Creates a tool bar manager for an existing tool bar control. This manager + * becomes responsible for the control, and will dispose of it when the + * manager is disposed. + * NOTE When creating a ToolBarManager from an existing + * {@link ToolBar} you will not get the accessible listener provided by + * JFace. + * @see #ToolBarManager() + * @see #ToolBarManager(int) + * + * @param toolbar + * the tool bar control + */ + public this(ToolBar toolbar) { + this(); + this.toolBar = toolbar; + } + + /** + * Creates and returns this manager's tool bar control. Does not create a + * new control if one already exists. Also create an {@link AccessibleListener} + * for the {@link ToolBar}. + * + * @param parent + * the parent control + * @return the tool bar control + */ + public ToolBar createControl(Composite parent) { + if (!toolBarExist() && parent !is null) { + toolBar = new ToolBar(parent, itemStyle); + toolBar.setMenu(getContextMenuControl()); + update(true); + + toolBar.getAccessible().addAccessibleListener(getAccessibleListener()); + } + + return toolBar; + } + + /** + * Get the accessible listener for the tool bar. + * + * @return AccessibleListener + * + * @since 3.1 + */ + private AccessibleListener getAccessibleListener() { + return new class AccessibleAdapter { + public void getName(AccessibleEvent e) { + if (e.childID !is ACC.CHILDID_SELF) { + ToolItem item = toolBar.getItem(e.childID); + if (item !is null) { + String toolTip = item.getToolTipText(); + if (toolTip !is null) { + e.result = toolTip; + } + } + } + } + }; + } + + /** + * Disposes of this tool bar 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 tool bar manager and its + * associated contribution items. Use removeAll for that + * purpose. + */ + public void dispose() { + + if (toolBarExist()) { + toolBar.dispose(); + } + toolBar = null; + + IContributionItem[] items = getItems(); + for (int i = 0; i < items.length; i++) { + items[i].dispose(); + } + + if (getContextMenuManager() !is null) { + getContextMenuManager().dispose(); + setContextMenuManager(null); + } + } + + /** + * Returns the tool bar control for this manager. + * + * @return the tool bar control, or null if none (before + * creating or after disposal) + */ + public ToolBar getControl() { + return toolBar; + } + + /** + * Re-lays out the tool bar. + *

+ * The default implementation of this framework method re-lays out the + * parent when the number of items crosses the zero threshold. Subclasses + * should override this method to implement their own re-layout strategy + * + * @param layoutBar + * the tool bar control + * @param oldCount + * the old number of items + * @param newCount + * the new number of items + */ + protected void relayout(ToolBar layoutBar, int oldCount, int newCount) { + if ((oldCount is 0) !is (newCount is 0)) { + layoutBar.getParent().layout(); + } + } + + /** + * Returns whether the tool bar control is created and not disposed. + * + * @return true if the control is created and not disposed, + * false otherwise + */ + private bool toolBarExist() { + return toolBar !is null && !toolBar.isDisposed(); + } + + /* + * (non-Javadoc) Method declared on IContributionManager. + */ + public void update(bool force) { + + // long startTime= 0; + // if (DEBUG) { + // dumpStatistics(); + // startTime= (new Date()).getTime(); + // } + + if (isDirty() || force) { + + if (toolBarExist()) { + + int oldCount = toolBar.getItemCount(); + + // clean contains all active items without double separators + IContributionItem[] items = getItems(); + auto clean = new ArraySeq!(Object); + clean.capacity(items.length); + IContributionItem separator = null; + // long cleanStartTime= 0; + // if (DEBUG) { + // cleanStartTime= (new Date()).getTime(); + // } + 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(cast(Object)separator); + } + separator = null; + } + clean.append(cast(Object)ci); + } + } + // if (DEBUG) { + // System.out.println(" Time needed to build clean vector: " + + // ((new Date()).getTime() - cleanStartTime)); + // } + + // determine obsolete items (removed or non active) + ToolItem[] mi = toolBar.getItems(); + auto toRemove = new ArraySeq!(Object); + toRemove.capacity(mi.length); + for (int i = 0; i < mi.length; i++) { + Object data = mi[i].getData(); + if (data is null + || !clean.contains(data) + || (cast(IContributionItem) data && (cast(IContributionItem) data) + .isDynamic())) { + toRemove.append(mi[i]); + } + } + + // Turn redraw off if the number of items to be added + // is above a certain threshold, to minimize flicker, + // otherwise the toolbar can be seen to redraw after each item. + // Do this before any modifications are made. + // We assume each contribution item will contribute at least one + // toolbar item. + bool useRedraw = (clean.size() - (mi.length - toRemove + .size())) >= 3; + try { + if (useRedraw) { + toolBar.setRedraw(false); + } + + // remove obsolete items + for (int i = toRemove.size(); --i >= 0;) { + ToolItem item = cast(ToolItem) toRemove.get(i); + if (!item.isDisposed()) { + Control ctrl = item.getControl(); + if (ctrl !is null) { + item.setControl(null); + ctrl.dispose(); + } + item.dispose(); + } + } + + // add new items + IContributionItem src, dest; + mi = toolBar.getItems(); + int srcIx = 0; + int destIx = 0; + foreach( e; clean ){ + src = cast(IContributionItem) e; + + // 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++; + continue; + } + + if (dest !is null && dest.isSeparator() + && src.isSeparator()) { + mi[srcIx].setData(cast(Object)src); + srcIx++; + destIx++; + continue; + } + + int start = toolBar.getItemCount(); + src.fill(toolBar, destIx); + int newItems = toolBar.getItemCount() - start; + for (int i = 0; i < newItems; i++) { + ToolItem item = toolBar.getItem(destIx++); + item.setData(cast(Object)src); + } + } + + // remove any old tool items not accounted for + for (int i = mi.length; --i >= srcIx;) { + ToolItem item = mi[i]; + if (!item.isDisposed()) { + Control ctrl = item.getControl(); + if (ctrl !is null) { + item.setControl(null); + ctrl.dispose(); + } + item.dispose(); + } + } + + setDirty(false); + + // turn redraw back on if we turned it off above + } finally { + if (useRedraw) { + toolBar.setRedraw(true); + } + } + + int newCount = toolBar.getItemCount(); + relayout(toolBar, oldCount, newCount); + } + + } + + // if (DEBUG) { + // System.out.println(" Time needed for update: " + ((new + // Date()).getTime() - startTime)); + // System.out.println(); + // } + } + + /** + * Returns the control of the Menu Manager. If the menu manager does not + * have a control then one is created. + * + * @return menu widget associated with manager + */ + private Menu getContextMenuControl() { + if ((contextMenuManager !is null) && (toolBar !is null)) { + Menu menuWidget = contextMenuManager.getMenu(); + if ((menuWidget is null) || (menuWidget.isDisposed())) { + menuWidget = contextMenuManager.createContextMenu(toolBar); + } + return menuWidget; + } + return null; + } + + /** + * Returns the context menu manager for this tool bar manager. + * + * @return the context menu manager, or null if none + * @since 3.0 + */ + public MenuManager getContextMenuManager() { + return contextMenuManager; + } + + /** + * Sets the context menu manager for this tool bar manager to the given menu + * manager. If the tool bar control exists, it also adds the menu control to + * the tool bar. + * + * @param contextMenuManager + * the context menu manager, or null if none + * @since 3.0 + */ + public void setContextMenuManager(MenuManager contextMenuManager) { + this.contextMenuManager = contextMenuManager; + if (toolBar !is null) { + toolBar.setMenu(getContextMenuControl()); + } + } + +} diff -r eb6b3e6de869 -r ca63e2bea4bf dwtx/jface/internal/provisional/action/IToolBarContributionItem.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/internal/provisional/action/IToolBarContributionItem.d Thu Apr 03 04:50:25 2008 +0200 @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 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.internal.provisional.action.IToolBarContributionItem; + +import dwtx.jface.action.IContributionItem; +import dwtx.jface.action.IContributionManager; +import dwtx.jface.action.IToolBarManager; + +import dwt.dwthelper.utils; + +/** + * The intention of this interface is to provide in interface for + * ToolBarContributionItem so that the implementation can be replaced. + * + *

+ * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is a guarantee neither that this API will + * work nor that it will remain the same. Please do not use this API without + * consulting with the Platform/UI team. + *

+ * + * @since 3.2 + */ +public interface IToolBarContributionItem : IContributionItem { + + /** + * Returns the current height of the corresponding cool item. + * + * @return the current height + */ + public int getCurrentHeight(); + + /** + * Returns the current width of the corresponding cool item. + * + * @return the current size + */ + public int getCurrentWidth(); + + /** + * Returns the minimum number of tool items to show in the cool item. + * + * @return the minimum number of tool items to show, or SHOW_ALL_ITEMS + * if a value was not set + * @see #setMinimumItemsToShow(int) + */ + public int getMinimumItemsToShow(); + + /** + * Returns whether chevron support is enabled. + * + * @return true if chevron support is enabled, false + * otherwise + */ + public bool getUseChevron(); + + /** + * Sets the current height of the cool item. Update(SIZE) should be called + * to adjust the widget. + * + * @param currentHeight + * the current height to set + */ + public void setCurrentHeight(int currentHeight); + + /** + * Sets the current width of the cool item. Update(SIZE) should be called + * to adjust the widget. + * + * @param currentWidth + * the current width to set + */ + public void setCurrentWidth(int currentWidth); + + /** + * Sets the minimum number of tool items to show in the cool item. If this + * number is less than the total tool items, a chevron will appear and the + * hidden tool items appear in a drop down menu. By default, all the tool + * items are shown in the cool item. + * + * @param minimumItemsToShow + * the minimum number of tool items to show. + * @see #getMinimumItemsToShow() + * @see #setUseChevron(bool) + */ + public void setMinimumItemsToShow(int minimumItemsToShow); + + /** + * Enables or disables chevron support for the cool item. By default, + * chevron support is enabled. + * + * @param value + * true to enable chevron support, false + * otherwise. + */ + public void setUseChevron(bool value); + + /** + * Returns the internal tool bar manager of the contribution item. + * + * @return the tool bar manager, or null if one is not + * defined. + * @see IToolBarManager + */ + public IToolBarManager getToolBarManager(); + + /** + * 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 + * + * TODO may not need this, getToolBarManager may be enough. + */ + public IContributionManager getParent(); + +}