/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * 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: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.client.ui.rcp.widgets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jubula.client.ui.rcp.i18n.Messages; import org.eclipse.jubula.client.ui.utils.ErrorHandlingUtil; import org.eclipse.jubula.client.ui.utils.LayoutUtil; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.exception.Assert; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.List; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; /** * Composite with a tree and a list. Content can be shifted between the tree * and list. * * @author BREDEX GmbH * @created Jun 13, 2007 */ @SuppressWarnings("synthetic-access") public class TreeElementChooserComposite extends Composite { /** * @author BREDEX GmbH * @created Aug 17, 2007 */ public interface IUsedListModifiedListener { /** * Handle list modified event. * @param newListEntries All entries after the list has been modified */ public void usedListModified(String [] newListEntries); } /** horizontal style */ public static final int HORIZONTAL = 0; /** vertical style */ public static final int VERTICAL = 1; /** string that begins the display value */ private static final String DISPLAY_VALUE_START = " ["; //$NON-NLS-1$ /** string that ends the display value */ private static final String DISPLAY_VALUE_END = "]"; //$NON-NLS-1$ /***/ private static final String LABEL = "Label"; //$NON-NLS-1$ /** number of columns = 1 */ private static final int NUM_COLUMNS_1 = 1; /** number of columns = 2 */ private static final int NUM_COLUMNS_2 = 2; /** number of columns = 3 */ private static final int NUM_COLUMNS_3 = 3; /** the list field for the available items */ private Tree m_availableTree; /** the list field for the used items */ private List m_usedList; /** the button to shift a selection from used list to available tree */ private Button m_selectionAvailableToUsedButton; /** the button to shift all from available tree to used list */ private Button m_allAvailableToUsedButton; /** the button to shift a selection from used list to available tree */ private Button m_selectionUsedToAvailableButton; /** the button to shift all from available tree to used list */ private Button m_allUsedToAvailableButton; /** the button to swap an item between the tree and list */ private Button m_swapButton; /** disabled button content array */ private Object[] m_disabledButtonContents; /** button content array */ private Object[] m_buttonContents; /** Parents that have a child that is used */ private Set<String> m_usedParents = new HashSet<String>(); /** the StateController */ private final WidgetSelectionListener m_selectionListener = new WidgetSelectionListener(); /** Mapping between display Strings and corresponding GUI objects */ private Map<String, IChooserCompositeGuiObject> m_listItemsToGuiObjects = new HashMap<String, IChooserCompositeGuiObject>(); /** Mapping between TreeItems and corresponding GUI objects */ private Map<TreeItem, IChooserCompositeGuiObject> m_treeItemsToGuiObjects = new HashMap<TreeItem, IChooserCompositeGuiObject>(); /** list listeners */ private Set<IUsedListModifiedListener> m_listeners = new HashSet<IUsedListModifiedListener>(); /** * Composite, with two listBoxes. You can shift the content between the two ListBoxes. * @param parent The parent composite. * @param availableLabel Label text of the available tree. * @param availableObjects All available objects. * @param usedLabel Label text of the used list. * @param usedObjects Objects to be placed in the used list. * @param lineNumber The number of lines of both ListBoxes. * @param buttonTexts The texts of the 5 buttons (example: ">",">>","<","<<") * @param buttonToolTips The texts of the toolTips of 5 buttons. * @param style <p>ListElementChooserComposite.HORIZONTAL (ListBoxes are side by side) or</p> * <p>ListElementChooserComposite.VERTICAL (ListBoxe one is below ListBox two)</p> */ public TreeElementChooserComposite(Composite parent, String availableLabel, Set<IChooserCompositeGuiObject> availableObjects, String usedLabel, Set<IChooserCompositeGuiObject> usedObjects, int lineNumber, String[] buttonTexts, String[] buttonToolTips, int style) { super(parent, SWT.NONE); m_disabledButtonContents = buttonTexts; m_buttonContents = buttonTexts; createControl(availableLabel, availableObjects, usedLabel, usedObjects, lineNumber, buttonToolTips, style); } /** * @param parent The parent composite. * @param availableLabel Label text of the available tree. * @param availableObjects All available objects. * @param usedLabel Label text of the used list. * @param usedObjects Objects to be placed in the used list. * @param lineNumber The number of lines of both ListBoxes. * @param buttonImages The images of the 5 buttons. * @param disabledButtonImages The disabled images of the 5 buttons. * @param buttonToolTips The texts of the toolTips of 5 buttons. * @param style <p>ListElementChooserComposite.HORIZONTAL (ListBoxes are side by side) or</p> * <p>ListElementChooserComposite.VERTICAL (ListBoxe one is below ListBox two)</p> */ public TreeElementChooserComposite(Composite parent, String availableLabel, Set<IChooserCompositeGuiObject> availableObjects, String usedLabel, Set<IChooserCompositeGuiObject> usedObjects, int lineNumber, Image[] buttonImages, Image[] disabledButtonImages, String[] buttonToolTips, int style) { super(parent, SWT.NONE); m_disabledButtonContents = disabledButtonImages; m_buttonContents = buttonImages; createControl(availableLabel, availableObjects, usedLabel, usedObjects, lineNumber, buttonToolTips, style); } /** * Creates the ListElementChooserComposite. * @param availableLabel Label text of the available tree. * @param availableObjects All available objects. * @param usedLabel Label text of the used list. * @param usedObjects Objects to be placed in the used list. * @param lineNumber The number of lines of both ListBoxes. * @param buttonToolTips The texts of the toolTips of 5 buttons. * @param style ListElementChooserComposite.HORIZONTAL or ListElementChooserComposite.VERTICAL */ private void createControl(String availableLabel, Set<IChooserCompositeGuiObject> availableObjects, String usedLabel, Set<IChooserCompositeGuiObject> usedObjects, int lineNumber, String[] buttonToolTips, int style) { if (style == HORIZONTAL) { createHorizontalLayout(availableLabel, usedLabel, lineNumber, buttonToolTips, style); } else { Composite composite = this; GridLayout compositeLayout = new GridLayout(); compositeLayout.numColumns = NUM_COLUMNS_3; compositeLayout.marginHeight = 0; compositeLayout.marginWidth = 0; composite.setLayout(compositeLayout); GridData compositeData = new GridData(); compositeData.horizontalAlignment = GridData.FILL; compositeData.grabExcessHorizontalSpace = true; composite.setLayoutData(compositeData); createVerticalLayout(composite, availableLabel, usedLabel, lineNumber, buttonToolTips, style); } initFields(availableObjects, usedObjects); addListener(); checkButtons(); } /** * Creates the layout with Vertical alignment. * @param parent The parent composite. * @param listOneLabel Label text of the first ListBox. * @param listTwoLabel Label text of the second ListBox. * @param lineNumber The number of lines of both ListBoxes. * @param buttonToolTips The texts of the toolTips of 5 buttons. * @param style ListElementChooserComposite.HORIZONTAL or ListElementChooserComposite.VERTICAL */ private void createVerticalLayout(Composite parent, String listOneLabel, String listTwoLabel, int lineNumber, String[] buttonToolTips, int style) { Composite compositeLeft = createComposite(parent, NUM_COLUMNS_1, GridData.FILL, true); Composite compositeMiddle = createComposite(parent, NUM_COLUMNS_1, GridData.FILL, false); Composite compositeRight = createComposite(parent, NUM_COLUMNS_1, GridData.FILL, true); m_availableTree = createAvailableTree(compositeLeft, listOneLabel, lineNumber); m_availableTree.addSelectionListener(m_selectionListener); createShiftButtons(style, compositeMiddle, buttonToolTips); m_usedList = createListField(compositeRight, listTwoLabel, lineNumber); m_usedList.addSelectionListener(m_selectionListener); } /** * Creates the layout with Horizontal alignment. * @param listOneLabel Label text of the first ListBox. * @param listTwoLabel Label text of the second ListBox. * @param lineNumber The number of lines of both ListBoxes. * @param buttonToolTips The texts of the toolTips of 5 buttons. * @param style ListElementChooserComposite.HORIZONTAL or ListElementChooserComposite.VERTICAL */ private void createHorizontalLayout(String listOneLabel, String listTwoLabel, int lineNumber, String[] buttonToolTips, int style) { Composite composite = this; GridLayout compositeLayout = new GridLayout(); compositeLayout.numColumns = NUM_COLUMNS_2; compositeLayout.marginHeight = 0; compositeLayout.marginWidth = 0; composite.setLayout(compositeLayout); GridData compositeData = new GridData(GridData.FILL_BOTH); compositeData.grabExcessHorizontalSpace = true; compositeData.grabExcessVerticalSpace = false; compositeData.verticalAlignment = GridData.BEGINNING; composite.setLayoutData(compositeData); m_availableTree = createAvailableTree(composite, listOneLabel, lineNumber); m_availableTree.addSelectionListener(m_selectionListener); createShiftButtons(style, composite, buttonToolTips); m_usedList = createListField(composite, listTwoLabel, lineNumber); m_usedList.addSelectionListener(m_selectionListener); } /** * Inits all swt field in this page. * @param availableObjects All available objects. * @param usedObjects Objects to be placed in the used list. */ protected void initFields(Set<IChooserCompositeGuiObject> availableObjects, Set<IChooserCompositeGuiObject> usedObjects) { Map<String, TreeItem> parentsThatExist = new HashMap<String, TreeItem>(); for (IChooserCompositeGuiObject guiObject : availableObjects) { String parent = guiObject.getParent(); if (!parentsThatExist.containsKey(parent)) { TreeItem parentItem = new TreeItem(m_availableTree, SWT.NONE); parentItem.setText(parent); parentsThatExist.put(parent, parentItem); } boolean isValueInUsedList = usedObjects.contains(guiObject); if (!isValueInUsedList) { TreeItem valueItem = new TreeItem(parentsThatExist.get(parent), SWT.NONE); valueItem.setText(guiObject.getTitle()); m_treeItemsToGuiObjects.put(valueItem, guiObject); } } sortTreeItems(); for (IChooserCompositeGuiObject item : usedObjects) { m_usedList.add(item.getDisplayString()); m_listItemsToGuiObjects.put(item.getDisplayString(), item); m_usedParents.add(item.getParent()); } sortListItems(); } /** * Sorts all items in the used list alphabetically. */ private void sortListItems() { String [] items = m_usedList.getItems(); Arrays.sort(items); m_usedList.removeAll(); for (String listItem : items) { m_usedList.add(listItem); } } /** * Creates the 5 arrow buttons * @param style ListElementChooserComposite.HORIZONTAL or ListElementChooserComposite.VERTICAL * @param parent The parent composite. * @param buttonToolTips The texts of the toolTips of 5 buttons. */ private void createShiftButtons(int style, Composite parent, String[] buttonToolTips) { if (style == HORIZONTAL) { createComposite(parent, NUM_COLUMNS_1, GridData.BEGINNING, false); Composite composite = createComposite(parent, NUM_COLUMNS_2, GridData.FILL, true); Composite leftComposite = createComposite(composite, NUM_COLUMNS_2, GridData.FILL, true); Composite rightComposite = createComposite(composite, NUM_COLUMNS_2, GridData.FILL, true); m_selectionAvailableToUsedButton = new Button(leftComposite, SWT.PUSH); m_allAvailableToUsedButton = new Button(leftComposite, SWT.PUSH); m_selectionUsedToAvailableButton = new Button(rightComposite, SWT.PUSH); m_allUsedToAvailableButton = new Button(rightComposite, SWT.PUSH); m_swapButton = new Button(rightComposite, SWT.PUSH); } else { createLabel(parent, StringConstants.EMPTY); m_selectionAvailableToUsedButton = new Button(parent, SWT.PUSH); m_allAvailableToUsedButton = new Button(parent, SWT.PUSH); createLabel(parent, StringConstants.EMPTY); m_selectionUsedToAvailableButton = new Button(parent, SWT.PUSH); m_allUsedToAvailableButton = new Button(parent, SWT.PUSH); m_swapButton = new Button(parent, SWT.PUSH); } GridData selectionOneToTwoGridData = new GridData(); selectionOneToTwoGridData.horizontalAlignment = GridData.FILL; if (style == HORIZONTAL) { selectionOneToTwoGridData.horizontalAlignment = GridData.END; } selectionOneToTwoGridData.grabExcessHorizontalSpace = true; m_selectionAvailableToUsedButton.setLayoutData( selectionOneToTwoGridData); m_selectionAvailableToUsedButton.setImage( (Image)m_disabledButtonContents[0]); m_selectionAvailableToUsedButton.setEnabled(false); GridData allOneToTwoGridData = new GridData(); allOneToTwoGridData.horizontalAlignment = GridData.FILL; if (style == HORIZONTAL) { allOneToTwoGridData.horizontalAlignment = GridData.BEGINNING; } allOneToTwoGridData.grabExcessHorizontalSpace = true; m_allAvailableToUsedButton.setLayoutData(allOneToTwoGridData); m_allAvailableToUsedButton.setImage((Image)m_disabledButtonContents[1]); m_allAvailableToUsedButton.setEnabled(false); GridData selectionTwoToOneGridData = new GridData(); selectionTwoToOneGridData.horizontalAlignment = GridData.FILL; if (style == HORIZONTAL) { selectionTwoToOneGridData.horizontalAlignment = GridData.END; } selectionTwoToOneGridData.grabExcessHorizontalSpace = true; m_selectionUsedToAvailableButton.setLayoutData( selectionTwoToOneGridData); m_selectionUsedToAvailableButton.setImage( (Image)m_disabledButtonContents[2]); m_selectionUsedToAvailableButton.setEnabled(false); GridData allTwoToOneGridData = new GridData(); allTwoToOneGridData.horizontalAlignment = GridData.FILL; if (style == HORIZONTAL) { allTwoToOneGridData.horizontalAlignment = GridData.BEGINNING; } allTwoToOneGridData.grabExcessHorizontalSpace = true; m_allUsedToAvailableButton.setLayoutData(allTwoToOneGridData); m_allUsedToAvailableButton.setImage((Image)m_disabledButtonContents[3]); m_allUsedToAvailableButton.setEnabled(false); setButtonRepresentations(); setTooltips(buttonToolTips); } /** * Sets the text or image data for the buttons. */ private void setButtonRepresentations() { if (m_buttonContents instanceof Image[]) { m_selectionAvailableToUsedButton.setImage( (Image)m_buttonContents[0]); m_allAvailableToUsedButton.setImage((Image)m_buttonContents[1]); m_selectionUsedToAvailableButton.setImage( (Image)m_buttonContents[2]); m_allUsedToAvailableButton.setImage((Image)m_buttonContents[3]); } else { m_selectionAvailableToUsedButton.setText( (String)m_buttonContents[0]); m_allAvailableToUsedButton.setText((String)m_buttonContents[1]); m_selectionUsedToAvailableButton.setText( (String)m_buttonContents[2]); m_allUsedToAvailableButton.setText((String)m_buttonContents[3]); } } /** * Sets the tooltips for the buttons. * @param buttonToolTips The texts of the toolTips of 5 buttons. */ private void setTooltips(String[] buttonToolTips) { m_selectionAvailableToUsedButton.setToolTipText(buttonToolTips[0]); m_allAvailableToUsedButton.setToolTipText(buttonToolTips[1]); m_selectionUsedToAvailableButton.setToolTipText(buttonToolTips[2]); m_allUsedToAvailableButton.setToolTipText(buttonToolTips[3]); m_swapButton.setToolTipText(buttonToolTips[4]); } /** * Sorts all items in the available tree alphabetically. */ private void sortTreeItems() { SortedMap<String, java.util.List<String>> sortedTree = new TreeMap<String, java.util.List<String>>(); Set<IChooserCompositeGuiObject> tempSet = new HashSet<IChooserCompositeGuiObject>(); boolean [] isItemExpanded = new boolean [m_availableTree.getItemCount()]; for (int i = 0; i < m_availableTree.getItemCount(); i++) { TreeItem parentItem = m_availableTree.getItem(i); isItemExpanded[i] = parentItem.getExpanded(); java.util.List<String> childNames = new ArrayList<String>(); for (TreeItem childItem : parentItem.getItems()) { childNames.add(childItem.getText()); IChooserCompositeGuiObject guiObj = m_treeItemsToGuiObjects.get(childItem); tempSet.add(guiObj); } Collections.sort(childNames); sortedTree.put(parentItem.getText(), childNames); } m_availableTree.removeAll(); m_treeItemsToGuiObjects.clear(); for (String parentName : sortedTree.keySet()) { TreeItem parentItem = new TreeItem(m_availableTree, SWT.NONE); parentItem.setText(parentName); for (String childName : sortedTree.get(parentName)) { TreeItem childItem = new TreeItem(parentItem, SWT.NONE); childItem.setText(childName); for (IChooserCompositeGuiObject obj : tempSet) { if (obj.getParent().equals(parentName) && obj.getTitle().equals(childName)) { m_treeItemsToGuiObjects.put(childItem, obj); break; } } } } for (int i = 0; i < m_availableTree.getItemCount(); i++) { if (isItemExpanded[i]) { TreeItem item = m_availableTree.getItem(i); Event expandEvent = new Event(); expandEvent.time = (int)System.currentTimeMillis(); expandEvent.type = SWT.Expand; expandEvent.widget = m_availableTree; expandEvent.item = item; m_availableTree.notifyListeners(SWT.Expand, expandEvent); item.setExpanded(true); m_availableTree.update(); } } } /** * Use the GUI object represented by the given item. * @param item the item that represents the object to use. */ private void useObject(TreeItem item) { IChooserCompositeGuiObject guiObj = m_treeItemsToGuiObjects.get(item); String displayString = guiObj.getDisplayString(); m_listItemsToGuiObjects.put(displayString, guiObj); m_usedList.add(displayString); item.dispose(); m_usedParents.add(guiObj.getParent()); sortListItems(); } /** * Make the given object available. * @param obj the object to make available. * @param swapping Flag to indicate that a 'swap' is currently in progress. */ private void makeObjectAvailable(IChooserCompositeGuiObject obj, boolean swapping) { m_listItemsToGuiObjects.remove(obj.getDisplayString()); if (!swapping) { m_usedParents.remove(obj.getParent()); } m_usedList.remove(obj.getDisplayString()); if (obj.getParent() != null) { TreeItem parentItem = null; String parentText = obj.getParent(); for (TreeItem parent : m_availableTree.getItems()) { if (parent.getText().equals(parentText)) { parentItem = parent; break; } } if (parentItem != null) { TreeItem item = new TreeItem(parentItem, SWT.NONE); item.setText(obj.getTitle()); m_treeItemsToGuiObjects.put(item, obj); sortTreeItems(); } } } /** * Informs all listeners that the list has been modified. */ private void fireListModified() { for (IUsedListModifiedListener listener : m_listeners) { listener.usedListModified(m_usedList.getItems()); } } /** * Creates a label for this composite. * @param text The label text to set. * @param parent The composite. * @return a new label */ private Label createLabel(Composite parent, String text) { Label label = new Label(parent, SWT.NONE); label.setText(text); GridData labelGrid = new GridData(GridData.BEGINNING, GridData.CENTER, false, false, 1, 1); label.setLayoutData(labelGrid); return label; } /** * Creates a new composite. * @param parent The parent composite. * @param numColumns the number of columns for this composite. * @param alignment The horizontalAlignment. * @param horizontalSpace The horizontalSpace. * @return The new composite. */ private Composite createComposite(Composite parent, int numColumns, int alignment, boolean horizontalSpace) { Composite composite = new Composite(parent, SWT.NONE); GridLayout compositeLayout = new GridLayout(); compositeLayout.numColumns = numColumns; compositeLayout.marginHeight = 0; compositeLayout.marginWidth = 0; composite.setLayout(compositeLayout); GridData compositeData = new GridData(); compositeData.horizontalAlignment = alignment; compositeData.grabExcessHorizontalSpace = horizontalSpace; composite.setLayoutData(compositeData); return composite; } /** * Creates a new multiline textfield * @param composite The parent composite. * @param labelText The text for the label. * @param lines The quantity of lines of this list. * @return The new multiline textfield. */ private List createListField(Composite composite, String labelText, int lines) { Composite leftComposite = createComposite(composite, NUM_COLUMNS_2, GridData.BEGINNING, false); Composite rightComposite = createComposite(composite, NUM_COLUMNS_1, GridData.FILL, true); Label label = createLabel(leftComposite, labelText); List listField = new List(rightComposite, LayoutUtil.MULTI_TEXT_STYLE); listField.setData(LABEL, label); GridData listGridData = new GridData(); listGridData.horizontalAlignment = GridData.FILL; listGridData.horizontalSpan = NUM_COLUMNS_1; listGridData.grabExcessHorizontalSpace = true; listGridData.heightHint = Dialog.convertHeightInCharsToPixels( LayoutUtil.getFontMetrics(listField), lines); listField.setLayoutData(listGridData); return listField; } /** * Creates a new tree * @param composite The parent composite. * @param labelText The text for the label. * @param lines The quantity of lines of this list. * @return The new tree. */ private Tree createAvailableTree(Composite composite, String labelText, int lines) { Composite leftComposite = createComposite(composite, NUM_COLUMNS_2, GridData.BEGINNING, false); Composite rightComposite = createComposite(composite, NUM_COLUMNS_1, GridData.FILL, true); Label label = createLabel(leftComposite, labelText); Tree tree = new Tree(rightComposite, LayoutUtil.MULTI_TEXT_STYLE); tree.setData(LABEL, label); GridData listGridData = new GridData(); listGridData.horizontalAlignment = GridData.FILL; listGridData.horizontalSpan = NUM_COLUMNS_1; listGridData.grabExcessHorizontalSpace = true; listGridData.heightHint = Dialog.convertHeightInCharsToPixels( LayoutUtil.getFontMetrics(tree), lines); tree.setLayoutData(listGridData); return tree; } /** * @param key The key string * @param value The value string * @return a <code>String</code> that represents the combination of * <code>key</code> and <code>value</code> */ protected String getDisplayValue(String key, String value) { String displayValue = key + DISPLAY_VALUE_START + value + DISPLAY_VALUE_END; return displayValue; } /** * Handles the selectionEvent of the selectionOneToTwoButton. */ protected void handleSelectionAvailableToUsedButtonEvent() { if (m_selectionAvailableToUsedButton.getEnabled()) { for (TreeItem item : m_availableTree.getSelection()) { useObject(item); } sortListItems(); fireListModified(); checkButtons(); } } /** * Handles the selectionEvent of the selectionTwoToOneButton. */ protected void handleSelectionUsedToAvailableButtonEvent() { if (m_selectionUsedToAvailableButton.getEnabled()) { String [] usedSelection = m_usedList.getSelection(); if (checkSelectionUsedToAvailable(usedSelection) == null) { for (String sel : m_usedList.getSelection()) { IChooserCompositeGuiObject guiObj = m_listItemsToGuiObjects.get(sel); makeObjectAvailable(guiObj, false); } checkButtons(); fireListModified(); } else { Dialog dialog = ErrorHandlingUtil.createMessageDialog( MessageIDs.I_COULD_NOT_REMOVE_REUSED_PROJECTS); dialog.getReturnCode(); } } } /** * @param selection The array of selected elements. * @return An error message <code>String</code> if not all elements can be removed. * Otherwise <code>null</code>. */ protected String checkSelectionUsedToAvailable(String [] selection) { return null; } /** * Handles the selectionEvent of the allOneToTwoButton. */ protected void handleAllAvailableToUsedButtonEvent() { for (TreeItem keyItem : m_availableTree.getItems()) { // Should only be enabled if each key item has one and only one child item useObject(keyItem.getItem(0)); } String[] selection = m_usedList.getItems(); Arrays.sort(selection); m_usedList.removeAll(); m_usedList.setItems(selection); fireListModified(); checkButtons(); } /** * Handles the selectionEvent of the allTwoToOneButton. */ protected void handleAllUsedToAvailableButtonEvent() { String [] usedItems = m_usedList.getItems(); String errorMsg = checkSelectionUsedToAvailable(usedItems); if (errorMsg == null) { for (String listItem : usedItems) { makeObjectAvailable( m_listItemsToGuiObjects.get(listItem), false); } m_usedList.removeAll(); fireListModified(); checkButtons(); } else { Dialog dialog = ErrorHandlingUtil.createMessageDialog( MessageIDs.I_COULD_NOT_REMOVE_REUSED_PROJECTS); dialog.getReturnCode(); } } /** * Handles the selectionEvent of the allTwoToOneButton. */ protected void handleSwapButtonEvent() { String usedSelection = m_usedList.getSelection()[0]; TreeItem availableSelection = m_availableTree.getSelection()[0]; useObject(availableSelection); makeObjectAvailable(m_listItemsToGuiObjects.get(usedSelection), true); fireListModified(); checkButtons(); } /** * Dis-/Enables the UP-/Down-/SwapButtons. */ public void checkButtons() { // allOneToTwo: no parent item has more than one element and at least one parent has one element boolean parentHasMoreThanOneElement = false; boolean parentsHaveNoElements = true; boolean enableAllOneToTwo = true; // allTwoToOne: list contains at least one element boolean listContainsAnElement, enableAllTwoToOne = false; // selectionOneToTwo: some selection and no selection is a parent item and none of selections' parents are already somehow in list // and no selected items share a parent boolean someSelectionOne, selectionContainsParentItem = false; boolean partOfSelectionIsInList = false; boolean availableSelectionShareParent = false; boolean enableSelectionOneToTwo = true; // selectionTwoToOne: some selection boolean someSelectionTwo = false; boolean enableSelectionTwoToOne = true; // swap: one element selected for each and both selections share a common parent boolean oneElementSelectedOne, oneElementSelectedTwo = false; boolean elementsShareCommonParent = false; boolean enableSwap = true; parentHasMoreThanOneElement = hasMultipleChildren(); enableAllOneToTwo = !parentHasMoreThanOneElement && !parentsHaveNoElements; listContainsAnElement = m_usedList.getItemCount() > 0; enableAllTwoToOne = listContainsAnElement; TreeItem[] treeSelection = m_availableTree.getSelection(); someSelectionOne = treeSelection.length > 0; Set<String> parentsOfSelection = new HashSet<String>(); for (TreeItem selItem : treeSelection) { if (selItem.getParentItem() == null) { selectionContainsParentItem = true; break; } String parent = m_treeItemsToGuiObjects.get(selItem).getParent(); if (!parentsOfSelection.contains(parent)) { parentsOfSelection.add(parent); } else { availableSelectionShareParent = true; break; } if (isChildItemUsed(selItem.getParentItem().getText())) { partOfSelectionIsInList = true; break; } } enableSelectionOneToTwo = someSelectionOne && !selectionContainsParentItem && !partOfSelectionIsInList && !availableSelectionShareParent; someSelectionTwo = m_usedList.getSelectionCount() > 0; enableSelectionTwoToOne = someSelectionTwo; oneElementSelectedOne = m_availableTree.getSelectionCount() == 1; oneElementSelectedTwo = m_usedList.getSelectionCount() == 1; if (oneElementSelectedOne && oneElementSelectedTwo) { elementsShareCommonParent = isCommonParentForElements(selectionContainsParentItem); } enableSwap = elementsShareCommonParent; enableSelectionOneToTwoButton(enableSelectionOneToTwo); enableSelectionTwoToOneButton(enableSelectionTwoToOne); enableAllOneToTwoButton(enableAllOneToTwo); enableAllTwoToOneButton(enableAllTwoToOne); enableSwapButton(enableSwap); } /** * @param selectionContainsParentItem does the current selection contain a * parent item * @return <code>true</code> if the selected elements have the same parent. * Otherwise, <code>false</code>. */ private boolean isCommonParentForElements( boolean selectionContainsParentItem) { boolean elementsShareCommonParent; IChooserCompositeGuiObject availableObj = m_treeItemsToGuiObjects.get(m_availableTree.getSelection()[0]); IChooserCompositeGuiObject usedObj = m_listItemsToGuiObjects.get(m_usedList.getSelection()[0]); elementsShareCommonParent = !selectionContainsParentItem && availableObj != null && usedObj != null && availableObj.getParent().equals(usedObj.getParent()); return elementsShareCommonParent; } /** * @return <code>true</code> if any GUI objects share a parent. Otherwise, * <code>false</code>. */ private boolean hasMultipleChildren() { Set<IChooserCompositeGuiObject> guiObjects = new HashSet<IChooserCompositeGuiObject>( m_treeItemsToGuiObjects.values()); guiObjects.addAll(m_listItemsToGuiObjects.values()); Set<String> parentSet = new HashSet<String>(); for (IChooserCompositeGuiObject obj : guiObjects) { String parentString = obj.getParent(); if (!parentSet.contains(parentString)) { parentSet.add(parentString); } else { return true; } } return false; } /** * @param text the parent text. * @return <code>true</code> if a used GUI object has the given text as * parent. Otherwise, <code>false</code>. */ private boolean isChildItemUsed(String text) { return m_usedParents.contains(text); } /** * * @param enable <code>true</code> if the button should be enabled. <code>false</code> * if the button should be disabled. */ private void enableSelectionOneToTwoButton(boolean enable) { if (enable) { m_selectionAvailableToUsedButton.setImage( (Image)m_buttonContents[0]); m_selectionAvailableToUsedButton.setEnabled(true); } else { m_selectionAvailableToUsedButton.setImage( (Image)m_disabledButtonContents[0]); m_selectionAvailableToUsedButton.setEnabled(false); } } /** * * @param enable <code>true</code> if the button should be enabled. <code>false</code> * if the button should be disabled. */ private void enableSelectionTwoToOneButton(boolean enable) { if (enable) { m_selectionUsedToAvailableButton.setImage( (Image)m_buttonContents[2]); m_selectionUsedToAvailableButton.setEnabled(true); } else { m_selectionUsedToAvailableButton.setImage( (Image)m_disabledButtonContents[2]); m_selectionUsedToAvailableButton.setEnabled(false); } } /** * * @param enable <code>true</code> if the button should be enabled. <code>false</code> * if the button should be disabled. */ private void enableAllOneToTwoButton(boolean enable) { if (enable) { m_allAvailableToUsedButton.setImage( (Image)m_buttonContents[1]); m_allAvailableToUsedButton.setEnabled(true); } else { m_allAvailableToUsedButton.setImage( (Image)m_disabledButtonContents[1]); m_allAvailableToUsedButton.setEnabled(false); } } /** * * @param enable <code>true</code> if the button should be enabled. <code>false</code> * if the button should be disabled. */ private void enableAllTwoToOneButton(boolean enable) { if (enable) { m_allUsedToAvailableButton.setImage( (Image)m_buttonContents[3]); m_allUsedToAvailableButton.setEnabled(true); } else { m_allUsedToAvailableButton.setImage( (Image)m_disabledButtonContents[3]); m_allUsedToAvailableButton.setEnabled(false); } } /** * * @param enable <code>true</code> if the button should be enabled. <code>false</code> * if the button should be disabled. */ private void enableSwapButton(boolean enable) { if (enable) { m_swapButton.setImage( (Image)m_buttonContents[4]); m_swapButton.setEnabled(true); } else { m_swapButton.setImage( (Image)m_disabledButtonContents[4]); m_swapButton.setEnabled(false); } } /** * Disposes the SWT widgets. */ public void dispose() { removeListener(); super.dispose(); } /** * * @param listener The listener to add. */ public void addListModifiedListener(IUsedListModifiedListener listener) { m_listeners.add(listener); } /** * * @param listener The listener to remove. */ public void removeListModifiedListener(IUsedListModifiedListener listener) { m_listeners.remove(listener); } /** * Adds necessary listeners. */ private void addListener() { m_selectionUsedToAvailableButton.addSelectionListener( m_selectionListener); m_selectionAvailableToUsedButton.addSelectionListener( m_selectionListener); m_allAvailableToUsedButton.addSelectionListener(m_selectionListener); m_allUsedToAvailableButton.addSelectionListener(m_selectionListener); m_swapButton.addSelectionListener(m_selectionListener); } /** * Removes all listeners. */ private void removeListener() { m_selectionAvailableToUsedButton.removeSelectionListener( m_selectionListener); m_selectionUsedToAvailableButton.removeSelectionListener( m_selectionListener); m_allAvailableToUsedButton.removeSelectionListener(m_selectionListener); m_allUsedToAvailableButton.removeSelectionListener(m_selectionListener); m_availableTree.removeSelectionListener(m_selectionListener); m_usedList.removeSelectionListener(m_selectionListener); } /** * The object from the model that can be chosen/unchosen. * * @author BREDEX GmbH * @created Aug 16, 2007 */ public interface IChooserCompositeGuiObject { /** * Indicates the name of the tree item that will be the receiver's * parent when it is available. * * @return the name of the parent when the receiver is available. */ public String getParent(); /** * Indicates the displayed title of the object when it is available. * * @return the title that represents the receiver when it is available. */ public String getTitle(); /** * Indicates the displayed title of the object when it is used. * * @return the title that represents the receiver when it is used. */ public String getDisplayString(); /** * @return the model object represented by the receiver. */ public Object getModelObject(); } /** * This private inner class contains a new SelectionListener. * @author BREDEX GmbH * @created 10.02.2005 */ @SuppressWarnings("synthetic-access") private class WidgetSelectionListener implements SelectionListener { /** * {@inheritDoc} */ public void widgetSelected(SelectionEvent e) { Object o = e.getSource(); if (o.equals(m_selectionAvailableToUsedButton)) { handleSelectionAvailableToUsedButtonEvent(); return; } else if (o.equals(m_selectionUsedToAvailableButton)) { handleSelectionUsedToAvailableButtonEvent(); return; } else if (o.equals(m_allAvailableToUsedButton)) { handleAllAvailableToUsedButtonEvent(); return; } else if (o.equals(m_allUsedToAvailableButton)) { handleAllUsedToAvailableButtonEvent(); return; } else if (o.equals(m_swapButton)) { handleSwapButtonEvent(); return; } else if (o.equals(m_availableTree)) { checkButtons(); return; } else if (o.equals(m_usedList)) { checkButtons(); return; } Assert.notReached(Messages.EventActivatedByUnknownWidget + StringConstants.DOT); } /** * {@inheritDoc} */ public void widgetDefaultSelected(SelectionEvent e) { Object o = e.getSource(); if (o.equals(m_availableTree)) { handleSelectionAvailableToUsedButtonEvent(); return; } else if (o.equals(m_usedList)) { handleSelectionUsedToAvailableButtonEvent(); return; } Assert.notReached(Messages.EventActivatedByUnknownWidget + StringConstants.DOT); } } /** * @return Returns the allOneToTwoButton. */ public Button getAllAvailableToUsedButton() { return m_allAvailableToUsedButton; } /** * @return Returns the allTwoToOneButton. */ public Button getAllUsedToAvailableButton() { return m_allUsedToAvailableButton; } /** * @return Returns the listOne. */ public Tree getAvailableTree() { return m_availableTree; } /** * @return Returns the listTwo. */ public List getUsedList() { return m_usedList; } /** * @return Returns the selectionOneToTwoButton. */ public Button getSelectionAvailableToUsedButton() { return m_selectionAvailableToUsedButton; } /** * @return Returns the selectionTwoToOneButton. */ public Button getSelectionUsedToAvailableButton() { return m_selectionUsedToAvailableButton; } /** * @return Returns the label of ListBox one. */ public Label getListOneLabel() { return (Label)m_availableTree.getData(LABEL); } /** * @return Returns the label of ListBox two. */ public Label getListTwoLabel() { return (Label)m_usedList.getData(LABEL); } }