Java tutorial
/* * Debrief - the Open Source Maritime Analysis Application * http://debrief.info * * (C) 2000-2014, PlanetMayo Ltd * * This library is free software; you can redistribute it and/or * modify it under the terms of the Eclipse Public License v1.0 * (http://www.eclipse.org/legal/epl-v10.html) * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ package org.mwc.debrief.core.editors; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import java.util.Vector; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.operations.AbstractOperation; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.IElementComparer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.part.IPageSite; import org.eclipse.ui.part.Page; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.mwc.cmap.core.CorePlugin; import org.mwc.cmap.core.DataTypes.TrackData.TrackManager; import org.mwc.cmap.core.property_support.EditableWrapper; import org.mwc.cmap.core.property_support.RightClickSupport; import org.mwc.cmap.core.ui_support.CoreViewLabelProvider; import org.mwc.cmap.core.ui_support.DragDropSupport; import org.mwc.debrief.core.DebriefPlugin; import MWC.GUI.BaseLayer; import MWC.GUI.Editable; import MWC.GUI.Layer; import MWC.GUI.Layers; import MWC.GUI.Plottable; import MWC.GenericData.HiResDate; import MWC.GenericData.Watchable; import MWC.GenericData.WatchableList; public class PlotOutlinePage extends Page implements IContentOutlinePage { public static final String NAME_COLUMN_NAME = "Name"; public static final String VISIBILITY_COLUMN_NAME = "Visibility"; /** * whether we are already ignoring firing messages */ private static boolean _alreadyDeferring = false; MyTreeViewer _treeViewer; private CoreViewLabelProvider _myLabelProvider; private DragDropSupport _dragDropSupport; private ISelectionChangedListener _selectionChangeListener; Layers _myLayers; /* * don't bother with the drill-down adapter. we've removed it to save space in * the local toolbar private DrillDownAdapter drillDownAdapter; */ /** * create a new top-level layer */ private Action _createLayer; /** * make the current item the primary track */ Action _makePrimary; /** * set the current item as the secondary track */ Action _makeSecondary; /** * add the current item to the secondary track */ Action _addAsSecondary; /** * hide the selected item(s) */ private Action _hideAction; /** * reveal the selected item(s) */ private Action _revealAction; /** * toggle to indicate whether user wants narrative to always jump to * highlighted entry */ private Action _followSelectionToggle; /** * action to allow user to collapse all layer manager nodes */ private Action _collapseAllAction; /** * action to allow user to expand all layer manager nodes */ private Action _expandAllAction; private Layers.DataListener _myLayersListener; protected TrackManager _theTrackDataListener; private PlotEditor _plotEditor; public PlotOutlinePage(PlotEditor _plotEditor, Layers _myLayers) { this._plotEditor = _plotEditor; this._myLayers = _myLayers; this._theTrackDataListener = (TrackManager) _plotEditor.getAdapter(TrackManager.class); } @Override public void init(IPageSite pageSite) { super.init(pageSite); IActionBars actionBars = pageSite.getActionBars(); actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(), _plotEditor.getUndoAction()); actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(), _plotEditor.getRedoAction()); actionBars.updateActionBars(); } @Override public void createControl(Composite parent) { _treeViewer = new MyTreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); _treeViewer.setUseHashlookup(true); // drillDownAdapter = new DrillDownAdapter(_treeViewer); _treeViewer.setContentProvider(new ViewContentProvider()); _myLabelProvider = new CoreViewLabelProvider(); _treeViewer.setLabelProvider(_myLabelProvider); _treeViewer.setSorter(new NameSorter()); _treeViewer.setInput(_myLayers); _treeViewer.setComparer(new IElementComparer() { public boolean equals(final Object a, final Object b) { Object obj1 = a; Object obj2 = b; // do our special case for comparing plottables if (obj1 instanceof EditableWrapper) { final EditableWrapper pw = (EditableWrapper) obj1; obj1 = pw.getEditable(); } if (obj2 instanceof EditableWrapper) { final EditableWrapper pw = (EditableWrapper) obj2; obj2 = pw.getEditable(); } return obj1 == obj2; } public int hashCode(final Object element) { int res = 0; if (element instanceof EditableWrapper) { final EditableWrapper pw = (EditableWrapper) element; final Editable pl = pw.getEditable(); if (pl != null) res += pw.getEditable().hashCode(); } else res = element.hashCode(); return res; } }); _dragDropSupport = new DragDropSupport(_treeViewer); _treeViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY, _dragDropSupport.getTypes(), _dragDropSupport); _treeViewer.addDropSupport(DND.DROP_MOVE | DND.DROP_COPY, _dragDropSupport.getTypes(), _dragDropSupport); // and format the tree final Tree tree = _treeViewer.getTree(); tree.setHeaderVisible(true); formatTree(tree); makeActions(); hookContextMenu(); hookDoubleClickAction(); contributeToActionBars(); // set ourselves as selection source getSite().setSelectionProvider(_treeViewer); _selectionChangeListener = new ISelectionChangedListener() { public void selectionChanged(final SelectionChangedEvent event) { // right, see what it is final ISelection sel = event.getSelection(); if (sel instanceof StructuredSelection) { final StructuredSelection ss = (StructuredSelection) sel; final Object datum = ss.getFirstElement(); if (datum instanceof EditableWrapper) { final EditableWrapper pw = (EditableWrapper) datum; editableSelected(sel, pw); } } } }; _plotEditor.addSelectionChangedListener(_selectionChangeListener); // also listen out ourselves to any changes, so we can update the button // enablement _treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(final SelectionChangedEvent event) { final ISelection isel = event.getSelection(); if (isel instanceof StructuredSelection) { final StructuredSelection ss = (StructuredSelection) isel; // right, see if this is an item we can make primary _makePrimary.setEnabled(isValidPrimary(ss)); // and see if we can make it a secondary _makeSecondary.setEnabled(isValidSecondary(ss)); // and see if we can make it a secondary _addAsSecondary.setEnabled(isValidSecondary(ss)); } } }); // and declare our context sensitive help // FIXME CorePlugin.declareContextHelp(parent, "org.mwc.debrief.help.LayerMgr"); processNewLayers(); } private void contributeToActionBars() { final IActionBars bars = getSite().getActionBars(); fillLocalPullDown(bars); fillLocalToolBar(bars); } private void fillLocalToolBar(IActionBars bars) { IToolBarManager manager = bars.getToolBarManager(); manager.add(_followSelectionToggle); manager.add(_collapseAllAction); manager.add(_makePrimary); manager.add(_makeSecondary); manager.add(_addAsSecondary); manager.add(_hideAction); manager.add(_revealAction); // manager.add(_trackNewLayers); } private void fillLocalPullDown(IActionBars bars) { IMenuManager manager = bars.getMenuManager(); manager.add(_followSelectionToggle); manager.add(new Separator()); manager.add(_makePrimary); manager.add(_makeSecondary); manager.add(_addAsSecondary); manager.add(new Separator()); manager.add(_revealAction); manager.add(_hideAction); manager.add(new Separator()); manager.add(_expandAllAction); manager.add(_collapseAllAction); manager.add(new Separator()); manager.add(_createLayer); // FIXME // manager.add(CorePlugin.createOpenHelpAction( // "org.mwc.debrief.help.LayerMgr", null, this)); } private void makeActions() { // _trackNewLayers = // _myPartMonitor.createSyncedAction("Link to current plot", // "Always show layers for selected Plot", getSite()); _followSelectionToggle = new Action("Jump to selection", Action.AS_CHECK_BOX) { }; _followSelectionToggle.setText("Follow selection"); _followSelectionToggle.setChecked(true); _followSelectionToggle.setToolTipText("Ensure selected item in plot is always visible"); _followSelectionToggle.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/followselection.png")); _collapseAllAction = new Action("Collapse all", Action.AS_PUSH_BUTTON) { public void run() { // go for it. _treeViewer.collapseAll(); } }; _collapseAllAction.setText("Collapse all layers"); _collapseAllAction.setToolTipText("Collapse all layers in the Outline View"); _collapseAllAction.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/collapse_all.png")); _expandAllAction = new Action("Expand all", Action.AS_PUSH_BUTTON) { public void run() { // go for it. _treeViewer.expandAll(); } }; _expandAllAction.setText("Expand all layers"); _expandAllAction.setToolTipText("Expand all layers in the Outline View"); _expandAllAction.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/expand_all.png")); _createLayer = new Action() { public void run() { // ask the user final InputDialog id = new InputDialog(null, "Create new layer", "Layer name:", "", null); final int res = id.open(); // was ok pressed? if (res == InputDialog.OK) { // yup, create the layer final String newName = id.getValue(); // yes, create the layer final BaseLayer bl = new BaseLayer(); bl.setName(newName); // and add it _myLayers.addThisLayer(bl); } } }; _createLayer.setText("Create new layer"); _createLayer.setToolTipText("Create a new top-level layer"); _createLayer.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/new_layer.png")); _makePrimary = new Action() { public void run() { final AbstractOperation doIt = new SelectionOperation("Make primary", new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { final WatchableList list = (WatchableList) item; // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.setPrimary(list); } } }, new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.setPrimary(null); } } }, new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { final WatchableList list = (WatchableList) item; // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.setPrimary(list); } } }, _treeViewer, _myLayers); CorePlugin.run(doIt); applyOperationToSelection(new IOperateOn() { public void doItTo(final Editable item) { } }); } }; _makePrimary.setText("Make Primary"); _makePrimary.setToolTipText("Make this item the primary "); _makePrimary.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/make_primary.png")); _makePrimary.setEnabled(false); _makeSecondary = new Action() { public void run() { final AbstractOperation doIt = new SelectionOperation("Make secondary", new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { final WatchableList list = (WatchableList) item; // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.setSecondary(list); } } }, new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { final WatchableList list = (WatchableList) item; // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.removeSecondary(list); } } }, new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { final WatchableList list = (WatchableList) item; // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.setSecondary(list); } } }, _treeViewer, _myLayers); CorePlugin.run(doIt); } }; _makeSecondary.setText("Make Secondary"); _makeSecondary.setToolTipText("Set this item as the secondary track"); _makeSecondary.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/make_secondary.png")); _makeSecondary.setEnabled(false); _addAsSecondary = new Action() { public void run() { final AbstractOperation doIt = new SelectionOperation("Add as secondary", new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { final WatchableList list = (WatchableList) item; // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.addSecondary(list); } } }, new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { final WatchableList list = (WatchableList) item; // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.removeSecondary(list); } } }, new IOperateOn() { public void doItTo(final Editable item) { // is it a watchable-list? if (item instanceof WatchableList) { final WatchableList list = (WatchableList) item; // make it the primary if (_theTrackDataListener != null) _theTrackDataListener.addSecondary(list); } } }, _treeViewer, _myLayers); CorePlugin.run(doIt); } }; _addAsSecondary.setText("Add as Secondary"); _addAsSecondary.setToolTipText("Add this item to the secondary tracks"); _addAsSecondary.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/add_secondary.png")); _addAsSecondary.setEnabled(false); _hideAction = new Action() { public void run() { final AbstractOperation doIt = new SelectionOperation("Hide item", new IOperateOn() { public void doItTo(final Editable item) { setPlottableVisible(item, false); } }, new IOperateOn() { public void doItTo(final Editable item) { setPlottableVisible(item, true); } }, new IOperateOn() { public void doItTo(final Editable item) { setPlottableVisible(item, false); } }, _treeViewer, _myLayers); CorePlugin.run(doIt); } }; _hideAction.setText("Hide item"); _hideAction.setToolTipText("Hide selected items"); _hideAction.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/hide.png")); _revealAction = new Action() { public void run() { final AbstractOperation doIt = new SelectionOperation("reveal item", new IOperateOn() { public void doItTo(final Editable item) { setPlottableVisible(item, true); } }, new IOperateOn() { public void doItTo(final Editable item) { setPlottableVisible(item, false); } }, new IOperateOn() { public void doItTo(final Editable item) { setPlottableVisible(item, true); } }, _treeViewer, _myLayers); CorePlugin.run(doIt); } }; _revealAction.setText("Reveal item"); _revealAction.setToolTipText("Reveal selected items"); _revealAction.setImageDescriptor(DebriefPlugin.getImageDescriptor("icons/16/show.png")); } private void hookContextMenu() { final MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(final IMenuManager manager) { PlotOutlinePage.this.fillContextMenu(manager); } }); final Menu menu = menuMgr.createContextMenu(_treeViewer.getControl()); _treeViewer.getControl().setMenu(menu); getSite().registerContextMenu("#PopupMenu", menuMgr, _treeViewer); } void fillContextMenu(final IMenuManager manager) { // get the selected item final StructuredSelection sel = (StructuredSelection) _treeViewer.getSelection(); // right, we only worry about primary, secondary, hide, reveal if something // is selected if (sel.size() > 0) { // ok, allow hide/reveal manager.add(_hideAction); manager.add(_revealAction); // have a look at the data-types to sort out whether to primary/secondary if (isValidPrimary(sel)) manager.add(_makePrimary); if (isValidSecondary(sel)) { manager.add(_makeSecondary); manager.add(_addAsSecondary); } // now stick in the separator anyway manager.add(new Separator()); } // drillDownAdapter.addNavigationActions(manager); // Other plug-ins can contribute there actions here manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); // hey, sort out the data-specific items // build up a list of menu items // create some lists to store our selected items final Editable[] eList = new Editable[sel.size()]; final Layer[] parentLayers = new Layer[sel.size()]; final Layer[] updateLayers = new Layer[sel.size()]; // right, now populate them final Object[] oList = sel.toArray(); for (int i = 0; i < oList.length; i++) { final EditableWrapper wrapper = (EditableWrapper) oList[i]; eList[i] = wrapper.getEditable(); // sort out the parent layer final EditableWrapper theParent = wrapper.getParent(); // hmm, did we find one? if (theParent != null) // yes, store it parentLayers[i] = (Layer) wrapper.getParent().getEditable(); else // nope - store a null (to indicate it's a top-level layer) parentLayers[i] = null; updateLayers[i] = wrapper.getTopLevelLayer(); } // ok, sort out what we can do with all of this... RightClickSupport.getDropdownListFor(manager, eList, updateLayers, parentLayers, _myLayers, false); } /** * find out if the selection is valid for setting as primary * * @param ss * @return */ protected boolean isValidPrimary(final StructuredSelection ss) { boolean res = false; // we can only do this for one entry! if (ss.size() == 1) { final EditableWrapper pw = (EditableWrapper) ss.getFirstElement(); final Editable pl = pw.getEditable(); // hey, first see if it's even a candidate if (pl instanceof WatchableList) // do we have a track data listener? if (_theTrackDataListener != null) { // now see if it's already the primary if (pl != _theTrackDataListener.getPrimaryTrack()) { res = true; } } } return res; } /** * find out if the selection is valid for setting as primary * * @param ss * @return */ protected boolean isValidSecondary(final StructuredSelection ss) { boolean res = false; if (ss.size() >= 1) { Iterator<?> iter = ss.iterator(); while (iter.hasNext()) { EditableWrapper pw = (EditableWrapper) iter.next(); final Editable pl = pw.getEditable(); if (!(pl instanceof WatchableList)) { // nope - we can just make them all secondaries! drop out res = false; break; } else { // hey, it's a maybe. res = true; // ok, it's a candidate. now see if it's already one of the secondaries if (_theTrackDataListener == null) { CorePlugin.logError(Status.INFO, "PROBLEM: Outline View does not hold track data listener. Maintaner to track this occurrence", null); } else { final WatchableList[] secs = _theTrackDataListener.getSecondaryTracks(); if (secs != null) { for (int i = 0; i < secs.length; i++) { final WatchableList thisList = secs[i]; if (thisList == pl) { res = false; break; } } } } } } } return res; } protected static void setPlottableVisible(final Editable item, final boolean on) { if (item instanceof Plottable) { final Plottable pl = (Plottable) item; pl.setVisible(on); } } /** * user has double-clicked on an item. process. * * @param operation * the thingy we're doing */ void applyOperationToSelection(final IOperateOn operation) { final IStructuredSelection selection = (IStructuredSelection) _treeViewer.getSelection(); applyOperation(operation, selection, _myLayers); } private static void triggerChartUpdate(final Layer changedLayer, final Layers myLayers) { myLayers.fireReformatted(changedLayer); } public void editableSelected(final ISelection sel, final EditableWrapper pw) { if (_followSelectionToggle.isChecked()) { // ahh, just check if this is a whole new layers object if (pw.getEditable() instanceof Layers) { if (pw.getEditable() != _myLayers) { _myLayers = (Layers) pw.getEditable(); processNewLayers(); } } else { // just check that this is something we can work with if (sel instanceof StructuredSelection) { final StructuredSelection str = (StructuredSelection) sel; // hey, is there a payload? if (str.getFirstElement() != null) { // sure is. we only support single selections, so get the first // element final Object first = str.getFirstElement(); if (first instanceof EditableWrapper) { final EditableWrapper ew = (EditableWrapper) first; // is it already loaded by the lazy tree manager? final Widget res = _treeViewer.findEditable(ew.getEditable()); if (res == null) { // nope, laod that whole data object EditableWrapper thisP = ew.getParent(); final ArrayList<EditableWrapper> al = new ArrayList<EditableWrapper>(); // we may have a chain of parents (though it's unlikely). Never // the less, store them in reverse order, top-level first while (thisP != null) { al.add(0, thisP); thisP = thisP.getParent(); } // ok, now we have to open all these items, starting at the // highest level parent final Iterator<EditableWrapper> iter = al.iterator(); while (iter.hasNext()) { final EditableWrapper editableWrapper = (EditableWrapper) iter.next(); // ok, get the content final ViewContentProvider contentP = (ViewContentProvider) _treeViewer .getContentProvider(); // find the wrapped children of this object final Object[] contents = contentP.getChildren(editableWrapper); // loop through, expanding them for (final Object content : contents) { // expand the particular child. Note we go down through all // the layers, since the target // object may be several layers deep _treeViewer.expandToLevel(content, AbstractTreeViewer.ALL_LEVELS); } } } // now just display it. This part of the tree may not have been // loaded before, // but we're sure it is now. _treeViewer.setSelection(sel, _followSelectionToggle.isChecked()); } } } } } } void processNewLayers() { if (_myLayersListener == null) { _myLayersListener = new Layers.DataListener2() { public void dataModified(final Layers theData, final Layer changedLayer) { } public void dataExtended(final Layers theData) { dataExtended(theData, null, null); } public void dataReformatted(final Layers theData, final Layer changedLayer) { handleReformattedLayer(changedLayer); } public void dataExtended(final Layers theData, final Plottable newItem, final Layer parentLayer) { processNewData(theData, newItem, parentLayer); } }; } // right, listen for data being added _myLayers.addDataExtendedListener(_myLayersListener); // and listen for items being reformatted _myLayers.addDataReformattedListener(_myLayersListener); // do an initial population. processNewData(_myLayers, null, null); } private void hookDoubleClickAction() { _treeViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(final DoubleClickEvent event) { CorePlugin.openView(IPageLayout.ID_PROP_SHEET); } }); } private void formatTree(final Tree tree) { // define the columns final TreeColumn nameCol = new TreeColumn(tree, SWT.NONE); nameCol.setText(NAME_COLUMN_NAME); nameCol.setWidth(180); final TreeColumn visibleCol = new TreeColumn(tree, SWT.NONE); visibleCol.setText(VISIBILITY_COLUMN_NAME); visibleCol.setWidth(50); } @Override public Control getControl() { return _treeViewer.getControl(); } @Override public void setFocus() { _treeViewer.getControl().setFocus(); } @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { _treeViewer.addSelectionChangedListener(listener); } @Override public ISelection getSelection() { return _treeViewer.getSelection(); } @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { _treeViewer.removeSelectionChangedListener(listener); } @Override public void setSelection(ISelection selection) { _treeViewer.setSelection(selection); } public void dispose() { super.dispose(); // make sure we close the listeners clearLayerListener(); // remove selection listeners if (_plotEditor != null) { _plotEditor.removeSelectionChangedListener(_selectionChangeListener); _plotEditor.outlinePageClosed(); _plotEditor = null; } _selectionChangeListener = null; if (_myLabelProvider != null) { _myLabelProvider.disposeImages(); } } /** * stop listening to the layer, if necessary */ void clearLayerListener() { if (_myLayers != null) { _myLayers.removeDataExtendedListener(_myLayersListener); _myLayers.removeDataReformattedListener(_myLayersListener); _myLayersListener = null; _myLayers = null; } } private static class MyTreeViewer extends TreeViewer { public MyTreeViewer(final Tree parent) { super(parent); } public MyTreeViewer(final Composite parent, final int style) { super(parent, style); } public Widget findEditable(final Editable item) { return super.findItem(item); } } /** * class that embodies applying an operation to a series of selected points. * the series of points are remembered, so that they can be undone/redone * * @author ian.mayo */ private static final class SelectionOperation extends AbstractOperation { /** * the selected items */ StructuredSelection _theSelection; /** * what we are going to execute */ private final IOperateOn _execute; /** * what we are going to undo */ private final IOperateOn _undo; /** * what we are going to redo */ private final IOperateOn _redo; /** * who is giving us the selection */ private final ISelectionProvider _provider; /** * who we fire the update to */ private final Layers _destination; /** * define our operation * * @param label * what to label it * @param execute * the operation to execute * @param undo * how to do an undo * @param redo * how to do a redo * @param provider * who is going to provide the selection * @param destination * what to update on completion */ SelectionOperation(final String label, final IOperateOn execute, final IOperateOn undo, final IOperateOn redo, final ISelectionProvider provider, final Layers destination) { super(label); // get remembering _provider = provider; _execute = execute; _undo = undo; _redo = redo; _destination = destination; if (CorePlugin.getUndoContext() != null) { addContext(CorePlugin.getUndoContext()); } } /** * ok, do the operation */ public IStatus execute(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { // ok, take a copy of the selection - for our undo/redo operations _theSelection = (StructuredSelection) _provider.getSelection(); // cool, go for it applyOperation(_execute, _theSelection, _destination); return Status.OK_STATUS; } /** * ok, redo the operation */ public IStatus redo(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { applyOperation(_redo, _theSelection, _destination); return Status.OK_STATUS; } /** * ok, undo the operation */ public IStatus undo(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { applyOperation(_undo, _theSelection, _destination); return Status.OK_STATUS; } /** * @return */ public boolean canExecute() { return _execute != null; } /** * @return */ public boolean canRedo() { return _redo != null; } /** * @return */ public boolean canUndo() { return _undo != null; } } class NameSorter extends ViewerSorter { @SuppressWarnings("unchecked") public int compare(final Viewer viewer, final Object e1, final Object e2) { int res = 0; final EditableWrapper p1 = (EditableWrapper) e1; final EditableWrapper p2 = (EditableWrapper) e2; // just see if we have sorted editables if ((p1 instanceof Comparable) && (p2 instanceof Comparable)) { final Comparable<Object> w1 = (Comparable<Object>) p1; final Comparable<Object> w2 = (Comparable<Object>) p2; res = w1.compareTo(w2); } else { // ha. if they're watchables, sort them in time order if ((p1.getEditable() instanceof Watchable) && (p2.getEditable() instanceof Watchable)) { final Watchable wa = (Watchable) p1.getEditable(); final Watchable wb = (Watchable) p2.getEditable(); // hmm, just check we have times final HiResDate ha = wa.getTime(); final HiResDate hb = wb.getTime(); if ((ha != null) && (hb != null)) res = wa.getTime().compareTo(wb.getTime()); else res = p1.getEditable().getName().compareTo(p2.getEditable().getName()); } else if ((p1.getEditable() instanceof Comparable) && (p2.getEditable() instanceof Comparable)) { @SuppressWarnings("rawtypes") Comparable p1c = (Comparable) p1.getEditable(); @SuppressWarnings("rawtypes") Comparable p2c = (Comparable) p2.getEditable(); res = p1c.compareTo(p2c); // Note: use the native compare-to, not just comparing names // final String name1 = p1.getEditable().toString(); // final String name2 = p2.getEditable().toString(); // res = name1.compareTo(name2); } else { final String p1Name = p1.getEditable().getName(); final String p2Name = p2.getEditable().getName(); res = p1Name.compareTo(p2Name); } } return res; } } /** * recursive class used to build up a list containing the item together with * all child items * * @param list * the list we're building up * @param item * the item to add (together with its children) */ private void addItemAndChildrenToList(final Vector<Object> list, final TreeItem item) { final Object myData = item.getData(); if (myData != null) list.add(item.getData()); final TreeItem[] children = item.getItems(); if (children.length > 0) { for (int i = 0; i < children.length; i++) { final TreeItem thisChild = children[i]; addItemAndChildrenToList(list, thisChild); } } } private static Set<Layer> _pendingLayers = new TreeSet<Layer>(new Comparator<Layer>() { @SuppressWarnings({ "unchecked", "rawtypes" }) public int compare(final Layer arg0, final Layer arg1) { int res = 1; if (arg0.equals(arg1)) res = 0; if (arg0 instanceof Comparable) { final Comparable c0 = arg0; res = c0.compareTo(arg1); } return res; } }); /** * one or more layers have been changed. This method may get called lots of * times. Stack up the events - and just call our UI update method once at the * end * * @param changedLayer * the layer which has changed */ protected void handleReformattedLayer(final Layer changedLayer) { // right - store this layer (if we have one) if (changedLayer != null) _pendingLayers.add(changedLayer); if (_alreadyDeferring) { // hey - already processing - add this layer to the pending ones } else { _alreadyDeferring = true; // right. we're not already doing some processing final Display dis = Display.getDefault(); dis.asyncExec(new Runnable() { public void run() { processReformattedLayers(); } }); } } protected void processReformattedLayers() { try { // right, we'll be building up a list of objects to refresh (all of the // objects in the indicated layer) final Vector<Object> newList = new Vector<Object>(0, 1); Widget changed = null; if (_pendingLayers.size() > 0) { for (final Iterator<Layer> iter = _pendingLayers.iterator(); iter.hasNext();) { final Layer changedLayer = (Layer) iter.next(); changed = _treeViewer.findEditable(changedLayer); // see if we can find the element related to the indicated layer final TreeItem thisItem = (TreeItem) changed; if (thisItem != null) { // add the item and its children to the list addItemAndChildrenToList(newList, thisItem); } } } else { // hey, all of the layers need updating. // better get on with it. changed = _treeViewer.findEditable(_myLayers); final Tree theTree = (Tree) changed; final TreeItem[] children = theTree.getItems(); for (int i = 0; i < children.length; i++) { final TreeItem thisItem = children[i]; addItemAndChildrenToList(newList, thisItem); } } // delete the images for the specified items from the image cache // right, tell our label generator to ditch it's cache, since one or more // of the images may have changed // Issue #533 // _myLabelProvider.resetCacheFor(newList); _myLabelProvider.resetCacheFor(_treeViewer.getTree()); // and do the update final Object[] itemsToUpdate = newList.toArray(); _treeViewer.update(itemsToUpdate, new String[] { VISIBILITY_COLUMN_NAME }); } catch (final Exception e) { CorePlugin.getDefault().getLog() .log(new Status(IStatus.WARNING, CorePlugin.PLUGIN_ID, "Tree warning", e)); } finally { _alreadyDeferring = false; _pendingLayers.clear(); } } void processNewData(final Layers theData, final Editable newItem, final Layer parentLayer) { if (!_treeViewer.getTree().isDisposed()) { Display.getDefault().asyncExec(new Runnable() { public void run() { final TreePath[] paths = _treeViewer.getExpandedTreePaths(); // ok, fire the change in the UI thread _treeViewer.setInput(theData); // open up the paths that were previously open _treeViewer.setExpandedTreePaths(paths); // hmm, do we know about the new item? If so, better select it if (newItem != null) { // wrap the plottable final EditableWrapper parentWrapper = new EditableWrapper(parentLayer, null, theData); final EditableWrapper wrapped = new EditableWrapper(newItem, parentWrapper, theData); final ISelection selected = new StructuredSelection(wrapped); // and select it editableSelected(selected, wrapped); } } }); } } private static interface IOperateOn { public void doItTo(Editable item); } @SuppressWarnings("rawtypes") static void applyOperation(final IOperateOn operation, final IStructuredSelection selection, final Layers myLayers) { final Iterator iterator = selection.iterator(); boolean madeChange = false; Layer parentLayer = null; boolean multiLayer = false; while (iterator.hasNext()) { final Object obj = iterator.next(); final EditableWrapper thisP = (EditableWrapper) obj; final Editable res = thisP.getEditable(); final Editable thisOne = (Editable) res; Layer thisParentLayer = null; // ok - do the business operation.doItTo(thisOne); // and remember that it worked madeChange = true; thisParentLayer = thisP.getTopLevelLayer(); // ok. we've now got the parent layer // - is it the first one? if (parentLayer == null) { // yup. just store it parentLayer = thisParentLayer; } else { // nope, we've had at least one of these before if (parentLayer != thisParentLayer) { multiLayer = true; } } } // right. has a change been made? if (madeChange) { // yup. does it apply to just on layer - or all of them if (multiLayer) { // ok - update all layers triggerChartUpdate(null, myLayers); } else { // ok - just update the one layer triggerChartUpdate(parentLayer, myLayers); } } // ok, and update the layers } }