diff options
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te')
6 files changed, 1136 insertions, 2 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/activator/UIPlugin.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/activator/UIPlugin.java index 533665e09..fecdd412f 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/activator/UIPlugin.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/activator/UIPlugin.java @@ -11,6 +11,7 @@ package org.eclipse.tcf.te.ui.views.activator; import java.net.URL; +import org.eclipse.core.runtime.Platform; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.swt.graphics.Image; @@ -24,6 +25,7 @@ import org.eclipse.ui.IWindowListener; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -140,6 +142,11 @@ public class UIPlugin extends AbstractUIPlugin { */ @Override protected void initializeImageRegistry(ImageRegistry registry) { + Bundle bundle = Platform.getBundle("org.eclipse.ui"); //$NON-NLS-1$ + if (bundle != null) { + URL url = bundle.getEntry(ImageConsts.IMAGE_DIR_ROOT + "full/" + ImageConsts.IMAGE_DIR_ELCL + "refresh_nav.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + registry.put(ImageConsts.ACTION_Refresh_Enabled, ImageDescriptor.createFromURL(url)); + } URL url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_EVIEW + "prop_ps.gif"); //$NON-NLS-1$ registry.put(ImageConsts.EDITOR, ImageDescriptor.createFromURL(url)); url = UIPlugin.getDefault().getBundle().getEntry(ImageConsts.IMAGE_DIR_ROOT + ImageConsts.IMAGE_DIR_EVIEW + "targets_view.gif"); //$NON-NLS-1$ diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/controls/AbstractContextSelectorControl.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/controls/AbstractContextSelectorControl.java new file mode 100644 index 000000000..c2a1bacb0 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/controls/AbstractContextSelectorControl.java @@ -0,0 +1,896 @@ +/******************************************************************************* + * Copyright (c) 2012, 2013 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.controls; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.AssertionFailedException; +import org.eclipse.jface.dialogs.IDialogPage; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.viewers.ViewerSorter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Item; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.swt.widgets.Widget; +import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer; +import org.eclipse.tcf.te.runtime.model.interfaces.IModelNode; +import org.eclipse.tcf.te.runtime.properties.PropertiesContainer; +import org.eclipse.tcf.te.runtime.services.ServiceManager; +import org.eclipse.tcf.te.runtime.services.interfaces.IPropertiesAccessService; +import org.eclipse.tcf.te.ui.controls.AbstractDecoratedDialogPageControl; +import org.eclipse.tcf.te.ui.jface.interfaces.IValidatingContainer; +import org.eclipse.tcf.te.ui.swt.SWTControlUtil; +import org.eclipse.tcf.te.ui.views.interfaces.ICategory; +import org.eclipse.tcf.te.ui.views.nls.Messages; +import org.eclipse.ui.dialogs.ContainerCheckedTreeViewer; +import org.eclipse.ui.navigator.CommonViewerSorter; + +/** + * Abstract context selector control. + * <p> + * Allows to present a configurable set of elements from the data model from which the user can + * select one or more elements. + * <p> + * Default properties: + * <ul> + * <li>PROPERTY_SHOW_GHOST_MODEL_NODES = false</li> + * <li>PROPERTY_MULTI_CONTEXT_SELECTOR = false</li> + * </ul> + */ +public abstract class AbstractContextSelectorControl extends AbstractDecoratedDialogPageControl implements ISelectionProvider { + + /** + * Property: If set to <code>true</code>, ghost model nodes will be shown within the tree. + */ + public static final String PROPERTY_SHOW_GHOST_MODEL_NODES = "showGhostModelNodes"; //$NON-NLS-1$ + + /** + * Property: If set to <code>true</code>, the control will be created as multi + * context control. That means that more than one tree item will be + * checkmarkable. In single context selector mode, only one tree item + * can be checkmarked at the same time. + */ + public static final String PROPERTY_MULTI_CONTEXT_SELECTOR = "multiContextSelector"; //$NON-NLS-1$ + + // The last failure cause + private Throwable lastFailureCause; + // Flag for controlling if at least one element has to be selected + private boolean requireSelection = true; + + /** + * List of selection changed listeners. + */ + private final List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>(); + + /** + * Control properties. See predefined property constants within the class and subclass + * implementations. Property changes are not notified. + */ + private final IPropertiesContainer properties = new PropertiesContainer(); + + // Reference to the tree viewer control used. + private TreeViewer viewer; + // The current selection within the tree viewer. + /* default */ ISelection selection; + + /** + * Constant to return an empty viewer filter array. + */ + protected final static ViewerFilter[] NO_FILTERS = new ViewerFilter[0]; + + /** + * Constant to return an empty selected model context array. + */ + protected final static IModelNode[] NO_CONTEXTS = new IModelNode[0]; + + // Currently active set of viewer filters. + private ViewerFilter[] filters; + // Currently active checkbox tree viewer check state listener + private ICheckStateListener listener; + + /** + * Default implementation of the context selector controls tree viewer. + */ + protected class ContextSelectorTreeViewer extends ContainerCheckedTreeViewer { + + /** + * Constructor. + * + * @param parent The parent control. + * @param style The SWT style bits used to create the tree. + */ + public ContextSelectorTreeViewer(Composite parent, int style) { + // make sure that the passed in style bits are not contaminated + // by the CheckboxTreeViewer. + this(new Tree(parent, style)); + } + + /** + * Constructor. + * + * @param parent The parent control. + */ + public ContextSelectorTreeViewer(Composite parent) { + super(parent); + } + + /** + * Constructor. + * + * @param tree The tree control. + */ + public ContextSelectorTreeViewer(Tree tree) { + super(tree); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.TreeViewer#isExpandable(java.lang.Object) + */ + @Override + public boolean isExpandable(Object element) { + boolean expandable = super.isExpandable(element); + // adjust the expandable state if the element does not have + // children after the filtering. + if (expandable) { + expandable = getFilteredChildren(element).length > 0; + } + return expandable; + } + + /** + * Returns the child items for the given element if any. + * + * @param element The element. + * @return The child items of the element or <code>null</code> if none. + */ + public Item[] getChildren(Object element) { + Widget item = findItem(element); + return getChildren(item); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.dialogs.ContainerCheckedTreeViewer#doCheckStateChanged(java.lang.Object) + */ + @Override + protected void doCheckStateChanged(Object element) { + // Our ghost model elements requires some special handling, as + // these elements should be never checked fully. Try to determine + // if we have to double check on the parents state. + boolean skipDoubleCheckParentState = false; + + // If the element isn't one of our model elements, pass on to + // to super implementation + skipDoubleCheckParentState |= !(element instanceof IModelNode); + + // If the element is one of our model elements and it is not + // a ghost node, the parent can't be a ghost node either. + if (!skipDoubleCheckParentState) { + skipDoubleCheckParentState |= !isGhost((IModelNode) element); + } + + // If the element is a ghost model element, then we have to check + // on the parent as well. + if (!skipDoubleCheckParentState) { + IPropertiesAccessService service = ServiceManager.getInstance().getService(IPropertiesAccessService.class); + IModelNode parent = service != null ? (IModelNode)service.getParent(element) : null; + skipDoubleCheckParentState |= parent == null || !isGhost(parent); + } + + // Call the super implementation to check the item and + // updating parents and children the first time + super.doCheckStateChanged(element); + + if (!skipDoubleCheckParentState) { + // Get the tree item for the element and check the parent items + // for being associated with ghost model elements. + Widget item = findItem(element); + if (item instanceof TreeItem) { + TreeItem treeItem = ((TreeItem) item).getParentItem(); + while (treeItem != null) { + Object data = treeItem.getData(); + + // If a child item is checked, otherwise we wouldn't come here, and this + // parent item isn't expanded, we must(!) expand the item now here by force, + // otherwise we will loose the checked states of the children elements! + if (!treeItem.getExpanded()) { + treeItem.setExpanded(true); + } + + // Decide if we shall gray the checked state. + // --> The checked state is grayed if the item is a ghost. + boolean isGhost = data instanceof IModelNode && isGhost((IModelNode) data); + if (!treeItem.getGrayed() && isGhost) { + treeItem.setGrayed(true); + } + + // go one level up in the hierarchy + treeItem = treeItem.getParentItem(); + } + } + } + } + } + + /** + * Returns if or if not the given model node is a ghost node. + * + * @param node The model node. Must not be <code>null</code>. + * @return <code>True</code> if the node is a ghost node, <code>false</code> otherwise. + */ + /* default */ boolean isGhost(IModelNode node) { + Assert.isNotNull(node); + + IPropertiesAccessService service = ServiceManager.getInstance().getService(node, IPropertiesAccessService.class); + if (service != null) { + Object value = service.getProperty(node, IModelNode.PROPERTY_IS_GHOST); + if (value instanceof Boolean) { + return ((Boolean)value).booleanValue(); + } else if (value instanceof String) { + return Boolean.valueOf((String)value).booleanValue(); + } + return false; + } + + try { + return node.isProperty(IModelNode.PROPERTY_IS_GHOST, true); + } catch (AssertionFailedException e) { /* ignored on purpose */ } + + return false; + } + + /** + * Default implementation of the context selector controls check state listener. + */ + protected class ContextSelectedCheckStateListener implements ICheckStateListener { + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent) + */ + @Override + public void checkStateChanged(CheckStateChangedEvent event) { + Object element = event.getElement(); + boolean checked = event.getChecked(); + + onCheckStateChanged(element, checked); + + // validate the parent page if there is one set + if (getParentPage() instanceof IValidatingContainer) { + ((IValidatingContainer) getParentPage()).validate(); + } + } + } + + /** + * Default implementation of the context selector controls viewer filter. + */ + protected class ContextSelectorViewerFilter extends ViewerFilter { + + /** + * Returns if or if not ghost model elements should be visible within the model context + * selector controls tree viewer. Default is not to show the ghost model elements. + * + * @return <code>True</code> to show the ghost model elements, <code>false</code> otherwise. + */ + protected boolean doShowGhostModelElements() { + return getPropertiesContainer().getBooleanProperty(PROPERTY_SHOW_GHOST_MODEL_NODES); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + */ + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + if (element instanceof ICategory) { + return ((ICategory)element).isEnabled(); + } + else if (element instanceof IModelNode) { + if (isGhost((IModelNode) element)) { + return doShowGhostModelElements(); + } + return true; + } + + return false; + } + } + + /** + * Constructor. + * + * @param parentPage The parent target connection page this control is embedded in. Might be + * <code>null</code> if the control is not associated with a page. + */ + public AbstractContextSelectorControl(IDialogPage parentPage) { + super(parentPage); + selectionListeners.clear(); + // initialize the properties + initializeProperties(getPropertiesContainer()); + } + + /** + * Returns the properties container associated with this control. + * + * @return The properties container. + */ + public final IPropertiesContainer getPropertiesContainer() { + return properties; + } + + /** + * Initialize the properties associated with this control. + * + * @param properties The properties container. Must not be <code>null</code>. + */ + protected void initializeProperties(IPropertiesContainer properties) { + Assert.isNotNull(properties); + properties.setProperty(PROPERTY_SHOW_GHOST_MODEL_NODES, false); + properties.setProperty(PROPERTY_MULTI_CONTEXT_SELECTOR, false); + } + + /** + * Set the last failure cause to display. + * + * @param cause The last failure case or <code>null</code>. + */ + public final void setLastFailureCause(Throwable cause) { + lastFailureCause = cause; + if (getParentPage() instanceof IValidatingContainer) { + ((IValidatingContainer)getParentPage()).validate(); + } + } + + /** + * Returns the last failure cause to display. + * + * @return The last failure cause or <code>null</code>. + */ + public final Throwable getLastFailureCause() { + return lastFailureCause; + } + + /** + * Returns the default title text which should be used by the enclosing controls or windows if + * these controls do need to set a title. + * <p> + * The default implementation returns <code>null</code>. + * + * @return The default title text or <code>null</code> if none. + */ + public String getDefaultTitle() { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.ui.controls.BaseControl#dispose() + */ + @Override + public void dispose() { + + viewer = null; + + super.dispose(); + } + + /** + * Returns the list of checked model node contexts. The elements are in the order they appear + * within the tree from top to bottom. The client of the control is in charge detecting any type + * of hierarchy or other relationships between the elements. + * + * @return The list of checked model node contexts or an empty list of none. + */ + public IModelNode[] getCheckedModelContexts() { + // This method does return something useful only if it is a checkable + // tree viewer and the check style is set for the tree. + if (getViewer() instanceof ContainerCheckedTreeViewer && (getTreeViewerStyle() & SWT.CHECK) != 0) { + ContainerCheckedTreeViewer viewer = (ContainerCheckedTreeViewer) getViewer(); + // Get the list of checked elements. Checked elements includes the grayed elements + List<?> checked = viewer.getCheckedElements() != null ? Arrays.asList(viewer.getCheckedElements()) : Collections.emptyList(); + // Get the list of grayed elements. + List<?> grayed = viewer.getGrayedElements() != null ? Arrays.asList(viewer.getGrayedElements()) : Collections.emptyList(); + + // There must be at least one element checked + if (!checked.isEmpty()) { + List<IModelNode> contexts = new ArrayList<IModelNode>(); + for (Object element : checked) { + // If the context is a model node and the parent container node is fully + // checked, drop the model node and the use the container node only. + if (element instanceof IModelNode) { + IModelNode node = (IModelNode) element; + + // Determine the parent node + IPropertiesAccessService service = ServiceManager.getInstance().getService(node, IPropertiesAccessService.class); + IModelNode parent = service != null ? (IModelNode)service.getParent(node) : node.getParent(); + + if (parent != null && checked.contains(parent) && !grayed.contains(parent)) { + continue; + } + } + + // If the element is a model node and not grayed, + // add the element to the list of checked contexts. + if (element instanceof IModelNode && !grayed.contains(element)) { + contexts.add((IModelNode) element); + } + } + return contexts.toArray(new IModelNode[contexts.size()]); + } + } + return NO_CONTEXTS; + } + + + /** + * Called from the default check state listener implementation if the checked state of an element has changed. + * + * @param element The element checked or unchecked. Must not be <code>null</code>. + * @param checked <code>True</code> if the model node has been checked, <code>false</code> if + * unchecked. + */ + protected void onCheckStateChanged(Object element, boolean checked) { + // In case the control is operating in single context selector mode, + // we have to uncheck any other element than the given checked one. + if (checked && getPropertiesContainer().isProperty(PROPERTY_MULTI_CONTEXT_SELECTOR, false)) { + if (getViewer() instanceof ContextSelectorTreeViewer) { + // Node: Within here, only methods which do not fire the check state listeners + // again must be used! + ContextSelectorTreeViewer viewer = (ContextSelectorTreeViewer)getViewer(); + + // If the checked node is a container node and has children, select + // the first children of the container. + Item[] childItems = viewer.getChildren(element); + if (childItems != null && childItems.length > 1) { + // Take the first item as element to be checked + viewer.setCheckedElements(new Object[] { childItems[0].getData() }); + } else { + // Set the passed in element node checked + viewer.setCheckedElements(new Object[] { element }); + } + } + } + } + + /** + * Sets the list of checked model node contexts. + * + * @param contexts The list of model node contexts. Must not be <code>null</code>. + */ + public void setCheckedModelContexts(IModelNode[] contexts) { + Assert.isNotNull(contexts); + + // This method does nothing if the tree viewer isn't a checkable tree viewer. + if (getViewer() instanceof ContainerCheckedTreeViewer && (getTreeViewerStyle() & SWT.CHECK) != 0) { + ContainerCheckedTreeViewer viewer = (ContainerCheckedTreeViewer) getViewer(); + // Set the checked elements. This will trigger the validation of the + // checked state of all the parent and children elements. + viewer.setCheckedElements(contexts); + // Make sure that at least the first checked element is visible to the user + if (contexts.length > 0) { + viewer.setSelection(new StructuredSelection(contexts[0]), true); + } + } + } + + /** + * Returns the controls associated tree viewer. + * + * @return The tree viewer instance or <code>null</code> if not created yet. + */ + public final TreeViewer getViewer() { + return viewer; + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.ui.controls.BaseControl#setupPanel(org.eclipse.swt.widgets.Composite) + */ + @Override + public void setupPanel(Composite parent) { + super.setupPanel(parent); + + // Create the container composite for the tree control. + Composite composite = doCreateTopContainerComposite(parent); + Assert.isNotNull(composite); + + // Create the viewer + viewer = createTreeViewerControl(composite); + + // And now configure the listeners + configureControlListener(); + + // Trigger a selection changed event to give listeners + // a chance to initialize their enabled state correctly + viewer.setSelection(viewer.getSelection()); + } + + /** + * Create the top container composite. + * + * @param parent The parent composite. Must not be <code>null</code>. + * @return The top container composite. Must not be <code>null</code>. + */ + protected Composite doCreateTopContainerComposite(Composite parent) { + Assert.isNotNull(parent); + + // Set the default layout data attributes for the composite + int style = GridData.FILL_BOTH; + int heightHint = SWT.DEFAULT; + + // Fallback to standard non-form controls and create a composite which extends + // in both directions and has no margins + Composite composite = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + composite.setLayout(layout); + GridData layoutData = new GridData(style); + layoutData.heightHint = heightHint; + composite.setLayoutData(layoutData); + + return composite; + } + + /** + * Creates the tree viewer control. Override to return a custom tree viewer implementation. + * + * @param parent The parent composite of the tree viewer. Must not be <code>null</code>. + * @return The tree viewer control. Must be never <code>null</code>. + */ + protected TreeViewer createTreeViewerControl(Composite parent) { + Assert.isNotNull(parent); + + CheckboxTreeViewer viewer = doCreateNewTreeViewerControl(parent, getTreeViewerStyle()); + doConfigureTreeViewerControl(viewer); + + viewer.setInput(getInitialViewerInput()); + doEnableControl(viewer); + + return viewer; + } + + /** + * Creates a new instance of the tree viewer control to use. This method will be called from + * {@link #createTreeViewerControl(Composite)} if creating the tree viewer instance. + * + * @param parent The parent composite of the tree viewer. Must not be <code>null</code>. + * @param style The SWT style bits. + * + * @return The tree viewer instance. Must not be <code>null</code>. + */ + protected CheckboxTreeViewer doCreateNewTreeViewerControl(Composite parent, int style) { + return new ContextSelectorTreeViewer(parent, style); + } + + /** + * Configure the tree viewer control. + * + * @param viewer The tree viewer instance. Must not be <code>null</code>. + */ + protected void doConfigureTreeViewerControl(CheckboxTreeViewer viewer) { + Assert.isNotNull(viewer); + + viewer.setUseHashlookup(true); + + doConfigureTreeLayoutData(viewer.getTree()); + doConfigureTreeContentAndLabelProvider(viewer); + viewer.setSorter(doCreateViewerSorter()); + + if ((getTreeViewerStyle() & SWT.CHECK) != 0 && getViewerCheckStateListener(viewer) != null) { + viewer.addCheckStateListener(getViewerCheckStateListener(viewer)); + } + + if (hasViewerFilters()) { + viewer.setFilters(getViewerFilters()); + } + viewer.addSelectionChangedListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + selection = event.getSelection(); + fireSelectionChanged(); + } + }); + doCreateControlDecoration(viewer.getTree()); + } + + /** + * Configure the tree's layout data. The layout data set to the tree must be of type + * <code>GridData</code>. + * + * @param tree The tree to configure. Must not be <code>null</code>. + */ + protected void doConfigureTreeLayoutData(Tree tree) { + GridData layoutData = new GridData(GridData.FILL_HORIZONTAL); + layoutData.heightHint = 150; + tree.setLayoutData(layoutData); + } + + /** + * Returns the style bits to apply to the tree viewer. + * <p> + * The default set tree viewer style bits are: + * <ul> + * <li>SWT.SINGLE</li> + * <li>SWT.H_SCROLL</li> + * <li>SWT.V_SCROLL</li> + * <li>SWT.BORDER</li> + * + * @return The style bits to apply to the tree viewer. + */ + protected int getTreeViewerStyle() { + return SWT.CHECK | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.MULTI; + } + + /** + * Creates the viewer sorter instance to associated to the controls tree viewer. + * + * @return The viewer sorter to associate or <code>null</code> if none. + */ + protected ViewerSorter doCreateViewerSorter() { + return new CommonViewerSorter(); + } + + /** + * Returns if or if not to associate viewer filters to the controls tree viewer. If this method + * returns <code>true</code>, {@link #doCreateViewerFilters()} must return not null! + * + * @return <code>True</code> if to associate viewer filters, <code>false</code> otherwise. + */ + protected boolean hasViewerFilters() { + return true; + } + + /** + * Creates a returns a new set of the viewer filters to associated to the controls tree viewer. + * This method will be called from {@link #getViewerFilters()} in case the method + * {@link #hasViewerFilters()} returns <code>true</code> and no viewer filters got created + * before. + * + * @return The viewer filters to associate or <code>null</code> if none. + */ + protected ViewerFilter[] doCreateViewerFilters() { + return new ViewerFilter[] { new ContextSelectorViewerFilter() }; + } + + /** + * Returns the associated viewer filters of the controls tree viewer. If the control does have + * viewer filters ({@link #hasViewerFilters()} returns <code>true</code>) and the viewer filters + * had not yet been created, the method calls {@link #doCreateViewerFilters()}. + * + * @return The associated viewer filters of the controls tree viewer or the constant + * {@link #NO_FILTERS}. + */ + protected ViewerFilter[] getViewerFilters() { + if (filters == null && hasViewerFilters()) { + filters = doCreateViewerFilters(); + } + return filters != null ? filters : NO_FILTERS; + } + + /** + * Creates a new checkbox tree viewer check state listener. This method will be called from + * {@link #getViewerCheckStateListener()} in case the listener did not got created before. + * + * @param viewer The checkbox tree viewer. Must not be <code>null</code>. + * @return The checkbox tree viewer check state listener or <code>null</code> if none. + */ + protected ICheckStateListener doCreateViewerCheckStateListener(CheckboxTreeViewer viewer) { + Assert.isNotNull(viewer); + return new ContextSelectedCheckStateListener(); + } + + /** + * Returns the associated checkbox tree viewer check state listener. If the listener had not yet + * been created, the method calls {@link #doCreateLabelProvider()}. + * + * @param viewer The checkbox tree viewer. Must not be <code>null</code>. + * @return The associated checkbox tree viewer check state listener or <code>null</code> if + * none. + */ + protected ICheckStateListener getViewerCheckStateListener(CheckboxTreeViewer viewer) { + Assert.isNotNull(viewer); + if (listener == null) { + listener = doCreateViewerCheckStateListener(viewer); + } + return listener; + } + + /** + * Returns the initial input object to set to the controls tree viewer. + * + * @return The initial viewer input to set or <code>null</code> if none. + */ + protected abstract Object getInitialViewerInput(); + + /** + * Configure content and label provider. + * @param viewer The tree viewer. + */ + protected abstract void doConfigureTreeContentAndLabelProvider(TreeViewer viewer); + + /** + * Enables the tree control. + * + * @param viewer The tree viewer object. Must not be <code>null</code>. + */ + protected void doEnableControl(TreeViewer viewer) { + Assert.isNotNull(viewer); + SWTControlUtil.setEnabled(viewer.getTree(), viewer.getTree().getItemCount() > 0); + } + + /** + * Refresh the controls viewer and check the viewers enablement. This method is called by the + * standard refresh toolbar item, if the control has a toolbar. + */ + public void refresh() { + if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) { + viewer.refresh(true); + doEnableControl(viewer); + } + } + + /** + * Called from {@link #setupPanel(Composite)} before returning the control to the caller. + * Override to plug-in and configure any custom listener the subclassed control might need. + */ + protected void configureControlListener() { + } + + /** + * Sets the given selection to the viewer. + * + * @param selection The selection to set. Must not be <code>null</code>. + */ + @Override + public void setSelection(ISelection selection) { + Assert.isNotNull(selection); + this.selection = selection; + if (viewer != null) { + viewer.setSelection(selection, true); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection() + */ + @Override + public ISelection getSelection() { + return selection; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener) + */ + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + if (listener != null && !selectionListeners.contains(listener)) { + selectionListeners.add(listener); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener) + */ + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + if (listener != null) { + selectionListeners.remove(listener); + } + } + + /** + * Fire the selection changed event to the registered listeners. + */ + /* default */ void fireSelectionChanged() { + if (selection != null) { + SelectionChangedEvent event = new SelectionChangedEvent(this, selection); + for (ISelectionChangedListener listener : selectionListeners) { + listener.selectionChanged(event); + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.ui.controls.BaseControl#isValid() + */ + @Override + public boolean isValid() { + boolean valid = super.isValid(); + if (!valid) return valid; + + // If there is a last failure cause set, show that failure cause + valid = getLastFailureCause() == null; + if (!valid) { + setMessage(getLastFailureCause().getLocalizedMessage(), IMessageProvider.ERROR); + } + + // The remote context selector control is only valid, if at least one + // element has been checked (if operating with CHECK style set) + if (valid && (getTreeViewerStyle() & SWT.CHECK) != 0 && requireSelection) { + int count = getCheckedModelContexts().length; + valid = count == 1 || (count > 1 && getPropertiesContainer().isProperty(PROPERTY_MULTI_CONTEXT_SELECTOR, true)); + + // if we are not valid here, it can only mean, that there is + // no connectable checked. + if (!valid) { + String messageId = "AbstractContextSelectorControl_error_noContextSelected"; //$NON-NLS-1$ + if (getPropertiesContainer().isProperty(PROPERTY_MULTI_CONTEXT_SELECTOR, true)) { + messageId += "_multi"; //$NON-NLS-1$ + } + else { + messageId += "_single"; //$NON-NLS-1$ + } + + setMessage(getMessageForId(messageId), getMessageTypeForId(messageId, IMessageProvider.ERROR)); + } + } + + if (getControlDecoration() != null) { + // Setup and show the control decoration if necessary + if (isEnabled() && (!valid || (getMessage() != null && getMessageType() != IMessageProvider.NONE))) { + // Update the control decorator + updateControlDecoration(getMessage(), getMessageType()); + } else { + updateControlDecoration(null, IMessageProvider.NONE); + } + } + + + return valid; + } + + /** + * Returns the message text for the given message id. Subclass in case different + * message text should be used for standard messages. + * + * @param messageId The message id. Must not be <code>null</code>. + * @return The message text. + */ + protected String getMessageForId(String messageId) { + Assert.isNotNull(messageId); + return Messages.getString(messageId); + } + + /** + * Returns the message type for the given message id. Subclass in case different + * message types should by used for standard messages. The default implementation + * returns the proposed message type unchanged. + * + * @param messageId The message id. Must not be <code>null</code>. + * @param proposed The proposed message type. + * @return The message type for the given message id. + */ + protected int getMessageTypeForId(String messageId, int proposed) { + Assert.isNotNull(messageId); + return proposed; + } + + /** + * Configures whether a selection is required or not. + */ + public void setRequireSelection(boolean value) { + requireSelection = value; + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/interfaces/ImageConsts.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/interfaces/ImageConsts.java index c54db9908..3e8aa4fa5 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/interfaces/ImageConsts.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/interfaces/ImageConsts.java @@ -23,6 +23,12 @@ public interface ImageConsts { public final static String IMAGE_DIR_ROOT = "icons/"; //$NON-NLS-1$ /** + * The directory where to load enabled local toolbar images from, + * relative to the image root directory. + */ + public final static String IMAGE_DIR_ELCL = "elcl16/"; //$NON-NLS-1$ + + /** * The directory where to load view related images from, relative to * the image root directory. */ @@ -48,7 +54,12 @@ public interface ImageConsts { // ***** The image constants ***** - /** + /** + * The key to access the refresh action image (enabled). + */ + public static final String ACTION_Refresh_Enabled = "RefreshAction_enabled"; //$NON-NLS-1$ + + /** * The key to access the editor image. */ public static final String EDITOR = "Editor"; //$NON-NLS-1$ diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/nls/Messages.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/nls/Messages.java index a6cb81811..799b05446 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/nls/Messages.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/nls/Messages.java @@ -9,6 +9,8 @@ *******************************************************************************/ package org.eclipse.tcf.te.ui.views.nls; +import java.lang.reflect.Field; + import org.eclipse.osgi.util.NLS; /** @@ -27,6 +29,42 @@ public class Messages extends NLS { NLS.initializeMessages(BUNDLE_NAME, Messages.class); } + /** + * Returns the corresponding string for the given externalized strings + * key or <code>null</code> if the key does not exist. + * + * @param key The externalized strings key or <code>null</code>. + * @return The corresponding string or <code>null</code>. + */ + public static String getString(String key) { + if (key != null) { + try { + Field field = Messages.class.getDeclaredField(key); + return (String)field.get(null); + } catch (Exception e) { /* ignored on purpose */ } + } + + return null; + } + + /** + * Returns if or if not this NLS manager contains a constant for + * the given externalized strings key. + * + * @param key The externalized strings key or <code>null</code>. + * @return <code>True</code> if a constant for the given key exists, <code>false</code> otherwise. + */ + public static boolean hasString(String key) { + if (key != null) { + try { + Field field = Messages.class.getDeclaredField(key); + return field != null; + } catch (NoSuchFieldException e) { /* ignored on purpose */ } + } + + return false; + } + // **** Declare externalized string id's down here ***** public static String NewActionProvider_NewMenu_label; @@ -66,4 +104,11 @@ public class Messages extends NLS { public static String UpdateActiveFiltersOperation_OperationName; public static String ViewsUtil_reopen_error; + + public static String AbstractContextSelectorControl_error_noContextSelected_single; + public static String AbstractContextSelectorControl_error_noContextSelected_multi; + + public static String AbstractContextSelectorSection_toolbar_refresh_tooltip; + public static String AbstractContextSelectorSection_title; + } diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/nls/Messages.properties index f39f0ecd9..7d3e01360 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/nls/Messages.properties +++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/nls/Messages.properties @@ -49,5 +49,10 @@ ConfigFiltersHandler_PromptMessage=Select the filters to apply (matching items w UpdateActiveExtensionsOperation_OperationName=Update CommonViewer Extensions UpdateActiveFiltersOperation_OperationName=Update CommonViewer Filters - ViewsUtil_reopen_error=Failed to reopen editor. + +AbstractContextSelectorControl_error_noContextSelected_single=Please select a remote context. +AbstractContextSelectorControl_error_noContextSelected_multi=Please select one or more remote contexts. + +AbstractContextSelectorSection_toolbar_refresh_tooltip=Refresh +AbstractContextSelectorSection_title=Remote Context diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/sections/AbstractContextSelectorSection.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/sections/AbstractContextSelectorSection.java new file mode 100644 index 000000000..891f883c0 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.views/src/org/eclipse/tcf/te/ui/views/sections/AbstractContextSelectorSection.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2012 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.sections; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tcf.te.ui.forms.parts.AbstractSection; +import org.eclipse.tcf.te.ui.interfaces.data.IDataExchangeNode; +import org.eclipse.tcf.te.ui.views.activator.UIPlugin; +import org.eclipse.tcf.te.ui.views.controls.AbstractContextSelectorControl; +import org.eclipse.tcf.te.ui.views.interfaces.ImageConsts; +import org.eclipse.tcf.te.ui.views.nls.Messages; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.widgets.ExpandableComposite; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.Section; + +/** + * Context selector section implementation. + */ +public abstract class AbstractContextSelectorSection extends AbstractSection implements IDataExchangeNode { + + // Reference to the section sub controls + protected AbstractContextSelectorControl selector; + + /** + * Context selector control refresh action implementation. + */ + protected class RefreshAction extends Action { + + /** + * Constructor. + */ + public RefreshAction() { + super(null, IAction.AS_PUSH_BUTTON); + setImageDescriptor(UIPlugin.getImageDescriptor(ImageConsts.ACTION_Refresh_Enabled)); + setToolTipText(Messages.AbstractContextSelectorSection_toolbar_refresh_tooltip); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.action.Action#run() + */ + @Override + public void run() { + if (selector != null && selector.getViewer() != null) { + selector.getViewer().refresh(); + } + } + } + + /** + * Constructor. + * + * @param form The parent managed form. Must not be <code>null</code>. + * @param parent The parent composite. Must not be <code>null</code>. + * @param + */ + public AbstractContextSelectorSection(IManagedForm form, Composite parent, int style) { + super(form, parent, style); + getSection().setBackground(parent.getBackground()); + createClient(getSection(), form.getToolkit()); + } + + /** + * Constructor. + * + * @param form The parent managed form. Must not be <code>null</code>. + * @param parent The parent composite. Must not be <code>null</code>. + */ + public AbstractContextSelectorSection(IManagedForm form, Composite parent) { + this(form, parent, ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#createClient(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit) + */ + @Override + protected void createClient(Section section, FormToolkit toolkit) { + Assert.isNotNull(section); + Assert.isNotNull(toolkit); + + // Configure the section + section.setText(Messages.AbstractContextSelectorSection_title); + if (section.getParent().getLayout() instanceof GridLayout) { + section.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + } + + // Create the section client + Composite client = createClientContainer(section, 1, toolkit); + Assert.isNotNull(client); + section.setClient(client); + client.setBackground(section.getBackground()); + + // Create a toolbar for the section + createSectionToolbar(section, toolkit); + + // Create the section sub controls + selector = doCreateContextSelector(); + selector.setFormToolkit(toolkit); + selector.setupPanel(client); + + // Mark the control update as completed now + setIsUpdating(false); + } + + /** + * Create the context selector control. + * @return The context selector control. + */ + protected abstract AbstractContextSelectorControl doCreateContextSelector(); + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#getAdapter(java.lang.Class) + */ + @Override + public Object getAdapter(Class adapter) { + if (AbstractContextSelectorControl.class.isAssignableFrom(adapter)) { + return selector; + } + return super.getAdapter(adapter); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.forms.AbstractFormPart#dispose() + */ + @Override + public void dispose() { + if (selector != null) { selector.dispose(); selector = null; } + super.dispose(); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#createSectionToolbarItems(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit, org.eclipse.jface.action.ToolBarManager) + */ + @Override + protected void createSectionToolbarItems(Section section, FormToolkit toolkit, ToolBarManager tlbMgr) { + super.createSectionToolbarItems(section, toolkit, tlbMgr); + tlbMgr.add(new RefreshAction()); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#isValid() + */ + @Override + public boolean isValid() { + boolean valid = super.isValid(); + + if (valid) { + valid = selector.isValid(); + if (!valid) { + setMessage(selector.getMessage(), selector.getMessageType()); + } + } + + return valid; + } +} |