| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.views.properties; |
| |
| import java.text.Collator; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.jface.action.IStatusLineManager; |
| import org.eclipse.jface.util.ListenerList; |
| import org.eclipse.jface.viewers.CellEditor; |
| import org.eclipse.jface.viewers.ICellEditorListener; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.TreeEditor; |
| import org.eclipse.swt.events.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.MouseAdapter; |
| import org.eclipse.swt.events.MouseEvent; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.TreeEvent; |
| import org.eclipse.swt.events.TreeListener; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeColumn; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.swt.widgets.Widget; |
| |
| /** |
| * The PropertySheetViewer displays the properties of objects. The model for the |
| * viewer consists of a hierarchy of <code>IPropertySheetEntry</code>. |
| * <p> |
| * This viewer also supports the optional catogorization of the first level |
| * <code>IPropertySheetEntry</code> s by using instances of |
| * <code>PropertySheetCategory</code>. |
| * |
| */ |
| /* package */ |
| class PropertySheetViewer extends Viewer { |
| // The input objects for the viewer |
| private Object[] input; |
| |
| // The root entry of the viewer |
| private IPropertySheetEntry rootEntry; |
| |
| // The current categories |
| private PropertySheetCategory[] categories; |
| |
| // SWT widgets |
| private Tree tree; |
| |
| private TreeEditor treeEditor; |
| |
| private static String[] columnLabels = { |
| PropertiesMessages.getString("PropertyViewer.property"), PropertiesMessages.getString("PropertyViewer.value") }; //$NON-NLS-2$ //$NON-NLS-1$ |
| |
| private static String MISCELLANEOUS_CATEGORY_NAME = PropertiesMessages |
| .getString("PropertyViewer.misc"); //$NON-NLS-1$ |
| |
| // Cell editor support. |
| private int columnToEdit = 1; |
| |
| private CellEditor cellEditor; |
| |
| private IPropertySheetEntryListener entryListener; |
| |
| private ICellEditorListener editorListener; |
| |
| // Flag to indicate if categories (if any) should be shown |
| private boolean isShowingCategories = true; |
| |
| // Flag to indicate expert properties should be shown |
| private boolean isShowingExpertProperties = false; |
| |
| // The status line manager for showing messages |
| private IStatusLineManager statusLineManager; |
| |
| // Cell editor activation listeners |
| private ListenerList activationListeners = new ListenerList(3); |
| |
| /** |
| * Creates a property sheet viewer on a newly-created tree control |
| * under the given parent. The viewer has no input, and no root entry. |
| * |
| * @param parent |
| * the parent control |
| */ |
| public PropertySheetViewer(Composite parent) { |
| tree = new Tree(parent, SWT.FULL_SELECTION | SWT.SINGLE |
| | SWT.HIDE_SELECTION); |
| |
| // configure the widget |
| tree.setLinesVisible(true); |
| tree.setHeaderVisible(true); |
| |
| // configure the columns |
| addColumns(); |
| |
| // add our listeners to the widget |
| hookControl(); |
| |
| // create a new tree editor |
| treeEditor = new TreeEditor(tree); |
| |
| // create the entry and editor listener |
| createEntryListener(); |
| createEditorListener(); |
| } |
| |
| /** |
| * Activate a cell editor for the given selected tree item. |
| * |
| * @param item |
| * the selected tree item |
| */ |
| private void activateCellEditor(TreeItem item) { |
| // ensure the cell editor is visible |
| tree.showSelection(); |
| |
| // Get the entry for this item |
| IPropertySheetEntry activeEntry = (IPropertySheetEntry) item.getData(); |
| |
| // Get the cell editor for the entry. |
| // Note that the editor parent must be the Tree control |
| cellEditor = activeEntry.getEditor(tree); |
| |
| if (cellEditor == null) |
| // unable to create the editor |
| return; |
| |
| // activate the cell editor |
| cellEditor.activate(); |
| |
| // if the cell editor has no control we can stop now |
| Control control = cellEditor.getControl(); |
| if (control == null) { |
| cellEditor.deactivate(); |
| cellEditor = null; |
| return; |
| } |
| |
| // add our editor listener |
| cellEditor.addListener(editorListener); |
| |
| // set the layout of the tree editor to match the cell editor |
| CellEditor.LayoutData layout = cellEditor.getLayoutData(); |
| treeEditor.horizontalAlignment = layout.horizontalAlignment; |
| treeEditor.grabHorizontal = layout.grabHorizontal; |
| treeEditor.minimumWidth = layout.minimumWidth; |
| treeEditor.setEditor(control, item, columnToEdit); |
| |
| // set the error text from the cel editor |
| setErrorMessage(cellEditor.getErrorMessage()); |
| |
| // give focus to the cell editor |
| cellEditor.setFocus(); |
| |
| // notify of activation |
| fireCellEditorActivated(cellEditor); |
| } |
| |
| /** |
| * Adds a cell editor activation listener. Has no effect if an identical |
| * activation listener is already registered. |
| * |
| * @param listener |
| * a cell editor activation listener |
| */ |
| /* package */ |
| void addActivationListener(ICellEditorActivationListener listener) { |
| activationListeners.add(listener); |
| } |
| |
| /** |
| * Add columns to the tree and set up the layout manager accordingly. |
| */ |
| private void addColumns() { |
| |
| // create the columns |
| TreeColumn[] columns = tree.getColumns(); |
| for (int i = 0; i < columnLabels.length; i++) { |
| String string = columnLabels[i]; |
| if (string != null) { |
| TreeColumn column; |
| if (i < columns.length) |
| column = columns[i]; |
| else |
| column = new TreeColumn(tree, 0); |
| column.setText(string); |
| } |
| } |
| |
| tree.getParent().addControlListener(new ControlAdapter() { |
| public void controlResized(ControlEvent e) { |
| updateColumnSizes(); |
| } |
| }); |
| } |
| |
| private void updateColumnSizes() { |
| // the parent has not yet layed out the tree; force the tree's size to be updated earlier |
| tree.getParent().layout(); |
| |
| int w = tree.getSize().x; |
| TreeColumn[] columns = tree.getColumns(); |
| columns[0].setWidth(w * 40 / 100); |
| columns[1].setWidth(w - columns[0].getWidth() - 4); |
| } |
| |
| /** |
| * Asks the entry currently being edited to apply its current cell editor |
| * value. |
| */ |
| private void applyEditorValue() { |
| TreeItem treeItem = treeEditor.getItem(); |
| // treeItem can be null when view is opened |
| if (treeItem == null || treeItem.isDisposed()) |
| return; |
| IPropertySheetEntry entry = (IPropertySheetEntry) treeItem.getData(); |
| entry.applyEditorValue(); |
| } |
| |
| /** |
| * Creates the child items for the given widget (item or tree). This |
| * method is called when the item is expanded for the first time or when an |
| * item is assigned as the root of the tree. |
| * @param widget TreeItem or Tree to create the children in. |
| */ |
| private void createChildren(Widget widget) { |
| // get the current child items |
| TreeItem[] childItems = getChildItems(widget); |
| |
| if (childItems.length > 0) { |
| Object data = childItems[0].getData(); |
| if (data != null) |
| // children already there! |
| return; |
| // remove the dummy |
| childItems[0].dispose(); |
| } |
| |
| // get the children and create their tree items |
| Object node = widget.getData(); |
| List children = getChildren(node); |
| if (children.isEmpty()) |
| // this item does't actually have any children |
| return; |
| for (int i = 0; i < children.size(); i++) { |
| // create a new tree item |
| createItem(children.get(i), widget, i); |
| } |
| } |
| |
| /** |
| * Creates a new cell editor listener. |
| */ |
| private void createEditorListener() { |
| editorListener = new ICellEditorListener() { |
| public void cancelEditor() { |
| deactivateCellEditor(); |
| } |
| |
| public void editorValueChanged(boolean oldValidState, |
| boolean newValidState) { |
| //Do nothing |
| } |
| |
| public void applyEditorValue() { |
| //Do nothing |
| } |
| }; |
| } |
| |
| /** |
| * Creates a new property sheet entry listener. |
| */ |
| private void createEntryListener() { |
| entryListener = new IPropertySheetEntryListener() { |
| public void childEntriesChanged(IPropertySheetEntry entry) { |
| // update the children of the given entry |
| if (entry == rootEntry) |
| updateChildrenOf(entry, tree); |
| else { |
| TreeItem item = findItem(entry); |
| if (item != null) |
| updateChildrenOf(entry, item); |
| } |
| } |
| |
| public void valueChanged(IPropertySheetEntry entry) { |
| // update the given entry |
| TreeItem item = findItem(entry); |
| if (item != null) |
| updateEntry(entry, item); |
| } |
| |
| public void errorMessageChanged(IPropertySheetEntry entry) { |
| // update the error message |
| setErrorMessage(entry.getErrorText()); |
| } |
| }; |
| } |
| |
| /** |
| * Creates a new tree item, sets the given entry or category (node)in |
| * its user data field, and adds a listener to the node if it is an entry. |
| * |
| * @param node |
| * the entry or category associated with this item |
| * @param parent |
| * the parent widget |
| * @param index |
| * indicates the position to insert the item into its parent |
| */ |
| private void createItem(Object node, Widget parent, int index) { |
| // create the item |
| TreeItem item; |
| if (parent instanceof TreeItem) |
| item = new TreeItem((TreeItem) parent, SWT.NONE, index); |
| else |
| item = new TreeItem((Tree) parent, SWT.NONE, index); |
| |
| // set the user data field |
| item.setData(node); |
| |
| // add our listener |
| if (node instanceof IPropertySheetEntry) |
| ((IPropertySheetEntry) node) |
| .addPropertySheetEntryListener(entryListener); |
| |
| // update the visual presentation |
| if (node instanceof IPropertySheetEntry) |
| updateEntry((IPropertySheetEntry) node, item); |
| else |
| updateCategory((PropertySheetCategory) node, item); |
| } |
| |
| /** |
| * Deactivate the currently active cell editor. |
| */ |
| /* package */ |
| void deactivateCellEditor() { |
| treeEditor.setEditor(null, null, columnToEdit); |
| if (cellEditor != null) { |
| cellEditor.deactivate(); |
| fireCellEditorDeactivated(cellEditor); |
| cellEditor.removeListener(editorListener); |
| cellEditor = null; |
| } |
| // clear any error message from the editor |
| setErrorMessage(null); |
| } |
| |
| /** |
| * Sends out a selection changed event for the entry tree to all registered |
| * listeners. |
| */ |
| private void entrySelectionChanged() { |
| SelectionChangedEvent changeEvent = new SelectionChangedEvent(this, |
| getSelection()); |
| fireSelectionChanged(changeEvent); |
| } |
| |
| /** |
| * Return a tree item in the property sheet that has the same entry in |
| * its user data field as the supplied entry. Return <code>null</code> if |
| * there is no such item. |
| * |
| * @param entry |
| * the entry to serach for |
| * @return the TreeItem for the entry or <code>null</code> if |
| * there isn't one. |
| */ |
| private TreeItem findItem(IPropertySheetEntry entry) { |
| // Iterate through treeItems to find item |
| TreeItem[] items = tree.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| TreeItem item = items[i]; |
| TreeItem findItem = findItem(entry, item); |
| if (findItem != null) |
| return findItem; |
| } |
| return null; |
| } |
| |
| /** |
| * Return a tree item in the property sheet that has the same entry in |
| * its user data field as the supplied entry. Return <code>null</code> if |
| * there is no such item. |
| * |
| * @param entry |
| * the entry to search for |
| * @param item |
| * the item look in |
| * @return the TreeItem for the entry or <code>null</code> if |
| * there isn't one. |
| */ |
| private TreeItem findItem(IPropertySheetEntry entry, TreeItem item) { |
| // compare with current item |
| if (entry == item.getData()) |
| return item; |
| |
| // recurse over children |
| TreeItem[] items = item.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| TreeItem childItem = items[i]; |
| TreeItem findItem = findItem(entry, childItem); |
| if (findItem != null) |
| return findItem; |
| } |
| return null; |
| } |
| |
| /** |
| * Notifies all registered cell editor activation listeners of a cell editor |
| * activation. |
| * |
| * @param activatedCellEditor |
| * the activated cell editor |
| */ |
| private void fireCellEditorActivated(CellEditor activatedCellEditor) { |
| Object[] listeners = activationListeners.getListeners(); |
| for (int i = 0; i < listeners.length; ++i) { |
| ((ICellEditorActivationListener) listeners[i]) |
| .cellEditorActivated(activatedCellEditor); |
| } |
| } |
| |
| /** |
| * Notifies all registered cell editor activation listeners of a cell editor |
| * deactivation. |
| * |
| * @param activatedCellEditor |
| * the deactivated cell editor |
| */ |
| private void fireCellEditorDeactivated(CellEditor activatedCellEditor) { |
| Object[] listeners = activationListeners.getListeners(); |
| for (int i = 0; i < listeners.length; ++i) { |
| ((ICellEditorActivationListener) listeners[i]) |
| .cellEditorDeactivated(activatedCellEditor); |
| } |
| } |
| |
| /** |
| * Returns the active cell editor of this property sheet viewer or |
| * <code>null</code> if no cell editor is active. |
| * |
| * @return the active cell editor |
| */ |
| public CellEditor getActiveCellEditor() { |
| return cellEditor; |
| } |
| |
| private TreeItem[] getChildItems(Widget widget) { |
| if (widget instanceof Tree) { |
| return ((Tree) widget).getItems(); |
| } |
| else if (widget instanceof TreeItem) { |
| return ((TreeItem) widget).getItems(); |
| } |
| // shouldn't happen |
| return new TreeItem[0]; |
| } |
| |
| /** |
| * Returns the children of the given category or entry |
| * |
| * @param node a category or entry |
| * @return the children of the given category or entry |
| * (element type <code>IPropertySheetEntry</code> or |
| * <code>PropertySheetCategory</code>) |
| */ |
| private List getChildren(Object node) { |
| // cast the entry or category |
| IPropertySheetEntry entry = null; |
| PropertySheetCategory category = null; |
| if (node instanceof IPropertySheetEntry) |
| entry = (IPropertySheetEntry) node; |
| else |
| category = (PropertySheetCategory) node; |
| |
| // get the child entries or categories |
| List children; |
| if (category == null) |
| children = getChildren(entry); |
| else |
| children = getChildren(category); |
| |
| return children; |
| } |
| |
| /** |
| * Returns the child entries of the given entry |
| * @param entry The entry to search |
| * |
| * @return the children of the given entry (element type |
| * <code>IPropertySheetEntry</code>) |
| */ |
| private List getChildren(IPropertySheetEntry entry) { |
| // if the entry is the root and we are showing categories, and we have |
| // more than the |
| // defualt category, return the categories |
| if (entry == rootEntry && isShowingCategories) { |
| if (categories.length > 1 |
| || (categories.length == 1 && !categories[0] |
| .getCategoryName().equals( |
| MISCELLANEOUS_CATEGORY_NAME))) |
| return Arrays.asList(categories); |
| } |
| |
| // return the filtered child entries |
| return getFilteredEntries(entry.getChildEntries()); |
| } |
| |
| /** |
| * Returns the child entries of the given category |
| * |
| * @param category The category to search |
| * |
| * @return the children of the given category (element type |
| * <code>IPropertySheetEntry</code>) |
| */ |
| private List getChildren(PropertySheetCategory category) { |
| return getFilteredEntries(category.getChildEntries()); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared on Viewer. |
| */ |
| public Control getControl() { |
| return tree; |
| } |
| |
| /** |
| * Returns the entries which match the current filter. |
| * |
| * @param entries the entries to filter |
| * @return the entries which match the current filter |
| * (element type <code>IPropertySheetEntry</code>) |
| */ |
| private List getFilteredEntries(IPropertySheetEntry[] entries) { |
| // if no filter just return all entries |
| if (isShowingExpertProperties) |
| return Arrays.asList(entries); |
| |
| // check each entry for the filter |
| List filteredEntries = new ArrayList(entries.length); |
| for (int i = 0; i < entries.length; i++) { |
| String[] filters = entries[i].getFilters(); |
| boolean expert = false; |
| if (filters != null) { |
| for (int j = 0; j < filters.length; j++) { |
| if (filters[j].equals(IPropertySheetEntry.FILTER_ID_EXPERT)) { |
| expert = true; |
| break; |
| } |
| } |
| } |
| if (!expert) |
| filteredEntries.add(entries[i]); |
| } |
| return filteredEntries; |
| } |
| |
| /** |
| * The <code>PropertySheetViewer</code> implementation of this method |
| * declared on <code>IInputProvider</code> returns the objects for which |
| * the viewer is currently showing properties. It returns an |
| * <code>Object[]</code> or <code>null</code>. |
| */ |
| public Object getInput() { |
| return input; |
| } |
| |
| /** |
| * Returns the root entry for this property sheet viewer. The root entry is |
| * not visible in the viewer. |
| * |
| * @return the root entry or <code>null</code>. |
| */ |
| public IPropertySheetEntry getRootEntry() { |
| return rootEntry; |
| } |
| |
| /** |
| * The <code>PropertySheetViewer</code> implementation of this |
| * <code>ISelectionProvider</code> method returns the result as a |
| * <code>StructuredSelection</code>. |
| * <p> |
| * Note that this method only includes <code>IPropertySheetEntry</code> in |
| * the selection (no categories). |
| * </p> |
| */ |
| public ISelection getSelection() { |
| if (tree.getSelectionCount() == 0) |
| return StructuredSelection.EMPTY; |
| TreeItem[] sel = tree.getSelection(); |
| List entries = new ArrayList(sel.length); |
| for (int i = 0; i < sel.length; i++) { |
| TreeItem ti = sel[i]; |
| Object data = ti.getData(); |
| if (data instanceof IPropertySheetEntry) |
| entries.add(data); |
| } |
| return new StructuredSelection(entries); |
| } |
| |
| /** |
| * Selection in the viewer occurred. Check if there is an active cell |
| * editor. If yes, deactivate it and check if a new cell editor must be |
| * activated. |
| * |
| * @param selection |
| * the TreeItem that is selected |
| */ |
| private void handleSelect(TreeItem selection) { |
| // deactivate the current cell editor |
| if (cellEditor != null) { |
| applyEditorValue(); |
| deactivateCellEditor(); |
| } |
| |
| // get the new selection |
| TreeItem[] sel = new TreeItem[] { selection }; |
| if (sel.length == 0) { |
| setMessage(null); |
| setErrorMessage(null); |
| } else { |
| Object object = sel[0].getData(); // assume single selection |
| if (object instanceof IPropertySheetEntry) { |
| // get the entry for this item |
| IPropertySheetEntry activeEntry = (IPropertySheetEntry) object; |
| |
| // display the description for the item |
| setMessage(activeEntry.getDescription()); |
| |
| // activate a cell editor on the selection |
| activateCellEditor(sel[0]); |
| } |
| } |
| entrySelectionChanged(); |
| } |
| |
| /** |
| * The expand icon for a node in this viewer has been selected to collapse a |
| * subtree. Deactivate the cell editor |
| * |
| * @param event |
| * the SWT tree event |
| */ |
| private void handleTreeCollapse(TreeEvent event) { |
| if (cellEditor != null) { |
| applyEditorValue(); |
| deactivateCellEditor(); |
| } |
| } |
| |
| /** |
| * The expand icon for a node in this viewer has been selected to expand the |
| * subtree. Create the children 1 level deep. |
| * <p> |
| * Note that we use a "dummy" item (no user data) to show a "+" icon beside |
| * an item which has children before the item is expanded now that it is |
| * being expanded we have to create the real child items |
| * </p> |
| * |
| * @param event |
| * the SWT tree event |
| */ |
| private void handleTreeExpand(TreeEvent event) { |
| createChildren(event.item); |
| } |
| |
| /** |
| * Hides the categories. |
| */ |
| /* package */ |
| void hideCategories() { |
| isShowingCategories = false; |
| categories = null; |
| refresh(); |
| } |
| |
| /** |
| * Hides the expert properties. |
| */ |
| /* package */ |
| void hideExpert() { |
| isShowingExpertProperties = false; |
| refresh(); |
| } |
| |
| /** |
| * Establish this viewer as a listener on the control |
| */ |
| private void hookControl() { |
| // Handle selections in the Tree |
| // Part1: Double click only (allow traversal via keyboard without |
| // activation |
| tree.addSelectionListener(new SelectionAdapter() { |
| public void widgetDefaultSelected(SelectionEvent e) { |
| handleSelect((TreeItem) e.item); |
| } |
| }); |
| // Part2: handle single click activation of cell editor |
| tree.addMouseListener(new MouseAdapter() { |
| public void mouseDown(MouseEvent event) { |
| // only activate if there is a cell editor |
| Point pt = new Point(event.x, event.y); |
| TreeItem item = tree.getItem(pt); |
| if (item != null) { |
| handleSelect(item); |
| } |
| } |
| }); |
| |
| // Add a tree listener to expand and collapse which |
| // allows for lazy creation of children |
| tree.addTreeListener(new TreeListener() { |
| public void treeExpanded(final TreeEvent event) { |
| handleTreeExpand(event); |
| } |
| |
| public void treeCollapsed(final TreeEvent event) { |
| handleTreeCollapse(event); |
| } |
| }); |
| |
| // Refresh the tree when F5 pressed |
| tree.addKeyListener(new KeyAdapter() { |
| public void keyReleased(KeyEvent e) { |
| if (e.character == SWT.ESC) |
| deactivateCellEditor(); |
| else if (e.keyCode == SWT.F5) |
| // The following will simulate a reselect |
| setInput(getInput()); |
| } |
| }); |
| } |
| |
| /** |
| * Updates all of the items in the tree. |
| * <p> |
| * Note that this means ensuring that the tree items reflect the state of |
| * the model (entry tree) it does not mean telling the model to update |
| * itself. |
| * </p> |
| */ |
| public void refresh() { |
| if (rootEntry != null) { |
| updateChildrenOf(rootEntry, tree); |
| } |
| } |
| |
| /** |
| * Removes the given cell editor activation listener from this viewer. Has |
| * no effect if an identical activation listener is not registered. |
| * |
| * @param listener |
| * a cell editor activation listener |
| */ |
| /* package */ |
| void removeActivationListener(ICellEditorActivationListener listener) { |
| activationListeners.remove(listener); |
| } |
| |
| /** |
| * Remove the given item from the tree. Remove our listener if the |
| * item's user data is a an entry then set the user data to null |
| * |
| * @param item |
| * the item to remove |
| */ |
| private void removeItem(TreeItem item) { |
| Object data = item.getData(); |
| if (data instanceof IPropertySheetEntry) |
| ((IPropertySheetEntry) data) |
| .removePropertySheetEntryListener(entryListener); |
| item.setData(null); |
| item.dispose(); |
| } |
| |
| /** |
| * Reset the selected properties to their default values. |
| */ |
| public void resetProperties() { |
| // Determine the selection |
| IStructuredSelection selection = (IStructuredSelection) getSelection(); |
| |
| // Iterate over entries and reset them |
| Iterator itr = selection.iterator(); |
| while (itr.hasNext()) |
| ((IPropertySheetEntry) itr.next()).resetPropertyValue(); |
| } |
| |
| /** |
| * Sets the error message to be displayed in the status line. |
| * |
| * @param errorMessage |
| * the message to be displayed, or <code>null</code> |
| */ |
| private void setErrorMessage(String errorMessage) { |
| // show the error message |
| if (statusLineManager != null) |
| statusLineManager.setErrorMessage(errorMessage); |
| } |
| |
| /** |
| * The <code>PropertySheetViewer</code> implementation of this method |
| * declared on <code>Viewer</code> method sets the objects for which the |
| * viewer is currently showing properties. |
| * <p> |
| * The input must be an <code>Object[]</code> or <code>null</code>. |
| * </p> |
| * |
| * @param newInput |
| * the input of this viewer, or <code>null</code> if none |
| */ |
| public void setInput(Object newInput) { |
| // need to save any changed value when user clicks elsewhere |
| applyEditorValue(); |
| // deactivate our cell editor |
| deactivateCellEditor(); |
| |
| // set the new input to the root entry |
| input = (Object[]) newInput; |
| if (input == null) |
| input = new Object[0]; |
| |
| if (rootEntry != null) { |
| rootEntry.setValues(input); |
| // ensure first level children are visible |
| updateChildrenOf(rootEntry, tree); |
| } |
| } |
| |
| /** |
| * Sets the message to be displayed in the status line. This message is |
| * displayed when there is no error message. |
| * |
| * @param message |
| * the message to be displayed, or <code>null</code> |
| */ |
| private void setMessage(String message) { |
| // show the message |
| if (statusLineManager != null) |
| statusLineManager.setMessage(message); |
| } |
| |
| /** |
| * Sets the root entry for this property sheet viewer. The root entry is not |
| * visible in the viewer. |
| * |
| * @param root |
| * the root entry |
| */ |
| public void setRootEntry(IPropertySheetEntry root) { |
| // If we have a root entry, remove our entry listener |
| if (rootEntry != null) |
| rootEntry.removePropertySheetEntryListener(entryListener); |
| |
| rootEntry = root; |
| |
| // Set the root as user data on the tree |
| tree.setData(rootEntry); |
| |
| // Add an IPropertySheetEntryListener to listen for entry change |
| // notifications |
| rootEntry.addPropertySheetEntryListener(entryListener); |
| |
| // Pass our input to the root, this will trigger entry change |
| // callbacks to update this viewer |
| setInput(input); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean) |
| */ |
| public void setSelection(ISelection selection, boolean reveal) { |
| //Do nothing by default |
| } |
| |
| /** |
| * Sets the status line manager this view will use to show messages. |
| * |
| * @param manager |
| * the status line manager |
| */ |
| public void setStatusLineManager(IStatusLineManager manager) { |
| statusLineManager = manager; |
| } |
| |
| /** |
| * Shows the categories. |
| */ |
| /* package */ |
| void showCategories() { |
| isShowingCategories = true; |
| refresh(); |
| } |
| |
| /** |
| * Shows the expert properties. |
| */ |
| /* package */ |
| void showExpert() { |
| isShowingExpertProperties = true; |
| refresh(); |
| } |
| |
| /** |
| * Updates the categories. Reuses old categories if possible. |
| */ |
| private void updateCategories() { |
| // lazy initialize |
| if (categories == null) |
| categories = new PropertySheetCategory[0]; |
| |
| // get all the filtered child entries of the root |
| List childEntries = getFilteredEntries(rootEntry.getChildEntries()); |
| |
| // if the list is empty, just set an empty categories array |
| if (childEntries.size() == 0) { |
| categories = new PropertySheetCategory[0]; |
| return; |
| } |
| |
| // cache old categories by their descriptor name |
| Map categoryCache = new HashMap(categories.length * 2 + 1); |
| for (int i = 0; i < categories.length; i++) { |
| categories[i].removeAllEntries(); |
| categoryCache.put(categories[i].getCategoryName(), categories[i]); |
| } |
| |
| // create a list of categories to get rid of |
| List categoriesToRemove = new ArrayList(Arrays.asList(categories)); |
| |
| // Determine the categories |
| PropertySheetCategory misc = (PropertySheetCategory) categoryCache |
| .get(MISCELLANEOUS_CATEGORY_NAME); |
| if (misc == null) |
| misc = new PropertySheetCategory(MISCELLANEOUS_CATEGORY_NAME); |
| boolean addMisc = false; |
| |
| for (int i = 0; i < childEntries.size(); i++) { |
| IPropertySheetEntry childEntry = (IPropertySheetEntry) childEntries |
| .get(i); |
| String categoryName = childEntry.getCategory(); |
| if (categoryName == null) { |
| misc.addEntry(childEntry); |
| addMisc = true; |
| categoriesToRemove.remove(misc); |
| } else { |
| PropertySheetCategory category = (PropertySheetCategory) categoryCache |
| .get(categoryName); |
| if (category == null) { |
| category = new PropertySheetCategory(categoryName); |
| categoryCache.put(categoryName, category); |
| } else { |
| categoriesToRemove.remove(category); |
| } |
| category.addEntry(childEntry); |
| } |
| } |
| |
| // Add the PSE_MISC category if it has entries |
| if (addMisc) |
| categoryCache.put(MISCELLANEOUS_CATEGORY_NAME, misc); |
| |
| // Sort the categories |
| List list = new ArrayList(categoryCache.values()); |
| for (int i = 0; i < categoriesToRemove.size(); i++) |
| list.remove(categoriesToRemove.get(i)); |
| Collections.sort(list, new Comparator() { |
| Collator coll = Collator.getInstance(Locale.getDefault()); |
| |
| public int compare(Object a, Object b) { |
| PropertySheetCategory c1, c2; |
| String dname1, dname2; |
| c1 = (PropertySheetCategory) a; |
| dname1 = c1.getCategoryName(); |
| c2 = (PropertySheetCategory) b; |
| dname2 = c2.getCategoryName(); |
| return coll.compare(dname1, dname2); |
| } |
| }); |
| |
| categories = (PropertySheetCategory[]) list |
| .toArray(new PropertySheetCategory[list.size()]); |
| } |
| |
| /** |
| * Update the category (but not its parent or children). |
| * |
| * @param category |
| * the category to update |
| * @param item |
| * the tree item for the given entry |
| */ |
| private void updateCategory(PropertySheetCategory category, |
| TreeItem item) { |
| // ensure that backpointer is correct |
| item.setData(category); |
| |
| // Update the name and value columns |
| item.setText(0, category.getCategoryName()); |
| item.setText(1, ""); //$NON-NLS-1$ |
| |
| // update the "+" icon |
| if (category.getAutoExpand()) { |
| // we auto expand categories when they first appear |
| createChildren(item); |
| item.setExpanded(true); |
| category.setAutoExpand(false); |
| } else { |
| // we do not want to auto expand categories if the user has |
| // collpased them |
| updatePlus(category, item); |
| } |
| } |
| |
| /** |
| * Update the child entries or categories of the given entry or category. If |
| * the given node is the root entry and we are showing categories then the |
| * child entries are categories, otherwise they are entries. |
| * |
| * @param node |
| * the entry or category whose children we will update |
| * @param widget |
| * the widget for the given entry, either a |
| * <code>TableTree</code> if the node is the root node or a |
| * <code>TreeItem</code> otherwise. |
| */ |
| private void updateChildrenOf(Object node, Widget widget) { |
| // cast the entry or category |
| IPropertySheetEntry entry = null; |
| PropertySheetCategory category = null; |
| if (node instanceof IPropertySheetEntry) |
| entry = (IPropertySheetEntry) node; |
| else |
| category = (PropertySheetCategory) node; |
| |
| // get the current child tree items |
| TreeItem[] childItems = getChildItems(widget); |
| |
| // optimization! prune collapsed subtrees |
| TreeItem item = null; |
| if (widget instanceof TreeItem) { |
| item = (TreeItem) widget; |
| } |
| if (item != null && !item.getExpanded()) { |
| // remove all children |
| for (int i = 0; i < childItems.length; i++) { |
| if (childItems[i].getData() != null) { |
| removeItem(childItems[i]); |
| } |
| } |
| |
| // append a dummy if necessary |
| if (category != null || entry.hasChildEntries()) { |
| // may already have a dummy |
| // It is either a category (which always has at least one child) |
| // or an entry with chidren. |
| // Note that this test is not perfect, if we have filtering on |
| // then there in fact may be no entires to show when the user |
| // presses the "+" expand icon. But this is an acceptable |
| // compromise. |
| childItems = getChildItems(widget); |
| if (childItems.length == 0) { |
| new TreeItem(item, SWT.NULL); |
| } |
| } |
| return; |
| } |
| |
| // get the child entries or categories |
| if (node == rootEntry && isShowingCategories) |
| // update the categories |
| updateCategories(); |
| List children = getChildren(node); |
| |
| // remove items |
| Set set = new HashSet(childItems.length * 2 + 1); |
| |
| for (int i = 0; i < childItems.length; i++) { |
| Object data = childItems[i].getData(); |
| if (data != null) { |
| Object e = data; |
| int ix = children.indexOf(e); |
| if (ix < 0) { // not found |
| removeItem(childItems[i]); |
| } else { // found |
| set.add(e); |
| } |
| } else if (data == null) { // the dummy |
| item.dispose(); |
| } |
| } |
| |
| // WORKAROUND |
| int oldCnt = -1; |
| if (widget == tree) |
| oldCnt = tree.getItemCount(); |
| |
| // add new items |
| int newSize = children.size(); |
| for (int i = 0; i < newSize; i++) { |
| Object el = children.get(i); |
| if (!set.contains(el)) |
| createItem(el, widget, i); |
| } |
| |
| // WORKAROUND |
| if (widget == tree && oldCnt == 0 && tree.getItemCount() == 1) { |
| tree.setRedraw(false); |
| tree.setRedraw(true); |
| } |
| |
| // get the child tree items after our changes |
| childItems = getChildItems(widget); |
| |
| // update the child items |
| // This ensures that the children are in the correct order |
| // are showing the correct values. |
| for (int i = 0; i < newSize; i++) { |
| Object el = children.get(i); |
| if (el instanceof IPropertySheetEntry) |
| updateEntry((IPropertySheetEntry) el, childItems[i]); |
| else { |
| updateCategory((PropertySheetCategory) el, childItems[i]); |
| updateChildrenOf(el, childItems[i]); |
| } |
| } |
| // The tree's original selection may no longer apply after the update, |
| // so fire the selection changed event. |
| entrySelectionChanged(); |
| } |
| |
| /** |
| * Update the given entry (but not its children or parent) |
| * |
| * @param entry |
| * the entry we will update |
| * @param item |
| * the tree item for the given entry |
| */ |
| private void updateEntry(IPropertySheetEntry entry, TreeItem item) { |
| // ensure that backpointer is correct |
| item.setData(entry); |
| |
| // update the name and value columns |
| item.setText(0, entry.getDisplayName()); |
| item.setText(1, entry.getValueAsString()); |
| Image image = entry.getImage(); |
| if (item.getImage(1) != image) |
| item.setImage(1, image); |
| |
| // update the "+" icon |
| updatePlus(entry, item); |
| } |
| |
| /** |
| * Updates the "+"/"-" icon of the tree item from the given entry |
| * or category. |
| * |
| * @param node the entry or category |
| * @param item the tree item being updated |
| */ |
| private void updatePlus(Object node, TreeItem item) { |
| // cast the entry or category |
| IPropertySheetEntry entry = null; |
| PropertySheetCategory category = null; |
| if (node instanceof IPropertySheetEntry) |
| entry = (IPropertySheetEntry) node; |
| else |
| category = (PropertySheetCategory) node; |
| |
| boolean hasPlus = item.getItemCount() > 0; |
| boolean needsPlus = category != null || entry.hasChildEntries(); |
| boolean removeAll = false; |
| boolean addDummy = false; |
| |
| if (hasPlus != needsPlus) { |
| if (needsPlus) { |
| addDummy = true; |
| } else { |
| removeAll = true; |
| } |
| } |
| if (removeAll) { |
| // remove all children |
| TreeItem[] items = item.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| removeItem(items[i]); |
| } |
| } |
| |
| if (addDummy) { |
| new TreeItem(item, SWT.NULL); // append a dummy to create the |
| // plus sign |
| } |
| } |
| } |