/* * Copyright (c) 2008 Stiftung Deutsches Elektronen-Synchrotron, * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY. * * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS. * WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE * IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR * CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. * NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. * DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, * OR MODIFICATIONS. * THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION, * USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY * AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM */ package org.csstudio.sds.ui.internal.layers; import org.csstudio.sds.internal.model.ILayerModelListener; import org.csstudio.sds.internal.model.Layer; import org.csstudio.sds.internal.model.LayerSupport; import org.eclipse.gef.commands.CommandStack; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSourceAdapter; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.model.BaseWorkbenchContentProvider; import org.eclipse.ui.model.WorkbenchLabelProvider; /** * This class provides a {@link TreeViewer} to display the defined {@link Layer}s * of a Display in the Run-Mode. It also allows to toggle the visibility of the * Layers. If an {@link IWorkbenchPartSite} is set, the {@link TreeViewer} as a * popup-menu to add or remove or to change the order of the layer. * * @author Kai Meyer * */ public final class LayerTreeViewer implements ILayerModelListener { /** * The id of the parent view. */ private final String _viewID; /** * The {@link IWorkbenchPartSite} where the popup-menu is registered. */ private IWorkbenchPartSite _site; /** * The treeviewer which displays the layers. */ private TreeViewer _treeViewer; /** * The current {@link LayerSupport}. */ private LayerSupport _layerSupport; /** * The {@link CommandStack}, where several commands are executed. * If t is not <code>null</code> the commands can be undone. */ private CommandStack _commandStack = null; /** * Constructor. * @param site The {@link IWorkbenchPartSite} for the popup-menu (can be null) * @param parent The parent {@link Composite} for the {@link TreeViewer} * @param viewID The ID of the parent view */ public LayerTreeViewer(final IWorkbenchPartSite site, final Composite parent, final String viewID) { _site = site; _viewID = viewID; // configure viewer _treeViewer = new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); _treeViewer.setLabelProvider(new WorkbenchLabelProvider()); _treeViewer.setContentProvider(new BaseWorkbenchContentProvider() { @Override public Object[] getElements(final Object element) { return ((LayerSupport) element).getLayers().toArray(); } }); // handle selections (sets active layer) _treeViewer .addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged( final SelectionChangedEvent event) { IStructuredSelection sel = (IStructuredSelection) event .getSelection(); Layer layer = (Layer) sel.getFirstElement(); if (layer != null && _layerSupport != null) { _layerSupport.setActiveLayer(layer); } } }); // handle doubleclicks (toggles layer visibility) _treeViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(final DoubleClickEvent event) { IStructuredSelection sel = (IStructuredSelection) event .getSelection(); Layer layer = (Layer) sel.getFirstElement(); if (layer != null) { if (_commandStack == null) { layer.setVisible(!layer.isVisible()); } else { _commandStack .execute(new ToggleVisibilityCommand(layer)); } } } }); // DnD addDragSupport(_treeViewer); addDropSupport(_treeViewer); // context menu hookPopupMenu(_treeViewer); } /** * Hooks the PopupMenu to the given TreeViewer. * * @param viewer * The TreeViewer */ private void hookPopupMenu(final TreeViewer viewer) { MenuManager popupMenu = new MenuManager(_viewID); popupMenu.add(new Separator("additions")); Menu menu = popupMenu.createContextMenu(viewer.getTree()); viewer.getTree().setMenu(menu); if (_site != null) { _site.registerContextMenu(popupMenu, viewer); } } /** * Adds drag support for layers to the specified viewer. * * @param treeViewer * the viewer that should support dragging of layers */ private void addDragSupport(final TreeViewer treeViewer) { // Allow data to be copied or moved from the drag source treeViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT, new Transfer[] { LayerTransfer .getInstance() }, new DragSourceAdapter() { @Override public void dragStart(final DragSourceEvent event) { if (_treeViewer.getSelection() == null) { event.doit = false; } } @Override public void dragSetData(final DragSourceEvent event) { if (LayerTransfer.getInstance().isSupportedType(event.dataType)) { IStructuredSelection sel = (IStructuredSelection) _treeViewer .getSelection(); Layer layer = (Layer) sel.getFirstElement(); if (layer != null) { LayerTransfer.getInstance().setSelectedLayer(layer); event.data = layer; } } } @Override public void dragFinished(final DragSourceEvent event) { LayerTransfer.getInstance().setSelectedLayer(null); } }); } /** * Adds drop support for layers to the specified viewer. * * @param treeViewer * the viewer that should support dropping of layers */ private void addDropSupport(final TreeViewer treeViewer) { // drop treeViewer.addDropSupport(DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT, new Transfer[] { LayerTransfer .getInstance() }, new DropTargetAdapter() { @Override public void drop(final DropTargetEvent event) { if (LayerTransfer.getInstance().isSupportedType( event.currentDataType)) { // get the layer that was moved Layer movedLayer = (Layer) LayerTransfer.getInstance() .nativeToJava(event.currentDataType); // get the layer, on which the moved layer was dropped Layer dropLayer = (Layer) (event.item != null ? event.item .getData() : null); if (movedLayer != null && dropLayer != null && _layerSupport != null) { int index = _layerSupport.getLayerIndex(dropLayer); if (_commandStack == null) { _layerSupport .changeLayerPosition(movedLayer, index); } else { _commandStack.execute(new MoveLayerCommand( _layerSupport, movedLayer, index)); } } } } @Override public void dropAccept(final DropTargetEvent event) { if (!LayerTransfer.getInstance().isSupportedType( event.currentDataType) || event.item == null) { event.detail = DND.DROP_NONE; } else { event.detail = DND.DROP_MOVE; } } @Override public void dragEnter(final DropTargetEvent event) { if (!LayerTransfer.getInstance().isSupportedType( event.currentDataType) || event.item == null) { event.detail = DND.DROP_NONE; } else { event.detail = DND.DROP_MOVE; } } @Override public void dragOver(final DropTargetEvent event) { if (!LayerTransfer.getInstance().isSupportedType( event.currentDataType) || event.item == null) { event.detail = DND.DROP_NONE; } else { event.detail = DND.DROP_MOVE; } } }); } /** * {@inheritDoc} */ @Override public void layerChanged(final Layer layer, final String property) { // just refresh the viewer completely if anything changes _treeViewer.refresh(); _treeViewer.setSelection(new StructuredSelection(_layerSupport .getActiveLayer())); } /** * Sets the selection of the internal {@link TreeViewer}. * @param selection The new selection */ public void setSelection(final ISelection selection) { _treeViewer.setSelection(selection); } /** * Set the layout data for the internal {@link TreeViewer}. * @param layoutData The layout Data */ public void setLayoutData(final Object layoutData) { _treeViewer.getTree().setLayoutData(layoutData); } /** * Sets the currently used {@link LayerSupport}. * @param layerSupport The {@link LayerSupport} */ public void setLayerSupport(final LayerSupport layerSupport) { _layerSupport = layerSupport; _layerSupport.addLayerModelListener(this); _treeViewer.setInput(_layerSupport); } /** * Sets the currently used {@link CommandStack}. * Set the {@link CommandStack} to enable Undo/Redo functionality. * @param commandStack The {@link CommandStack} (can be null) */ public void setCommandStack(final CommandStack commandStack) { _commandStack = commandStack; } /** * Disposes the {@link TreeViewer}. */ public void dispose() { _layerSupport.removeLayerModelListener(this); _layerSupport = null; _treeViewer.getTree().dispose(); } }