/******************************************************************************* * Copyright (c) 2011, 2014 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.ui.views.editor.pages; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import org.eclipse.core.commands.Command; import org.eclipse.core.commands.ParameterizedCommand; import org.eclipse.core.expressions.EvaluationContext; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelDecorator; 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.swt.SWTException; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.tcf.te.core.interfaces.IFilterable; import org.eclipse.tcf.te.core.interfaces.IPropertyChangeProvider; import org.eclipse.tcf.te.ui.forms.CustomFormToolkit; import org.eclipse.tcf.te.ui.interfaces.ITreeControlInputChangedListener; import org.eclipse.tcf.te.ui.trees.AbstractTreeControl; import org.eclipse.tcf.te.ui.trees.TreeControl; import org.eclipse.tcf.te.ui.utils.TreeViewerUtil; import org.eclipse.tcf.te.ui.views.activator.UIPlugin; import org.eclipse.ui.ISources; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.widgets.Form; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.part.MultiPageSelectionProvider; import org.osgi.framework.Bundle; /** * Tree viewer based editor page implementation. */ public abstract class AbstractTreeViewerExplorerEditorPage extends AbstractCustomFormToolkitEditorPage implements IDoubleClickListener { // The references to the pages subcontrol's (needed for disposal) private TreeControl treeControl; private IToolBarManager toolbarMgr; private PropertyChangeListener pcListener; private ISelectionChangedListener scListener; private FocusListener fListener; private Image formImage; /* (non-Javadoc) * @see org.eclipse.ui.forms.editor.FormPage#dispose() */ @Override public void dispose() { IPropertyChangeProvider provider = getPropertyChangeProvider(); if(provider != null && pcListener != null) { provider.removePropertyChangeListener(pcListener); } if (treeControl != null && treeControl.getViewer() != null) { treeControl.getViewer().removeSelectionChangedListener(scListener); if (treeControl.getViewer() instanceof TreeViewer) { ((TreeViewer)treeControl.getViewer()).removeDoubleClickListener(this); Tree tree = ((TreeViewer)treeControl.getViewer()).getTree(); if (tree != null && !tree.isDisposed()) { tree.removeFocusListener(fListener); } } } if (treeControl != null) { treeControl.dispose(); treeControl = null; } super.dispose(); } /** * Set the initial focus to the tree. */ @Override public void setFocus() { Control control = treeControl.getViewer().getControl(); if(control != null && !control.isDisposed()) { control.setFocus(); } else { super.setFocus(); } } /* * (non-Javadoc) * @see org.eclipse.tcf.te.ui.views.editor.pages.AbstractEditorPage#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object) */ @Override public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { super.setInitializationData(config, propertyName, data); String iconPath = config.getAttribute("icon"); //$NON-NLS-1$ if(iconPath != null) { String bundleId = config.getContributor().getName(); Bundle bundle = Platform.getBundle(bundleId); if(bundle != null) { URL iconURL = bundle.getEntry(iconPath); if(iconURL != null) { formImage = UIPlugin.getImage(iconURL.toString()); if (formImage == null) { ImageDescriptor iconDesc = ImageDescriptor.createFromURL(iconURL); if(iconDesc != null) { formImage = iconDesc.createImage(); UIPlugin.getDefault().getImageRegistry().put(iconURL.toString(), formImage); } } } } } treeControl = doCreateTreeControl(); Assert.isNotNull(treeControl); treeControl.addInputChangedListener(new ITreeControlInputChangedListener() { @Override public void inputChanged(AbstractTreeControl control, Object oldInput, Object newInput) { Assert.isNotNull(control); if (getTreeControl() != control) return; // Adjust the table column width after an input element change if (control.getViewer() instanceof TreeViewer && ((TreeViewer)control.getViewer()).getTree() != null) { Tree tree = ((TreeViewer)control.getViewer()).getTree(); if (!tree.isDisposed()) adjustTreeColumnWidth(tree); } } }); } /* * (non-Javadoc) * @see org.eclipse.tcf.te.ui.views.editor.pages.AbstractCustomFormToolkitEditorPage#getFormImage() */ @Override protected Image getFormImage() { return formImage; } /* * (non-Javadoc) * @see org.eclipse.tcf.te.ui.views.editor.pages.AbstractCustomFormToolkitEditorPage#createToolbarContributionItems(org.eclipse.jface.action.IToolBarManager) */ @Override protected void createToolbarContributionItems(IToolBarManager manager) { this.toolbarMgr = manager; super.createToolbarContributionItems(manager); treeControl.createToolbarContributionItems(manager); } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.views.editor.pages.AbstractCustomFormToolkitEditorPage#doCreateFormContent(org.eclipse.swt.widgets.Composite, org.eclipse.tcf.te.ui.forms.CustomFormToolkit) */ @Override protected void doCreateFormContent(Composite parent, CustomFormToolkit toolkit) { Assert.isNotNull(parent); Assert.isNotNull(toolkit); // Setup the tree control Assert.isNotNull(treeControl); treeControl.setupFormPanel(parent, toolkit); // Register the context menu at the parent workbench part site. getSite().registerContextMenu(getId(), treeControl.getContextMenuManager(), treeControl.getViewer()); // Set the initial input Object input = getViewerInput(); treeControl.getViewer().setInput(input); addViewerListeners(); // adjust the tree column width initially to take the full size of control adjustTreeColumnWidth(treeControl.getViewer()); updateUI(); } /** * Determines the current visible width of the tree control and * adjust the column width according to there relative weight. * * @param viewer The viewer or <code>null</code>. */ protected void adjustTreeColumnWidth(Viewer viewer) { if (!(viewer instanceof TreeViewer)) return; final TreeViewer treeViewer = (TreeViewer)viewer; final Tree tree = treeViewer.getTree(); tree.addControlListener(new ControlListener() { @Override public void controlResized(ControlEvent e) { adjustTreeColumnWidth(tree); tree.removeControlListener(this); } @Override public void controlMoved(ControlEvent e) { } }); } /** * Adjust the width of the tree columns of the given tree to fill in the * available screen area. * * @param tree The tree. Must not be <code>null</code>. */ protected void adjustTreeColumnWidth(Tree tree) { Assert.isNotNull(tree); int sumColumnWidth = 0; int treeWidth = tree.getSize().x - tree.getVerticalBar().getSize().x; TreeColumn[] columns = tree.getColumns(); // Summarize the tree column width for (TreeColumn column : columns) { Object widthHint = column.getData("widthHint"); //$NON-NLS-1$ sumColumnWidth += widthHint instanceof Integer ? ((Integer)widthHint).intValue() : column.getWidth(); } // Calculate the new width for each column int sumColumnWidth2 = 0; TreeColumn maxColumn = null; for (TreeColumn column : columns) { Object widthHint = column.getData("widthHint"); //$NON-NLS-1$ int width = widthHint instanceof Integer ? ((Integer)widthHint).intValue() : column.getWidth(); int weight = (width * 100) / sumColumnWidth; int newWidth = (weight * treeWidth) / 100; sumColumnWidth2 += newWidth; column.setWidth(newWidth); if (maxColumn == null || maxColumn.getWidth() < column.getWidth()) { maxColumn = column; } } // If we end up with a slighter larger width of all columns than // the tree widget is, reduce the size of the largest column if (sumColumnWidth2 > treeWidth && maxColumn != null) { int delta = sumColumnWidth2 - treeWidth + 2; maxColumn.setWidth(maxColumn.getWidth() - delta); } } /** * Add tree viewer listeners to the tree control. */ private void addViewerListeners() { TreeViewer viewer = (TreeViewer) treeControl.getViewer(); scListener = new ISelectionChangedListener(){ @Override public void selectionChanged(SelectionChangedEvent event) { propagateSelection(); } }; viewer.addSelectionChangedListener(scListener); fListener = new FocusAdapter() { @Override public void focusGained(FocusEvent e) { propagateSelection(); } }; viewer.getTree().addFocusListener(fListener); viewer.addDoubleClickListener(this); IPropertyChangeProvider provider = getPropertyChangeProvider(); if(provider != null) { pcListener = new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent event) { Object object = event.getSource(); Object input = getTreeViewerInput(); if (object == input) { if (Display.getCurrent() != null) { updateUI(); } else { Display display = getSite().getShell().getDisplay(); display.asyncExec(new Runnable() { @Override public void run() { updateUI(); } }); } } } }; provider.addPropertyChangeListener(pcListener); } } @Override public Object getAdapter(Class adapter) { if(TreeViewer.class.equals(adapter)) { return treeControl.getViewer(); } return super.getAdapter(adapter); } /** * Get an adapter instance from the adaptable with the specified * adapter interface. * * @param adaptable The adaptable to get the adapter. * @param adapter The adapter interface class. * @return An adapter or null if it does not adapt to this type. */ @SuppressWarnings("rawtypes") private Object getAdapter(Object adaptable, Class adapter) { Object adapted = null; if(adapter.isInstance(adaptable)) { adapted = adaptable; } if(adapted == null && adaptable instanceof IAdaptable) { adapted = ((IAdaptable)adaptable).getAdapter(adapter); } if(adapted == null && adaptable != null) { adapted = Platform.getAdapterManager().getAdapter(adaptable, adapter); } return adapted; } /** * Get an adapter of IFilteringLabelDecorator. * * @return an IFilteringLabelDecorator adapter or null if it does not adapt to IFilteringLabelDecorator. */ private IFilterable adaptFilterable() { Object input = getTreeViewerInput(); if (input != null) { return (IFilterable) getAdapter(input, IFilterable.class); } return null; } protected abstract Object getViewerInput(); /** * Get the viewer input adapter for the input. * * @param input the input of the tree viewer. * @return The adapter. */ private IPropertyChangeProvider getPropertyChangeProvider() { Object input = getTreeViewerInput(); if (input != null) { return (IPropertyChangeProvider) getAdapter(input, IPropertyChangeProvider.class); } return null; } Object getTreeViewerInput() { if (treeControl != null && treeControl.getViewer() != null) { return treeControl.getViewer().getInput(); } return null; } /** * Creates and returns a tree control. * * @return The new tree control. */ protected TreeControl doCreateTreeControl() { return new TreeControl(getViewerId(), this); } /** * Returns the associated tree control. * * @return The associated tree control or <code>null</code>. */ public final TreeControl getTreeControl() { return treeControl; } /** * Update the page's ui including its toolbar and title text and image. */ protected void updateUI() { toolbarMgr.update(true); IManagedForm managedForm = getManagedForm(); Form form = managedForm.getForm().getForm(); Object element = getTreeViewerInput(); boolean filterEnabled = false; IFilterable filterDecorator = adaptFilterable(); if (filterDecorator != null) { TreeViewer viewer = (TreeViewer) treeControl.getViewer(); filterEnabled = TreeViewerUtil.isFiltering(viewer, TreePath.EMPTY); } ILabelDecorator titleDecorator = getTitleBarDecorator(); String text = getFormTitle(); if (text != null) { if (titleDecorator != null) { text = titleDecorator.decorateText(text, element); } if (filterEnabled) { TreeViewer viewer = (TreeViewer) treeControl.getViewer(); text = TreeViewerUtil.getDecoratedText(text, viewer, TreePath.EMPTY); } } Image image = getFormImage(); if (image != null) { if (titleDecorator != null) { image = titleDecorator.decorateImage(image, element); } if (filterEnabled) { TreeViewer viewer = (TreeViewer) treeControl.getViewer(); image = TreeViewerUtil.getDecoratedImage(image, viewer, TreePath.EMPTY); } } if (text != null) { try { form.setText(text); } catch (Exception e) { // Ignore any disposed exception } } if (image != null) { try { form.setImage(image); } catch (Exception e) { // Ignore any disposed exception } } } /** * Get the title bar's decorator or null if there's no decorator for it. */ protected ILabelDecorator getTitleBarDecorator() { return null; } /** * Propagate the current selection to the editor's selection provider. */ protected void propagateSelection() { ISelection selection = treeControl.getViewer().getSelection(); ISelectionProvider selectionProvider = getSite().getSelectionProvider(); // If the parent control is already disposed, we have no real chance of // testing for it. Catch the SWT exception here just in case. try { selectionProvider.setSelection(selection.isEmpty() ? new StructuredSelection(getEditorInputNode()) : selection); if (selectionProvider instanceof MultiPageSelectionProvider) { SelectionChangedEvent changedEvent = new SelectionChangedEvent(selectionProvider, selection); ((MultiPageSelectionProvider) selectionProvider).fireSelectionChanged(changedEvent); } } catch (SWTException e) { /* ignored on purpose */ } } /** * Get the id of the command invoked when the tree is double-clicked. * If the id is null, then no command is invoked. * * @return The double-click command id. */ protected String getDoubleClickCommandId() { return null; } /** * Get the tree viewer's id. This viewer id is used by * viewer extension to define columns and filters. * * @return This viewer's id or null. */ protected abstract String getViewerId(); /* * (non-Javadoc) * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent) */ @Override public void doubleClick(final DoubleClickEvent event) { // If an handled and enabled command is registered for the ICommonActionConstants.OPEN // retargetable action id, redirect the double click handling to the command handler. // // Note: The default tree node expansion must be re-implemented in the active handler! String commandId = getDoubleClickCommandId(); Command cmd = null; if(commandId != null) { ICommandService service = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class); cmd = service != null ? service.getCommand(commandId) : null; } if (cmd != null && cmd.isDefined() && cmd.isEnabled()) { final Command command = cmd; SafeRunner.run(new SafeRunnable(){ @Override public void handleException(Throwable e) { // Ignore exception } @Override public void run() throws Exception { IHandlerService handlerSvc = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class); Assert.isNotNull(handlerSvc); ISelection selection = event.getSelection(); EvaluationContext ctx = new EvaluationContext(handlerSvc.getCurrentState(), selection); ctx.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection); ctx.addVariable(ISources.ACTIVE_MENU_SELECTION_NAME, selection); ctx.setAllowPluginActivation(true); ParameterizedCommand pCmd = ParameterizedCommand.generateCommand(command, null); Assert.isNotNull(pCmd); handlerSvc.executeCommandInContext(pCmd, null, ctx); }}); } else { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); Object element = selection.getFirstElement(); TreeViewer viewer = (TreeViewer) treeControl.getViewer(); if (viewer.isExpandable(element)) { viewer.setExpandedState(element, !viewer.getExpandedState(element)); } } } }