/*
* 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.properties.view;
import org.csstudio.sds.ui.SdsUiPlugin;
import org.csstudio.sds.ui.internal.editor.DisplayEditor;
import org.csstudio.sds.ui.internal.localization.Messages;
import org.csstudio.ui.util.CustomMediaFactory;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.help.IContext;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.CellEditor;
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.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.ISaveablePart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.help.IContextComputer;
import org.eclipse.ui.help.IWorkbenchHelpSystem;
import org.eclipse.ui.part.CellEditorActionHandler;
import org.eclipse.ui.part.Page;
/**
* The standard implementation of property sheet page which presents a table of
* property names and values obtained from the current selection in the active
* workbench part.
* <p>
* This page obtains the information about what properties to display from the
* current selection (which it tracks).
* </p>
* <p>
* The model for this page is a hierarchy of <code>IPropertySheetEntry</code>.
* The page may be configured with a custom model by setting the root entry.
* <p>
* If no root entry is set then a default model is created which uses the
* <code>IPropertySource</code> interface to obtain the properties of the
* current selection. This requires that the selected objects provide an
* <code>IPropertySource</code> adapter (or implement
* <code>IPropertySource</code> directly). This restriction can be overcome by
* providing this page with an <code>IPropertySourceProvider</code>. If
* supplied, this provider will be used by the default model to obtain a
* property source for the current selection
* </p>
* <p>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
*
* @see IPropertySource
*
* @author Sven Wende
*/
public final class PropertySheetPage extends Page implements
IPropertySheetPage, IAdaptable {
/**
* Help context id (value
* <code>"org.eclipse.ui.property_sheet_page_help_context"</code>).
*/
public static final String HELP_CONTEXT_PROPERTY_SHEET_PAGE = "org.eclipse.ui.property_sheet_page_help_context"; //$NON-NLS-1$
/**
* The property sheet viewer.
*/
private PropertySheetViewer _viewer;
/**
* A property sheet sorter.
*/
private PropertySheetSorter _sorter;
/**
* The root entry.
*/
private IPropertySheetEntry _rootEntry;
/**
* A property source provider.
*/
private IPropertySourceProvider _provider;
/**
* The "restore defaults" action.
*/
private DefaultsAction _defaultsAction;
/**
* The "filter" action.
*/
private FilterAction _filterAction;
/**
* The "hide/show categories" action.
*/
private CategoriesAction _categoriesAction;
/**
* The "copy property" action.
*/
private CopyPropertyAction _copyAction;
/**
* The "configure dynamic aspects" action.
*/
private ConfigureDynamicAspectsAction _configureDynamicAspectsAction;
/**
* The "remove dynamic aspects" action.
*/
private RemoveDynamicAspectsAction _removeDynamicAspectsAction;
/**
* A cell editor activation listener.
*/
private ICellEditorActivationListener _cellEditorActivationListener;
/**
* A cell editor action handler.
*/
private CellEditorActionHandler _cellEditorActionHandler;
/**
* A clipboard instance.
*/
private Clipboard _clipboard;
/**
* The source workbench part, which provides the selections and property
* sources.
*/
private IWorkbenchPart _sourcePart;
/**
* A part listener.
*/
private PartListener _partListener = new PartListener();
/**
* Creates a new property sheet page.
*/
public PropertySheetPage() {
super();
}
/**
* {@inheritDoc}
*/
@Override
public void createControl(final Composite parent) {
// create a new viewer
_viewer = new PropertySheetViewer(parent);
_viewer.setSorter(_sorter);
// set the model for the viewer
if (_rootEntry == null) {
// create a new root
PropertySheetEntry root = new PropertySheetEntry();
if (_provider != null) {
// set the property source provider
root.setPropertySourceProvider(_provider);
}
_rootEntry = root;
}
_viewer.setRootEntry(_rootEntry);
_viewer.addActivationListener(getCellEditorActivationListener());
// add a listener to track when the entry selection changes
_viewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
handleEntrySelection(event.getSelection());
}
});
initDragAndDrop();
makeActions();
// Create the popup menu for the page.
MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
menuMgr.add(_copyAction);
menuMgr.add(_configureDynamicAspectsAction);
menuMgr.add(_removeDynamicAspectsAction);
menuMgr.add(new Separator());
menuMgr.add(_defaultsAction);
Menu menu = menuMgr.createContextMenu(_viewer.getControl());
_viewer.getControl().setMenu(menu);
// TODO: Men� f�r Object Contributions offen halten, oder nicht ?
// (swende)
// PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart().getSite().registerContextMenu(menuMgr,
// viewer);
// Set help on the viewer
_viewer.getControl().addHelpListener(new HelpListener() {
/*
* @see HelpListener#helpRequested(HelpEvent)
*/
@Override
public void helpRequested(final HelpEvent e) {
// Get the context for the selected item
IStructuredSelection selection = (IStructuredSelection) _viewer
.getSelection();
if (!selection.isEmpty()) {
IPropertySheetEntry entry = (IPropertySheetEntry) selection
.getFirstElement();
Object helpContextId = entry.getHelpContextIds();
if (helpContextId != null) {
if (helpContextId instanceof String) {
PlatformUI.getWorkbench().getHelpSystem()
.displayHelp((String) helpContextId);
return;
}
// Since 2.0 the only valid type for helpContextIds
// is a String (a single id).
// However for backward compatibility we have to handle
// and array of contexts (Strings and/or IContexts)
// or a context computer.
Object[] contexts = null;
if (helpContextId instanceof IContextComputer) {
// get local contexts
contexts = ((IContextComputer) helpContextId)
.getLocalContexts(e);
} else {
contexts = (Object[]) helpContextId;
}
IWorkbenchHelpSystem help = PlatformUI.getWorkbench()
.getHelpSystem();
// Ignore all but the first element in the array
if (contexts[0] instanceof IContext) {
help.displayHelp((IContext) contexts[0]);
} else {
help.displayHelp((String) contexts[0]);
}
return;
}
}
// No help for the selection so show page help
PlatformUI.getWorkbench().getHelpSystem().displayHelp(
HELP_CONTEXT_PROPERTY_SHEET_PAGE);
}
});
}
/**
* The <code>PropertySheetPage</code> implementation of this
* <code>IPage</code> method disposes of this page's entries.
*/
@Override
public void dispose() {
super.dispose();
if (_sourcePart != null) {
_sourcePart.getSite().getPage().removePartListener(_partListener);
}
if (_rootEntry != null) {
_rootEntry.dispose();
_rootEntry = null;
}
if (_clipboard != null) {
_clipboard.dispose();
_clipboard = null;
}
}
/**
* {@inheritDoc}
*/
@Override
public Object getAdapter(final Class adapter) {
if (ISaveablePart.class.equals(adapter)) {
return getSaveablePart();
}
return null;
}
/**
* Returns an <code>ISaveablePart</code> that delegates to the source part
* for the current page if it implements <code>ISaveablePart</code>, or
* <code>null</code> otherwise.
*
* @return an <code>ISaveablePart</code> or <code>null</code>
* @since 3.2
*/
protected ISaveablePart getSaveablePart() {
if (_sourcePart instanceof ISaveablePart) {
return (ISaveablePart) _sourcePart;
}
return null;
}
/**
* Returns the cell editor activation listener for this page.
*
* @return ICellEditorActivationListener the cell editor activation listener
* for this page
*/
private ICellEditorActivationListener getCellEditorActivationListener() {
if (_cellEditorActivationListener == null) {
_cellEditorActivationListener = new ICellEditorActivationListener() {
@Override
public void cellEditorActivated(final CellEditor cellEditor) {
if (_cellEditorActionHandler != null) {
_cellEditorActionHandler.addCellEditor(cellEditor);
}
}
@Override
public void cellEditorDeactivated(final CellEditor cellEditor) {
if (_cellEditorActionHandler != null) {
_cellEditorActionHandler.removeCellEditor(cellEditor);
}
}
};
}
return _cellEditorActivationListener;
}
/**
* {@inheritDoc}
*/
@Override
public Control getControl() {
if (_viewer == null) {
return null;
}
return _viewer.getControl();
}
/**
* Handles a selection change in the entry table.
*
* @param selection
* the new selection
*/
public void handleEntrySelection(final ISelection selection) {
if (_defaultsAction != null) {
if (selection.isEmpty()) {
_defaultsAction.setEnabled(false);
return;
}
// see if item is editable
boolean editable = _viewer.getActiveCellEditor() != null;
_defaultsAction.setEnabled(editable);
}
}
/**
* Adds drag and drop support.
*/
protected void initDragAndDrop() {
int operations = DND.DROP_COPY;
Transfer[] transferTypes = new Transfer[] { TextTransfer.getInstance() };
DragSourceListener listener = new DragSourceAdapter() {
@Override
public void dragSetData(final DragSourceEvent event) {
performDragSetData(event);
}
@Override
public void dragFinished(final DragSourceEvent event) {
// Nothing to do here
}
};
DragSource dragSource = new DragSource(_viewer.getControl(), operations);
dragSource.setTransfer(transferTypes);
dragSource.addDragListener(listener);
}
/**
* The user is attempting to drag. Add the appropriate data to the event.
*
* @param event
* The event sent from the drag and drop support.
*/
void performDragSetData(final DragSourceEvent event) {
// Get the selected property
IStructuredSelection selection = (IStructuredSelection) _viewer
.getSelection();
if (selection.isEmpty()) {
return;
}
// Assume single selection
IPropertySheetEntry entry = (IPropertySheetEntry) selection
.getFirstElement();
// Place text as the data
StringBuffer buffer = new StringBuffer();
buffer.append(entry.getDisplayName());
buffer.append("\t"); //$NON-NLS-1$
buffer.append(entry.getValueAsString());
event.data = buffer.toString();
}
/**
* Make action objects.
*/
private void makeActions() {
ISharedImages sharedImages = PlatformUI.getWorkbench()
.getSharedImages();
// Restore Default Value
_defaultsAction = new DefaultsAction(_viewer, "defaults"); //$NON-NLS-1$
_defaultsAction.setText(Messages.Defaults_text);
_defaultsAction.setToolTipText(Messages.Defaults_toolTip);
_defaultsAction.setImageDescriptor(CustomMediaFactory.getInstance()
.getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/defaults_ps.gif")); //$NON-NLS-1$
// _defaultsAction.setDisabledImageDescriptor(CustomMediaFactory
// .getInstance().getImageDescriptorFromPlugin(
// SdsUiPlugin.PLUGIN_ID, "icons/defaults_ps.gif")); //$NON-NLS-1$
_defaultsAction.setEnabled(false);
// Show Advanced Properties
_filterAction = new FilterAction(_viewer, "filter"); //$NON-NLS-1$
_filterAction.setText(Messages.Filter_text);
_filterAction.setToolTipText(Messages.Filter_toolTip);
_filterAction.setImageDescriptor(CustomMediaFactory.getInstance()
.getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/filter_ps.gif")); //$NON-NLS-1$
_filterAction.setChecked(false);
// Show Categories
_categoriesAction = new CategoriesAction(_viewer, "categories"); //$NON-NLS-1$
_categoriesAction.setText(Messages.Categories_text);
_categoriesAction.setToolTipText(Messages.Categories_toolTip);
_categoriesAction.setImageDescriptor(CustomMediaFactory.getInstance()
.getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/tree_mode.gif")); //$NON-NLS-1$
_categoriesAction.setChecked(true);
// Copy
Shell shell = _viewer.getControl().getShell();
_clipboard = new Clipboard(shell.getDisplay());
_copyAction = new CopyPropertyAction(_viewer, "copy", _clipboard); //$NON-NLS-1$
_copyAction.setText(Messages.CopyProperty_text);
_copyAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
// Configure dynamic aspects of a property
_configureDynamicAspectsAction = new ConfigureDynamicAspectsAction(
_viewer, "configureDynamicAspects"); //$NON-NLS-1$
_configureDynamicAspectsAction.setText("Configure Dynamic Aspects");
_configureDynamicAspectsAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
// Remove dynamic aspects of a property
_removeDynamicAspectsAction = new RemoveDynamicAspectsAction(_viewer,
"removeDynamicAspects");
_removeDynamicAspectsAction.setText("Remove Dynamic Aspects");
_removeDynamicAspectsAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
}
/**
* {@inheritDoc}
*/
@Override
public void makeContributions(final IMenuManager menuManager,
final IToolBarManager toolBarManager,
final IStatusLineManager statusLineManager) {
// add actions to the tool bar
toolBarManager.add(_categoriesAction);
toolBarManager.add(_filterAction);
toolBarManager.add(_defaultsAction);
// add actions to the menu
menuManager.add(_categoriesAction);
menuManager.add(_filterAction);
// set status line manager into the viewer
_viewer.setStatusLineManager(statusLineManager);
}
/**
* Updates the model for the viewer.
* <p>
* Note that this means ensuring that the model reflects the state of the
* current viewer input.
* </p>
*/
public void refresh() {
if (_viewer == null) {
return;
}
// calling setInput on the viewer will cause the model to refresh
_viewer.setInput(_viewer.getInput());
}
/**
* {@inheritDoc}
*/
@Override
public void selectionChanged(final IWorkbenchPart part,
final ISelection selection) {
if (_viewer == null) {
return;
}
if (part instanceof DisplayEditor) {
if (_sourcePart != null) {
_sourcePart.getSite().getPage().removePartListener(_partListener);
_sourcePart = null;
}
// change the viewer input since the workbench selection has changed.
if (selection instanceof IStructuredSelection) {
_sourcePart = part;
_viewer.setInput(((IStructuredSelection) selection).toArray());
}
if (_sourcePart != null) {
_sourcePart.getSite().getPage().addPartListener(_partListener);
}
}
}
/**
* The <code>PropertySheetPage</code> implementation of this
* <code>IPage</code> method calls <code>makeContributions</code> for
* backwards compatibility with previous versions of <code>IPage</code>.
* <p>
* Subclasses may reimplement.
* </p>
*
* @param actionBars
* the action bars
*/
@Override
public void setActionBars(final IActionBars actionBars) {
super.setActionBars(actionBars);
_cellEditorActionHandler = new CellEditorActionHandler(actionBars);
_cellEditorActionHandler.setCopyAction(_copyAction);
}
/**
* Sets focus to a part in the page.
*/
@Override
public void setFocus() {
_viewer.getControl().setFocus();
}
/**
* Sets the given property source provider as the property source provider.
* <p>
* Calling this method is only valid if you are using this page's default
* root entry.
* </p>
*
* @param newProvider
* the property source provider
*/
public void setPropertySourceProvider(
final IPropertySourceProvider newProvider) {
_provider = newProvider;
if (_rootEntry instanceof PropertySheetEntry) {
((PropertySheetEntry) _rootEntry)
.setPropertySourceProvider(_provider);
// the following will trigger an update
_viewer.setRootEntry(_rootEntry);
}
}
/**
* Sets the given entry as the model for the page.
*
* @param entry
* the root entry
*/
public void setRootEntry(final IPropertySheetEntry entry) {
_rootEntry = entry;
if (_viewer != null) {
// the following will trigger an update
_viewer.setRootEntry(_rootEntry);
}
}
/**
* Sets the sorter used for sorting categories and entries in the viewer of
* this page.
* <p>
* The default sorter sorts categories and entries alphabetically.
* </p>
*
* @param sorter
* the sorter to set (<code>null</code> will reset to the
* default sorter)
* @since 3.1
*/
protected void setSorter(final PropertySheetSorter sorter) {
this._sorter = sorter;
if (_viewer != null) {
_viewer.setSorter(sorter);
// the following will trigger an update
if (null != _viewer.getRootEntry()) {
_viewer.setRootEntry(_rootEntry);
}
}
}
/**
* Part listener which cleans up this page when the source part is closed.
* This is hooked only when there is a source part.
*
* @since 3.2
*/
protected final class PartListener implements IPartListener {
/**
* {@inheritDoc}
*/
@Override
public void partActivated(final IWorkbenchPart part) {
}
/**
* {@inheritDoc}
*/
@Override
public void partBroughtToTop(final IWorkbenchPart part) {
}
/**
* {@inheritDoc}
*/
@Override
public void partClosed(final IWorkbenchPart part) {
if (_sourcePart == part) {
_sourcePart = null;
if (_viewer != null && !_viewer.getControl().isDisposed()) {
_viewer.setInput(new Object[0]);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void partDeactivated(final IWorkbenchPart part) {
}
/**
* {@inheritDoc}
*/
@Override
public void partOpened(final IWorkbenchPart part) {
}
}
}