diff options
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tm.te.ui/src/org')
16 files changed, 2466 insertions, 0 deletions
diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/AbstractViewerComparator.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/AbstractViewerComparator.java new file mode 100644 index 000000000..4d4e1e9fe --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/AbstractViewerComparator.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui; + +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; + +/** + * Target Explorer: Common viewer comparator implementation. + */ +public abstract class AbstractViewerComparator extends ViewerComparator { + // Reference to the viewer + private final Viewer fViewer; + + /** + * Constructor. + * + * @param viewer The parent viewer. Must be not <code>null</code>. + */ + public AbstractViewerComparator(Viewer viewer) { + assert viewer != null; + fViewer = viewer; + } + + /** + * Returns the parent viewer instance. + * + * @return The parent viewer instance. + */ + protected final Viewer getParentViewer() { + return fViewer; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + */ + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) { + return doCompare(e1, e2, doGetSortColumnLabel(viewer), doGetSortColumnIndex(viewer) , doDetermineInverter(viewer)); + } + return super.compare(viewer, e1, e2); + } + + /* (non-Javadoc) + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(Object o1, Object o2) { + return doCompare(o1, o2, null, -1, doDetermineInverter(getParentViewer())); + } + + /** + * Compare the given nodes by the given sort column and inverter. + * + * @param node1 The first node or <code>null</code>. + * @param node2 The second node or <code>null</code>. + * @param sortColumn The sort column text or <code>null</code>. + * @param index The sort column index or <code>-1</code>. + * @param inverter The inverter. + * + * @return The compare result. + */ + protected abstract int doCompare(Object node1, Object node2, String sortColumn, int index, int inverter); + + /** + * Returns the text to compare for the given node and column index. + * + * @param node The node or <code>null</code>. + * @param index The column index or <code>-1</code>. + * + * @return The text for the given node and column index or <code>null</code>. + */ + protected abstract String doGetText(Object node, int index); + + /** + * Determine if or if not the sort direction needs to be inverted. + * + * @param viewer The viewer or <code>null</code>. + * @return <code>1</code> for original sort order, or <code>-1</code> for inverted sort order. + */ + protected abstract int doDetermineInverter(Viewer viewer); + + /** + * Return the label of the sort column of the given viewer. + * + * @param viewer The viewer or <code>null</code>. + * @return The label of the sort column or an empty string. + */ + protected abstract String doGetSortColumnLabel(Viewer viewer); + + /** + * Return the index of the sort column of the given viewer. + * + * @param viewer The viewer or <code>null</code>. + * @return The index of the sort column or <code>-1</code>. + */ + protected abstract int doGetSortColumnIndex(Viewer viewer); +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/WorkbenchPartControl.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/WorkbenchPartControl.java new file mode 100644 index 000000000..6dab3be48 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/WorkbenchPartControl.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui; + +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tm.te.ui.activator.UIPlugin; +import org.eclipse.tm.te.ui.forms.CustomFormToolkit; +import org.eclipse.ui.ISelectionService; +import org.eclipse.ui.IWorkbenchPart; + + +/** + * Target Explorer: Common workbench part control implementation. + */ +public class WorkbenchPartControl extends PlatformObject { + /** + * Reference to the parent workbench part the control might be embedded in. + */ + private final IWorkbenchPart fParentPart; + + /** + * Reference to the form toolkit instance provided via {@link #setupFormPanel(Composite, CustomFormToolkit)}. + */ + private CustomFormToolkit fFormToolkit = null; + + /** + * Reference to the parent control. + */ + private Composite fParentControl; + + /** + * Constructor. + */ + public WorkbenchPartControl() { + this(null); + } + + /** + * Constructor. + * + * @param parentPart The parent workbench part this control is embedded in or <code>null</code>. + */ + public WorkbenchPartControl(IWorkbenchPart parentPart) { + super(); + fParentPart = parentPart; + } + + /** + * Returns the parent workbench part the control might be embedded in. + * + * @return The parent workbench part or <code>null</code>. + */ + public final IWorkbenchPart getParentPart() { + return fParentPart; + } + + /** + * Returns if the <code>setupPanel(...)</code> method has been called at least once with + * a non-null parent control. + * + * @return <code>true</code> if the associated parent control is not <code>null</code>, <code>false</code> otherwise. + */ + public final boolean isControlCreated() { + return (fParentControl != null); + } + + /** + * Returns the parent control of the control. + * + * @return The parent control or <code>null</code>. + */ + public final Composite getParentControl() { + return fParentControl; + } + + /** + * Cleanup all resources the control might have been created. + */ + public void dispose() { + fParentControl = null; + } + + /** + * Creates the controls UI elements. + * + * @param parent The parent composite. Must not be <code>null</code>. + * @param toolkit The {@link CustomFormToolkit} instance. Must be not <code>null</code>. + */ + public void setupFormPanel(Composite parent, CustomFormToolkit toolkit) { + assert parent != null && toolkit != null; + fParentControl = parent; + fFormToolkit = toolkit; + } + + /** + * Returns the associated form toolkit instance. + * + * @return The form toolkit instance or <code>null</code> if not initialized yet. + */ + protected final CustomFormToolkit getFormToolkit() { + return fFormToolkit; + } + + /** + * Returns the selection service of the workbench. + * + * @return The selection service or <code>null</code>. + */ + protected final ISelectionService getSelectionService() { + ISelectionService selectionService = null; + // Check if plugin, workbench and active workbench window are still valid + if (UIPlugin.getDefault() != null && UIPlugin.getDefault().getWorkbench() != null + && UIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow() != null) { + selectionService = UIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getSelectionService(); + } + return selectionService; + } + +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/activator/UIPlugin.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/activator/UIPlugin.java new file mode 100644 index 000000000..d8d6e1e1b --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/activator/UIPlugin.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.activator; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class UIPlugin extends AbstractUIPlugin { + // The shared instance + private static UIPlugin plugin; + + /** + * The constructor + */ + public UIPlugin() { + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static UIPlugin getDefault() { + return plugin; + } + + /** + * Convenience method which returns the unique identifier of this plugin. + */ + public static String getUniqueIdentifier() { + if (getDefault() != null && getDefault().getBundle() != null) { + return getDefault().getBundle().getSymbolicName(); + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/dialogs/CustomTrayDialog.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/dialogs/CustomTrayDialog.java new file mode 100644 index 000000000..702bab109 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/dialogs/CustomTrayDialog.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.dialogs; + +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.TrayDialog; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tm.te.ui.activator.UIPlugin; +import org.eclipse.ui.PlatformUI; + + +/** + * Target Explorer: Custom tray dialog implementation. + */ +public class CustomTrayDialog extends TrayDialog { + private String fContextHelpId = null; + + // the dialog storage + private IDialogSettings fDialogSettings; + + /** + * Constructor. + * + * @param shell The parent shell or <code>null</code>. + */ + public CustomTrayDialog(Shell shell) { + this(shell, null); + } + + /** + * Constructor. + * + * @param shell The parent shell or <code>null</code>. + * @param contextHelpId The dialog context help id or <code>null</code>. + */ + public CustomTrayDialog(Shell shell, String contextHelpId) { + super(shell); + initializeDialogSettings(); + setContextHelpId(contextHelpId); + } + + /** + * Configure the dialogs context help id. + * + * @param contextHelpId The context help id or <code>null</code>. + */ + protected void setContextHelpId(String contextHelpId) { + fContextHelpId = contextHelpId; + setHelpAvailable(fContextHelpId != null); + } + + /** + * Initialize the dialog settings storage. + */ + protected void initializeDialogSettings() { + IDialogSettings settings = doGetDialogSettingsToInitialize(); + assert settings != null; + IDialogSettings section = settings.getSection(getDialogSettingsSectionName()); + if (section == null) { + section = settings.addNewSection(getDialogSettingsSectionName()); + } + setDialogSettings(section); + } + + /** + * Returns the dialog settings container to use and to initialize. This + * method is called from <code>initializeDialogSettings</code> and allows + * overriding the dialog settings container without changing the dialog + * settings structure. + * + * @return The dialog settings container to use. Must be not <code>null</code>. + */ + protected IDialogSettings doGetDialogSettingsToInitialize() { + return UIPlugin.getDefault().getDialogSettings(); + } + + /** + * Returns the section name to use for separating different persistent + * dialog settings from different dialogs. + * + * @return The section name used to store the persistent dialog settings within the plugins persistent + * dialog settings store. + */ + public String getDialogSettingsSectionName() { + return "CustomTrayDialog"; //$NON-NLS-1$ + } + + /** + * Returns the associated dialog settings storage. + * + * @return The dialog settings storage. + */ + public IDialogSettings getDialogSettings() { + // The dialog settings may not been initialized here. Initialize first in this case + // to be sure that we do have always the correct dialog settings. + if (fDialogSettings == null) { + initializeDialogSettings(); + } + return fDialogSettings; + } + + /** + * Sets the associated dialog settings storage. + * + * @return The dialog settings storage. + */ + public void setDialogSettings(IDialogSettings dialogSettings) { + fDialogSettings = dialogSettings; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createDialogArea(Composite parent) { + if (fContextHelpId != null) { + PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, fContextHelpId); + } + + // Let the super implementation create the dialog area control + Control control = super.createDialogArea(parent); + // But fix the layout data for the top control + if (control instanceof Composite) { + configureDialogAreaControl((Composite)control); + } + + return control; + } + + /** + * Configure the dialog top control. + * + * @param composite The dialog top control. Must be not <code>null</code>. + */ + protected void configureDialogAreaControl(Composite composite) { + assert composite != null; + Layout layout = composite.getLayout(); + if (layout == null || layout instanceof GridLayout) { + composite.setLayout(new GridLayout()); + } + } + + /** + * Cleanup when dialog is closed. + */ + protected void dispose() { + fDialogSettings = null; + } + + /** + * Cleanup the Dialog and close it. + */ + @Override + public boolean close() { + dispose(); + return super.close(); + } + + /** + * Sets the title for this dialog. + * + * @param title The title. + */ + public void setDialogTitle(String title) { + if (getShell() != null && !getShell().isDisposed()) { + getShell().setText(title); + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/forms/CustomFormToolkit.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/forms/CustomFormToolkit.java new file mode 100644 index 000000000..5cb96b578 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/forms/CustomFormToolkit.java @@ -0,0 +1,393 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.forms; + +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.forms.events.ExpansionEvent; +import org.eclipse.ui.forms.events.IExpansionListener; +import org.eclipse.ui.forms.widgets.ExpandableComposite; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.forms.widgets.Section; + +/** + * Target Explorer: Custom form toolkit for using form elements within + * dialog and wizard pages, or other containers. + */ +public class CustomFormToolkit extends PlatformObject { + // The reference of the wrapped toolkit + private final FormToolkit fToolkit; + + /** + * Constructor. + * + * @param toolkit The {@link FormToolkit} instance to wrap. Must not be <code>null</code>. + */ + public CustomFormToolkit(FormToolkit toolkit) { + super(); + assert toolkit != null; + fToolkit = toolkit; + } + + /** + * Returns the wrapped {@link FormToolkit} instance. + * + * @return The wrapped {@link FormToolkit} instance. + */ + public final FormToolkit getFormToolkit() { + return fToolkit; + } + + /** + * Dispose the form toolkit wrapper. + */ + public void dispose() { + fToolkit.dispose(); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class) + */ + @Override + @SuppressWarnings("rawtypes") + public Object getAdapter(Class adapter) { + if (FormToolkit.class.isAssignableFrom(adapter)) + return getFormToolkit(); + + return super.getAdapter(adapter); + } + + /** + * Returns the number of pixels corresponding to the height of the given + * number of characters. + * <p> + * This methods uses the static {@link Dialog#convertHeightInCharsToPixels(org.eclipse.swt.graphics.FontMetrics, int)} + * method for calculation. + * <p> + * @param chars The number of characters + * @return The corresponding height in pixels + */ + protected int convertHeightInCharsToPixels(Control control, int chars) { + int height = 0; + if (control != null && !control.isDisposed()) { + GC gc = new GC(control); + gc.setFont(JFaceResources.getDialogFont()); + height = Dialog.convertHeightInCharsToPixels(gc.getFontMetrics(), chars); + gc.dispose(); + } + + return height; + } + + /** + * Creates a new scrollable form container within the given parent. If + * <code>overwriteBackground</code> is set, the parent background color + * and background image is applied to the created scrollable form. + * + * @param parent The parent composite. Must not be <code>null</code>. + * @param title The form title or <code>null</code> if none. + * @param overwriteBackground If <code>true</code>, the parent background color and image are applied to the scrollable form. + * + * @return The scrollable form instance. + */ + public ScrolledForm createScrolledForm(Composite parent, String title, boolean overwriteBackground) { + assert parent != null; + + // Create the scrolled form which is the scrollable container for the expandable composite + final ScrolledForm scrollableForm = getFormToolkit().createScrolledForm(parent); + + // Overwrite background color and image if requested + if (overwriteBackground) { + scrollableForm.setBackground(parent.getBackground()); + scrollableForm.setBackgroundImage(parent.getBackgroundImage()); + } + + // If a title is given, set and decorate the header + if (title != null && scrollableForm.getForm() != null) { + scrollableForm.getForm().setText(title); + getFormToolkit().decorateFormHeading(scrollableForm.getForm()); + } + + return scrollableForm; + } + + /** + * Creates an expandable composite within the given parent scrollable form using the given title. + * If <code>overwriteBackground</code> is set, the parent background color and background image + * is applied to the created expandable composite. + * + * @param scrolledForm The parent scrolled form. Must not be <code>null</code>. + * @param title The expandable composite title. Must not be <code>null</code>. + * @param entriesToShow The number of entries to show within the expanded area. Must be greater than 0. + * @param expanded The initial expanded state of the expandable composite. + * @param overwriteBackground If <code>true</code>, the parent background color and image are applied to the expandable composite. + * + * @return The expandable composite. + */ + public final ExpandableComposite createExpandableComposite(final ScrolledForm scrolledForm, + String title, final int entriesToShow, + boolean expanded, boolean overwriteBackground) { + assert scrolledForm != null && title != null && entriesToShow > 0; + + // Create the expandable composite within the scrollable container + final ExpandableComposite expandable = getFormToolkit().createExpandableComposite(scrolledForm.getBody(), ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT); + expandable.setText(title); + + // Overwrite background color and image if requested + if (overwriteBackground) { + expandable.setBackground(scrolledForm.getBackground()); + expandable.setBackgroundImage(scrolledForm.getBackgroundImage()); + } + + expandable.setLayout(new GridLayout()); + expandable.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Create an associate an expansion listener to the expandable form + expandable.addExpansionListener(new IExpansionListener() { + boolean notExpanded = true; + + /* (non-Javadoc) + * @see org.eclipse.ui.forms.events.IExpansionListener#expansionStateChanged(org.eclipse.ui.forms.events.ExpansionEvent) + */ + public void expansionStateChanged(ExpansionEvent e) { + // Always set the scrolled form to re-flow. Otherwise it wouldn't + // re-arrange the controls following this expandable composite on + // collapse. + scrolledForm.reflow(true); + + // Get the shell from the scrolled form. + Shell shell = scrolledForm.getShell(); + if (shell != null && !shell.isDisposed() && e.getState() && notExpanded) { + // And recalculate the bounds on expand + shell.setRedraw(false); + Rectangle shellBounds = shell.getBounds(); + + // Assume at minimum 4 controls within the expandable area. + shellBounds.height += convertHeightInCharsToPixels(expandable, Math.max(4, entriesToShow)) + IDialogConstants.VERTICAL_SPACING; + + shell.setBounds(shellBounds); + shell.setRedraw(true); + notExpanded = false; + } + } + + /* (non-Javadoc) + * @see org.eclipse.ui.forms.events.IExpansionListener#expansionStateChanging(org.eclipse.ui.forms.events.ExpansionEvent) + */ + public void expansionStateChanging(ExpansionEvent e) { + } + }); + + // Create the client area the caller can use as parent for the control + Composite client = getFormToolkit().createComposite(expandable); + client.setLayout(new GridLayout()); + + // Overwrite background color and image if requested + if (overwriteBackground) { + client.setBackground(scrolledForm.getBackground()); + client.setBackgroundImage(scrolledForm.getBackgroundImage()); + } + + // Set the initial expansion state + expandable.setExpanded(expanded); + // And associated the client + expandable.setClient(client); + + return expandable; + } + + /** + * Creates an expandable section within the given parent scrollable form using the given title. + * If <code>overwriteBackground</code> is set, the parent background color and background image + * is applied to the created section. + * + * @param parent The parent scrolled form. Must not be <code>null</code>. + * @param title The expandable composite title. Must not be <code>null</code>. + * @param entriesToShow The number of entries to show within the expanded area. Must be greater than 0. + * @param expanded The initial expanded state of the section. + * @param overwriteBackground If <code>true</code>, the parent background color and image are applied to the section. + * + * @return The section. + */ + public final Section createSection(final ScrolledForm scrolledForm, + String title, final int entriesToShow, + boolean expanded, boolean overwriteBackground) { + assert scrolledForm != null && title != null && entriesToShow > 0; + + // Create the section within the scrollable container + final Section section = getFormToolkit().createSection(scrolledForm.getBody(), ExpandableComposite.TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT); + section.setText(title); + + // Overwrite background color and image if requested + if (overwriteBackground) { + section.setBackground(scrolledForm.getBackground()); + section.setBackgroundImage(scrolledForm.getBackgroundImage()); + } + + section.setLayout(new GridLayout()); + section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Create an associate an expansion listener to the expandable form + section.addExpansionListener(new IExpansionListener() { + boolean notExpanded = true; + + /* (non-Javadoc) + * @see org.eclipse.ui.forms.events.IExpansionListener#expansionStateChanged(org.eclipse.ui.forms.events.ExpansionEvent) + */ + public void expansionStateChanged(ExpansionEvent e) { + // Always set the scrolled form to re-flow. Otherwise it wouldn't + // re-arrange the controls following this expandable composite on + // collapse. + scrolledForm.reflow(true); + + // Get the shell from the scrolled form. + Shell shell = scrolledForm.getShell(); + if (shell != null && !shell.isDisposed() && e.getState() && notExpanded) { + // And recalculate the bounds on expand + shell.setRedraw(false); + Rectangle shellBounds = shell.getBounds(); + + // Assume at minimum 4 controls within the expandable area. + shellBounds.height += convertHeightInCharsToPixels(section, Math.max(4, entriesToShow)) + IDialogConstants.VERTICAL_SPACING; + + shell.setBounds(shellBounds); + shell.setRedraw(true); + notExpanded = false; + } + } + + /* (non-Javadoc) + * @see org.eclipse.ui.forms.events.IExpansionListener#expansionStateChanging(org.eclipse.ui.forms.events.ExpansionEvent) + */ + public void expansionStateChanging(ExpansionEvent e) { + } + }); + + // Create the client area the caller can use as parent for the control + Composite client = getFormToolkit().createComposite(section); + client.setLayout(new GridLayout()); + + // Overwrite background color and image if requested + if (overwriteBackground) { + client.setBackground(scrolledForm.getBackground()); + client.setBackgroundImage(scrolledForm.getBackgroundImage()); + } + + // Set the initial expansion state + section.setExpanded(expanded); + // And associated the client + section.setClient(client); + + return section; + } + + /** + * Creates an non-expandable section within the given parent scrollable form using the given title. + * If <code>overwriteBackground</code> is set, the parent background color and background image + * is applied to the created section. + * + * @param parent The parent scrolled form. Must not be <code>null</code>. + * @param title The expandable composite title. Must not be <code>null</code>. + * @param overwriteBackground If <code>true</code>, the parent background color and image are applied to the section. + * + * @return The section. + */ + public final Section createSection(final ScrolledForm scrolledForm, String title, boolean overwriteBackground) { + assert scrolledForm != null && title != null; + + // Create the section within the scrollable container + final Section section = getFormToolkit().createSection(scrolledForm.getBody(), ExpandableComposite.TITLE_BAR | ExpandableComposite.CLIENT_INDENT); + section.setText(title); + + // Overwrite background color and image if requested + if (overwriteBackground) { + section.setBackground(scrolledForm.getBackground()); + section.setBackgroundImage(scrolledForm.getBackgroundImage()); + } + + // Configure the layout + section.setLayout(new GridLayout()); + section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Create the client area the caller can use as parent for the control + Composite client = getFormToolkit().createComposite(section); + client.setLayout(new GridLayout()); + + // Overwrite background color and image if requested + if (overwriteBackground) { + client.setBackground(section.getBackground()); + client.setBackgroundImage(section.getBackgroundImage()); + } + + // And associated the client + section.setClient(client); + + return section; + } + + /** + * Creates a composite with a highlighted note entry and a message text. + * This is designed to take up the full width of the page. + * + * @param parent The parent composite. Must not be <code>null</code>. + * @param title The note title. Must not be <code>null</code>. + * @param message The note message Must not be <code>null</code>. + * @param widthHint The note message width hint in pixel or <code>SWT.DEFAULT</code>. + * @param overwriteBackground If <code>true</code>, the parent background color and image are applied to the note composite. + * + * @return The note composite. + */ + public final Composite createNoteComposite(Composite parent, String title, String message, int widthHint, boolean overwriteBackground) { + assert parent != null && title != null && message != null; + + Composite composite = getFormToolkit().createComposite(parent, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + layout.marginHeight = 0; layout.marginWidth = 0; + composite.setLayout(layout); + composite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); + composite.setFont(parent.getFont()); + + Label noteLabel = getFormToolkit().createLabel(composite, title, SWT.BOLD); + noteLabel.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT)); + noteLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); + + Label messageLabel = getFormToolkit().createLabel(composite, message); + GridData layoutData = new GridData(GridData.FILL_HORIZONTAL); + layoutData.widthHint = widthHint; + messageLabel.setLayoutData(layoutData); + messageLabel.setFont(parent.getFont()); + + // Overwrite background color and image if requested + if (overwriteBackground) { + composite.setBackground(parent.getBackground()); + composite.setBackgroundImage(parent.getBackgroundImage()); + + noteLabel.setBackground(parent.getBackground()); + noteLabel.setBackgroundImage(parent.getBackgroundImage()); + + messageLabel.setBackground(parent.getBackground()); + messageLabel.setBackgroundImage(parent.getBackgroundImage()); + } + + return composite; + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/images/AbstractImageDescriptor.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/images/AbstractImageDescriptor.java new file mode 100644 index 000000000..626617b0a --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/images/AbstractImageDescriptor.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.images; + +import org.eclipse.jface.resource.CompositeImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Point; + +/** + * Target Explorer: Image descriptor for creating overlays. + */ +public abstract class AbstractImageDescriptor extends CompositeImageDescriptor { + + private String fKey; + private ImageRegistry fRegistry; + + public AbstractImageDescriptor(ImageRegistry reg) { + fRegistry = reg; + } + + protected void setKey(String key) { + fKey = key; + } + + public String getKey() { + return fKey; + } + + protected ImageRegistry getRegistry() { + return fRegistry; + } + + protected void drawCentered(String key, int width, int height) { + drawCentered(fRegistry.get(key), width, height); + } + + protected void drawCentered(Image image, int width, int height) { + if (image != null) { + ImageData imageData = image.getImageData(); + if (imageData != null) { + int x = StrictMath.max(0, (width - imageData.width + 1) / 2); + int y = StrictMath.max(0, (height - imageData.height + 1) / 2); + drawImage(imageData, x, y); + } + } + } + + protected void drawCenterRight(String key, int width, int height) { + Image baseImage = fRegistry.get(key); + if (baseImage != null) { + ImageData imageData = baseImage.getImageData(); + if (imageData != null) { + int x = StrictMath.max(0, width - imageData.width); + int y = StrictMath.max(0, (height - imageData.height + 1) / 2); + drawImage(imageData, x, y); + } + } + } + + protected void drawTopLeft(String key) { + Image baseImage = fRegistry.get(key); + if (baseImage != null) { + ImageData imageData = baseImage.getImageData(); + if (imageData != null) { + drawImage(imageData, 0, 0); + } + } + } + + protected void drawTopRight(String key, int width, int height) { + Image baseImage = fRegistry.get(key); + if (baseImage != null) { + ImageData imageData = baseImage.getImageData(); + if (imageData != null) { + int x = StrictMath.max(0, width - imageData.width); + drawImage(imageData, x, 0); + } + } + } + + protected void drawBottomCenter(String key, int width, int height) { + Image image = fRegistry.get(key); + if (image != null) { + ImageData imageData = image.getImageData(); + if (imageData != null) { + int x = StrictMath.max(0, (width - imageData.width + 1) / 2); + int y = StrictMath.max(0, height - imageData.height); + drawImage(imageData, x, y); + } + } + } + + protected void drawBottomLeft(String key) { + if (getSize() != null) { + Point size = getSize(); + drawBottomLeft(key, size.x, size.y); + } else { + // the default eclipse style guide recommendation is 16x16 + drawBottomLeft(key, 16, 16); + } + } + + protected void drawBottomLeft(String key, int width, int height) { + Image image = fRegistry.get(key); + if (image != null) { + ImageData imageData = image.getImageData(); + if (imageData != null) { + int y = StrictMath.max(0, height - imageData.height); + drawImage(imageData, 0, y); + } + } + } + + protected void drawCenterLeft(String key, int width, int height) { + Image image = fRegistry.get(key); + if (image != null) { + ImageData imageData = image.getImageData(); + if (imageData != null) { + int y = StrictMath.max(0, (height - imageData.height) / 2); + drawImage(imageData, 0, y); + } + } + } + + protected void drawBottomRight(String key) { + if (getSize() != null) { + Point size = getSize(); + drawBottomRight(key, size.x, size.y); + } else { + // the default eclipse style guide recommendation is 16x16 + drawBottomRight(key, 16, 16); + } + } + + protected void drawBottomRight(String key, int width, int height) { + Image image = fRegistry.get(key); + if (image != null) { + ImageData imageData = image.getImageData(); + if (imageData != null) { + int x = StrictMath.max(0, width - imageData.width); + int y = StrictMath.max(0, height - imageData.height); + drawImage(imageData, x, y); + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.resource.CompositeImageDescriptor#getTransparentPixel() + */ + @Override + protected int getTransparentPixel() { + Image baseImage = getBaseImage(); + if (baseImage != null && baseImage.getImageData() != null) { + return baseImage.getImageData().transparentPixel; + } + return super.getTransparentPixel(); + } + + /** + * Returns the base image used for the combined image description. This + * method is called from <code>getTransparentPixel()</code> to query the + * transparent color of the palette. + * + * @return The base image or <code>null</code> if none. + */ + protected abstract Image getBaseImage(); +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/images/AbstractImageRegistry.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/images/AbstractImageRegistry.java new file mode 100644 index 000000000..849b7d875 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/images/AbstractImageRegistry.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.images; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.graphics.Image; +import org.eclipse.tm.te.ui.activator.UIPlugin; +import org.eclipse.tm.te.ui.nls.Messages; +import org.osgi.framework.Bundle; + + + +/** + * Target Explorer: Abstract image registry that allows for defining fallback paths for images. + */ +public abstract class AbstractImageRegistry extends ImageRegistry { + private List<ImageRegistry> fDelegates = new ArrayList<ImageRegistry>(); + private Map<String,String> fPlugins = new HashMap<String,String>(); + private Map<String,String[]> fLocations = new HashMap<String,String[]>(); + private URL fBaseUrl; + + protected AbstractImageRegistry(Plugin plugin) { + fBaseUrl = plugin.getBundle().getEntry("/"); //$NON-NLS-1$ + } + + /** + * Adds the given image registry as delegate. Delegates are queried if + * an image or image descriptor cannot be found locally. If the image + * registry delegate had been added before, the method will do nothing. + * + * @param registry The image registry. Must be not <code>null</code>. + */ + protected final void addImageRegistryDelegate(ImageRegistry registry) { + assert registry != null; + if (!fDelegates.contains(registry)) fDelegates.add(registry); + } + + /** + * Removes the given image registry from the list of delegates. + * + * @param registry The image registry. Must be not <code>null</code>. + */ + protected final void removeImageRegistryDelegate(ImageRegistry registry) { + assert registry != null; + fDelegates.remove(registry); + } + + /** + * Defines the key for a local image, that must be found below the icons directory + * in the plugin. + * @param key Key by which the image can be referred by. + * @param dir Directory relative to icons/ + * @param name The name of the file defining the icon. The name will be used as + * key. + */ + protected void localImage(String key, String dir, String name) { + if (dir== null || dir.equals(""))//$NON-NLS-1$ + fLocations.put(key, new String[] {"icons/" + name}); //$NON-NLS-1$ + else + fLocations.put(key, new String[] {"icons/" + dir + "/" + name}); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Defines the key for a non-local image, that must be found below the icons directory + * of some plugin. + * @param key Key by which the image can be referred by. + * @param plugin The plugin id, where the icon is searched. + * @param dirs A couple of directories below icons/ in the plugin. If loading fails, + * the next dir will be taken as fallback. + * @param name The name of the file defining the icon. The name will be used as + * key. + */ + protected void externalImage(String key, String plugin, String[] dirs, String name) { + if (plugin != null) { + fPlugins.put(key, plugin); + } + String[] locations = new String[dirs.length]; + for (int i = 0; i < dirs.length; i++) { + String dir = dirs[i]; + if (dir== null || dir.equals(""))//$NON-NLS-1$ + locations[i] = "icons/" + name; //$NON-NLS-1$ + else + locations[i] = "icons/" + dir + "/" + name; //$NON-NLS-1$ //$NON-NLS-2$ + } + fLocations.put(key, locations); + } + + final private Image internalDoGet(String key) { + // First query the parent (local) image registry if + // an image for the given key is registered. + Image i = super.get(key); + if (i != null) return i; + + // If no image had been returned, try the delegates + for (ImageRegistry delegate : fDelegates) { + i = delegate.get(key); + if (i != null) break; + } + + return i; + } + + final private ImageDescriptor internalDoGetDescriptor(String key) { + // First query the parent (local) image registry if + // an image for the given key is registered. + ImageDescriptor d = super.getDescriptor(key); + if (d != null) return d; + + // If no image had been returned, try the delegates + for (ImageRegistry delegate : fDelegates) { + d = delegate.getDescriptor(key); + if (d != null) break; + } + + return d; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.resource.ImageRegistry#get(java.lang.String) + */ + @Override + final public Image get(String key) { + Image i = internalDoGet(key); + if (i != null) { + return i; + } + + ImageDescriptor d = createFileImageDescriptor(key); + if (d != null) { + put(key, d); + return internalDoGet(key); + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.resource.ImageRegistry#getDescriptor(java.lang.String) + */ + @Override + final public ImageDescriptor getDescriptor(String key) { + ImageDescriptor d = internalDoGetDescriptor(key); + if (d != null) { + return d; + } + + d = createFileImageDescriptor(key); + if (d != null) { + put(key, d); + return d; + } + return null; + } + + private ImageDescriptor createFileImageDescriptor(String key) { + URL url = fBaseUrl; + String pluginId = fPlugins.get(key); + if (pluginId != null) { + Bundle bundle= Platform.getBundle(pluginId); + if (bundle != null) { + url = bundle.getEntry("/"); //$NON-NLS-1$ + } + } + String[] locations= fLocations.get(key); + if (locations != null) { + for (int i = 0; i < locations.length; i++) { + String loc = locations[i]; + URL full; + try { + full = new URL(url, loc); + ImageDescriptor candidate = ImageDescriptor.createFromURL(full); + if (candidate != null && candidate.getImageData() != null) { + return candidate; + } + } catch (MalformedURLException e) { + IStatus status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), + Messages.AbstractImageRegistry_error_malformedImage, e); + UIPlugin.getDefault().getLog().log(status); + } catch (SWTException e) { + // try the next one. + } + } + } + return null; + } + + /** + * Get a shared Image for a given descriptor + */ + public Image getSharedImage(AbstractImageDescriptor d) { + String key = d.getKey(); + Image shared = super.get(key); + if (shared != null) { + return shared; + } + put(key, d); + return super.get(key); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/IUIConstants.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/IUIConstants.java new file mode 100644 index 000000000..3ff613ac9 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/interfaces/IUIConstants.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.interfaces; + +import org.eclipse.tm.te.ui.activator.UIPlugin; + +/** + * Target Explorer: Common UI constants. + * + * @author uwe.stieber@windriver.com + */ +public interface IUIConstants { + + /** + * The Target Explorer common controls context menu id base part. + */ + public static final String ID_CONTROL_MENUS_BASE = UIPlugin.getUniqueIdentifier() + ".controls"; //$NON-NLS-1$ +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nls/Messages.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nls/Messages.java new file mode 100644 index 000000000..297235b6b --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nls/Messages.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.nls; + +import org.eclipse.osgi.util.NLS; + +/** + * Target Explorer: Common UI plugin externalized strings management. + */ +public class Messages extends NLS { + + // The plug-in resource bundle name + private static final String BUNDLE_NAME = "org.eclipse.tm.te.ui.nls.Messages"; //$NON-NLS-1$ + + /** + * Static constructor. + */ + static { + // Load message values from bundle file + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + // **** Declare externalized string id's down here ***** + + public static String AbstractImageRegistry_error_malformedImage; + + public static String NodePropertiesTableControl_section_title; + public static String NodePropertiesTableControl_section_title_noSelection; + public static String NodePropertiesTableControl_column_name_label; + public static String NodePropertiesTableControl_column_value_label; + + public static String PendingOperation_label; + + public static String EditBrowseTextControl_button_label; +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nls/Messages.properties new file mode 100644 index 000000000..b943bab0c --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nls/Messages.properties @@ -0,0 +1,15 @@ +# +# org.eclipse.tm.te.ui +# Externalized Strings. +# + +AbstractImageRegistry_error_malformedImage=Malformed Image + +NodePropertiesTableControl_section_title={0} Information +NodePropertiesTableControl_section_title_noSelection=Node +NodePropertiesTableControl_column_name_label=Property +NodePropertiesTableControl_column_value_label=Value + +PendingOperation_label=Pending... + +EditBrowseTextControl_button_label=Browse... diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nodes/PendingOperation.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nodes/PendingOperation.java new file mode 100644 index 000000000..1859001c6 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/nodes/PendingOperation.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.nodes; + +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.tm.te.ui.nls.Messages; + + +/** + * Target Explorer: Pending operation data node. + */ +public class PendingOperation extends PlatformObject { + + /** + * Returns the pending operation node name. + * + * @return The node name. + */ + public final String getName() { + return Messages.PendingOperation_label; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public final int hashCode() { + return getName().hashCode(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public final String toString() { + return getName(); + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/TableNode.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/TableNode.java new file mode 100644 index 000000000..e4daa98c9 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/TableNode.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.tables; + +import org.eclipse.core.runtime.PlatformObject; + +/** + * Target Explorer: Immutable representation of a table node. + */ +public final class TableNode extends PlatformObject { + /** + * The node name. + */ + public final String name; + + /** + * The node value. + */ + public final String value; + + /** + * Constructor. + * + * @param name The node name. Must be not <code>null</code>. + * @param value The node value. Must be not <code>null</code>. + */ + public TableNode(String name, String value) { + assert name != null && value != null; + this.name = name; + this.value = value; + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/TableViewerComparator.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/TableViewerComparator.java new file mode 100644 index 000000000..c00691b66 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/TableViewerComparator.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.tables; + +import java.util.Arrays; + +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Table; +import org.eclipse.tm.te.ui.AbstractViewerComparator; + + +/** + * Target Explorer: Common table control viewer comparator implementation. + */ +public class TableViewerComparator extends AbstractViewerComparator { + private final ITableLabelProvider fLabelProvider; + + /** + * Constructor. + * + * @param viewer The parent viewer. Must be not <code>null</code>. + * @param labelProvider The table label provider. Must be not <code>null</code>. + */ + public TableViewerComparator(Viewer viewer, ITableLabelProvider labelProvider) { + super(viewer); + assert labelProvider != null; + fLabelProvider = labelProvider; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doDetermineInverter(org.eclipse.jface.viewers.Viewer) + */ + @Override + protected int doDetermineInverter(Viewer viewer) { + int inverter = 1; + + // Viewer must be of type TableViewer and the table must not be disposed yet + if (viewer instanceof TableViewer && ((TableViewer)viewer).getTable() != null) { + Table table = ((TableViewer)viewer).getTable(); + if (!table.isDisposed() && table.getSortDirection() == SWT.DOWN) inverter = -1; + } + + return inverter; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doGetText(java.lang.Object, int) + */ + @Override + protected String doGetText(Object node, int index) { + if (node != null && fLabelProvider != null) { + return index != -1 ? fLabelProvider.getColumnText(node, index) : ((ILabelProvider)fLabelProvider).getText(node); + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doGetSortColumnLabel(org.eclipse.jface.viewers.Viewer) + */ + @Override + protected String doGetSortColumnLabel(Viewer viewer) { + // Viewer must be of type TableViewer and the table must not be disposed yet + if (viewer instanceof TableViewer && ((TableViewer)viewer).getTable() != null && !((TableViewer)viewer).getTable().isDisposed()) { + Table table = ((TableViewer)viewer).getTable(); + return table.getSortColumn() != null ? table.getSortColumn().getText() : ""; //$NON-NLS-1$ + } + return ""; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doGetSortColumnIndex(org.eclipse.jface.viewers.Viewer) + */ + @Override + protected int doGetSortColumnIndex(Viewer viewer) { + // Viewer must be of type TableViewer and the table must not be disposed yet + if (viewer instanceof TableViewer && ((TableViewer)viewer).getTable() != null && !((TableViewer)viewer).getTable().isDisposed()) { + Table table = ((TableViewer)viewer).getTable(); + return table.getSortColumn() != null ? Arrays.asList(table.getColumns()).indexOf(table.getSortColumn()) : -1; + } + return -1; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doCompare(java.lang.Object, java.lang.Object, java.lang.String, int, int) + */ + @SuppressWarnings("unchecked") + @Override + protected int doCompare(Object node1, Object node2, String sortColumn, int index, int inverter) { + if (node1 == null && node2 == null) return 0; + if (node1 != null && node2 == null) return 1; + if (node1 == null && node2 != null) return -1; + + // Get the labels + String text1 = doGetText(node1, index); + String text2 = doGetText(node2, index); + + // If the text is matching ".*[0-9]+$" -> compare numerical instead of alphabetical + if (text1 != null && text1.matches(".*[0-9]+$") && text2 != null && text2.matches(".*[0-9]+$")) { //$NON-NLS-1$ //$NON-NLS-2$ + // Split numbers and text (note that this effectively removes the number ... splitted[1] == ""). + String[] splitted1 = text1.split("[0-9]+$", 2); //$NON-NLS-1$ + String[] splitted2 = text2.split("[0-9]+$", 2); //$NON-NLS-1$ + + // Get the parts to match alphabetical + String alpha1 = splitted1[0]; + String alpha2 = splitted2[0]; + + // The numerical parts is what remains if we strip the alpha parts from the original text + String num1 = text1.replace(alpha1, ""); //$NON-NLS-1$ + String num2 = text2.replace(alpha2, ""); //$NON-NLS-1$ + + // Compare the alpha parts + int result = getComparator().compare(alpha1, alpha2) * inverter; + // Only if the alpha parts are equal, compare the numerical parts too + if (result == 0) { + result = Integer.decode(num1).compareTo(Integer.decode(num2)) * inverter; + } + + return result; + } + + // Compare the text alphabetical + return getComparator().compare(text1, text2) * inverter; + } +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/properties/NodePropertiesTableControl.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/properties/NodePropertiesTableControl.java new file mode 100644 index 000000000..4fe2f3d68 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/tables/properties/NodePropertiesTableControl.java @@ -0,0 +1,417 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.tables.properties; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TableLayout; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.tm.te.ui.WorkbenchPartControl; +import org.eclipse.tm.te.ui.forms.CustomFormToolkit; +import org.eclipse.tm.te.ui.interfaces.IUIConstants; +import org.eclipse.tm.te.ui.nls.Messages; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.forms.widgets.Section; + + +/** + * Target Explorer: Abstract node properties table control implementation. + */ +public abstract class NodePropertiesTableControl extends WorkbenchPartControl { + // Reference to the table viewer + private TableViewer fViewer; + // Reference to the selection changed listener + private ISelectionChangedListener fEditorSelectionChangedListener; + + // We remember the sorting order (ascending vs. descending) for each + // column separately. That way we can come up with the sort order switching + // correctly if the user changes from one column to the next. If set + // to Boolean.FALSE, the sort order for the column is descending (default) + private final Map<TableColumn, Boolean> fColumnSortOrder = new LinkedHashMap<TableColumn, Boolean>(); + + /** + * Default node properties table control selection changed listener implementation. + * The selection changed listener is registered to the editor tree control. + */ + protected class NodePropertiesTableControlSelectionChangedListener implements ISelectionChangedListener { + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) + */ + public void selectionChanged(SelectionChangedEvent event) { + if (getViewer() != null) { + getViewer().setInput(event.getSelection()); + } + } + } + + /** + * Constructor. + * + * @param parentPart The parent workbench part this control is embedded in or <code>null</code>. + */ + public NodePropertiesTableControl(IWorkbenchPart parentPart) { + super(parentPart); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.WorkbenchPartControl#dispose() + */ + @Override + public void dispose() { + // Dispose the editor tree control selection changed listener + if (fEditorSelectionChangedListener != null) { + ISelectionProvider selectionProvider = (ISelectionProvider)getParentPart().getAdapter(ISelectionProvider.class); + if (selectionProvider != null) { + selectionProvider.removeSelectionChangedListener(fEditorSelectionChangedListener); + fEditorSelectionChangedListener = null; + } + } + + super.dispose(); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.WorkbenchPartControl#setupFormPanel(org.eclipse.swt.widgets.Composite, org.eclipse.tm.te.ui.forms.CustomFormToolkit) + */ + @Override + public void setupFormPanel(Composite parent, CustomFormToolkit toolkit) { + super.setupFormPanel(parent, toolkit); + + // Create the table viewer + fViewer = doCreateTableViewer(parent); + // Configure the table viewer + configureTableViewer(fViewer); + // Configure the table + configureTable(fViewer.getTable(), fViewer.getComparator() != null); + + // Register the control as selection listener to the editor control + ISelectionProvider selectionProvider = getParentPart() != null ? (ISelectionProvider)getParentPart().getAdapter(ISelectionProvider.class) : null; + if (selectionProvider != null) { + // Create the selection changed listener instance + fEditorSelectionChangedListener = doCreateEditorSelectionChangedListener(); + selectionProvider.addSelectionChangedListener(fEditorSelectionChangedListener); + } + + // Prepare popup menu and toolbar + createContributionItems(fViewer); + + // Set the current selection as input + fViewer.setInput(selectionProvider != null ? selectionProvider.getSelection() : null); + } + + /** + * Creates a new editor tree control selection changed listener instance. + * + * @return The editor tree control selection changed listener instance. + */ + protected ISelectionChangedListener doCreateEditorSelectionChangedListener() { + return new NodePropertiesTableControlSelectionChangedListener(); + } + + /** + * Creates the table viewer instance. + * + * @param parent The parent composite. Must not be <code>null</code>. + * @return The table viewer. + */ + protected TableViewer doCreateTableViewer(Composite parent) { + assert parent != null; + + TableViewer tableViewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.MULTI | SWT.BORDER); + + return tableViewer; + } + + /** + * Configure the table Viewer. + * + * @param tableViewer The table viewer. Must not be <code>null</code>. + */ + protected void configureTableViewer(TableViewer tableViewer) { + assert tableViewer != null; + + tableViewer.setLabelProvider(doCreateTableViewerLabelProvider(tableViewer)); + tableViewer.setContentProvider(doCreateTableViewerContentProvider(tableViewer)); + tableViewer.setComparator(doCreateTableViewerComparator(tableViewer)); + } + + /** + * Creates the table viewer label provider instance. + * + * @param viewer The table viewer. Must be not <code>null</code>. + * @return The table viewer label provider instance. + */ + protected abstract ITableLabelProvider doCreateTableViewerLabelProvider(TableViewer viewer); + + /** + * Creates the table viewer content provider instance. + * + * @param viewer The table viewer. Must be not <code>null</code>. + * @return The table viewer content provider instance. + */ + protected abstract IStructuredContentProvider doCreateTableViewerContentProvider(TableViewer viewer); + + /** + * Creates the table viewer comparator instance. + * + * @param viewer The table viewer. Must be not <code>null</code>. + * @return The table viewer comparator instance or <code>null</code> to turn of sorting. + */ + protected ViewerComparator doCreateTableViewerComparator(TableViewer viewer) { + return null; + } + + /** + * Configure the table. + * + * @param table The table. Must not be <code>null</code>. + * @param sorted Specify <code>true</code> if the table shall support sorting, <code>false</code> otherwise. + */ + protected void configureTable(Table table, boolean sorted) { + assert table != null; + + // Create and configure the table columns + createTableColumns(table, sorted); + + table.setHeaderVisible(true); + table.setLinesVisible(true); + } + + /** + * Create the table columns. + * + * @param table The table. Must not be <code>null</code>. + * @param sorted Specify <code>true</code> if the table shall support sorting, <code>false</code> otherwise. + */ + protected void createTableColumns(final Table table, boolean sorted) { + assert table != null; + + TableColumn sortColumn = null; + + TableColumn column = new TableColumn(table, SWT.LEFT); + column.setText(Messages.NodePropertiesTableControl_column_name_label); + fColumnSortOrder.put(column, Boolean.TRUE); + if (sorted) column.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (e.widget instanceof TableColumn) { + switchSortColumn(table, (TableColumn)e.widget); + } + } + }); + // The property name is the default sorting column + sortColumn = column; + + column = new TableColumn(table, SWT.LEFT); + column.setText(Messages.NodePropertiesTableControl_column_value_label); + fColumnSortOrder.put(column, Boolean.FALSE); + if (sorted) column.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (e.widget instanceof TableColumn) { + switchSortColumn(table, (TableColumn)e.widget); + } + } + }); + + TableLayout tableLayout = new TableLayout(); + tableLayout.addColumnData(new ColumnWeightData(30)); + tableLayout.addColumnData(new ColumnWeightData(70)); + table.setLayout(tableLayout); + + GridData layoutData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING); + table.setLayoutData(layoutData); + + if (sorted) { + // set the default sort column + table.setSortColumn(sortColumn); + table.setSortDirection(fColumnSortOrder.get(sortColumn).booleanValue() ? SWT.UP : SWT.DOWN); + } + } + + /** + * Switches the sort order for the given column and set the + * new sort order and sort column to the given table. + * + * @param table The table. + * @param column The table column + */ + protected final void switchSortColumn(Table table, TableColumn column) { + if (table == null || table.isDisposed() || column == null || column.isDisposed()) { + return; + } + // Get the current sorting order for the given column + boolean newSortOrder = !fColumnSortOrder.get(column).booleanValue(); + // Set sort column and sort direction + table.setSortColumn(column); + table.setSortDirection(newSortOrder ? SWT.UP : SWT.DOWN); + // And update the remembered sort order in the map + fColumnSortOrder.put(column, Boolean.valueOf(newSortOrder)); + + getViewer().refresh(); + } + + /** + * Create the context menu and toolbar groups. + * + * @param viewer The table viewer. Must not be <code>null</code>. + */ + protected void createContributionItems(TableViewer viewer) { + assert viewer != null; + + // Create the menu manager + MenuManager manager = new MenuManager("#PopupMenu"); //$NON-NLS-1$ + // Attach the menu listener + manager.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + }); + // All items are removed when menu is closing + manager.setRemoveAllWhenShown(true); + // Associated with the tree + viewer.getTable().setMenu(manager.createContextMenu(viewer.getTable())); + + // Register the context menu at the parent workbench part site. + if (getParentPart() != null && getParentPart().getSite() != null && getContextMenuId() != null) { + IWorkbenchPartSite site = getParentPart().getSite(); + site.registerContextMenu(getContextMenuId(), manager, viewer); + } + + // The toolbar is a bit more complicated as we want to have the + // toolbar placed within the section title. + createToolbarContributionItem(viewer); + } + + /** + * Returns the controls context menu id. + * + * @return The context menu id or <code>null</code>. + */ + protected String getContextMenuId() { + return IUIConstants.ID_CONTROL_MENUS_BASE + ".menu.propertiesTable"; //$NON-NLS-1$ + } + + /** + * Creates the toolbar within the section parent of the given filtered tree. + * + * @param viewer The table viewer. Must not be <code>null</code>. + */ + protected void createToolbarContributionItem(TableViewer viewer) { + assert viewer != null; + + // Determine the section parent from the filtered tree + Composite parent = viewer.getTable().getParent(); + while (parent != null && !(parent instanceof Section)) { + parent = parent.getParent(); + } + + // We are done here if we cannot find a section parent or the parent is disposed + if (parent == null || parent.isDisposed()) { + return; + } + + // Create the toolbar control + ToolBar toolbar = new ToolBar(parent, SWT.FLAT | SWT.HORIZONTAL | SWT.RIGHT); + + // The cursor within the toolbar shall change to an hand + final Cursor handCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND); + toolbar.setCursor(handCursor); + // Cursor needs to be explicitly disposed + toolbar.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + if ((handCursor != null) && (handCursor.isDisposed() == false)) { + handCursor.dispose(); + } + } + }); + + // If the parent composite is a forms section, set the toolbar + // as text client to the section header + if (parent instanceof Section) { + Section section = (Section)parent; + // Set the toolbar as text client + section.setTextClient(toolbar); + } + + // create the toolbar items + createToolBarItems(toolbar); + } + + /** + * Create the toolbar items to be added to the toolbar. Override + * to add the wanted toolbar items. + * <p> + * <b>Note:</b> The toolbar items are added from left to right. + * + * @param toolbar The toolbar to add the toolbar items too. Must not be <code>null</code>. + */ + protected void createToolBarItems(ToolBar toolbar) { + assert toolbar != null; + } + + /** + * Returns the viewer instance. + * + * @return The viewer instance or <code>null</code>. + */ + public Viewer getViewer() { + return fViewer; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) { + if (Viewer.class.isAssignableFrom(adapter)) { + // We have to double check if our real viewer is assignable to + // the requested Viewer class. + Viewer viewer = getViewer(); + if (!adapter.isAssignableFrom(viewer.getClass())) { + viewer = null; + } + return viewer; + } else if (ISelectionListener.class.isAssignableFrom(adapter)) { + return fEditorSelectionChangedListener; + } + + return super.getAdapter(adapter); + } + +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/trees/AbstractTreeControl.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/trees/AbstractTreeControl.java new file mode 100644 index 000000000..e4ae46050 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/trees/AbstractTreeControl.java @@ -0,0 +1,307 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.trees; + + +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.tm.te.ui.WorkbenchPartControl; +import org.eclipse.tm.te.ui.forms.CustomFormToolkit; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.forms.widgets.Section; + + +/** + * Target Explorer: Abstract tree control implementation. + */ +public abstract class AbstractTreeControl extends WorkbenchPartControl { + // Reference to the tree viewer instance + private TreeViewer fViewer; + // Reference to the selection changed listener + private ISelectionChangedListener fSelectionChangedListener; + + /** + * Constructor. + */ + public AbstractTreeControl() { + super(); + } + + /** + * Constructor. + * + * @param parentPart The parent workbench part this control is embedded in or <code>null</code>. + */ + public AbstractTreeControl(IWorkbenchPart parentPart) { + super(parentPart); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.WorkbenchPartControl#dispose() + */ + @Override + public void dispose() { + // Unregister the selection changed listener + if (fSelectionChangedListener != null) { + if (getViewer() != null) { + getViewer().removeSelectionChangedListener(fSelectionChangedListener); + } + fSelectionChangedListener = null; + } + + super.dispose(); + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.WorkbenchPartControl#setupFormPanel(org.eclipse.swt.widgets.Composite, org.eclipse.tm.te.ui.forms.CustomFormToolkit) + */ + @Override + public void setupFormPanel(Composite parent, CustomFormToolkit toolkit) { + super.setupFormPanel(parent, toolkit); + + // Create the tree viewer + fViewer = doCreateTreeViewer(parent); + // And configure the tree viewer + configureTreeViewer(fViewer); + + // Prepare popup menu and toolbar + createContributionItems(fViewer); + } + + /** + * Creates the tree viewer instance. + * + * @param parent The parent composite. Must be not <code>null</code>. + * @return The tree viewer. + */ + protected TreeViewer doCreateTreeViewer(Composite parent) { + assert parent != null; + return new TreeViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE); + } + + /** + * Configure the tree viewer. + * + * @param viewer The tree viewer. Must be not <code>null</code>. + */ + protected void configureTreeViewer(TreeViewer viewer) { + assert viewer != null; + + viewer.setAutoExpandLevel(getAutoExpandLevel()); + + viewer.setLabelProvider(doCreateTreeViewerLabelProvider(viewer)); + viewer.setContentProvider(doCreateTreeViewerContentProvider(viewer)); + viewer.setComparator(doCreateTreeViewerComparator(viewer)); + + viewer.getTree().setLayoutData(doCreateTreeViewerLayoutData(viewer)); + + // Attach the selection changed listener + fSelectionChangedListener = doCreateTreeViewerSelectionChangedListener(viewer); + if (fSelectionChangedListener != null) { + viewer.addSelectionChangedListener(fSelectionChangedListener); + } + } + + /** + * Returns the number of levels to auto expand. + * If the method returns <code>0</code>, no auto expansion will happen + * + * @return The number of levels to auto expand or <code>0</code>. + */ + protected int getAutoExpandLevel() { + return 2; + } + + /** + * Creates the tree viewer layout data instance. + * + * @param viewer The tree viewer. Must be not <code>null</code>. + * @return The tree viewer layout data instance. + */ + protected Object doCreateTreeViewerLayoutData(TreeViewer viewer) { + return new GridData(GridData.FILL_BOTH); + } + + /** + * Creates the tree viewer label provider instance. + * + * @param viewer The tree viewer. Must be not <code>null</code>. + * @return The tree viewer label provider instance. + */ + protected abstract ILabelProvider doCreateTreeViewerLabelProvider(TreeViewer viewer); + + /** + * Creates the tree viewer content provider instance. + * + * @param viewer The tree viewer. Must be not <code>null</code>. + * @return The tree viewer content provider instance. + */ + protected abstract ITreeContentProvider doCreateTreeViewerContentProvider(TreeViewer viewer); + + /** + * Creates the tree viewer comparator instance. + * + * @param viewer The tree viewer. Must be not <code>null</code>. + * @return The tree viewer comparator instance or <code>null</code> to turn of sorting. + */ + protected ViewerComparator doCreateTreeViewerComparator(TreeViewer viewer) { + assert viewer != null; + return null; + } + + /** + * Creates a new selection changed listener instance. + * + * @param viewer The tree viewer. Must be not <code>null</code>. + * @return The selection changed listener instance. + */ + protected abstract ISelectionChangedListener doCreateTreeViewerSelectionChangedListener(TreeViewer viewer); + + /** + * Create the context menu and toolbar groups. + * + * @param viewer The tree viewer instance. Must not be <code>null</code>. + */ + protected void createContributionItems(TreeViewer viewer) { + assert viewer != null; + + // Create the menu manager + MenuManager manager = new MenuManager("#PopupMenu"); //$NON-NLS-1$ + // Attach the menu listener + manager.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + }); + // All items are removed when menu is closing + manager.setRemoveAllWhenShown(true); + // Associated with the tree + viewer.getTree().setMenu(manager.createContextMenu(viewer.getTree())); + + // Register the context menu at the parent workbench part site. + if (getParentPart() != null && getParentPart().getSite() != null && getContextMenuId() != null) { + getParentPart().getSite().registerContextMenu(getContextMenuId(), manager, viewer); + } + + // The toolbar is a bit more complicated as we want to have the + // toolbar placed within the section title. + createToolbarContributionItem(viewer); + } + + /** + * Returns the context menu id. + * + * @return The context menu id. + */ + protected abstract String getContextMenuId(); + + /** + * Creates the toolbar within the section parent of the given tree viewer. + * + * @param viewer The tree viewer instance. Must not be <code>null</code>. + */ + protected void createToolbarContributionItem(TreeViewer viewer) { + assert viewer != null; + + // Determine the section parent from the tree viewer + Composite parent = viewer.getTree().getParent(); + while (parent != null && !(parent instanceof Section)) { + parent = parent.getParent(); + } + + // We are done here if we cannot find a section parent or the parent is disposed + if (parent == null || parent.isDisposed()) { + return; + } + + // Create the toolbar control + ToolBar toolbar = new ToolBar(parent, SWT.FLAT | SWT.HORIZONTAL | SWT.RIGHT); + + // The cursor within the toolbar shall change to an hand + final Cursor handCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND); + toolbar.setCursor(handCursor); + // Cursor needs to be explicitly disposed + toolbar.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + if ((handCursor != null) && (handCursor.isDisposed() == false)) { + handCursor.dispose(); + } + } + }); + + // If the parent composite is a forms section, set the toolbar + // as text client to the section header + if (parent instanceof Section) { + Section section = (Section)parent; + // Set the toolbar as text client + section.setTextClient(toolbar); + } + + // create the toolbar items + createToolBarItems(toolbar); + } + + /** + * Create the toolbar items to be added to the toolbar. Override + * to add the wanted toolbar items. + * <p> + * <b>Note:</b> The toolbar items are added from left to right. + * + * @param toolbar The toolbar to add the toolbar items too. Must not be <code>null</code>. + */ + protected void createToolBarItems(ToolBar toolbar) { + assert toolbar != null; + } + + /** + * Returns the viewer instance. + * + * @return The viewer instance or <code>null</code>. + */ + public Viewer getViewer() { + return fViewer; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) { + if (Viewer.class.isAssignableFrom(adapter)) { + // We have to double check if our real viewer is assignable to + // the requested Viewer class. + Viewer viewer = getViewer(); + if (!adapter.isAssignableFrom(viewer.getClass())) { + viewer = null; + } + return viewer; + } + + return super.getAdapter(adapter); + } + +} diff --git a/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/trees/TreeViewerComparator.java b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/trees/TreeViewerComparator.java new file mode 100644 index 000000000..b24a4b2af --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tm.te.ui/src/org/eclipse/tm/te/ui/trees/TreeViewerComparator.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2011 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: + * Uwe Stieber (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.te.ui.trees; + +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.tm.te.ui.AbstractViewerComparator; + + +/** + * Target Explorer: Common tree control viewer comparator implementation. + */ +public class TreeViewerComparator extends AbstractViewerComparator { + private final ILabelProvider fLabelProvider; + + /** + * Constructor. + * + * @param viewer The parent viewer. Must be not <code>null</code>. + * @param labelProvider The label provider. Must be not <code>null</code>. + */ + public TreeViewerComparator(Viewer viewer, ILabelProvider labelProvider) { + super(viewer); + assert labelProvider != null; + fLabelProvider = labelProvider; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doDetermineInverter(org.eclipse.jface.viewers.Viewer) + */ + @Override + protected int doDetermineInverter(Viewer viewer) { + int inverter = 1; + + // Viewer must be of type TreeViewer and the tree must not be disposed yet + if (viewer instanceof TreeViewer && ((TreeViewer)viewer).getTree() != null) { + Tree tree = ((TreeViewer)viewer).getTree(); + if (!tree.isDisposed() && tree.getSortDirection() == SWT.DOWN) inverter = -1; + } + + return inverter; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doGetText(java.lang.Object, int) + */ + @Override + protected String doGetText(Object node, int index) { + if (node != null && fLabelProvider != null) { + return fLabelProvider.getText(node); + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doGetSortColumnLabel(org.eclipse.jface.viewers.Viewer) + */ + @Override + protected String doGetSortColumnLabel(Viewer viewer) { + // Viewer must be of type TreeViewer and the tree must not be disposed yet + if (viewer instanceof TreeViewer && ((TreeViewer)viewer).getTree() != null && !((TreeViewer)viewer).getTree().isDisposed()) { + Tree tree = ((TreeViewer)viewer).getTree(); + return tree.getSortColumn() != null ? tree.getSortColumn().getText() : ""; //$NON-NLS-1$ + } + return ""; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doGetSortColumnIndex(org.eclipse.jface.viewers.Viewer) + */ + @Override + protected int doGetSortColumnIndex(Viewer viewer) { + if (viewer instanceof TreeViewer && ((TreeViewer)viewer).getTree() != null && !((TreeViewer)viewer).getTree().isDisposed()) { + Tree tree = ((TreeViewer)viewer).getTree(); + return tree.getSortColumn() != null ? Arrays.asList(tree.getColumns()).indexOf(tree.getSortColumn()) : -1; + } + return -1; + } + + /* (non-Javadoc) + * @see org.eclipse.tm.te.ui.controls.AbstractViewerComparator#doCompare(java.lang.Object, java.lang.Object, java.lang.String, int, int) + */ + @SuppressWarnings("unchecked") + @Override + protected int doCompare(Object node1, Object node2, String sortColumn, int index, int inverter) { + if (node1 == null && node2 == null) return 0; + if (node1 != null && node2 == null) return 1; + if (node1 == null && node2 != null) return -1; + + // Get the labels + String text1 = doGetText(node1, index); + String text2 = doGetText(node2, index); + + // Normalize labels + if (text1 == null) text1 = ""; //$NON-NLS-1$ + if (text2 == null) text2 = ""; //$NON-NLS-1$ + + // The tree sorts not strictly alphabetical. First comes entries starting with numbers, + // second entries starting with uppercase and than all the rest. Additional, if a label contains + // uppercase characters, it is sorted in before any labels being lowercase only. + if (text1.length() > 0 && text2.length() > 0) { + // Get the first characters of both + char c1 = text1.charAt(0); + char c2 = text2.charAt(0); + + if (Character.isDigit(c1) || Character.isDigit(c2)) { + // Check on the differences. If both are digits, the standard compare will do it + if (Character.isDigit(c1) && !Character.isDigit(c2)) return -1 * inverter; + if (!Character.isDigit(c1) && Character.isDigit(c2)) return 1 * inverter; + } + + if (Character.isUpperCase(c1) || Character.isUpperCase(c2)) { + // Check on the differences. If both are uppercase characters, the standard compare will do it + if (Character.isUpperCase(c1) && !Character.isUpperCase(c2)) return -1 * inverter; + if (!Character.isUpperCase(c1) && Character.isUpperCase(c2)) return 1 * inverter; + } + + Matcher m1 = Pattern.compile("(\\D+)(\\d+)").matcher(text1); //$NON-NLS-1$ + Matcher m2 = Pattern.compile("(\\D+)(\\d+)").matcher(text2); //$NON-NLS-1$ + if (m1.matches() && m2.matches()) { + String p11 = m1.group(1); + String p12 = m1.group(2); + + String p21 = m2.group(1); + String p22 = m2.group(2); + + if (p11 != null && p11.equals(p21)) { + // Compare the second parts as number + try { + int result = 0; + long l1 = Long.parseLong(p12); + long l2 = Long.parseLong(p22); + + if (l1 > l2) result = 1; + if (l1 < l2) result = -1; + + return result; + } catch (NumberFormatException e) { /* ignored on purpose */ } + } + } + + if (text1.matches(".*[A-Z]+.*") || text2.matches(".*[A-Z]+.*")) { //$NON-NLS-1$ //$NON-NLS-2$ + if (text1.matches(".*[A-Z]+.*") && !text2.matches(".*[A-Z]+.*")) return -1 * inverter; //$NON-NLS-1$ //$NON-NLS-2$ + if (!text1.matches(".*[A-Z]+.*") && text2.matches(".*[A-Z]+.*")) return 1 * inverter; //$NON-NLS-1$ //$NON-NLS-2$ + + // Additionally, it even depends on the position of the first uppercase + // character if both strings contains them :-( + int minLength = Math.min(text1.length(), text2.length()); + for (int i = 0; i < minLength; i++) { + char ch1 = text1.charAt(i); + char ch2 = text2.charAt(i); + + if (Character.isUpperCase(ch1) && !Character.isUpperCase(ch2)) return -1 * inverter; + if (!Character.isUpperCase(ch1) && Character.isUpperCase(ch2)) return 1 * inverter; + // If both are uppercase, we break the loop and compare as usual + if (Character.isUpperCase(ch1) && Character.isUpperCase(ch2)) break; + } + } + } + + // Compare the text alphabetical + return getComparator().compare(text1, text2) * inverter; + } +} |