blob: 2d34141ce09eeae7a7a762d10189c672256c9953 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Gunnar Wagenknecht - fix for bug 21756 [PropertiesView] property view sorting
*******************************************************************************/
package org.eclipse.ui.views.properties;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.help.IContext;
import org.eclipse.jface.action.Action;
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.internal.ConfigureColumnsDialog;
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.jface.window.SameShellProvider;
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.swt.widgets.Tree;
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.internal.views.ViewsPlugin;
import org.eclipse.ui.internal.views.properties.PropertiesMessages;
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 restiction 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
* @noextend This class is not intended to be subclassed by clients.
*/
public 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$
private PropertySheetViewer viewer;
private PropertySheetSorter sorter;
private IPropertySheetEntry rootEntry;
private IPropertySourceProvider provider;
private DefaultsAction defaultsAction;
private FilterAction filterAction;
private CategoriesAction categoriesAction;
private CopyPropertyAction copyAction;
private ICellEditorActivationListener cellEditorActivationListener;
private CellEditorActionHandler cellEditorActionHandler;
private Clipboard clipboard;
private IWorkbenchPart sourcePart;
/**
* 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
*/
private class PartListener implements IPartListener {
public void partActivated(IWorkbenchPart part) {
}
public void partBroughtToTop(IWorkbenchPart part) {
}
public void partClosed(IWorkbenchPart part) {
if (sourcePart == part) {
sourcePart = null;
if (viewer != null && !viewer.getControl().isDisposed()) {
viewer.setInput(new Object[0]);
}
}
}
public void partDeactivated(IWorkbenchPart part) {
}
public void partOpened(IWorkbenchPart part) {
}
}
private PartListener partListener = new PartListener();
private Action columnsAction;
/**
* Creates a new property sheet page.
*/
public PropertySheetPage() {
super();
}
/* (non-Javadoc)
* Method declared on <code>IPage</code>.
*/
public void createControl(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() {
public void selectionChanged(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(new Separator());
menuMgr.add(defaultsAction);
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
// Set help on the viewer
viewer.getControl().addHelpListener(new HelpListener() {
/*
* @see HelpListener#helpRequested(HelpEvent)
*/
public void helpRequested(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.
*/
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;
}
}
/**
* The <code>PropertySheetPage</code> implementation of this <code>IAdaptable</code> method
* handles the <code>ISaveablePart</code> adapter by delegating to the source part.
*
* @since 3.2
*/
public Object getAdapter(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() {
public void cellEditorActivated(CellEditor cellEditor) {
if (cellEditorActionHandler != null) {
cellEditorActionHandler.addCellEditor(cellEditor);
}
}
public void cellEditorDeactivated(CellEditor cellEditor) {
if (cellEditorActionHandler != null) {
cellEditorActionHandler.removeCellEditor(cellEditor);
}
}
};
}
return cellEditorActivationListener;
}
/* (non-Javadoc)
* Method declared on IPage (and Page).
*/
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(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() {
public void dragSetData(DragSourceEvent event) {
performDragSetData(event);
}
public void dragFinished(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(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(PropertiesMessages.Defaults_text);
defaultsAction.setToolTipText(PropertiesMessages.Defaults_toolTip);
defaultsAction
.setImageDescriptor(ViewsPlugin.getViewImageDescriptor("elcl16/defaults_ps.gif")); //$NON-NLS-1$
defaultsAction
.setDisabledImageDescriptor(ViewsPlugin.getViewImageDescriptor("dlcl16/defaults_ps.gif")); //$NON-NLS-1$
defaultsAction.setEnabled(false);
// Show Advanced Properties
filterAction = new FilterAction(viewer, "filter"); //$NON-NLS-1$
filterAction.setText(PropertiesMessages.Filter_text);
filterAction.setToolTipText(PropertiesMessages.Filter_toolTip);
filterAction
.setImageDescriptor(ViewsPlugin.getViewImageDescriptor("elcl16/filter_ps.gif")); //$NON-NLS-1$
filterAction.setChecked(false);
// Show Categories
categoriesAction = new CategoriesAction(viewer, "categories"); //$NON-NLS-1$
categoriesAction.setText(PropertiesMessages.Categories_text);
categoriesAction.setToolTipText(PropertiesMessages.Categories_toolTip);
categoriesAction
.setImageDescriptor(ViewsPlugin.getViewImageDescriptor("elcl16/tree_mode.gif")); //$NON-NLS-1$
categoriesAction.setChecked(true);
// Columns...
columnsAction = new Action(PropertiesMessages.Columns_text){
public void run() {
Tree tree = (Tree) viewer.getControl();
new ConfigureColumnsDialog(new SameShellProvider(tree), tree).open();
}
};
columnsAction.setToolTipText(PropertiesMessages.Columns_toolTip);
// Copy
Shell shell = viewer.getControl().getShell();
clipboard = new Clipboard(shell.getDisplay());
copyAction = new CopyPropertyAction(viewer, "copy", clipboard); //$NON-NLS-1$
copyAction.setText(PropertiesMessages.CopyProperty_text);
copyAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
}
/* (non-Javadoc)
* Method declared on IPage (and Page).
*/
public void makeContributions(IMenuManager menuManager,
IToolBarManager toolBarManager, 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);
menuManager.add(columnsAction);
// 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());
}
/* (non-Javadoc)
* Method declared on ISelectionListener.
*/
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
if (viewer == null) {
return;
}
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>
*/
public void setActionBars(IActionBars actionBars) {
super.setActionBars(actionBars);
cellEditorActionHandler = new CellEditorActionHandler(actionBars);
cellEditorActionHandler.setCopyAction(copyAction);
}
/**
* Sets focus to a part in the page.
*/
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(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(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(PropertySheetSorter sorter) {
this.sorter = sorter;
if (viewer != null) {
viewer.setSorter(sorter);
// the following will trigger an update
if(null != viewer.getRootEntry()) {
viewer.setRootEntry(rootEntry);
}
}
}
}