X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.utils.ui%2Fsrc%2Forg%2Fsimantics%2Futils%2Fui%2Fdialogs%2FColumnFilteredItemsSelectionDialog.java;h=095b5d1bbe1a2c91092033584ad40d683448c886;hb=refs%2Fchanges%2F38%2F238%2F2;hp=e9be6d8873c4b72c014069d23b1da96151cf6e31;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/dialogs/ColumnFilteredItemsSelectionDialog.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/dialogs/ColumnFilteredItemsSelectionDialog.java index e9be6d887..095b5d1bb 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/dialogs/ColumnFilteredItemsSelectionDialog.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/dialogs/ColumnFilteredItemsSelectionDialog.java @@ -1,3314 +1,3314 @@ -/******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - * Willian Mitsuda - * - Fix for bug 196553 - [Dialogs] Support IColorProvider/IFontProvider in FilteredItemsSelectionDialog - * Peter Friese - * - Fix for bug 208602 - [Dialogs] Open Type dialog needs accessible labels - * Simon Muschel - bug 258493 - *******************************************************************************/ -package org.simantics.utils.ui.dialogs; - -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.IHandler; -import org.eclipse.core.runtime.Assert; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.ListenerList; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.ProgressMonitorWrapper; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.SubProgressMonitor; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ActionContributionItem; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.action.IMenuListener; -import org.eclipse.jface.action.IMenuManager; -import org.eclipse.jface.action.LegacyActionTools; -import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.dialogs.IDialogSettings; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.ContentViewer; -import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IColorProvider; -import org.eclipse.jface.viewers.IContentProvider; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IFontProvider; -import org.eclipse.jface.viewers.ILabelDecorator; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ILazyContentProvider; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.LabelProvider; -import org.eclipse.jface.viewers.LabelProviderChangedEvent; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.StyledCellLabelProvider; -import org.eclipse.jface.viewers.StyledString; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerCell; -import org.eclipse.jface.viewers.ViewerFilter; -import org.eclipse.osgi.util.NLS; -import org.eclipse.swt.SWT; -import org.eclipse.swt.accessibility.ACC; -import org.eclipse.swt.accessibility.AccessibleAdapter; -import org.eclipse.swt.accessibility.AccessibleEvent; -import org.eclipse.swt.custom.CLabel; -import org.eclipse.swt.custom.ViewForm; -import org.eclipse.swt.events.KeyAdapter; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -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.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.swt.widgets.ToolItem; -import org.eclipse.ui.ActiveShellExpression; -import org.eclipse.ui.IMemento; -import org.eclipse.ui.IWorkbenchCommandConstants; -import org.eclipse.ui.IWorkbenchPreferenceConstants; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.WorkbenchException; -import org.eclipse.ui.XMLMemento; -import org.eclipse.ui.dialogs.SearchPattern; -import org.eclipse.ui.dialogs.SelectionStatusDialog; -import org.eclipse.ui.handlers.IHandlerActivation; -import org.eclipse.ui.handlers.IHandlerService; -import org.eclipse.ui.internal.IWorkbenchGraphicConstants; -import org.eclipse.ui.internal.WorkbenchImages; -import org.eclipse.ui.internal.WorkbenchMessages; -import org.eclipse.ui.internal.WorkbenchPlugin; -import org.eclipse.ui.progress.UIJob; -import org.eclipse.ui.statushandlers.StatusManager; - -/** - * Shows a list of items to the user with a text entry field for a string - * pattern used to filter the list of items. - * - * This is a copy from org.eclipse.ui.dialogs.FilteredItemsSelectionDialog - * with a hook for column creation and a fix to a (possible) bug that - * prevented the empty pattern to be handled correctly. This version of the - * dialog also has the possibility to force an update of the contents if new - * elements are added. - * - * TODO: Clean up warnings. - * - * @author Janne Kauttio (modifications only) - * - * @since 3.3 - */ -@SuppressWarnings({ "restriction", "rawtypes", "unchecked" }) -public abstract class ColumnFilteredItemsSelectionDialog extends - SelectionStatusDialog { - - private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$ - - private static final String SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$ - - private static final String HISTORY_SETTINGS = "History"; //$NON-NLS-1$ - - private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$ - - private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$ - - /** - * Represents an empty selection in the pattern input field (used only for - * initial pattern). - */ - public static final int NONE = 0; - - /** - * Pattern input field selection where caret is at the beginning (used only - * for initial pattern). - */ - public static final int CARET_BEGINNING = 1; - - /** - * Represents a full selection in the pattern input field (used only for - * initial pattern). - */ - public static final int FULL_SELECTION = 2; - - private Text pattern; - - private TableViewer viewer; - - private DetailsContentViewer details; - - /** - * It is a duplicate of a field in the CLabel class in DetailsContentViewer. - * It is maintained, because the setDetailsLabelProvider() - * could be called before content area is created. - */ - private ILabelProvider detailsLabelProvider; - - private ItemsListLabelProvider itemsListLabelProvider; - - private MenuManager menuManager; - - private MenuManager contextMenuManager; - - private boolean multi; - - private ToolBar toolBar; - - private ToolItem toolItem; - - private Label progressLabel; - - private ToggleStatusLineAction toggleStatusLineAction; - - private RemoveHistoryItemAction removeHistoryItemAction; - - private ActionContributionItem removeHistoryActionContributionItem; - - private IStatus status; - - private RefreshCacheJob refreshCacheJob; - - private RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob(); - - private Object[] currentSelection; - - private ContentProvider contentProvider; - - private FilterHistoryJob filterHistoryJob; - - private FilterJob filterJob; - - private ItemsFilter filter; - - private List lastCompletedResult; - - private ItemsFilter lastCompletedFilter; - - private String initialPatternText; - - private int selectionMode; - - private ItemsListSeparator itemsListSeparator; - - private static final String EMPTY_STRING = ""; //$NON-NLS-1$ - - private boolean refreshWithLastSelection = false; - - private IHandlerActivation showViewHandler; - - /** - * Creates a new instance of the class. - * - * @param shell - * shell to parent the dialog on - * @param multi - * indicates whether dialog allows to select more than one - * position in its list of items - */ - public ColumnFilteredItemsSelectionDialog(Shell shell, boolean multi) { - super(shell); - this.multi = multi; - filterHistoryJob = new FilterHistoryJob(); - filterJob = new FilterJob(); - contentProvider = new ContentProvider(); - refreshCacheJob = new RefreshCacheJob(); - itemsListSeparator = new ItemsListSeparator(WorkbenchMessages.FilteredItemsSelectionDialog_separatorLabel); - selectionMode = NONE; - } - - /** - * Creates a new instance of the class. Created dialog won't allow to select - * more than one item. - * - * @param shell - * shell to parent the dialog on - */ - public ColumnFilteredItemsSelectionDialog(Shell shell) { - this(shell, false); - } - - /** - * Adds viewer filter to the dialog items list. - * - * @param filter - * the new filter - */ - protected void addListFilter(ViewerFilter filter) { - contentProvider.addFilter(filter); - } - - /** - * Sets a new label provider for items in the list. If the label provider - * also implements {@link - * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider - * .IStyledLabelProvider}, the style text labels provided by it will be used - * provided that the corresponding preference is set. - * - * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS - * - * @param listLabelProvider - * the label provider for items in the list - */ - public void setListLabelProvider(ILabelProvider listLabelProvider) { - getItemsListLabelProvider().setProvider(listLabelProvider); - } - - /** - * Returns the label decorator for selected items in the list. - * - * @return the label decorator for selected items in the list - */ - private ILabelDecorator getListSelectionLabelDecorator() { - return getItemsListLabelProvider().getSelectionDecorator(); - } - - /** - * Sets the label decorator for selected items in the list. - * - * @param listSelectionLabelDecorator - * the label decorator for selected items in the list - */ - public void setListSelectionLabelDecorator( - ILabelDecorator listSelectionLabelDecorator) { - getItemsListLabelProvider().setSelectionDecorator( - listSelectionLabelDecorator); - } - - /** - * Returns the item list label provider. - * - * @return the item list label provider - */ - private ItemsListLabelProvider getItemsListLabelProvider() { - if (itemsListLabelProvider == null) { - itemsListLabelProvider = new ItemsListLabelProvider( - new LabelProvider(), null); - } - return itemsListLabelProvider; - } - - /** - * Sets label provider for the details field. - * - * For a single selection, the element sent to - * {@link ILabelProvider#getImage(Object)} and - * {@link ILabelProvider#getText(Object)} is the selected object, for - * multiple selection a {@link String} with amount of selected items is the - * element. - * - * @see #getSelectedItems() getSelectedItems() can be used to retrieve - * selected items and get the items count. - * - * @param detailsLabelProvider - * the label provider for the details field - */ - public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider) { - this.detailsLabelProvider = detailsLabelProvider; - if (details != null) { - details.setLabelProvider(detailsLabelProvider); - } - } - - private ILabelProvider getDetailsLabelProvider() { - if (detailsLabelProvider == null) { - detailsLabelProvider = new LabelProvider(); - } - return detailsLabelProvider; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.window.Window#create() - */ - public void create() { - super.create(); - pattern.setFocus(); - } - - /** - * Restores dialog using persisted settings. The default implementation - * restores the status of the details line and the selection history. - * - * @param settings - * settings used to restore dialog - */ - protected void restoreDialog(IDialogSettings settings) { - boolean toggleStatusLine = true; - - if (settings.get(SHOW_STATUS_LINE) != null) { - toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE); - } - - toggleStatusLineAction.setChecked(toggleStatusLine); - - details.setVisible(toggleStatusLine); - - String setting = settings.get(HISTORY_SETTINGS); - if (setting != null) { - try { - IMemento memento = XMLMemento.createReadRoot(new StringReader(setting)); - this.contentProvider.loadHistory(memento); - } catch (WorkbenchException e) { - // Simply don't restore the settings - StatusManager.getManager().handle(new Status( - IStatus.ERROR, - PlatformUI.PLUGIN_ID, - IStatus.ERROR, - WorkbenchMessages.FilteredItemsSelectionDialog_restoreError, - e)); - } - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.window.Window#close() - */ - public boolean close() { - this.filterJob.cancel(); - this.refreshCacheJob.cancel(); - this.refreshProgressMessageJob.cancel(); - if (showViewHandler != null) { - IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); - service.deactivateHandler(showViewHandler); - showViewHandler.getHandler().dispose(); - showViewHandler = null; - } - if (menuManager != null) - menuManager.dispose(); - if (contextMenuManager != null) - contextMenuManager.dispose(); - storeDialog(getDialogSettings()); - return super.close(); - } - - /** - * Stores dialog settings. - * - * @param settings - * settings used to store dialog - */ - protected void storeDialog(IDialogSettings settings) { - settings.put(SHOW_STATUS_LINE, toggleStatusLineAction.isChecked()); - - XMLMemento memento = XMLMemento.createWriteRoot(HISTORY_SETTINGS); - this.contentProvider.saveHistory(memento); - StringWriter writer = new StringWriter(); - try { - memento.save(writer); - settings.put(HISTORY_SETTINGS, writer.getBuffer().toString()); - } catch (IOException e) { - // Simply don't store the settings - StatusManager.getManager().handle(new Status( - IStatus.ERROR, - PlatformUI.PLUGIN_ID, - IStatus.ERROR, - WorkbenchMessages.FilteredItemsSelectionDialog_storeError, - e)); - } - } - - /** - * Create a new header which is labelled by headerLabel. - * - * @param parent - * @return Label the label of the header - */ - private Label createHeader(Composite parent) { - Composite header = new Composite(parent, SWT.NONE); - - GridLayout layout = new GridLayout(); - layout.numColumns = 2; - layout.marginWidth = 0; - layout.marginHeight = 0; - header.setLayout(layout); - - Label headerLabel = new Label(header, SWT.NONE); - headerLabel.setText((getMessage() != null && getMessage().trim().length() > 0) ? getMessage() - : WorkbenchMessages.FilteredItemsSelectionDialog_patternLabel); - headerLabel.addTraverseListener(new TraverseListener() { - public void keyTraversed(TraverseEvent e) { - if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) { - e.detail = SWT.TRAVERSE_NONE; - pattern.setFocus(); - } - } - }); - - GridData gd = new GridData(GridData.FILL_HORIZONTAL); - headerLabel.setLayoutData(gd); - - createViewMenu(header); - header.setLayoutData(gd); - return headerLabel; - } - - /** - * Create the labels for the list and the progress. Return the list label. - * - * @param parent - * @return Label - */ - private Label createLabels(Composite parent) { - Composite labels = new Composite(parent, SWT.NONE); - - GridLayout layout = new GridLayout(); - layout.numColumns = 2; - layout.marginWidth = 0; - layout.marginHeight = 0; - labels.setLayout(layout); - - Label listLabel = new Label(labels, SWT.NONE); - listLabel.setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel); - - listLabel.addTraverseListener(new TraverseListener() { - public void keyTraversed(TraverseEvent e) { - if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) { - e.detail = SWT.TRAVERSE_NONE; - viewer.getTable().setFocus(); - } - } - }); - - GridData gd = new GridData(GridData.FILL_HORIZONTAL); - listLabel.setLayoutData(gd); - - progressLabel = new Label(labels, SWT.RIGHT); - progressLabel.setLayoutData(gd); - - labels.setLayoutData(gd); - return listLabel; - } - - private void createViewMenu(Composite parent) { - toolBar = new ToolBar(parent, SWT.FLAT); - toolItem = new ToolItem(toolBar, SWT.PUSH, 0); - - GridData data = new GridData(); - data.horizontalAlignment = GridData.END; - toolBar.setLayoutData(data); - - toolBar.addMouseListener(new MouseAdapter() { - public void mouseDown(MouseEvent e) { - showViewMenu(); - } - }); - - toolItem.setImage(WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU)); - toolItem.setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu); - toolItem.addSelectionListener(new SelectionAdapter() { - public void widgetSelected(SelectionEvent e) { - showViewMenu(); - } - }); - - menuManager = new MenuManager(); - - fillViewMenu(menuManager); - - IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); - IHandler handler = new AbstractHandler() { - public Object execute(ExecutionEvent event) { - showViewMenu(); - return null; - } - }; - showViewHandler = service.activateHandler( - IWorkbenchCommandConstants.WINDOW_SHOW_VIEW_MENU, handler, - new ActiveShellExpression(getShell())); - } - - /** - * Fills the menu of the dialog. - * - * @param menuManager - * the menu manager - */ - protected void fillViewMenu(IMenuManager menuManager) { - toggleStatusLineAction = new ToggleStatusLineAction(); - menuManager.add(toggleStatusLineAction); - } - - private void showViewMenu() { - Menu menu = menuManager.createContextMenu(getShell()); - Rectangle bounds = toolItem.getBounds(); - Point topLeft = new Point(bounds.x, bounds.y + bounds.height); - topLeft = toolBar.toDisplay(topLeft); - menu.setLocation(topLeft.x, topLeft.y); - menu.setVisible(true); - } - - /** - * Hook that allows to add actions to the context menu. - *

- * Subclasses may extend in order to add other actions.

- * - * @param menuManager the context menu manager - * @since 3.5 - */ - protected void fillContextMenu(IMenuManager menuManager) { - List selectedElements= ((StructuredSelection)viewer.getSelection()).toList(); - - Object item= null; - - for (Iterator it= selectedElements.iterator(); it.hasNext();) { - item= it.next(); - if (item instanceof ItemsListSeparator || !isHistoryElement(item)) { - return; - } - } - - if (selectedElements.size() > 0) { - removeHistoryItemAction.setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction); - - menuManager.add(removeHistoryActionContributionItem); - - } - } - - private void createPopupMenu() { - removeHistoryItemAction = new RemoveHistoryItemAction(); - removeHistoryActionContributionItem = new ActionContributionItem(removeHistoryItemAction); - - contextMenuManager = new MenuManager(); - contextMenuManager.setRemoveAllWhenShown(true); - contextMenuManager.addMenuListener(new IMenuListener() { - public void menuAboutToShow(IMenuManager manager) { - fillContextMenu(manager); - } - }); - - final Table table = viewer.getTable(); - Menu menu= contextMenuManager.createContextMenu(table); - table.setMenu(menu); - } - - /** - * Creates an extra content area, which will be located above the details. - * - * @param parent - * parent to create the dialog widgets in - * @return an extra content area - */ - protected abstract Control createExtendedContentArea(Composite parent); - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) - */ - protected Control createDialogArea(Composite parent) { - Composite dialogArea = (Composite) super.createDialogArea(parent); - - Composite content = new Composite(dialogArea, SWT.NONE); - GridData gd = new GridData(GridData.FILL_BOTH); - content.setLayoutData(gd); - - GridLayout layout = new GridLayout(); - layout.numColumns = 1; - layout.marginWidth = 0; - layout.marginHeight = 0; - content.setLayout(layout); - - final Label headerLabel = createHeader(content); - - pattern = new Text(content, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL); - pattern.getAccessible().addAccessibleListener(new AccessibleAdapter() { - public void getName(AccessibleEvent e) { - e.result = LegacyActionTools.removeMnemonics(headerLabel.getText()); - } - }); - gd = new GridData(GridData.FILL_HORIZONTAL); - pattern.setLayoutData(gd); - - final Label listLabel = createLabels(content); - - viewer = new TableViewer(content, (multi ? SWT.MULTI : SWT.SINGLE) - | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION); - viewer.getTable().getAccessible().addAccessibleListener( - new AccessibleAdapter() { - public void getName(AccessibleEvent e) { - if (e.childID == ACC.CHILDID_SELF) { - e.result = LegacyActionTools.removeMnemonics(listLabel.getText()); - } - } - }); - viewer.setContentProvider(contentProvider); - - // added column creation hook - createColumns(viewer); - - // show headers etc. if columns were added by the subclass - if (viewer.getTable().getColumnCount() > 0) { - viewer.getTable().setLinesVisible(true); - viewer.getTable().setHeaderVisible(true); - } - - viewer.setInput(new Object[0]); - viewer.setItemCount(contentProvider.getNumberOfElements()); - gd = new GridData(GridData.FILL_BOTH); - applyDialogFont(viewer.getTable()); - gd.heightHint= viewer.getTable().getItemHeight() * 15; - viewer.getTable().setLayoutData(gd); - - createPopupMenu(); - - pattern.addModifyListener(new ModifyListener() { - public void modifyText(ModifyEvent e) { - applyFilter(); - } - }); - - pattern.addKeyListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - if (e.keyCode == SWT.ARROW_DOWN) { - if (viewer.getTable().getItemCount() > 0) { - viewer.getTable().setFocus(); - } - } - } - }); - - viewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - StructuredSelection selection = (StructuredSelection) event.getSelection(); - handleSelected(selection); - } - }); - - viewer.addDoubleClickListener(new IDoubleClickListener() { - public void doubleClick(DoubleClickEvent event) { - handleDoubleClick(); - } - }); - - viewer.getTable().addKeyListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - - if (e.keyCode == SWT.DEL) { - - List selectedElements = ((StructuredSelection) viewer.getSelection()).toList(); - - Object item = null; - boolean isSelectedHistory = true; - - for (Iterator it = selectedElements.iterator(); it.hasNext();) { - item = it.next(); - if (item instanceof ItemsListSeparator || !isHistoryElement(item)) { - isSelectedHistory = false; - break; - } - } - if (isSelectedHistory) - removeSelectedItems(selectedElements); - - } - - if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) != 0 - && (e.stateMask & SWT.CTRL) != 0) { - StructuredSelection selection = (StructuredSelection) viewer.getSelection(); - - if (selection.size() == 1) { - Object element = selection.getFirstElement(); - if (element.equals(viewer.getElementAt(0))) { - pattern.setFocus(); - } - if (viewer.getElementAt(viewer.getTable().getSelectionIndex() - 1) instanceof ItemsListSeparator) - viewer.getTable().setSelection(viewer.getTable().getSelectionIndex() - 1); - viewer.getTable().notifyListeners(SWT.Selection, new Event()); - - } - } - - if (e.keyCode == SWT.ARROW_DOWN - && (e.stateMask & SWT.SHIFT) != 0 - && (e.stateMask & SWT.CTRL) != 0) { - - if (viewer.getElementAt(viewer.getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator) - viewer.getTable().setSelection(viewer.getTable().getSelectionIndex() + 1); - viewer.getTable().notifyListeners(SWT.Selection, new Event()); - } - } - }); - - createExtendedContentArea(content); - - details = new DetailsContentViewer(content, SWT.BORDER | SWT.FLAT); - details.setVisible(toggleStatusLineAction.isChecked()); - details.setContentProvider(new NullContentProvider()); - details.setLabelProvider(getDetailsLabelProvider()); - - applyDialogFont(content); - - restoreDialog(getDialogSettings()); - - if (initialPatternText != null) { - pattern.setText(initialPatternText); - } - - switch (selectionMode) { - case CARET_BEGINNING: - pattern.setSelection(0, 0); - break; - case FULL_SELECTION: - pattern.setSelection(0, initialPatternText.length()); - break; - } - - // apply filter even if pattern is empty (display history) - applyFilter(); - - return dialogArea; - } - - /** - * Override this method to add columns to the content area of this dialog. - * - * Subclass implementation of this method should NOT call the super - * implementation as this will break the individual column label providers - * - * @param viewer - */ - protected void createColumns(TableViewer viewer) { - // no columns are added by default, just set the label provider for the viewer - viewer.setLabelProvider(getItemsListLabelProvider()); - } - - /** - * An utility method for adding a column to the TableViewer, should be called - * from inside createColumns. - * - * @param viewer - * @param label - * @param width - * @param labelProvider - */ - protected void createColumn(TableViewer viewer, String label, int width, ColumnLabelProvider labelProvider) { - TableViewerColumn viewercol = new TableViewerColumn(viewer, SWT.LEFT); - - TableColumn col = viewercol.getColumn(); - col.setText(label); - col.setWidth(width); - col.setResizable(true); - col.setMoveable(true); - - // TODO: should use the local label provider class instead but it - // should be made compatible with multiple columns first - viewercol.setLabelProvider(labelProvider); - } - - /** - * This method is a hook for subclasses to override default dialog behavior. - * The handleDoubleClick() method handles double clicks on - * the list of filtered elements. - *

- * Current implementation makes double-clicking on the list do the same as - * pressing OK button on the dialog. - */ - protected void handleDoubleClick() { - okPressed(); - } - - /** - * Refreshes the details field according to the current selection in the - * items list. - */ - private void refreshDetails() { - StructuredSelection selection = getSelectedItems(); - - switch (selection.size()) { - case 0: - details.setInput(null); - break; - case 1: - details.setInput(selection.getFirstElement()); - break; - default: - details.setInput(NLS.bind( - WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected, - new Integer(selection.size()))); - break; - } - - } - - /** - * Handle selection in the items list by updating labels of selected and - * unselected items and refresh the details field using the selection. - * - * @param selection - * the new selection - */ - protected void handleSelected(StructuredSelection selection) { - IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID, - IStatus.OK, EMPTY_STRING, null); - - Object[] lastSelection = currentSelection; - - currentSelection = selection.toArray(); - - if (selection.size() == 0) { - status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, - IStatus.ERROR, EMPTY_STRING, null); - - if (lastSelection != null && getListSelectionLabelDecorator() != null) { - viewer.update(lastSelection, null); - } - - currentSelection = null; - - } else { - status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, - IStatus.ERROR, EMPTY_STRING, null); - - List items = selection.toList(); - - Object item = null; - IStatus tempStatus = null; - - for (Iterator it = items.iterator(); it.hasNext();) { - Object o = it.next(); - - if (o instanceof ItemsListSeparator) { - continue; - } - - item = o; - tempStatus = validateItem(item); - - if (tempStatus.isOK()) { - status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID, - IStatus.OK, EMPTY_STRING, null); - } else { - status = tempStatus; - // if any selected element is not valid status is set to - // ERROR - break; - } - } - - if (lastSelection != null && getListSelectionLabelDecorator() != null) { - viewer.update(lastSelection, null); - } - - if (getListSelectionLabelDecorator() != null) { - viewer.update(currentSelection, null); - } - } - - refreshDetails(); - updateStatus(status); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings() - */ - protected IDialogSettings getDialogBoundsSettings() { - IDialogSettings settings = getDialogSettings(); - IDialogSettings section = settings.getSection(DIALOG_BOUNDS_SETTINGS); - if (section == null) { - section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS); - section.put(DIALOG_HEIGHT, 500); - section.put(DIALOG_WIDTH, 600); - } - return section; - } - - /** - * Returns the dialog settings. Returned object can't be null. - * - * @return return dialog settings for this dialog - */ - protected abstract IDialogSettings getDialogSettings(); - - /** - * Refreshes the dialog - has to be called in UI thread. - */ - public void refresh() { - if (viewer != null && !viewer.getTable().isDisposed()) { - - List lastRefreshSelection = ((StructuredSelection) viewer.getSelection()).toList(); - viewer.getTable().deselectAll(); - - viewer.setItemCount(contentProvider.getNumberOfElements()); - viewer.refresh(); - - if (viewer.getTable().getItemCount() > 0) { - // preserve previous selection - if (refreshWithLastSelection && lastRefreshSelection != null - && lastRefreshSelection.size() > 0) { - viewer.setSelection(new StructuredSelection( - lastRefreshSelection)); - } else { - refreshWithLastSelection = true; - viewer.getTable().setSelection(0); - viewer.getTable().notifyListeners(SWT.Selection, new Event()); - } - } else { - viewer.setSelection(StructuredSelection.EMPTY); - } - - } - - scheduleProgressMessageRefresh(); - } - - /** - * Updates the progress label. - * - * @deprecated - */ - public void updateProgressLabel() { - scheduleProgressMessageRefresh(); - } - - /** - * Notifies the content provider - fires filtering of content provider - * elements. During the filtering, a separator between history and workspace - * matches is added. - *

- * This is a long running operation and should be called in a job. - * - * @param checkDuplicates - * true if data concerning elements duplication - * should be computed - it takes much more time than the standard - * filtering - * @param monitor - * a progress monitor or null if no monitor is - * available - */ - public void reloadCache(boolean checkDuplicates, IProgressMonitor monitor) { - if (viewer != null && !viewer.getTable().isDisposed() && contentProvider != null) { - contentProvider.reloadCache(checkDuplicates, monitor); - } - } - - /** - * Schedule refresh job. - */ - public void scheduleRefresh() { - refreshCacheJob.cancelAll(); - refreshCacheJob.schedule(); - } - - /** - * Schedules progress message refresh. - */ - public void scheduleProgressMessageRefresh() { - if (filterJob.getState() != Job.RUNNING && refreshProgressMessageJob.getState() != Job.RUNNING) - refreshProgressMessageJob.scheduleProgressRefresh(null); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult() - */ - protected void computeResult() { - - List selectedElements = ((StructuredSelection) viewer.getSelection()) - .toList(); - - List objectsToReturn = new ArrayList(); - - Object item = null; - - for (Iterator it = selectedElements.iterator(); it.hasNext();) { - item = it.next(); - - if (!(item instanceof ItemsListSeparator)) { - accessedHistoryItem(item); - objectsToReturn.add(item); - } - } - - setResult(objectsToReturn); - } - - /* - * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus) - */ - protected void updateStatus(IStatus status) { - this.status = status; - super.updateStatus(status); - } - - /* - * @see Dialog#okPressed() - */ - protected void okPressed() { - if (status != null && (status.isOK() || status.getCode() == IStatus.INFO)) { - super.okPressed(); - } - } - - /** - * Sets the initial pattern used by the filter. This text is copied into the - * selection input on the dialog. A full selection is used in the pattern - * input field. - * - * @param text - * initial pattern for the filter - * @see ColumnFilteredItemsSelectionDialog#FULL_SELECTION - */ - public void setInitialPattern(String text) { - setInitialPattern(text, FULL_SELECTION); - } - - /** - * Sets the initial pattern used by the filter. This text is copied into the - * selection input on the dialog. The selectionMode is used - * to choose selection type for the input field. - * - * @param text - * initial pattern for the filter - * @param selectionMode - * one of: {@link ColumnFilteredItemsSelectionDialog#NONE}, - * {@link ColumnFilteredItemsSelectionDialog#CARET_BEGINNING}, - * {@link ColumnFilteredItemsSelectionDialog#FULL_SELECTION} - */ - public void setInitialPattern(String text, int selectionMode) { - this.initialPatternText = text; - this.selectionMode = selectionMode; - } - - /** - * Gets initial pattern. - * - * @return initial pattern, or null if initial pattern is not - * set - */ - protected String getInitialPattern() { - return this.initialPatternText; - } - - /** - * Returns the current selection. - * - * @return the current selection - */ - protected StructuredSelection getSelectedItems() { - - StructuredSelection selection = (StructuredSelection) viewer.getSelection(); - - List selectedItems = selection.toList(); - Object itemToRemove = null; - - for (Iterator it = selection.iterator(); it.hasNext();) { - Object item = it.next(); - if (item instanceof ItemsListSeparator) { - itemToRemove = item; - break; - } - } - - if (itemToRemove == null) - return new StructuredSelection(selectedItems); - // Create a new selection without the collision - List newItems = new ArrayList(selectedItems); - newItems.remove(itemToRemove); - return new StructuredSelection(newItems); - - } - - /** - * Validates the item. When items on the items list are selected or - * deselected, it validates each item in the selection and the dialog status - * depends on all validations. - * - * @param item - * an item to be checked - * @return status of the dialog to be set - */ - protected abstract IStatus validateItem(Object item); - - /** - * Creates an instance of a filter. - * - * @return a filter for items on the items list. Can be null, - * no filtering will be applied then, causing no item to be shown in - * the list. - */ - protected abstract ItemsFilter createFilter(); - - /** - * Applies the filter created by createFilter() method to the - * items list. When new filter is different than previous one it will cause - * refiltering. - */ - protected void applyFilter() { - ItemsFilter newFilter = createFilter(); - - // don't apply filtering for patterns which mean the same, for example: - // *a**b and ***a*b - if (filter != null && filter.equalsFilter(newFilter)) { - return; - } - - filterHistoryJob.cancel(); - filterJob.cancel(); - - this.filter = newFilter; - - if (this.filter != null) { - filterHistoryJob.schedule(); - } - - } - - /** - * Returns comparator to sort items inside content provider. Returned object - * will be probably created as an anonymous class. Parameters passed to the - * compare(java.lang.Object, java.lang.Object) are going to - * be the same type as the one used in the content provider. - * - * @return comparator to sort items content provider - */ - protected abstract Comparator getItemsComparator(); - - /** - * Fills the content provider with matching items. - * - * @param contentProvider - * collector to add items to. - * {@link ColumnFilteredItemsSelectionDialog.AbstractContentProvider#add(Object, ColumnFilteredItemsSelectionDialog.ItemsFilter)} - * only adds items that pass the given itemsFilter. - * @param itemsFilter - * the items filter - * @param progressMonitor - * must be used to report search progress. The state of this - * progress monitor reflects the state of the filtering process. - * @throws CoreException - */ - protected abstract void fillContentProvider( - AbstractContentProvider contentProvider, ItemsFilter itemsFilter, - IProgressMonitor progressMonitor) throws CoreException; - - /** - * Force a refresh of the content provider. - */ - protected void forceRefresh() { - lastCompletedFilter = null; - lastCompletedResult = null; - filterHistoryJob.schedule(); - } - - /** - * Removes selected items from history. - * - * @param items - * items to be removed - */ - private void removeSelectedItems(List items) { - for (Iterator iter = items.iterator(); iter.hasNext();) { - Object item = iter.next(); - removeHistoryItem(item); - } - refreshWithLastSelection = false; - contentProvider.refresh(); - } - - /** - * Removes an item from history. - * - * @param item - * an item to remove - * @return removed item - */ - protected Object removeHistoryItem(Object item) { - return contentProvider.removeHistoryElement(item); - } - - /** - * Adds item to history. - * - * @param item - * the item to be added - */ - protected void accessedHistoryItem(Object item) { - contentProvider.addHistoryElement(item); - } - - /** - * Returns a history comparator. - * - * @return decorated comparator - */ - private Comparator getHistoryComparator() { - return new HistoryComparator(); - } - - /** - * Returns the history of selected elements. - * - * @return history of selected elements, or null if it is not - * set - */ - protected SelectionHistory getSelectionHistory() { - return this.contentProvider.getSelectionHistory(); - } - - /** - * Sets new history. - * - * @param selectionHistory - * the history - */ - protected void setSelectionHistory(SelectionHistory selectionHistory) { - if (this.contentProvider != null) - this.contentProvider.setSelectionHistory(selectionHistory); - } - - /** - * Indicates whether the given item is a history item. - * - * @param item - * the item to be investigated - * @return true if the given item exists in history, - * false otherwise - */ - public boolean isHistoryElement(Object item) { - return this.contentProvider.isHistoryElement(item); - } - - /** - * Indicates whether the given item is a duplicate. - * - * @param item - * the item to be investigated - * @return true if the item is duplicate, false - * otherwise - */ - public boolean isDuplicateElement(Object item) { - return this.contentProvider.isDuplicateElement(item); - } - - /** - * Sets separator label - * - * @param separatorLabel - * the label showed on separator - */ - public void setSeparatorLabel(String separatorLabel) { - this.itemsListSeparator = new ItemsListSeparator(separatorLabel); - } - - /** - * Returns name for then given object. - * - * @param item - * an object from the content provider. Subclasses should pay - * attention to the passed argument. They should either only pass - * objects of a known type (one used in content provider) or make - * sure that passed parameter is the expected one (by type - * checking like instanceof inside the method). - * @return name of the given item - */ - public abstract String getElementName(Object item); - - private class ToggleStatusLineAction extends Action { - - /** - * Creates a new instance of the class. - */ - public ToggleStatusLineAction() { - super(WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction, IAction.AS_CHECK_BOX); - } - - public void run() { - details.setVisible(isChecked()); - } - } - - /** - * Only refreshes UI on the basis of an already sorted and filtered set of - * items. - *

- * Standard invocation scenario: - *

    - *
  1. filtering job (FilterJob class extending - * Job class)
  2. - *
  3. cache refresh without checking for duplicates (RefreshCacheJob - * class extending Job class)
  4. - *
  5. UI refresh (RefreshJob class extending - * UIJob class)
  6. - *
  7. cache refresh with checking for duplicates (CacheRefreshJob - * class extending Job class)
  8. - *
  9. UI refresh (RefreshJob class extending UIJob - * class)
  10. - *
- * The scenario is rather complicated, but it had to be applied, because: - *
    - *
  • refreshing cache is rather a long action and cannot be run in the UI - - * cannot be run in a UIJob
  • - *
  • refreshing cache checking for duplicates is twice as long as - * refreshing cache without checking for duplicates; results of the search - * could be displayed earlier
  • - *
  • refreshing the UI have to be run in a UIJob
  • - *
- * - * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob - * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob - * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob - */ - private class RefreshJob extends UIJob { - - /** - * Creates a new instance of the class. - */ - public RefreshJob() { - super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(), - WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob); - setSystem(true); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) - */ - public IStatus runInUIThread(IProgressMonitor monitor) { - if (monitor.isCanceled()) - return new Status(IStatus.OK, WorkbenchPlugin.PI_WORKBENCH, - IStatus.OK, EMPTY_STRING, null); - - if (ColumnFilteredItemsSelectionDialog.this != null) { - ColumnFilteredItemsSelectionDialog.this.refresh(); - } - - return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK, - EMPTY_STRING, null); - } - - } - - /** - * Refreshes the progress message cyclically with 500 milliseconds delay. - * RefreshProgressMessageJob is strictly connected with - * GranualProgressMonitor and use it to to get progress - * message and to decide about break of cyclical refresh. - */ - private class RefreshProgressMessageJob extends UIJob { - - private GranualProgressMonitor progressMonitor; - - /** - * Creates a new instance of the class. - */ - public RefreshProgressMessageJob() { - super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(), - WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob); - setSystem(true); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) - */ - public IStatus runInUIThread(IProgressMonitor monitor) { - - if (!progressLabel.isDisposed()) - progressLabel.setText(progressMonitor != null ? progressMonitor.getMessage() : EMPTY_STRING); - - if (progressMonitor == null || progressMonitor.isDone()) { - return new Status(IStatus.CANCEL, PlatformUI.PLUGIN_ID, - IStatus.CANCEL, EMPTY_STRING, null); - } - - // Schedule cyclical with 500 milliseconds delay - schedule(500); - - return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK, - EMPTY_STRING, null); - } - - /** - * Schedule progress refresh job. - * - * @param progressMonitor - * used during refresh progress label - */ - public void scheduleProgressRefresh( - GranualProgressMonitor progressMonitor) { - this.progressMonitor = progressMonitor; - // Schedule with initial delay to avoid flickering when the user - // types quickly - schedule(200); - } - - } - - /** - * A job responsible for computing filtered items list presented using - * RefreshJob. - * - * @see ColumnFilteredItemsSelectionDialog.RefreshJob - * - */ - private class RefreshCacheJob extends Job { - - private RefreshJob refreshJob = new RefreshJob(); - - /** - * Creates a new instance of the class. - */ - public RefreshCacheJob() { - super(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob); - setSystem(true); - } - - /** - * Stops the job and all sub-jobs. - */ - public void cancelAll() { - cancel(); - refreshJob.cancel(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) - */ - protected IStatus run(IProgressMonitor monitor) { - if (monitor.isCanceled()) { - return new Status(IStatus.CANCEL, WorkbenchPlugin.PI_WORKBENCH, - IStatus.CANCEL, EMPTY_STRING, null); - } - - if (ColumnFilteredItemsSelectionDialog.this != null) { - GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(monitor); - ColumnFilteredItemsSelectionDialog.this.reloadCache(true, wrappedMonitor); - } - - if (!monitor.isCanceled()) { - refreshJob.schedule(); - } - - return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK, - EMPTY_STRING, null); - - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.jobs.Job#canceling() - */ - protected void canceling() { - super.canceling(); - contentProvider.stopReloadingCache(); - } - - } - - private class RemoveHistoryItemAction extends Action { - - /** - * Creates a new instance of the class. - */ - public RemoveHistoryItemAction() { - super(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.action.Action#run() - */ - public void run() { - List selectedElements = ((StructuredSelection) viewer.getSelection()).toList(); - removeSelectedItems(selectedElements); - } - } - - private static boolean showColoredLabels() { - return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS); - } - - private class ItemsListLabelProvider extends StyledCellLabelProvider - implements ILabelProviderListener { - private ILabelProvider provider; - - private ILabelDecorator selectionDecorator; - - // Need to keep our own list of listeners - private ListenerList listeners = new ListenerList(); - - /** - * Creates a new instance of the class. - * - * @param provider - * the label provider for all items, not null - * @param selectionDecorator - * the decorator for selected items, can be null - */ - public ItemsListLabelProvider(ILabelProvider provider, - ILabelDecorator selectionDecorator) { - Assert.isNotNull(provider); - this.provider = provider; - this.selectionDecorator = selectionDecorator; - - setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider); - - provider.addListener(this); - - if (selectionDecorator != null) { - selectionDecorator.addListener(this); - } - } - - /** - * Sets new selection decorator. - * - * @param newSelectionDecorator - * new label decorator for selected items in the list - */ - public void setSelectionDecorator(ILabelDecorator newSelectionDecorator) { - if (selectionDecorator != null) { - selectionDecorator.removeListener(this); - selectionDecorator.dispose(); - } - - selectionDecorator = newSelectionDecorator; - - if (selectionDecorator != null) { - selectionDecorator.addListener(this); - } - } - - /** - * Gets selection decorator. - * - * @return the label decorator for selected items in the list - */ - public ILabelDecorator getSelectionDecorator() { - return selectionDecorator; - } - - /** - * Sets new label provider. - * - * @param newProvider - * new label provider for items in the list, not - * null - */ - public void setProvider(ILabelProvider newProvider) { - Assert.isNotNull(newProvider); - provider.removeListener(this); - provider.dispose(); - - provider = newProvider; - - if (provider != null) { - provider.addListener(this); - } - - setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider); - } - - private Image getImage(Object element) { - if (element instanceof ItemsListSeparator) { - return WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR); - } - - return provider.getImage(element); - } - - private boolean isSelected(Object element) { - if (element != null && currentSelection != null) { - for (int i = 0; i < currentSelection.length; i++) { - if (element.equals(currentSelection[i])) - return true; - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) - */ - private String getText(Object element) { - if (element instanceof ItemsListSeparator) { - return getSeparatorLabel(((ItemsListSeparator) element).getName()); - } - - String str = provider.getText(element); - if (selectionDecorator != null && isSelected(element)) { - return selectionDecorator.decorateText(str.toString(), element); - } - - return str; - } - - private StyledString getStyledText(Object element, - IStyledLabelProvider provider) { - StyledString string = provider.getStyledText(element); - - if (selectionDecorator != null && isSelected(element)) { - String decorated = selectionDecorator.decorateText(string.getString(), element); - return StyledCellLabelProvider.styleDecoratedString(decorated, null, string); - // no need to add colors when element is selected - } - return string; - } - - public void update(ViewerCell cell) { - Object element = cell.getElement(); - - if (!(element instanceof ItemsListSeparator) && provider instanceof IStyledLabelProvider) { - IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) provider; - StyledString styledString = getStyledText(element, styledLabelProvider); - - cell.setText(styledString.getString()); - cell.setStyleRanges(styledString.getStyleRanges()); - cell.setImage(styledLabelProvider.getImage(element)); - } else { - cell.setText(getText(element)); - cell.setImage(getImage(element)); - } - cell.setFont(getFont(element)); - cell.setForeground(getForeground(element)); - cell.setBackground(getBackground(element)); - - super.update(cell); - } - - private String getSeparatorLabel(String separatorLabel) { - Rectangle rect = viewer.getTable().getBounds(); - - int borderWidth = viewer.getTable().computeTrim(0, 0, 0, 0).width; - - int imageWidth = WorkbenchImages.getImage( - IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width; - - int width = rect.width - borderWidth - imageWidth; - - GC gc = new GC(viewer.getTable()); - gc.setFont(viewer.getTable().getFont()); - - int fSeparatorWidth = gc.getAdvanceWidth('-'); - int fMessageLength = gc.textExtent(separatorLabel).x; - - gc.dispose(); - - StringBuffer dashes = new StringBuffer(); - int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2; - for (int i = 0; i < chars; i++) { - dashes.append('-'); - } - - StringBuffer result = new StringBuffer(); - result.append(dashes); - result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$ - result.append(dashes); - return result.toString().trim(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener) - */ - public void addListener(ILabelProviderListener listener) { - listeners.add(listener); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose() - */ - public void dispose() { - provider.removeListener(this); - provider.dispose(); - - if (selectionDecorator != null) { - selectionDecorator.removeListener(this); - selectionDecorator.dispose(); - } - - super.dispose(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, - * java.lang.String) - */ - public boolean isLabelProperty(Object element, String property) { - if (provider.isLabelProperty(element, property)) { - return true; - } - if (selectionDecorator != null - && selectionDecorator.isLabelProperty(element, property)) { - return true; - } - return false; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener) - */ - public void removeListener(ILabelProviderListener listener) { - listeners.remove(listener); - } - - private Color getBackground(Object element) { - if (element instanceof ItemsListSeparator) { - return null; - } - if (provider instanceof IColorProvider) { - return ((IColorProvider) provider).getBackground(element); - } - return null; - } - - private Color getForeground(Object element) { - if (element instanceof ItemsListSeparator) { - return Display.getCurrent().getSystemColor( - SWT.COLOR_WIDGET_NORMAL_SHADOW); - } - if (provider instanceof IColorProvider) { - return ((IColorProvider) provider).getForeground(element); - } - return null; - } - - private Font getFont(Object element) { - if (element instanceof ItemsListSeparator) { - return null; - } - if (provider instanceof IFontProvider) { - return ((IFontProvider) provider).getFont(element); - } - return null; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent) - */ - public void labelProviderChanged(LabelProviderChangedEvent event) { - Object[] l = listeners.getListeners(); - for (int i = 0; i < listeners.size(); i++) { - ((ILabelProviderListener) l[i]).labelProviderChanged(event); - } - } - } - - /** - * Used in ItemsListContentProvider, separates history and non-history - * items. - */ - private class ItemsListSeparator { - - private String name; - - /** - * Creates a new instance of the class. - * - * @param name - * the name of the separator - */ - public ItemsListSeparator(String name) { - this.name = name; - } - - /** - * Returns the name of this separator. - * - * @return the name of the separator - */ - public String getName() { - return name; - } - } - - /** - * GranualProgressMonitor is used for monitoring progress of filtering - * process. It is used by RefreshProgressMessageJob to - * refresh progress message. State of this monitor illustrates state of - * filtering or cache refreshing process. - * - */ - private class GranualProgressMonitor extends ProgressMonitorWrapper { - - private String name; - - private String subName; - - private int totalWork; - - private double worked; - - private boolean done; - - /** - * Creates instance of GranualProgressMonitor. - * - * @param monitor - * progress to be wrapped - */ - public GranualProgressMonitor(IProgressMonitor monitor) { - super(monitor); - } - - /** - * Checks if filtering has been done - * - * @return true if filtering work has been done false in other way - */ - public boolean isDone() { - return done; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String) - */ - public void setTaskName(String name) { - super.setTaskName(name); - this.name = name; - this.subName = null; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String) - */ - public void subTask(String name) { - super.subTask(name); - this.subName = name; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String, - * int) - */ - public void beginTask(String name, int totalWork) { - super.beginTask(name, totalWork); - if (this.name == null) - this.name = name; - this.totalWork = totalWork; - refreshProgressMessageJob.scheduleProgressRefresh(this); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int) - */ - public void worked(int work) { - super.worked(work); - internalWorked(work); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done() - */ - public void done() { - done = true; - super.done(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean) - */ - public void setCanceled(boolean b) { - done = b; - super.setCanceled(b); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double) - */ - public void internalWorked(double work) { - worked = worked + work; - } - - private String getMessage() { - if (done) - return ""; //$NON-NLS-1$ - - String message; - - if (name == null) { - message = subName == null ? "" : subName; //$NON-NLS-1$ - } else { - message = subName == null ? name - : NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage, - new Object[] { name, subName }); - } - if (totalWork == 0) - return message; - - return NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage, - new Object[] { message, new Integer((int) ((worked * 100) / totalWork)) }); - - } - - } - - /** - * Filters items history and schedule filter job. - */ - private class FilterHistoryJob extends Job { - - /** - * Filter used during the filtering process. - */ - private ItemsFilter itemsFilter; - - /** - * Creates new instance of receiver. - */ - public FilterHistoryJob() { - super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel); - setSystem(true); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) - */ - protected IStatus run(IProgressMonitor monitor) { - this.itemsFilter = filter; - - contentProvider.reset(); - - refreshWithLastSelection = false; - - contentProvider.addHistoryItems(itemsFilter); - - if (!(lastCompletedFilter != null && lastCompletedFilter.isSubFilter(this.itemsFilter))) - contentProvider.refresh(); - - filterJob.schedule(); - - return Status.OK_STATUS; - } - - } - - /** - * Filters items in indicated set and history. During filtering, it - * refreshes the dialog (progress monitor and elements list). - * - * Depending on the filter, FilterJob decides which kind of - * search will be run inside filterContent. If the last - * filtering is done (last completed filter), is not null, and the new - * filter is a sub-filter ({@link ColumnFilteredItemsSelectionDialog.ItemsFilter#isSubFilter(ColumnFilteredItemsSelectionDialog.ItemsFilter)}) - * of the last, then FilterJob only filters in the cache. If - * it is the first filtering or the new filter isn't a sub-filter of the - * last one, a full search is run. - */ - private class FilterJob extends Job { - - /** - * Filter used during the filtering process. - */ - protected ItemsFilter itemsFilter; - - /** - * Creates new instance of FilterJob - */ - public FilterJob() { - super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel); - setSystem(true); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) - */ - protected final IStatus run(IProgressMonitor parent) { - GranualProgressMonitor monitor = new GranualProgressMonitor(parent); - return doRun(monitor); - } - - /** - * Executes job using the given filtering progress monitor. A hook for - * subclasses. - * - * @param monitor - * progress monitor - * @return result of the execution - */ - protected IStatus doRun(GranualProgressMonitor monitor) { - try { - internalRun(monitor); - } catch (CoreException e) { - cancel(); - return new Status( - IStatus.ERROR, - PlatformUI.PLUGIN_ID, - IStatus.ERROR, - WorkbenchMessages.FilteredItemsSelectionDialog_jobError, - e); - } - return Status.OK_STATUS; - } - - /** - * Main method for the job. - * - * @param monitor - * @throws CoreException - */ - private void internalRun(GranualProgressMonitor monitor) - throws CoreException { - try { - if (monitor.isCanceled()) - return; - - this.itemsFilter = filter; - - // why is the content not filtered if the patter is empty? - // this makes no sense since the search pattern is able to - // handle the empty pattern just fine, and even has settings - // for what to do in this case - //if (filter.getPattern().length() != 0) { - // filterContent(monitor); - //} - - filterContent(monitor); - - if (monitor.isCanceled()) - return; - - contentProvider.refresh(); - } finally { - monitor.done(); - } - } - - /** - * Filters items. - * - * @param monitor - * for monitoring progress - * @throws CoreException - */ - protected void filterContent(GranualProgressMonitor monitor) - throws CoreException { - - if (lastCompletedFilter != null - && lastCompletedFilter.isSubFilter(this.itemsFilter)) { - - int length = lastCompletedResult.size() / 500; - monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName, length); - - for (int pos = 0; pos < lastCompletedResult.size(); pos++) { - - Object item = lastCompletedResult.get(pos); - if (monitor.isCanceled()) - break; - contentProvider.add(item, itemsFilter); - - if ((pos % 500) == 0) { - monitor.worked(1); - } - } - - } else { - - lastCompletedFilter = null; - lastCompletedResult = null; - - SubProgressMonitor subMonitor = null; - if (monitor != null) { - monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_searchJob_taskName, 100); - subMonitor = new SubProgressMonitor(monitor, 95); - - } - - fillContentProvider(contentProvider, itemsFilter, subMonitor); - - if (monitor != null && !monitor.isCanceled()) { - monitor.worked(2); - contentProvider.rememberResult(itemsFilter); - monitor.worked(3); - } - } - - } - - } - - /** - * History stores a list of key, object pairs. The list is bounded at a - * certain size. If the list exceeds this size the oldest element is removed - * from the list. An element can be added/renewed with a call to - * accessed(Object). - *

- * The history can be stored to/loaded from an XML file. - */ - protected static abstract class SelectionHistory { - - private static final String DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$ - - private static final String DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$ - - private static final int MAX_HISTORY_SIZE = 60; - - private final Set historyList; - - private final String rootNodeName; - - private final String infoNodeName; - - private SelectionHistory(String rootNodeName, String infoNodeName) { - - historyList = Collections.synchronizedSet(new LinkedHashSet() { - - private static final long serialVersionUID = 0L; - - /* - * (non-Javadoc) - * - * @see java.util.LinkedList#add(java.lang.Object) - */ - public boolean add(Object arg0) { - if (this.size() >= MAX_HISTORY_SIZE) { - Iterator iterator = this.iterator(); - iterator.next(); - iterator.remove(); - } - return super.add(arg0); - } - - }); - - this.rootNodeName = rootNodeName; - this.infoNodeName = infoNodeName; - } - - /** - * Creates new instance of SelectionHistory. - */ - public SelectionHistory() { - this(DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME); - } - - /** - * Adds object to history. - * - * @param object - * the item to be added to the history - */ - public synchronized void accessed(Object object) { - historyList.remove(object); - historyList.add(object); - } - - /** - * Returns true if history contains object. - * - * @param object - * the item for which check will be executed - * @return true if history contains object - * false in other way - */ - public synchronized boolean contains(Object object) { - return historyList.contains(object); - } - - /** - * Returns true if history is empty. - * - * @return true if history is empty - */ - public synchronized boolean isEmpty() { - return historyList.isEmpty(); - } - - /** - * Remove element from history. - * - * @param element - * to remove form the history - * @return true if this list contained the specified - * element - */ - public synchronized boolean remove(Object element) { - return historyList.remove(element); - } - - /** - * Load history elements from memento. - * - * @param memento - * memento from which the history will be retrieved - */ - public void load(IMemento memento) { - - XMLMemento historyMemento = (XMLMemento) memento - .getChild(rootNodeName); - - if (historyMemento == null) { - return; - } - - IMemento[] mementoElements = historyMemento - .getChildren(infoNodeName); - for (int i = 0; i < mementoElements.length; ++i) { - IMemento mementoElement = mementoElements[i]; - Object object = restoreItemFromMemento(mementoElement); - if (object != null) { - historyList.add(object); - } - } - } - - /** - * Save history elements to memento. - * - * @param memento - * memento to which the history will be added - */ - public void save(IMemento memento) { - - IMemento historyMemento = memento.createChild(rootNodeName); - - Object[] items = getHistoryItems(); - for (int i = 0; i < items.length; i++) { - Object item = items[i]; - IMemento elementMemento = historyMemento - .createChild(infoNodeName); - storeItemToMemento(item, elementMemento); - } - - } - - /** - * Gets array of history items. - * - * @return array of history elements - */ - public synchronized Object[] getHistoryItems() { - return historyList.toArray(); - } - - /** - * Creates an object using given memento. - * - * @param memento - * memento used for creating new object - * - * @return the restored object - */ - protected abstract Object restoreItemFromMemento(IMemento memento); - - /** - * Store object in IMemento. - * - * @param item - * the item to store - * @param memento - * the memento to store to - */ - protected abstract void storeItemToMemento(Object item, IMemento memento); - - } - - /** - * Filters elements using SearchPattern by comparing the names of items with - * the filter pattern. - */ - protected abstract class ItemsFilter { - - protected SearchPattern patternMatcher; - - /** - * Creates new instance of ItemsFilter. - */ - public ItemsFilter() { - this(new SearchPattern()); - } - - /** - * Creates new instance of ItemsFilter. - * - * @param searchPattern - * the pattern to be used when filtering - */ - public ItemsFilter(SearchPattern searchPattern) { - patternMatcher = searchPattern; - String stringPattern = ""; //$NON-NLS-1$ - if (pattern != null && !pattern.getText().equals("*")) { //$NON-NLS-1$ - stringPattern = pattern.getText(); - } - patternMatcher.setPattern(stringPattern); - } - - /** - * Check if the given filter is a sub-filter of this filter. The default - * implementation checks if the SearchPattern from the - * given filter is a sub-pattern of the one from this filter. - *

- * WARNING: This method is not defined in reading order, i.e. - * a.isSubFilter(b) is true iff - * b is a sub-filter of a, and not - * vice-versa. - *

- * - * @param filter - * the filter to be checked, or null - * @return true if the given filter is sub-filter of this - * filter, false if the given filter isn't a - * sub-filter or is null - * - * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern) - */ - public boolean isSubFilter(ItemsFilter filter) { - if (filter != null) { - return this.patternMatcher.isSubPattern(filter.patternMatcher); - } - return false; - } - - /** - * Checks whether the provided filter is equal to the current filter. - * The default implementation checks if SearchPattern - * from current filter is equal to the one from provided filter. - * - * @param filter - * filter to be checked, or null - * @return true if the given filter is equal to current - * filter, false if given filter isn't equal to - * current one or if it is null - * - * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern) - */ - public boolean equalsFilter(ItemsFilter filter) { - if (filter != null - && filter.patternMatcher.equalsPattern(this.patternMatcher)) { - return true; - } - return false; - } - - /** - * Checks whether the pattern's match rule is camel case. - * - * @return true if pattern's match rule is camel case, - * false otherwise - */ - public boolean isCamelCasePattern() { - return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH; - } - - /** - * Returns the pattern string. - * - * @return pattern for this filter - * - * @see SearchPattern#getPattern() - */ - public String getPattern() { - return patternMatcher.getPattern(); - } - - /** - * Returns the rule to apply for matching keys. - * - * @return an implementation-specific match rule - * - * @see SearchPattern#getMatchRule() for match rules returned by the - * default implementation - */ - public int getMatchRule() { - return patternMatcher.getMatchRule(); - } - - /** - * Matches text with filter. - * - * @param text - * the text to match with the filter - * @return true if text matches with filter pattern, - * false otherwise - */ - protected boolean matches(String text) { - return patternMatcher.matches(text); - } - - /** - * General method for matching raw name pattern. Checks whether current - * pattern is prefix of name provided item. - * - * @param item - * item to check - * @return true if current pattern is a prefix of name - * provided item, false if item's name is shorter - * than prefix or sequences of characters don't match. - */ - public boolean matchesRawNamePattern(Object item) { - String prefix = patternMatcher.getPattern(); - String text = getElementName(item); - - if (text == null) - return false; - - int textLength = text.length(); - int prefixLength = prefix.length(); - if (textLength < prefixLength) { - return false; - } - for (int i = prefixLength - 1; i >= 0; i--) { - if (Character.toLowerCase(prefix.charAt(i)) != Character - .toLowerCase(text.charAt(i))) - return false; - } - return true; - } - - /** - * Matches an item against filter conditions. - * - * @param item - * @return true if item matches against filter conditions, false - * otherwise - */ - public abstract boolean matchItem(Object item); - - /** - * Checks consistency of an item. Item is inconsistent if was changed or - * removed. - * - * @param item - * @return true if item is consistent, false - * if item is inconsistent - */ - public abstract boolean isConsistentItem(Object item); - - } - - /** - * An interface to content providers for - * FilterItemsSelectionDialog. - */ - protected abstract class AbstractContentProvider { - /** - * Adds the item to the content provider iff the filter matches the - * item. Otherwise does nothing. - * - * @param item - * the item to add - * @param itemsFilter - * the filter - * - * @see ColumnFilteredItemsSelectionDialog.ItemsFilter#matchItem(Object) - */ - public abstract void add(Object item, ItemsFilter itemsFilter); - } - - /** - * Collects filtered elements. Contains one synchronized, sorted set for - * collecting filtered elements. All collected elements are sorted using - * comparator. Comparator is returned by getElementComparator() method. - * Implementation of ItemsFilter is used to filter elements. - * The key function of filter used in to filtering is - * matchElement(Object item). - *

- * The ContentProvider class also provides item filtering - * methods. The filtering has been moved from the standard TableView - * getFilteredItems() method to content provider, because - * ILazyContentProvider and virtual tables are used. This - * class is responsible for adding a separator below history items and - * marking each items as duplicate if its name repeats more than once on the - * filtered list. - */ - private class ContentProvider extends AbstractContentProvider implements - IStructuredContentProvider, ILazyContentProvider { - - private SelectionHistory selectionHistory; - - /** - * Raw result of the searching (unsorted, unfiltered). - *

- * Standard object flow: - * items -> lastSortedItems -> lastFilteredItems - */ - private Set items; - - /** - * Items that are duplicates. - */ - private Set duplicates; - - /** - * List of ViewerFilters to be used during filtering - */ - private List filters; - - /** - * Result of the last filtering. - *

- * Standard object flow: - * items -> lastSortedItems -> lastFilteredItems - */ - private List lastFilteredItems; - - /** - * Result of the last sorting. - *

- * Standard object flow: - * items -> lastSortedItems -> lastFilteredItems - */ - private List lastSortedItems; - - /** - * Used for getFilteredItems() method canceling (when the - * job that invoked the method was canceled). - *

- * Method canceling could be based (only) on monitor canceling - * unfortunately sometimes the method getFilteredElements() - * could be run with a null monitor, the reset flag have - * to be left intact. - */ - private boolean reset; - - /** - * Creates new instance of ContentProvider. - */ - public ContentProvider() { - this.items = Collections.synchronizedSet(new HashSet(2048)); - this.duplicates = Collections.synchronizedSet(new HashSet(256)); - this.lastFilteredItems = new ArrayList(); - this.lastSortedItems = Collections.synchronizedList(new ArrayList(2048)); - } - - /** - * Sets selection history. - * - * @param selectionHistory - * The selectionHistory to set. - */ - public void setSelectionHistory(SelectionHistory selectionHistory) { - this.selectionHistory = selectionHistory; - } - - /** - * @return Returns the selectionHistory. - */ - public SelectionHistory getSelectionHistory() { - return selectionHistory; - } - - /** - * Removes all content items and resets progress message. - */ - public void reset() { - reset = true; - this.items.clear(); - this.duplicates.clear(); - this.lastSortedItems.clear(); - } - - /** - * Stops reloading cache - getFilteredItems() method. - */ - public void stopReloadingCache() { - reset = true; - } - - /** - * Adds filtered item. - * - * @param item - * @param itemsFilter - */ - public void add(Object item, ItemsFilter itemsFilter) { - if (itemsFilter == filter) { - if (itemsFilter != null) { - if (itemsFilter.matchItem(item)) { - this.items.add(item); - } - } else { - this.items.add(item); - } - } - } - - /** - * Add all history items to contentProvider. - * - * @param itemsFilter - */ - public void addHistoryItems(ItemsFilter itemsFilter) { - if (this.selectionHistory != null) { - Object[] items = this.selectionHistory.getHistoryItems(); - for (int i = 0; i < items.length; i++) { - Object item = items[i]; - if (itemsFilter == filter) { - if (itemsFilter != null) { - if (itemsFilter.matchItem(item)) { - if (itemsFilter.isConsistentItem(item)) { - this.items.add(item); - } else { - this.selectionHistory.remove(item); - } - } - } - } - } - } - } - - /** - * Refresh dialog. - */ - public void refresh() { - scheduleRefresh(); - } - - /** - * Removes items from history and refreshes the view. - * - * @param item - * to remove - * - * @return removed item - */ - public Object removeHistoryElement(Object item) { - if (this.selectionHistory != null) - this.selectionHistory.remove(item); - if (filter == null || filter.getPattern().length() == 0) { - items.remove(item); - duplicates.remove(item); - this.lastSortedItems.remove(item); - } - - synchronized (lastSortedItems) { - Collections.sort(lastSortedItems, getHistoryComparator()); - } - return item; - } - - /** - * Adds item to history and refresh view. - * - * @param item - * to add - */ - public void addHistoryElement(Object item) { - if (this.selectionHistory != null) - this.selectionHistory.accessed(item); - if (filter == null || !filter.matchItem(item)) { - this.items.remove(item); - this.duplicates.remove(item); - this.lastSortedItems.remove(item); - } - synchronized (lastSortedItems) { - Collections.sort(lastSortedItems, getHistoryComparator()); - } - this.refresh(); - } - - /** - * @param item - * @return true if given item is part of the history - */ - public boolean isHistoryElement(Object item) { - if (this.selectionHistory != null) { - return this.selectionHistory.contains(item); - } - return false; - } - - /** - * Sets/unsets given item as duplicate. - * - * @param item - * item to change - * - * @param isDuplicate - * duplicate flag - */ - public void setDuplicateElement(Object item, boolean isDuplicate) { - if (this.items.contains(item)) { - if (isDuplicate) - this.duplicates.add(item); - else - this.duplicates.remove(item); - } - } - - /** - * Indicates whether given item is a duplicate. - * - * @param item - * item to check - * @return true if item is duplicate - */ - public boolean isDuplicateElement(Object item) { - return duplicates.contains(item); - } - - /** - * Load history from memento. - * - * @param memento - * memento from which the history will be retrieved - */ - public void loadHistory(IMemento memento) { - if (this.selectionHistory != null) - this.selectionHistory.load(memento); - } - - /** - * Save history to memento. - * - * @param memento - * memento to which the history will be added - */ - public void saveHistory(IMemento memento) { - if (this.selectionHistory != null) - this.selectionHistory.save(memento); - } - - /** - * Gets sorted items. - * - * @return sorted items - */ - private Object[] getSortedItems() { - if (lastSortedItems.size() != items.size()) { - synchronized (lastSortedItems) { - lastSortedItems.clear(); - lastSortedItems.addAll(items); - Collections.sort(lastSortedItems, getHistoryComparator()); - } - } - return lastSortedItems.toArray(); - } - - /** - * Remember result of filtering. - * - * @param itemsFilter - */ - public void rememberResult(ItemsFilter itemsFilter) { - List itemsList = Collections.synchronizedList(Arrays - .asList(getSortedItems())); - // synchronization - if (itemsFilter == filter) { - lastCompletedFilter = itemsFilter; - lastCompletedResult = itemsList; - } - - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) - */ - public Object[] getElements(Object inputElement) { - return lastFilteredItems.toArray(); - } - - public int getNumberOfElements() { - return lastFilteredItems.size(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IContentProvider#dispose() - */ - public void dispose() { - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, - * java.lang.Object, java.lang.Object) - */ - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int) - */ - public void updateElement(int index) { - - ColumnFilteredItemsSelectionDialog.this.viewer.replace((lastFilteredItems - .size() > index) ? lastFilteredItems.get(index) : null, - index); - - } - - /** - * Main method responsible for getting the filtered items and checking - * for duplicates. It is based on the - * {@link ColumnFilteredItemsSelectionDialog.ContentProvider#getFilteredItems(Object, IProgressMonitor)}. - * - * @param checkDuplicates - * true if data concerning elements - * duplication should be computed - it takes much more time - * than standard filtering - * - * @param monitor - * progress monitor - */ - public void reloadCache(boolean checkDuplicates, - IProgressMonitor monitor) { - - reset = false; - - if (monitor != null) { - // the work is divided into two actions of the same length - int totalWork = checkDuplicates ? 200 : 100; - - monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob, totalWork); - } - - // the TableViewer's root (the input) is treated as parent - - lastFilteredItems = Arrays.asList(getFilteredItems(viewer.getInput(), - monitor != null ? new SubProgressMonitor(monitor, 100) : null)); - - if (reset || (monitor != null && monitor.isCanceled())) { - if (monitor != null) - monitor.done(); - return; - } - - if (checkDuplicates) { - checkDuplicates(monitor); - } - if (monitor != null) - monitor.done(); - } - - private void checkDuplicates(IProgressMonitor monitor) { - synchronized (lastFilteredItems) { - IProgressMonitor subMonitor = null; - int reportEvery = lastFilteredItems.size() / 20; - if (monitor != null) { - subMonitor = new SubProgressMonitor(monitor, 100); - subMonitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates, 5); - } - HashMap helperMap = new HashMap(); - for (int i = 0; i < lastFilteredItems.size(); i++) { - if (reset || (subMonitor != null && subMonitor.isCanceled())) - return; - Object item = lastFilteredItems.get(i); - - if (!(item instanceof ItemsListSeparator)) { - Object previousItem = helperMap.put(getElementName(item), item); - if (previousItem != null) { - setDuplicateElement(previousItem, true); - setDuplicateElement(item, true); - } else { - setDuplicateElement(item, false); - } - } - - if (subMonitor != null && reportEvery != 0 - && (i + 1) % reportEvery == 0) - subMonitor.worked(1); - } - helperMap.clear(); - } - } - - /** - * Returns an array of items filtered using the provided - * ViewerFilters with a separator added. - * - * @param parent - * the parent - * @param monitor - * progress monitor, can be null - * @return an array of filtered items - */ - protected Object[] getFilteredItems(Object parent, - IProgressMonitor monitor) { - int ticks = 100; - if (monitor == null) { - monitor = new NullProgressMonitor(); - } - - monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements, ticks); - if (filters != null) { - ticks /= (filters.size() + 2); - } else { - ticks /= 2; - } - - // get already sorted array - Object[] filteredElements = getSortedItems(); - - monitor.worked(ticks); - - // filter the elements using provided ViewerFilters - if (filters != null && filteredElements != null) { - for (Iterator iter = filters.iterator(); iter.hasNext();) { - ViewerFilter f = (ViewerFilter) iter.next(); - filteredElements = f.filter(viewer, parent, filteredElements); - monitor.worked(ticks); - } - } - - if (filteredElements == null || monitor.isCanceled()) { - monitor.done(); - return new Object[0]; - } - - ArrayList preparedElements = new ArrayList(); - boolean hasHistory = false; - - if (filteredElements.length > 0) { - if (isHistoryElement(filteredElements[0])) { - hasHistory = true; - } - } - - int reportEvery = filteredElements.length / ticks; - - // add separator - for (int i = 0; i < filteredElements.length; i++) { - Object item = filteredElements[i]; - - if (hasHistory && !isHistoryElement(item)) { - preparedElements.add(itemsListSeparator); - hasHistory = false; - } - - preparedElements.add(item); - - if (reportEvery != 0 && ((i + 1) % reportEvery == 0)) { - monitor.worked(1); - } - } - - monitor.done(); - - return preparedElements.toArray(); - } - - /** - * Adds a filter to this content provider. For an example usage of such - * filters look at the project org.eclipse.ui.ide, class - * org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter. - * - * - * @param filter - * the filter to be added - */ - public void addFilter(ViewerFilter filter) { - if (filters == null) { - filters = new ArrayList(); - } - filters.add(filter); - // currently filters are only added when dialog is restored - // if it is changed, refreshing the whole TableViewer should be - // added - } - - } - - /** - * A content provider that does nothing. - */ - private class NullContentProvider implements IContentProvider { - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IContentProvider#dispose() - */ - public void dispose() { - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, - * java.lang.Object, java.lang.Object) - */ - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - } - - /** - * DetailsContentViewer objects are wrappers for labels. - * DetailsContentViewer provides means to change label's image and text when - * the attached LabelProvider is updated. - */ - private class DetailsContentViewer extends ContentViewer { - - private CLabel label; - - /** - * Unfortunately, it was impossible to delegate displaying border to - * label. The ViewForm is used because - * CLabel displays shadow when border is present. - */ - private ViewForm viewForm; - - /** - * Constructs a new instance of this class given its parent and a style - * value describing its behavior and appearance. - * - * @param parent - * the parent component - * @param style - * SWT style bits - */ - public DetailsContentViewer(Composite parent, int style) { - viewForm = new ViewForm(parent, style); - GridData gd = new GridData(GridData.FILL_HORIZONTAL); - gd.horizontalSpan = 2; - viewForm.setLayoutData(gd); - label = new CLabel(viewForm, SWT.FLAT); - label.setFont(parent.getFont()); - viewForm.setContent(label); - hookControl(label); - } - - /** - * Shows/hides the content viewer. - * - * @param visible - * if the content viewer should be visible. - */ - public void setVisible(boolean visible) { - GridData gd = (GridData) viewForm.getLayoutData(); - gd.exclude = !visible; - viewForm.getParent().layout(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, - * java.lang.Object) - */ - protected void inputChanged(Object input, Object oldInput) { - if (oldInput == null) { - if (input == null) { - return; - } - refresh(); - return; - } - - refresh(); - - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent) - */ - protected void handleLabelProviderChanged( - LabelProviderChangedEvent event) { - if (event != null) { - refresh(event.getElements()); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.Viewer#getControl() - */ - public Control getControl() { - return label; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.Viewer#getSelection() - */ - public ISelection getSelection() { - // not supported - return null; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.Viewer#refresh() - */ - public void refresh() { - Object input = this.getInput(); - if (input != null) { - ILabelProvider labelProvider = (ILabelProvider) getLabelProvider(); - doRefresh(labelProvider.getText(input), labelProvider - .getImage(input)); - } else { - doRefresh(null, null); - } - } - - /** - * Sets the given text and image to the label. - * - * @param text - * the new text or null - * @param image - * the new image - */ - private void doRefresh(String text, Image image) { - if ( text != null ) { - text = LegacyActionTools.escapeMnemonics(text); - } - label.setText(text); - label.setImage(image); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, - * boolean) - */ - public void setSelection(ISelection selection, boolean reveal) { - // not supported - } - - /** - * Refreshes the label if currently chosen element is on the list. - * - * @param objs - * list of changed object - */ - private void refresh(Object[] objs) { - if (objs == null || getInput() == null) { - return; - } - Object input = getInput(); - for (int i = 0; i < objs.length; i++) { - if (objs[i].equals(input)) { - refresh(); - break; - } - } - } - } - - /** - * Compares items according to the history. - */ - private class HistoryComparator implements Comparator { - - /* - * (non-Javadoc) - * - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - public int compare(Object o1, Object o2) { - boolean h1 = isHistoryElement(o1); - boolean h2 = isHistoryElement(o2); - if (h1 == h2) - return getItemsComparator().compare(o1, o2); - - if (h1) - return -2; - if (h2) - return +2; - - return 0; - } - - } - - - /** - * Get the control where the search pattern is entered. Any filtering should - * be done using an {@link ItemsFilter}. This control should only be - * accessed for listeners that wish to handle events that do not affect - * filtering such as custom traversal. - * - * @return Control or null if the pattern control has not - * been created. - */ - public Control getPatternControl() { - return pattern; - } - -} - +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Willian Mitsuda + * - Fix for bug 196553 - [Dialogs] Support IColorProvider/IFontProvider in FilteredItemsSelectionDialog + * Peter Friese + * - Fix for bug 208602 - [Dialogs] Open Type dialog needs accessible labels + * Simon Muschel - bug 258493 + *******************************************************************************/ +package org.simantics.utils.ui.dialogs; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.ProgressMonitorWrapper; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.LegacyActionTools; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ContentViewer; +import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IFontProvider; +import org.eclipse.jface.viewers.ILabelDecorator; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.LabelProviderChangedEvent; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.StyledCellLabelProvider; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.ACC; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.custom.ViewForm; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +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.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.ui.ActiveShellExpression; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.IWorkbenchCommandConstants; +import org.eclipse.ui.IWorkbenchPreferenceConstants; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.WorkbenchException; +import org.eclipse.ui.XMLMemento; +import org.eclipse.ui.dialogs.SearchPattern; +import org.eclipse.ui.dialogs.SelectionStatusDialog; +import org.eclipse.ui.handlers.IHandlerActivation; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.internal.IWorkbenchGraphicConstants; +import org.eclipse.ui.internal.WorkbenchImages; +import org.eclipse.ui.internal.WorkbenchMessages; +import org.eclipse.ui.internal.WorkbenchPlugin; +import org.eclipse.ui.progress.UIJob; +import org.eclipse.ui.statushandlers.StatusManager; + +/** + * Shows a list of items to the user with a text entry field for a string + * pattern used to filter the list of items. + * + * This is a copy from org.eclipse.ui.dialogs.FilteredItemsSelectionDialog + * with a hook for column creation and a fix to a (possible) bug that + * prevented the empty pattern to be handled correctly. This version of the + * dialog also has the possibility to force an update of the contents if new + * elements are added. + * + * TODO: Clean up warnings. + * + * @author Janne Kauttio (modifications only) + * + * @since 3.3 + */ +@SuppressWarnings({ "restriction", "rawtypes", "unchecked" }) +public abstract class ColumnFilteredItemsSelectionDialog extends + SelectionStatusDialog { + + private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$ + + private static final String SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$ + + private static final String HISTORY_SETTINGS = "History"; //$NON-NLS-1$ + + private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$ + + private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$ + + /** + * Represents an empty selection in the pattern input field (used only for + * initial pattern). + */ + public static final int NONE = 0; + + /** + * Pattern input field selection where caret is at the beginning (used only + * for initial pattern). + */ + public static final int CARET_BEGINNING = 1; + + /** + * Represents a full selection in the pattern input field (used only for + * initial pattern). + */ + public static final int FULL_SELECTION = 2; + + private Text pattern; + + private TableViewer viewer; + + private DetailsContentViewer details; + + /** + * It is a duplicate of a field in the CLabel class in DetailsContentViewer. + * It is maintained, because the setDetailsLabelProvider() + * could be called before content area is created. + */ + private ILabelProvider detailsLabelProvider; + + private ItemsListLabelProvider itemsListLabelProvider; + + private MenuManager menuManager; + + private MenuManager contextMenuManager; + + private boolean multi; + + private ToolBar toolBar; + + private ToolItem toolItem; + + private Label progressLabel; + + private ToggleStatusLineAction toggleStatusLineAction; + + private RemoveHistoryItemAction removeHistoryItemAction; + + private ActionContributionItem removeHistoryActionContributionItem; + + private IStatus status; + + private RefreshCacheJob refreshCacheJob; + + private RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob(); + + private Object[] currentSelection; + + private ContentProvider contentProvider; + + private FilterHistoryJob filterHistoryJob; + + private FilterJob filterJob; + + private ItemsFilter filter; + + private List lastCompletedResult; + + private ItemsFilter lastCompletedFilter; + + private String initialPatternText; + + private int selectionMode; + + private ItemsListSeparator itemsListSeparator; + + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ + + private boolean refreshWithLastSelection = false; + + private IHandlerActivation showViewHandler; + + /** + * Creates a new instance of the class. + * + * @param shell + * shell to parent the dialog on + * @param multi + * indicates whether dialog allows to select more than one + * position in its list of items + */ + public ColumnFilteredItemsSelectionDialog(Shell shell, boolean multi) { + super(shell); + this.multi = multi; + filterHistoryJob = new FilterHistoryJob(); + filterJob = new FilterJob(); + contentProvider = new ContentProvider(); + refreshCacheJob = new RefreshCacheJob(); + itemsListSeparator = new ItemsListSeparator(WorkbenchMessages.FilteredItemsSelectionDialog_separatorLabel); + selectionMode = NONE; + } + + /** + * Creates a new instance of the class. Created dialog won't allow to select + * more than one item. + * + * @param shell + * shell to parent the dialog on + */ + public ColumnFilteredItemsSelectionDialog(Shell shell) { + this(shell, false); + } + + /** + * Adds viewer filter to the dialog items list. + * + * @param filter + * the new filter + */ + protected void addListFilter(ViewerFilter filter) { + contentProvider.addFilter(filter); + } + + /** + * Sets a new label provider for items in the list. If the label provider + * also implements {@link + * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider + * .IStyledLabelProvider}, the style text labels provided by it will be used + * provided that the corresponding preference is set. + * + * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS + * + * @param listLabelProvider + * the label provider for items in the list + */ + public void setListLabelProvider(ILabelProvider listLabelProvider) { + getItemsListLabelProvider().setProvider(listLabelProvider); + } + + /** + * Returns the label decorator for selected items in the list. + * + * @return the label decorator for selected items in the list + */ + private ILabelDecorator getListSelectionLabelDecorator() { + return getItemsListLabelProvider().getSelectionDecorator(); + } + + /** + * Sets the label decorator for selected items in the list. + * + * @param listSelectionLabelDecorator + * the label decorator for selected items in the list + */ + public void setListSelectionLabelDecorator( + ILabelDecorator listSelectionLabelDecorator) { + getItemsListLabelProvider().setSelectionDecorator( + listSelectionLabelDecorator); + } + + /** + * Returns the item list label provider. + * + * @return the item list label provider + */ + private ItemsListLabelProvider getItemsListLabelProvider() { + if (itemsListLabelProvider == null) { + itemsListLabelProvider = new ItemsListLabelProvider( + new LabelProvider(), null); + } + return itemsListLabelProvider; + } + + /** + * Sets label provider for the details field. + * + * For a single selection, the element sent to + * {@link ILabelProvider#getImage(Object)} and + * {@link ILabelProvider#getText(Object)} is the selected object, for + * multiple selection a {@link String} with amount of selected items is the + * element. + * + * @see #getSelectedItems() getSelectedItems() can be used to retrieve + * selected items and get the items count. + * + * @param detailsLabelProvider + * the label provider for the details field + */ + public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider) { + this.detailsLabelProvider = detailsLabelProvider; + if (details != null) { + details.setLabelProvider(detailsLabelProvider); + } + } + + private ILabelProvider getDetailsLabelProvider() { + if (detailsLabelProvider == null) { + detailsLabelProvider = new LabelProvider(); + } + return detailsLabelProvider; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.window.Window#create() + */ + public void create() { + super.create(); + pattern.setFocus(); + } + + /** + * Restores dialog using persisted settings. The default implementation + * restores the status of the details line and the selection history. + * + * @param settings + * settings used to restore dialog + */ + protected void restoreDialog(IDialogSettings settings) { + boolean toggleStatusLine = true; + + if (settings.get(SHOW_STATUS_LINE) != null) { + toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE); + } + + toggleStatusLineAction.setChecked(toggleStatusLine); + + details.setVisible(toggleStatusLine); + + String setting = settings.get(HISTORY_SETTINGS); + if (setting != null) { + try { + IMemento memento = XMLMemento.createReadRoot(new StringReader(setting)); + this.contentProvider.loadHistory(memento); + } catch (WorkbenchException e) { + // Simply don't restore the settings + StatusManager.getManager().handle(new Status( + IStatus.ERROR, + PlatformUI.PLUGIN_ID, + IStatus.ERROR, + WorkbenchMessages.FilteredItemsSelectionDialog_restoreError, + e)); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.window.Window#close() + */ + public boolean close() { + this.filterJob.cancel(); + this.refreshCacheJob.cancel(); + this.refreshProgressMessageJob.cancel(); + if (showViewHandler != null) { + IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); + service.deactivateHandler(showViewHandler); + showViewHandler.getHandler().dispose(); + showViewHandler = null; + } + if (menuManager != null) + menuManager.dispose(); + if (contextMenuManager != null) + contextMenuManager.dispose(); + storeDialog(getDialogSettings()); + return super.close(); + } + + /** + * Stores dialog settings. + * + * @param settings + * settings used to store dialog + */ + protected void storeDialog(IDialogSettings settings) { + settings.put(SHOW_STATUS_LINE, toggleStatusLineAction.isChecked()); + + XMLMemento memento = XMLMemento.createWriteRoot(HISTORY_SETTINGS); + this.contentProvider.saveHistory(memento); + StringWriter writer = new StringWriter(); + try { + memento.save(writer); + settings.put(HISTORY_SETTINGS, writer.getBuffer().toString()); + } catch (IOException e) { + // Simply don't store the settings + StatusManager.getManager().handle(new Status( + IStatus.ERROR, + PlatformUI.PLUGIN_ID, + IStatus.ERROR, + WorkbenchMessages.FilteredItemsSelectionDialog_storeError, + e)); + } + } + + /** + * Create a new header which is labelled by headerLabel. + * + * @param parent + * @return Label the label of the header + */ + private Label createHeader(Composite parent) { + Composite header = new Composite(parent, SWT.NONE); + + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + layout.marginWidth = 0; + layout.marginHeight = 0; + header.setLayout(layout); + + Label headerLabel = new Label(header, SWT.NONE); + headerLabel.setText((getMessage() != null && getMessage().trim().length() > 0) ? getMessage() + : WorkbenchMessages.FilteredItemsSelectionDialog_patternLabel); + headerLabel.addTraverseListener(new TraverseListener() { + public void keyTraversed(TraverseEvent e) { + if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) { + e.detail = SWT.TRAVERSE_NONE; + pattern.setFocus(); + } + } + }); + + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + headerLabel.setLayoutData(gd); + + createViewMenu(header); + header.setLayoutData(gd); + return headerLabel; + } + + /** + * Create the labels for the list and the progress. Return the list label. + * + * @param parent + * @return Label + */ + private Label createLabels(Composite parent) { + Composite labels = new Composite(parent, SWT.NONE); + + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + layout.marginWidth = 0; + layout.marginHeight = 0; + labels.setLayout(layout); + + Label listLabel = new Label(labels, SWT.NONE); + listLabel.setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel); + + listLabel.addTraverseListener(new TraverseListener() { + public void keyTraversed(TraverseEvent e) { + if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) { + e.detail = SWT.TRAVERSE_NONE; + viewer.getTable().setFocus(); + } + } + }); + + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + listLabel.setLayoutData(gd); + + progressLabel = new Label(labels, SWT.RIGHT); + progressLabel.setLayoutData(gd); + + labels.setLayoutData(gd); + return listLabel; + } + + private void createViewMenu(Composite parent) { + toolBar = new ToolBar(parent, SWT.FLAT); + toolItem = new ToolItem(toolBar, SWT.PUSH, 0); + + GridData data = new GridData(); + data.horizontalAlignment = GridData.END; + toolBar.setLayoutData(data); + + toolBar.addMouseListener(new MouseAdapter() { + public void mouseDown(MouseEvent e) { + showViewMenu(); + } + }); + + toolItem.setImage(WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU)); + toolItem.setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu); + toolItem.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + showViewMenu(); + } + }); + + menuManager = new MenuManager(); + + fillViewMenu(menuManager); + + IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); + IHandler handler = new AbstractHandler() { + public Object execute(ExecutionEvent event) { + showViewMenu(); + return null; + } + }; + showViewHandler = service.activateHandler( + IWorkbenchCommandConstants.WINDOW_SHOW_VIEW_MENU, handler, + new ActiveShellExpression(getShell())); + } + + /** + * Fills the menu of the dialog. + * + * @param menuManager + * the menu manager + */ + protected void fillViewMenu(IMenuManager menuManager) { + toggleStatusLineAction = new ToggleStatusLineAction(); + menuManager.add(toggleStatusLineAction); + } + + private void showViewMenu() { + Menu menu = menuManager.createContextMenu(getShell()); + Rectangle bounds = toolItem.getBounds(); + Point topLeft = new Point(bounds.x, bounds.y + bounds.height); + topLeft = toolBar.toDisplay(topLeft); + menu.setLocation(topLeft.x, topLeft.y); + menu.setVisible(true); + } + + /** + * Hook that allows to add actions to the context menu. + *

+ * Subclasses may extend in order to add other actions.

+ * + * @param menuManager the context menu manager + * @since 3.5 + */ + protected void fillContextMenu(IMenuManager menuManager) { + List selectedElements= ((StructuredSelection)viewer.getSelection()).toList(); + + Object item= null; + + for (Iterator it= selectedElements.iterator(); it.hasNext();) { + item= it.next(); + if (item instanceof ItemsListSeparator || !isHistoryElement(item)) { + return; + } + } + + if (selectedElements.size() > 0) { + removeHistoryItemAction.setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction); + + menuManager.add(removeHistoryActionContributionItem); + + } + } + + private void createPopupMenu() { + removeHistoryItemAction = new RemoveHistoryItemAction(); + removeHistoryActionContributionItem = new ActionContributionItem(removeHistoryItemAction); + + contextMenuManager = new MenuManager(); + contextMenuManager.setRemoveAllWhenShown(true); + contextMenuManager.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + fillContextMenu(manager); + } + }); + + final Table table = viewer.getTable(); + Menu menu= contextMenuManager.createContextMenu(table); + table.setMenu(menu); + } + + /** + * Creates an extra content area, which will be located above the details. + * + * @param parent + * parent to create the dialog widgets in + * @return an extra content area + */ + protected abstract Control createExtendedContentArea(Composite parent); + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) + */ + protected Control createDialogArea(Composite parent) { + Composite dialogArea = (Composite) super.createDialogArea(parent); + + Composite content = new Composite(dialogArea, SWT.NONE); + GridData gd = new GridData(GridData.FILL_BOTH); + content.setLayoutData(gd); + + GridLayout layout = new GridLayout(); + layout.numColumns = 1; + layout.marginWidth = 0; + layout.marginHeight = 0; + content.setLayout(layout); + + final Label headerLabel = createHeader(content); + + pattern = new Text(content, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL); + pattern.getAccessible().addAccessibleListener(new AccessibleAdapter() { + public void getName(AccessibleEvent e) { + e.result = LegacyActionTools.removeMnemonics(headerLabel.getText()); + } + }); + gd = new GridData(GridData.FILL_HORIZONTAL); + pattern.setLayoutData(gd); + + final Label listLabel = createLabels(content); + + viewer = new TableViewer(content, (multi ? SWT.MULTI : SWT.SINGLE) + | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION); + viewer.getTable().getAccessible().addAccessibleListener( + new AccessibleAdapter() { + public void getName(AccessibleEvent e) { + if (e.childID == ACC.CHILDID_SELF) { + e.result = LegacyActionTools.removeMnemonics(listLabel.getText()); + } + } + }); + viewer.setContentProvider(contentProvider); + + // added column creation hook + createColumns(viewer); + + // show headers etc. if columns were added by the subclass + if (viewer.getTable().getColumnCount() > 0) { + viewer.getTable().setLinesVisible(true); + viewer.getTable().setHeaderVisible(true); + } + + viewer.setInput(new Object[0]); + viewer.setItemCount(contentProvider.getNumberOfElements()); + gd = new GridData(GridData.FILL_BOTH); + applyDialogFont(viewer.getTable()); + gd.heightHint= viewer.getTable().getItemHeight() * 15; + viewer.getTable().setLayoutData(gd); + + createPopupMenu(); + + pattern.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + applyFilter(); + } + }); + + pattern.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if (e.keyCode == SWT.ARROW_DOWN) { + if (viewer.getTable().getItemCount() > 0) { + viewer.getTable().setFocus(); + } + } + } + }); + + viewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + StructuredSelection selection = (StructuredSelection) event.getSelection(); + handleSelected(selection); + } + }); + + viewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + handleDoubleClick(); + } + }); + + viewer.getTable().addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + + if (e.keyCode == SWT.DEL) { + + List selectedElements = ((StructuredSelection) viewer.getSelection()).toList(); + + Object item = null; + boolean isSelectedHistory = true; + + for (Iterator it = selectedElements.iterator(); it.hasNext();) { + item = it.next(); + if (item instanceof ItemsListSeparator || !isHistoryElement(item)) { + isSelectedHistory = false; + break; + } + } + if (isSelectedHistory) + removeSelectedItems(selectedElements); + + } + + if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) != 0 + && (e.stateMask & SWT.CTRL) != 0) { + StructuredSelection selection = (StructuredSelection) viewer.getSelection(); + + if (selection.size() == 1) { + Object element = selection.getFirstElement(); + if (element.equals(viewer.getElementAt(0))) { + pattern.setFocus(); + } + if (viewer.getElementAt(viewer.getTable().getSelectionIndex() - 1) instanceof ItemsListSeparator) + viewer.getTable().setSelection(viewer.getTable().getSelectionIndex() - 1); + viewer.getTable().notifyListeners(SWT.Selection, new Event()); + + } + } + + if (e.keyCode == SWT.ARROW_DOWN + && (e.stateMask & SWT.SHIFT) != 0 + && (e.stateMask & SWT.CTRL) != 0) { + + if (viewer.getElementAt(viewer.getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator) + viewer.getTable().setSelection(viewer.getTable().getSelectionIndex() + 1); + viewer.getTable().notifyListeners(SWT.Selection, new Event()); + } + } + }); + + createExtendedContentArea(content); + + details = new DetailsContentViewer(content, SWT.BORDER | SWT.FLAT); + details.setVisible(toggleStatusLineAction.isChecked()); + details.setContentProvider(new NullContentProvider()); + details.setLabelProvider(getDetailsLabelProvider()); + + applyDialogFont(content); + + restoreDialog(getDialogSettings()); + + if (initialPatternText != null) { + pattern.setText(initialPatternText); + } + + switch (selectionMode) { + case CARET_BEGINNING: + pattern.setSelection(0, 0); + break; + case FULL_SELECTION: + pattern.setSelection(0, initialPatternText.length()); + break; + } + + // apply filter even if pattern is empty (display history) + applyFilter(); + + return dialogArea; + } + + /** + * Override this method to add columns to the content area of this dialog. + * + * Subclass implementation of this method should NOT call the super + * implementation as this will break the individual column label providers + * + * @param viewer + */ + protected void createColumns(TableViewer viewer) { + // no columns are added by default, just set the label provider for the viewer + viewer.setLabelProvider(getItemsListLabelProvider()); + } + + /** + * An utility method for adding a column to the TableViewer, should be called + * from inside createColumns. + * + * @param viewer + * @param label + * @param width + * @param labelProvider + */ + protected void createColumn(TableViewer viewer, String label, int width, ColumnLabelProvider labelProvider) { + TableViewerColumn viewercol = new TableViewerColumn(viewer, SWT.LEFT); + + TableColumn col = viewercol.getColumn(); + col.setText(label); + col.setWidth(width); + col.setResizable(true); + col.setMoveable(true); + + // TODO: should use the local label provider class instead but it + // should be made compatible with multiple columns first + viewercol.setLabelProvider(labelProvider); + } + + /** + * This method is a hook for subclasses to override default dialog behavior. + * The handleDoubleClick() method handles double clicks on + * the list of filtered elements. + *

+ * Current implementation makes double-clicking on the list do the same as + * pressing OK button on the dialog. + */ + protected void handleDoubleClick() { + okPressed(); + } + + /** + * Refreshes the details field according to the current selection in the + * items list. + */ + private void refreshDetails() { + StructuredSelection selection = getSelectedItems(); + + switch (selection.size()) { + case 0: + details.setInput(null); + break; + case 1: + details.setInput(selection.getFirstElement()); + break; + default: + details.setInput(NLS.bind( + WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected, + new Integer(selection.size()))); + break; + } + + } + + /** + * Handle selection in the items list by updating labels of selected and + * unselected items and refresh the details field using the selection. + * + * @param selection + * the new selection + */ + protected void handleSelected(StructuredSelection selection) { + IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID, + IStatus.OK, EMPTY_STRING, null); + + Object[] lastSelection = currentSelection; + + currentSelection = selection.toArray(); + + if (selection.size() == 0) { + status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, + IStatus.ERROR, EMPTY_STRING, null); + + if (lastSelection != null && getListSelectionLabelDecorator() != null) { + viewer.update(lastSelection, null); + } + + currentSelection = null; + + } else { + status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, + IStatus.ERROR, EMPTY_STRING, null); + + List items = selection.toList(); + + Object item = null; + IStatus tempStatus = null; + + for (Iterator it = items.iterator(); it.hasNext();) { + Object o = it.next(); + + if (o instanceof ItemsListSeparator) { + continue; + } + + item = o; + tempStatus = validateItem(item); + + if (tempStatus.isOK()) { + status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID, + IStatus.OK, EMPTY_STRING, null); + } else { + status = tempStatus; + // if any selected element is not valid status is set to + // ERROR + break; + } + } + + if (lastSelection != null && getListSelectionLabelDecorator() != null) { + viewer.update(lastSelection, null); + } + + if (getListSelectionLabelDecorator() != null) { + viewer.update(currentSelection, null); + } + } + + refreshDetails(); + updateStatus(status); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings() + */ + protected IDialogSettings getDialogBoundsSettings() { + IDialogSettings settings = getDialogSettings(); + IDialogSettings section = settings.getSection(DIALOG_BOUNDS_SETTINGS); + if (section == null) { + section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS); + section.put(DIALOG_HEIGHT, 500); + section.put(DIALOG_WIDTH, 600); + } + return section; + } + + /** + * Returns the dialog settings. Returned object can't be null. + * + * @return return dialog settings for this dialog + */ + protected abstract IDialogSettings getDialogSettings(); + + /** + * Refreshes the dialog - has to be called in UI thread. + */ + public void refresh() { + if (viewer != null && !viewer.getTable().isDisposed()) { + + List lastRefreshSelection = ((StructuredSelection) viewer.getSelection()).toList(); + viewer.getTable().deselectAll(); + + viewer.setItemCount(contentProvider.getNumberOfElements()); + viewer.refresh(); + + if (viewer.getTable().getItemCount() > 0) { + // preserve previous selection + if (refreshWithLastSelection && lastRefreshSelection != null + && lastRefreshSelection.size() > 0) { + viewer.setSelection(new StructuredSelection( + lastRefreshSelection)); + } else { + refreshWithLastSelection = true; + viewer.getTable().setSelection(0); + viewer.getTable().notifyListeners(SWT.Selection, new Event()); + } + } else { + viewer.setSelection(StructuredSelection.EMPTY); + } + + } + + scheduleProgressMessageRefresh(); + } + + /** + * Updates the progress label. + * + * @deprecated + */ + public void updateProgressLabel() { + scheduleProgressMessageRefresh(); + } + + /** + * Notifies the content provider - fires filtering of content provider + * elements. During the filtering, a separator between history and workspace + * matches is added. + *

+ * This is a long running operation and should be called in a job. + * + * @param checkDuplicates + * true if data concerning elements duplication + * should be computed - it takes much more time than the standard + * filtering + * @param monitor + * a progress monitor or null if no monitor is + * available + */ + public void reloadCache(boolean checkDuplicates, IProgressMonitor monitor) { + if (viewer != null && !viewer.getTable().isDisposed() && contentProvider != null) { + contentProvider.reloadCache(checkDuplicates, monitor); + } + } + + /** + * Schedule refresh job. + */ + public void scheduleRefresh() { + refreshCacheJob.cancelAll(); + refreshCacheJob.schedule(); + } + + /** + * Schedules progress message refresh. + */ + public void scheduleProgressMessageRefresh() { + if (filterJob.getState() != Job.RUNNING && refreshProgressMessageJob.getState() != Job.RUNNING) + refreshProgressMessageJob.scheduleProgressRefresh(null); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult() + */ + protected void computeResult() { + + List selectedElements = ((StructuredSelection) viewer.getSelection()) + .toList(); + + List objectsToReturn = new ArrayList(); + + Object item = null; + + for (Iterator it = selectedElements.iterator(); it.hasNext();) { + item = it.next(); + + if (!(item instanceof ItemsListSeparator)) { + accessedHistoryItem(item); + objectsToReturn.add(item); + } + } + + setResult(objectsToReturn); + } + + /* + * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus) + */ + protected void updateStatus(IStatus status) { + this.status = status; + super.updateStatus(status); + } + + /* + * @see Dialog#okPressed() + */ + protected void okPressed() { + if (status != null && (status.isOK() || status.getCode() == IStatus.INFO)) { + super.okPressed(); + } + } + + /** + * Sets the initial pattern used by the filter. This text is copied into the + * selection input on the dialog. A full selection is used in the pattern + * input field. + * + * @param text + * initial pattern for the filter + * @see ColumnFilteredItemsSelectionDialog#FULL_SELECTION + */ + public void setInitialPattern(String text) { + setInitialPattern(text, FULL_SELECTION); + } + + /** + * Sets the initial pattern used by the filter. This text is copied into the + * selection input on the dialog. The selectionMode is used + * to choose selection type for the input field. + * + * @param text + * initial pattern for the filter + * @param selectionMode + * one of: {@link ColumnFilteredItemsSelectionDialog#NONE}, + * {@link ColumnFilteredItemsSelectionDialog#CARET_BEGINNING}, + * {@link ColumnFilteredItemsSelectionDialog#FULL_SELECTION} + */ + public void setInitialPattern(String text, int selectionMode) { + this.initialPatternText = text; + this.selectionMode = selectionMode; + } + + /** + * Gets initial pattern. + * + * @return initial pattern, or null if initial pattern is not + * set + */ + protected String getInitialPattern() { + return this.initialPatternText; + } + + /** + * Returns the current selection. + * + * @return the current selection + */ + protected StructuredSelection getSelectedItems() { + + StructuredSelection selection = (StructuredSelection) viewer.getSelection(); + + List selectedItems = selection.toList(); + Object itemToRemove = null; + + for (Iterator it = selection.iterator(); it.hasNext();) { + Object item = it.next(); + if (item instanceof ItemsListSeparator) { + itemToRemove = item; + break; + } + } + + if (itemToRemove == null) + return new StructuredSelection(selectedItems); + // Create a new selection without the collision + List newItems = new ArrayList(selectedItems); + newItems.remove(itemToRemove); + return new StructuredSelection(newItems); + + } + + /** + * Validates the item. When items on the items list are selected or + * deselected, it validates each item in the selection and the dialog status + * depends on all validations. + * + * @param item + * an item to be checked + * @return status of the dialog to be set + */ + protected abstract IStatus validateItem(Object item); + + /** + * Creates an instance of a filter. + * + * @return a filter for items on the items list. Can be null, + * no filtering will be applied then, causing no item to be shown in + * the list. + */ + protected abstract ItemsFilter createFilter(); + + /** + * Applies the filter created by createFilter() method to the + * items list. When new filter is different than previous one it will cause + * refiltering. + */ + protected void applyFilter() { + ItemsFilter newFilter = createFilter(); + + // don't apply filtering for patterns which mean the same, for example: + // *a**b and ***a*b + if (filter != null && filter.equalsFilter(newFilter)) { + return; + } + + filterHistoryJob.cancel(); + filterJob.cancel(); + + this.filter = newFilter; + + if (this.filter != null) { + filterHistoryJob.schedule(); + } + + } + + /** + * Returns comparator to sort items inside content provider. Returned object + * will be probably created as an anonymous class. Parameters passed to the + * compare(java.lang.Object, java.lang.Object) are going to + * be the same type as the one used in the content provider. + * + * @return comparator to sort items content provider + */ + protected abstract Comparator getItemsComparator(); + + /** + * Fills the content provider with matching items. + * + * @param contentProvider + * collector to add items to. + * {@link ColumnFilteredItemsSelectionDialog.AbstractContentProvider#add(Object, ColumnFilteredItemsSelectionDialog.ItemsFilter)} + * only adds items that pass the given itemsFilter. + * @param itemsFilter + * the items filter + * @param progressMonitor + * must be used to report search progress. The state of this + * progress monitor reflects the state of the filtering process. + * @throws CoreException + */ + protected abstract void fillContentProvider( + AbstractContentProvider contentProvider, ItemsFilter itemsFilter, + IProgressMonitor progressMonitor) throws CoreException; + + /** + * Force a refresh of the content provider. + */ + protected void forceRefresh() { + lastCompletedFilter = null; + lastCompletedResult = null; + filterHistoryJob.schedule(); + } + + /** + * Removes selected items from history. + * + * @param items + * items to be removed + */ + private void removeSelectedItems(List items) { + for (Iterator iter = items.iterator(); iter.hasNext();) { + Object item = iter.next(); + removeHistoryItem(item); + } + refreshWithLastSelection = false; + contentProvider.refresh(); + } + + /** + * Removes an item from history. + * + * @param item + * an item to remove + * @return removed item + */ + protected Object removeHistoryItem(Object item) { + return contentProvider.removeHistoryElement(item); + } + + /** + * Adds item to history. + * + * @param item + * the item to be added + */ + protected void accessedHistoryItem(Object item) { + contentProvider.addHistoryElement(item); + } + + /** + * Returns a history comparator. + * + * @return decorated comparator + */ + private Comparator getHistoryComparator() { + return new HistoryComparator(); + } + + /** + * Returns the history of selected elements. + * + * @return history of selected elements, or null if it is not + * set + */ + protected SelectionHistory getSelectionHistory() { + return this.contentProvider.getSelectionHistory(); + } + + /** + * Sets new history. + * + * @param selectionHistory + * the history + */ + protected void setSelectionHistory(SelectionHistory selectionHistory) { + if (this.contentProvider != null) + this.contentProvider.setSelectionHistory(selectionHistory); + } + + /** + * Indicates whether the given item is a history item. + * + * @param item + * the item to be investigated + * @return true if the given item exists in history, + * false otherwise + */ + public boolean isHistoryElement(Object item) { + return this.contentProvider.isHistoryElement(item); + } + + /** + * Indicates whether the given item is a duplicate. + * + * @param item + * the item to be investigated + * @return true if the item is duplicate, false + * otherwise + */ + public boolean isDuplicateElement(Object item) { + return this.contentProvider.isDuplicateElement(item); + } + + /** + * Sets separator label + * + * @param separatorLabel + * the label showed on separator + */ + public void setSeparatorLabel(String separatorLabel) { + this.itemsListSeparator = new ItemsListSeparator(separatorLabel); + } + + /** + * Returns name for then given object. + * + * @param item + * an object from the content provider. Subclasses should pay + * attention to the passed argument. They should either only pass + * objects of a known type (one used in content provider) or make + * sure that passed parameter is the expected one (by type + * checking like instanceof inside the method). + * @return name of the given item + */ + public abstract String getElementName(Object item); + + private class ToggleStatusLineAction extends Action { + + /** + * Creates a new instance of the class. + */ + public ToggleStatusLineAction() { + super(WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction, IAction.AS_CHECK_BOX); + } + + public void run() { + details.setVisible(isChecked()); + } + } + + /** + * Only refreshes UI on the basis of an already sorted and filtered set of + * items. + *

+ * Standard invocation scenario: + *

    + *
  1. filtering job (FilterJob class extending + * Job class)
  2. + *
  3. cache refresh without checking for duplicates (RefreshCacheJob + * class extending Job class)
  4. + *
  5. UI refresh (RefreshJob class extending + * UIJob class)
  6. + *
  7. cache refresh with checking for duplicates (CacheRefreshJob + * class extending Job class)
  8. + *
  9. UI refresh (RefreshJob class extending UIJob + * class)
  10. + *
+ * The scenario is rather complicated, but it had to be applied, because: + *
    + *
  • refreshing cache is rather a long action and cannot be run in the UI - + * cannot be run in a UIJob
  • + *
  • refreshing cache checking for duplicates is twice as long as + * refreshing cache without checking for duplicates; results of the search + * could be displayed earlier
  • + *
  • refreshing the UI have to be run in a UIJob
  • + *
+ * + * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob + * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob + * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob + */ + private class RefreshJob extends UIJob { + + /** + * Creates a new instance of the class. + */ + public RefreshJob() { + super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(), + WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob); + setSystem(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + public IStatus runInUIThread(IProgressMonitor monitor) { + if (monitor.isCanceled()) + return new Status(IStatus.OK, WorkbenchPlugin.PI_WORKBENCH, + IStatus.OK, EMPTY_STRING, null); + + if (ColumnFilteredItemsSelectionDialog.this != null) { + ColumnFilteredItemsSelectionDialog.this.refresh(); + } + + return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK, + EMPTY_STRING, null); + } + + } + + /** + * Refreshes the progress message cyclically with 500 milliseconds delay. + * RefreshProgressMessageJob is strictly connected with + * GranualProgressMonitor and use it to to get progress + * message and to decide about break of cyclical refresh. + */ + private class RefreshProgressMessageJob extends UIJob { + + private GranualProgressMonitor progressMonitor; + + /** + * Creates a new instance of the class. + */ + public RefreshProgressMessageJob() { + super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(), + WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob); + setSystem(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + public IStatus runInUIThread(IProgressMonitor monitor) { + + if (!progressLabel.isDisposed()) + progressLabel.setText(progressMonitor != null ? progressMonitor.getMessage() : EMPTY_STRING); + + if (progressMonitor == null || progressMonitor.isDone()) { + return new Status(IStatus.CANCEL, PlatformUI.PLUGIN_ID, + IStatus.CANCEL, EMPTY_STRING, null); + } + + // Schedule cyclical with 500 milliseconds delay + schedule(500); + + return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK, + EMPTY_STRING, null); + } + + /** + * Schedule progress refresh job. + * + * @param progressMonitor + * used during refresh progress label + */ + public void scheduleProgressRefresh( + GranualProgressMonitor progressMonitor) { + this.progressMonitor = progressMonitor; + // Schedule with initial delay to avoid flickering when the user + // types quickly + schedule(200); + } + + } + + /** + * A job responsible for computing filtered items list presented using + * RefreshJob. + * + * @see ColumnFilteredItemsSelectionDialog.RefreshJob + * + */ + private class RefreshCacheJob extends Job { + + private RefreshJob refreshJob = new RefreshJob(); + + /** + * Creates a new instance of the class. + */ + public RefreshCacheJob() { + super(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob); + setSystem(true); + } + + /** + * Stops the job and all sub-jobs. + */ + public void cancelAll() { + cancel(); + refreshJob.cancel(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + if (monitor.isCanceled()) { + return new Status(IStatus.CANCEL, WorkbenchPlugin.PI_WORKBENCH, + IStatus.CANCEL, EMPTY_STRING, null); + } + + if (ColumnFilteredItemsSelectionDialog.this != null) { + GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(monitor); + ColumnFilteredItemsSelectionDialog.this.reloadCache(true, wrappedMonitor); + } + + if (!monitor.isCanceled()) { + refreshJob.schedule(); + } + + return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK, + EMPTY_STRING, null); + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.jobs.Job#canceling() + */ + protected void canceling() { + super.canceling(); + contentProvider.stopReloadingCache(); + } + + } + + private class RemoveHistoryItemAction extends Action { + + /** + * Creates a new instance of the class. + */ + public RemoveHistoryItemAction() { + super(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.action.Action#run() + */ + public void run() { + List selectedElements = ((StructuredSelection) viewer.getSelection()).toList(); + removeSelectedItems(selectedElements); + } + } + + private static boolean showColoredLabels() { + return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS); + } + + private class ItemsListLabelProvider extends StyledCellLabelProvider + implements ILabelProviderListener { + private ILabelProvider provider; + + private ILabelDecorator selectionDecorator; + + // Need to keep our own list of listeners + private ListenerList listeners = new ListenerList(); + + /** + * Creates a new instance of the class. + * + * @param provider + * the label provider for all items, not null + * @param selectionDecorator + * the decorator for selected items, can be null + */ + public ItemsListLabelProvider(ILabelProvider provider, + ILabelDecorator selectionDecorator) { + Assert.isNotNull(provider); + this.provider = provider; + this.selectionDecorator = selectionDecorator; + + setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider); + + provider.addListener(this); + + if (selectionDecorator != null) { + selectionDecorator.addListener(this); + } + } + + /** + * Sets new selection decorator. + * + * @param newSelectionDecorator + * new label decorator for selected items in the list + */ + public void setSelectionDecorator(ILabelDecorator newSelectionDecorator) { + if (selectionDecorator != null) { + selectionDecorator.removeListener(this); + selectionDecorator.dispose(); + } + + selectionDecorator = newSelectionDecorator; + + if (selectionDecorator != null) { + selectionDecorator.addListener(this); + } + } + + /** + * Gets selection decorator. + * + * @return the label decorator for selected items in the list + */ + public ILabelDecorator getSelectionDecorator() { + return selectionDecorator; + } + + /** + * Sets new label provider. + * + * @param newProvider + * new label provider for items in the list, not + * null + */ + public void setProvider(ILabelProvider newProvider) { + Assert.isNotNull(newProvider); + provider.removeListener(this); + provider.dispose(); + + provider = newProvider; + + if (provider != null) { + provider.addListener(this); + } + + setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider); + } + + private Image getImage(Object element) { + if (element instanceof ItemsListSeparator) { + return WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR); + } + + return provider.getImage(element); + } + + private boolean isSelected(Object element) { + if (element != null && currentSelection != null) { + for (int i = 0; i < currentSelection.length; i++) { + if (element.equals(currentSelection[i])) + return true; + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) + */ + private String getText(Object element) { + if (element instanceof ItemsListSeparator) { + return getSeparatorLabel(((ItemsListSeparator) element).getName()); + } + + String str = provider.getText(element); + if (selectionDecorator != null && isSelected(element)) { + return selectionDecorator.decorateText(str.toString(), element); + } + + return str; + } + + private StyledString getStyledText(Object element, + IStyledLabelProvider provider) { + StyledString string = provider.getStyledText(element); + + if (selectionDecorator != null && isSelected(element)) { + String decorated = selectionDecorator.decorateText(string.getString(), element); + return StyledCellLabelProvider.styleDecoratedString(decorated, null, string); + // no need to add colors when element is selected + } + return string; + } + + public void update(ViewerCell cell) { + Object element = cell.getElement(); + + if (!(element instanceof ItemsListSeparator) && provider instanceof IStyledLabelProvider) { + IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) provider; + StyledString styledString = getStyledText(element, styledLabelProvider); + + cell.setText(styledString.getString()); + cell.setStyleRanges(styledString.getStyleRanges()); + cell.setImage(styledLabelProvider.getImage(element)); + } else { + cell.setText(getText(element)); + cell.setImage(getImage(element)); + } + cell.setFont(getFont(element)); + cell.setForeground(getForeground(element)); + cell.setBackground(getBackground(element)); + + super.update(cell); + } + + private String getSeparatorLabel(String separatorLabel) { + Rectangle rect = viewer.getTable().getBounds(); + + int borderWidth = viewer.getTable().computeTrim(0, 0, 0, 0).width; + + int imageWidth = WorkbenchImages.getImage( + IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width; + + int width = rect.width - borderWidth - imageWidth; + + GC gc = new GC(viewer.getTable()); + gc.setFont(viewer.getTable().getFont()); + + int fSeparatorWidth = gc.getAdvanceWidth('-'); + int fMessageLength = gc.textExtent(separatorLabel).x; + + gc.dispose(); + + StringBuffer dashes = new StringBuffer(); + int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2; + for (int i = 0; i < chars; i++) { + dashes.append('-'); + } + + StringBuffer result = new StringBuffer(); + result.append(dashes); + result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$ + result.append(dashes); + return result.toString().trim(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener) + */ + public void addListener(ILabelProviderListener listener) { + listeners.add(listener); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose() + */ + public void dispose() { + provider.removeListener(this); + provider.dispose(); + + if (selectionDecorator != null) { + selectionDecorator.removeListener(this); + selectionDecorator.dispose(); + } + + super.dispose(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, + * java.lang.String) + */ + public boolean isLabelProperty(Object element, String property) { + if (provider.isLabelProperty(element, property)) { + return true; + } + if (selectionDecorator != null + && selectionDecorator.isLabelProperty(element, property)) { + return true; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener) + */ + public void removeListener(ILabelProviderListener listener) { + listeners.remove(listener); + } + + private Color getBackground(Object element) { + if (element instanceof ItemsListSeparator) { + return null; + } + if (provider instanceof IColorProvider) { + return ((IColorProvider) provider).getBackground(element); + } + return null; + } + + private Color getForeground(Object element) { + if (element instanceof ItemsListSeparator) { + return Display.getCurrent().getSystemColor( + SWT.COLOR_WIDGET_NORMAL_SHADOW); + } + if (provider instanceof IColorProvider) { + return ((IColorProvider) provider).getForeground(element); + } + return null; + } + + private Font getFont(Object element) { + if (element instanceof ItemsListSeparator) { + return null; + } + if (provider instanceof IFontProvider) { + return ((IFontProvider) provider).getFont(element); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent) + */ + public void labelProviderChanged(LabelProviderChangedEvent event) { + Object[] l = listeners.getListeners(); + for (int i = 0; i < listeners.size(); i++) { + ((ILabelProviderListener) l[i]).labelProviderChanged(event); + } + } + } + + /** + * Used in ItemsListContentProvider, separates history and non-history + * items. + */ + private class ItemsListSeparator { + + private String name; + + /** + * Creates a new instance of the class. + * + * @param name + * the name of the separator + */ + public ItemsListSeparator(String name) { + this.name = name; + } + + /** + * Returns the name of this separator. + * + * @return the name of the separator + */ + public String getName() { + return name; + } + } + + /** + * GranualProgressMonitor is used for monitoring progress of filtering + * process. It is used by RefreshProgressMessageJob to + * refresh progress message. State of this monitor illustrates state of + * filtering or cache refreshing process. + * + */ + private class GranualProgressMonitor extends ProgressMonitorWrapper { + + private String name; + + private String subName; + + private int totalWork; + + private double worked; + + private boolean done; + + /** + * Creates instance of GranualProgressMonitor. + * + * @param monitor + * progress to be wrapped + */ + public GranualProgressMonitor(IProgressMonitor monitor) { + super(monitor); + } + + /** + * Checks if filtering has been done + * + * @return true if filtering work has been done false in other way + */ + public boolean isDone() { + return done; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String) + */ + public void setTaskName(String name) { + super.setTaskName(name); + this.name = name; + this.subName = null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String) + */ + public void subTask(String name) { + super.subTask(name); + this.subName = name; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String, + * int) + */ + public void beginTask(String name, int totalWork) { + super.beginTask(name, totalWork); + if (this.name == null) + this.name = name; + this.totalWork = totalWork; + refreshProgressMessageJob.scheduleProgressRefresh(this); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int) + */ + public void worked(int work) { + super.worked(work); + internalWorked(work); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done() + */ + public void done() { + done = true; + super.done(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean) + */ + public void setCanceled(boolean b) { + done = b; + super.setCanceled(b); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double) + */ + public void internalWorked(double work) { + worked = worked + work; + } + + private String getMessage() { + if (done) + return ""; //$NON-NLS-1$ + + String message; + + if (name == null) { + message = subName == null ? "" : subName; //$NON-NLS-1$ + } else { + message = subName == null ? name + : NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage, + new Object[] { name, subName }); + } + if (totalWork == 0) + return message; + + return NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage, + new Object[] { message, new Integer((int) ((worked * 100) / totalWork)) }); + + } + + } + + /** + * Filters items history and schedule filter job. + */ + private class FilterHistoryJob extends Job { + + /** + * Filter used during the filtering process. + */ + private ItemsFilter itemsFilter; + + /** + * Creates new instance of receiver. + */ + public FilterHistoryJob() { + super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel); + setSystem(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + this.itemsFilter = filter; + + contentProvider.reset(); + + refreshWithLastSelection = false; + + contentProvider.addHistoryItems(itemsFilter); + + if (!(lastCompletedFilter != null && lastCompletedFilter.isSubFilter(this.itemsFilter))) + contentProvider.refresh(); + + filterJob.schedule(); + + return Status.OK_STATUS; + } + + } + + /** + * Filters items in indicated set and history. During filtering, it + * refreshes the dialog (progress monitor and elements list). + * + * Depending on the filter, FilterJob decides which kind of + * search will be run inside filterContent. If the last + * filtering is done (last completed filter), is not null, and the new + * filter is a sub-filter ({@link ColumnFilteredItemsSelectionDialog.ItemsFilter#isSubFilter(ColumnFilteredItemsSelectionDialog.ItemsFilter)}) + * of the last, then FilterJob only filters in the cache. If + * it is the first filtering or the new filter isn't a sub-filter of the + * last one, a full search is run. + */ + private class FilterJob extends Job { + + /** + * Filter used during the filtering process. + */ + protected ItemsFilter itemsFilter; + + /** + * Creates new instance of FilterJob + */ + public FilterJob() { + super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel); + setSystem(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected final IStatus run(IProgressMonitor parent) { + GranualProgressMonitor monitor = new GranualProgressMonitor(parent); + return doRun(monitor); + } + + /** + * Executes job using the given filtering progress monitor. A hook for + * subclasses. + * + * @param monitor + * progress monitor + * @return result of the execution + */ + protected IStatus doRun(GranualProgressMonitor monitor) { + try { + internalRun(monitor); + } catch (CoreException e) { + cancel(); + return new Status( + IStatus.ERROR, + PlatformUI.PLUGIN_ID, + IStatus.ERROR, + WorkbenchMessages.FilteredItemsSelectionDialog_jobError, + e); + } + return Status.OK_STATUS; + } + + /** + * Main method for the job. + * + * @param monitor + * @throws CoreException + */ + private void internalRun(GranualProgressMonitor monitor) + throws CoreException { + try { + if (monitor.isCanceled()) + return; + + this.itemsFilter = filter; + + // why is the content not filtered if the patter is empty? + // this makes no sense since the search pattern is able to + // handle the empty pattern just fine, and even has settings + // for what to do in this case + //if (filter.getPattern().length() != 0) { + // filterContent(monitor); + //} + + filterContent(monitor); + + if (monitor.isCanceled()) + return; + + contentProvider.refresh(); + } finally { + monitor.done(); + } + } + + /** + * Filters items. + * + * @param monitor + * for monitoring progress + * @throws CoreException + */ + protected void filterContent(GranualProgressMonitor monitor) + throws CoreException { + + if (lastCompletedFilter != null + && lastCompletedFilter.isSubFilter(this.itemsFilter)) { + + int length = lastCompletedResult.size() / 500; + monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName, length); + + for (int pos = 0; pos < lastCompletedResult.size(); pos++) { + + Object item = lastCompletedResult.get(pos); + if (monitor.isCanceled()) + break; + contentProvider.add(item, itemsFilter); + + if ((pos % 500) == 0) { + monitor.worked(1); + } + } + + } else { + + lastCompletedFilter = null; + lastCompletedResult = null; + + SubProgressMonitor subMonitor = null; + if (monitor != null) { + monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_searchJob_taskName, 100); + subMonitor = new SubProgressMonitor(monitor, 95); + + } + + fillContentProvider(contentProvider, itemsFilter, subMonitor); + + if (monitor != null && !monitor.isCanceled()) { + monitor.worked(2); + contentProvider.rememberResult(itemsFilter); + monitor.worked(3); + } + } + + } + + } + + /** + * History stores a list of key, object pairs. The list is bounded at a + * certain size. If the list exceeds this size the oldest element is removed + * from the list. An element can be added/renewed with a call to + * accessed(Object). + *

+ * The history can be stored to/loaded from an XML file. + */ + protected static abstract class SelectionHistory { + + private static final String DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$ + + private static final String DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$ + + private static final int MAX_HISTORY_SIZE = 60; + + private final Set historyList; + + private final String rootNodeName; + + private final String infoNodeName; + + private SelectionHistory(String rootNodeName, String infoNodeName) { + + historyList = Collections.synchronizedSet(new LinkedHashSet() { + + private static final long serialVersionUID = 0L; + + /* + * (non-Javadoc) + * + * @see java.util.LinkedList#add(java.lang.Object) + */ + public boolean add(Object arg0) { + if (this.size() >= MAX_HISTORY_SIZE) { + Iterator iterator = this.iterator(); + iterator.next(); + iterator.remove(); + } + return super.add(arg0); + } + + }); + + this.rootNodeName = rootNodeName; + this.infoNodeName = infoNodeName; + } + + /** + * Creates new instance of SelectionHistory. + */ + public SelectionHistory() { + this(DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME); + } + + /** + * Adds object to history. + * + * @param object + * the item to be added to the history + */ + public synchronized void accessed(Object object) { + historyList.remove(object); + historyList.add(object); + } + + /** + * Returns true if history contains object. + * + * @param object + * the item for which check will be executed + * @return true if history contains object + * false in other way + */ + public synchronized boolean contains(Object object) { + return historyList.contains(object); + } + + /** + * Returns true if history is empty. + * + * @return true if history is empty + */ + public synchronized boolean isEmpty() { + return historyList.isEmpty(); + } + + /** + * Remove element from history. + * + * @param element + * to remove form the history + * @return true if this list contained the specified + * element + */ + public synchronized boolean remove(Object element) { + return historyList.remove(element); + } + + /** + * Load history elements from memento. + * + * @param memento + * memento from which the history will be retrieved + */ + public void load(IMemento memento) { + + XMLMemento historyMemento = (XMLMemento) memento + .getChild(rootNodeName); + + if (historyMemento == null) { + return; + } + + IMemento[] mementoElements = historyMemento + .getChildren(infoNodeName); + for (int i = 0; i < mementoElements.length; ++i) { + IMemento mementoElement = mementoElements[i]; + Object object = restoreItemFromMemento(mementoElement); + if (object != null) { + historyList.add(object); + } + } + } + + /** + * Save history elements to memento. + * + * @param memento + * memento to which the history will be added + */ + public void save(IMemento memento) { + + IMemento historyMemento = memento.createChild(rootNodeName); + + Object[] items = getHistoryItems(); + for (int i = 0; i < items.length; i++) { + Object item = items[i]; + IMemento elementMemento = historyMemento + .createChild(infoNodeName); + storeItemToMemento(item, elementMemento); + } + + } + + /** + * Gets array of history items. + * + * @return array of history elements + */ + public synchronized Object[] getHistoryItems() { + return historyList.toArray(); + } + + /** + * Creates an object using given memento. + * + * @param memento + * memento used for creating new object + * + * @return the restored object + */ + protected abstract Object restoreItemFromMemento(IMemento memento); + + /** + * Store object in IMemento. + * + * @param item + * the item to store + * @param memento + * the memento to store to + */ + protected abstract void storeItemToMemento(Object item, IMemento memento); + + } + + /** + * Filters elements using SearchPattern by comparing the names of items with + * the filter pattern. + */ + protected abstract class ItemsFilter { + + protected SearchPattern patternMatcher; + + /** + * Creates new instance of ItemsFilter. + */ + public ItemsFilter() { + this(new SearchPattern()); + } + + /** + * Creates new instance of ItemsFilter. + * + * @param searchPattern + * the pattern to be used when filtering + */ + public ItemsFilter(SearchPattern searchPattern) { + patternMatcher = searchPattern; + String stringPattern = ""; //$NON-NLS-1$ + if (pattern != null && !pattern.getText().equals("*")) { //$NON-NLS-1$ + stringPattern = pattern.getText(); + } + patternMatcher.setPattern(stringPattern); + } + + /** + * Check if the given filter is a sub-filter of this filter. The default + * implementation checks if the SearchPattern from the + * given filter is a sub-pattern of the one from this filter. + *

+ * WARNING: This method is not defined in reading order, i.e. + * a.isSubFilter(b) is true iff + * b is a sub-filter of a, and not + * vice-versa. + *

+ * + * @param filter + * the filter to be checked, or null + * @return true if the given filter is sub-filter of this + * filter, false if the given filter isn't a + * sub-filter or is null + * + * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern) + */ + public boolean isSubFilter(ItemsFilter filter) { + if (filter != null) { + return this.patternMatcher.isSubPattern(filter.patternMatcher); + } + return false; + } + + /** + * Checks whether the provided filter is equal to the current filter. + * The default implementation checks if SearchPattern + * from current filter is equal to the one from provided filter. + * + * @param filter + * filter to be checked, or null + * @return true if the given filter is equal to current + * filter, false if given filter isn't equal to + * current one or if it is null + * + * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern) + */ + public boolean equalsFilter(ItemsFilter filter) { + if (filter != null + && filter.patternMatcher.equalsPattern(this.patternMatcher)) { + return true; + } + return false; + } + + /** + * Checks whether the pattern's match rule is camel case. + * + * @return true if pattern's match rule is camel case, + * false otherwise + */ + public boolean isCamelCasePattern() { + return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH; + } + + /** + * Returns the pattern string. + * + * @return pattern for this filter + * + * @see SearchPattern#getPattern() + */ + public String getPattern() { + return patternMatcher.getPattern(); + } + + /** + * Returns the rule to apply for matching keys. + * + * @return an implementation-specific match rule + * + * @see SearchPattern#getMatchRule() for match rules returned by the + * default implementation + */ + public int getMatchRule() { + return patternMatcher.getMatchRule(); + } + + /** + * Matches text with filter. + * + * @param text + * the text to match with the filter + * @return true if text matches with filter pattern, + * false otherwise + */ + protected boolean matches(String text) { + return patternMatcher.matches(text); + } + + /** + * General method for matching raw name pattern. Checks whether current + * pattern is prefix of name provided item. + * + * @param item + * item to check + * @return true if current pattern is a prefix of name + * provided item, false if item's name is shorter + * than prefix or sequences of characters don't match. + */ + public boolean matchesRawNamePattern(Object item) { + String prefix = patternMatcher.getPattern(); + String text = getElementName(item); + + if (text == null) + return false; + + int textLength = text.length(); + int prefixLength = prefix.length(); + if (textLength < prefixLength) { + return false; + } + for (int i = prefixLength - 1; i >= 0; i--) { + if (Character.toLowerCase(prefix.charAt(i)) != Character + .toLowerCase(text.charAt(i))) + return false; + } + return true; + } + + /** + * Matches an item against filter conditions. + * + * @param item + * @return true if item matches against filter conditions, false + * otherwise + */ + public abstract boolean matchItem(Object item); + + /** + * Checks consistency of an item. Item is inconsistent if was changed or + * removed. + * + * @param item + * @return true if item is consistent, false + * if item is inconsistent + */ + public abstract boolean isConsistentItem(Object item); + + } + + /** + * An interface to content providers for + * FilterItemsSelectionDialog. + */ + protected abstract class AbstractContentProvider { + /** + * Adds the item to the content provider iff the filter matches the + * item. Otherwise does nothing. + * + * @param item + * the item to add + * @param itemsFilter + * the filter + * + * @see ColumnFilteredItemsSelectionDialog.ItemsFilter#matchItem(Object) + */ + public abstract void add(Object item, ItemsFilter itemsFilter); + } + + /** + * Collects filtered elements. Contains one synchronized, sorted set for + * collecting filtered elements. All collected elements are sorted using + * comparator. Comparator is returned by getElementComparator() method. + * Implementation of ItemsFilter is used to filter elements. + * The key function of filter used in to filtering is + * matchElement(Object item). + *

+ * The ContentProvider class also provides item filtering + * methods. The filtering has been moved from the standard TableView + * getFilteredItems() method to content provider, because + * ILazyContentProvider and virtual tables are used. This + * class is responsible for adding a separator below history items and + * marking each items as duplicate if its name repeats more than once on the + * filtered list. + */ + private class ContentProvider extends AbstractContentProvider implements + IStructuredContentProvider, ILazyContentProvider { + + private SelectionHistory selectionHistory; + + /** + * Raw result of the searching (unsorted, unfiltered). + *

+ * Standard object flow: + * items -> lastSortedItems -> lastFilteredItems + */ + private Set items; + + /** + * Items that are duplicates. + */ + private Set duplicates; + + /** + * List of ViewerFilters to be used during filtering + */ + private List filters; + + /** + * Result of the last filtering. + *

+ * Standard object flow: + * items -> lastSortedItems -> lastFilteredItems + */ + private List lastFilteredItems; + + /** + * Result of the last sorting. + *

+ * Standard object flow: + * items -> lastSortedItems -> lastFilteredItems + */ + private List lastSortedItems; + + /** + * Used for getFilteredItems() method canceling (when the + * job that invoked the method was canceled). + *

+ * Method canceling could be based (only) on monitor canceling + * unfortunately sometimes the method getFilteredElements() + * could be run with a null monitor, the reset flag have + * to be left intact. + */ + private boolean reset; + + /** + * Creates new instance of ContentProvider. + */ + public ContentProvider() { + this.items = Collections.synchronizedSet(new HashSet(2048)); + this.duplicates = Collections.synchronizedSet(new HashSet(256)); + this.lastFilteredItems = new ArrayList(); + this.lastSortedItems = Collections.synchronizedList(new ArrayList(2048)); + } + + /** + * Sets selection history. + * + * @param selectionHistory + * The selectionHistory to set. + */ + public void setSelectionHistory(SelectionHistory selectionHistory) { + this.selectionHistory = selectionHistory; + } + + /** + * @return Returns the selectionHistory. + */ + public SelectionHistory getSelectionHistory() { + return selectionHistory; + } + + /** + * Removes all content items and resets progress message. + */ + public void reset() { + reset = true; + this.items.clear(); + this.duplicates.clear(); + this.lastSortedItems.clear(); + } + + /** + * Stops reloading cache - getFilteredItems() method. + */ + public void stopReloadingCache() { + reset = true; + } + + /** + * Adds filtered item. + * + * @param item + * @param itemsFilter + */ + public void add(Object item, ItemsFilter itemsFilter) { + if (itemsFilter == filter) { + if (itemsFilter != null) { + if (itemsFilter.matchItem(item)) { + this.items.add(item); + } + } else { + this.items.add(item); + } + } + } + + /** + * Add all history items to contentProvider. + * + * @param itemsFilter + */ + public void addHistoryItems(ItemsFilter itemsFilter) { + if (this.selectionHistory != null) { + Object[] items = this.selectionHistory.getHistoryItems(); + for (int i = 0; i < items.length; i++) { + Object item = items[i]; + if (itemsFilter == filter) { + if (itemsFilter != null) { + if (itemsFilter.matchItem(item)) { + if (itemsFilter.isConsistentItem(item)) { + this.items.add(item); + } else { + this.selectionHistory.remove(item); + } + } + } + } + } + } + } + + /** + * Refresh dialog. + */ + public void refresh() { + scheduleRefresh(); + } + + /** + * Removes items from history and refreshes the view. + * + * @param item + * to remove + * + * @return removed item + */ + public Object removeHistoryElement(Object item) { + if (this.selectionHistory != null) + this.selectionHistory.remove(item); + if (filter == null || filter.getPattern().length() == 0) { + items.remove(item); + duplicates.remove(item); + this.lastSortedItems.remove(item); + } + + synchronized (lastSortedItems) { + Collections.sort(lastSortedItems, getHistoryComparator()); + } + return item; + } + + /** + * Adds item to history and refresh view. + * + * @param item + * to add + */ + public void addHistoryElement(Object item) { + if (this.selectionHistory != null) + this.selectionHistory.accessed(item); + if (filter == null || !filter.matchItem(item)) { + this.items.remove(item); + this.duplicates.remove(item); + this.lastSortedItems.remove(item); + } + synchronized (lastSortedItems) { + Collections.sort(lastSortedItems, getHistoryComparator()); + } + this.refresh(); + } + + /** + * @param item + * @return true if given item is part of the history + */ + public boolean isHistoryElement(Object item) { + if (this.selectionHistory != null) { + return this.selectionHistory.contains(item); + } + return false; + } + + /** + * Sets/unsets given item as duplicate. + * + * @param item + * item to change + * + * @param isDuplicate + * duplicate flag + */ + public void setDuplicateElement(Object item, boolean isDuplicate) { + if (this.items.contains(item)) { + if (isDuplicate) + this.duplicates.add(item); + else + this.duplicates.remove(item); + } + } + + /** + * Indicates whether given item is a duplicate. + * + * @param item + * item to check + * @return true if item is duplicate + */ + public boolean isDuplicateElement(Object item) { + return duplicates.contains(item); + } + + /** + * Load history from memento. + * + * @param memento + * memento from which the history will be retrieved + */ + public void loadHistory(IMemento memento) { + if (this.selectionHistory != null) + this.selectionHistory.load(memento); + } + + /** + * Save history to memento. + * + * @param memento + * memento to which the history will be added + */ + public void saveHistory(IMemento memento) { + if (this.selectionHistory != null) + this.selectionHistory.save(memento); + } + + /** + * Gets sorted items. + * + * @return sorted items + */ + private Object[] getSortedItems() { + if (lastSortedItems.size() != items.size()) { + synchronized (lastSortedItems) { + lastSortedItems.clear(); + lastSortedItems.addAll(items); + Collections.sort(lastSortedItems, getHistoryComparator()); + } + } + return lastSortedItems.toArray(); + } + + /** + * Remember result of filtering. + * + * @param itemsFilter + */ + public void rememberResult(ItemsFilter itemsFilter) { + List itemsList = Collections.synchronizedList(Arrays + .asList(getSortedItems())); + // synchronization + if (itemsFilter == filter) { + lastCompletedFilter = itemsFilter; + lastCompletedResult = itemsList; + } + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(Object inputElement) { + return lastFilteredItems.toArray(); + } + + public int getNumberOfElements() { + return lastFilteredItems.size(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int) + */ + public void updateElement(int index) { + + ColumnFilteredItemsSelectionDialog.this.viewer.replace((lastFilteredItems + .size() > index) ? lastFilteredItems.get(index) : null, + index); + + } + + /** + * Main method responsible for getting the filtered items and checking + * for duplicates. It is based on the + * {@link ColumnFilteredItemsSelectionDialog.ContentProvider#getFilteredItems(Object, IProgressMonitor)}. + * + * @param checkDuplicates + * true if data concerning elements + * duplication should be computed - it takes much more time + * than standard filtering + * + * @param monitor + * progress monitor + */ + public void reloadCache(boolean checkDuplicates, + IProgressMonitor monitor) { + + reset = false; + + if (monitor != null) { + // the work is divided into two actions of the same length + int totalWork = checkDuplicates ? 200 : 100; + + monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob, totalWork); + } + + // the TableViewer's root (the input) is treated as parent + + lastFilteredItems = Arrays.asList(getFilteredItems(viewer.getInput(), + monitor != null ? new SubProgressMonitor(monitor, 100) : null)); + + if (reset || (monitor != null && monitor.isCanceled())) { + if (monitor != null) + monitor.done(); + return; + } + + if (checkDuplicates) { + checkDuplicates(monitor); + } + if (monitor != null) + monitor.done(); + } + + private void checkDuplicates(IProgressMonitor monitor) { + synchronized (lastFilteredItems) { + IProgressMonitor subMonitor = null; + int reportEvery = lastFilteredItems.size() / 20; + if (monitor != null) { + subMonitor = new SubProgressMonitor(monitor, 100); + subMonitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates, 5); + } + HashMap helperMap = new HashMap(); + for (int i = 0; i < lastFilteredItems.size(); i++) { + if (reset || (subMonitor != null && subMonitor.isCanceled())) + return; + Object item = lastFilteredItems.get(i); + + if (!(item instanceof ItemsListSeparator)) { + Object previousItem = helperMap.put(getElementName(item), item); + if (previousItem != null) { + setDuplicateElement(previousItem, true); + setDuplicateElement(item, true); + } else { + setDuplicateElement(item, false); + } + } + + if (subMonitor != null && reportEvery != 0 + && (i + 1) % reportEvery == 0) + subMonitor.worked(1); + } + helperMap.clear(); + } + } + + /** + * Returns an array of items filtered using the provided + * ViewerFilters with a separator added. + * + * @param parent + * the parent + * @param monitor + * progress monitor, can be null + * @return an array of filtered items + */ + protected Object[] getFilteredItems(Object parent, + IProgressMonitor monitor) { + int ticks = 100; + if (monitor == null) { + monitor = new NullProgressMonitor(); + } + + monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements, ticks); + if (filters != null) { + ticks /= (filters.size() + 2); + } else { + ticks /= 2; + } + + // get already sorted array + Object[] filteredElements = getSortedItems(); + + monitor.worked(ticks); + + // filter the elements using provided ViewerFilters + if (filters != null && filteredElements != null) { + for (Iterator iter = filters.iterator(); iter.hasNext();) { + ViewerFilter f = (ViewerFilter) iter.next(); + filteredElements = f.filter(viewer, parent, filteredElements); + monitor.worked(ticks); + } + } + + if (filteredElements == null || monitor.isCanceled()) { + monitor.done(); + return new Object[0]; + } + + ArrayList preparedElements = new ArrayList(); + boolean hasHistory = false; + + if (filteredElements.length > 0) { + if (isHistoryElement(filteredElements[0])) { + hasHistory = true; + } + } + + int reportEvery = filteredElements.length / ticks; + + // add separator + for (int i = 0; i < filteredElements.length; i++) { + Object item = filteredElements[i]; + + if (hasHistory && !isHistoryElement(item)) { + preparedElements.add(itemsListSeparator); + hasHistory = false; + } + + preparedElements.add(item); + + if (reportEvery != 0 && ((i + 1) % reportEvery == 0)) { + monitor.worked(1); + } + } + + monitor.done(); + + return preparedElements.toArray(); + } + + /** + * Adds a filter to this content provider. For an example usage of such + * filters look at the project org.eclipse.ui.ide, class + * org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter. + * + * + * @param filter + * the filter to be added + */ + public void addFilter(ViewerFilter filter) { + if (filters == null) { + filters = new ArrayList(); + } + filters.add(filter); + // currently filters are only added when dialog is restored + // if it is changed, refreshing the whole TableViewer should be + // added + } + + } + + /** + * A content provider that does nothing. + */ + private class NullContentProvider implements IContentProvider { + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + } + + /** + * DetailsContentViewer objects are wrappers for labels. + * DetailsContentViewer provides means to change label's image and text when + * the attached LabelProvider is updated. + */ + private class DetailsContentViewer extends ContentViewer { + + private CLabel label; + + /** + * Unfortunately, it was impossible to delegate displaying border to + * label. The ViewForm is used because + * CLabel displays shadow when border is present. + */ + private ViewForm viewForm; + + /** + * Constructs a new instance of this class given its parent and a style + * value describing its behavior and appearance. + * + * @param parent + * the parent component + * @param style + * SWT style bits + */ + public DetailsContentViewer(Composite parent, int style) { + viewForm = new ViewForm(parent, style); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 2; + viewForm.setLayoutData(gd); + label = new CLabel(viewForm, SWT.FLAT); + label.setFont(parent.getFont()); + viewForm.setContent(label); + hookControl(label); + } + + /** + * Shows/hides the content viewer. + * + * @param visible + * if the content viewer should be visible. + */ + public void setVisible(boolean visible) { + GridData gd = (GridData) viewForm.getLayoutData(); + gd.exclude = !visible; + viewForm.getParent().layout(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, + * java.lang.Object) + */ + protected void inputChanged(Object input, Object oldInput) { + if (oldInput == null) { + if (input == null) { + return; + } + refresh(); + return; + } + + refresh(); + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent) + */ + protected void handleLabelProviderChanged( + LabelProviderChangedEvent event) { + if (event != null) { + refresh(event.getElements()); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.Viewer#getControl() + */ + public Control getControl() { + return label; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.Viewer#getSelection() + */ + public ISelection getSelection() { + // not supported + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.Viewer#refresh() + */ + public void refresh() { + Object input = this.getInput(); + if (input != null) { + ILabelProvider labelProvider = (ILabelProvider) getLabelProvider(); + doRefresh(labelProvider.getText(input), labelProvider + .getImage(input)); + } else { + doRefresh(null, null); + } + } + + /** + * Sets the given text and image to the label. + * + * @param text + * the new text or null + * @param image + * the new image + */ + private void doRefresh(String text, Image image) { + if ( text != null ) { + text = LegacyActionTools.escapeMnemonics(text); + } + label.setText(text); + label.setImage(image); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, + * boolean) + */ + public void setSelection(ISelection selection, boolean reveal) { + // not supported + } + + /** + * Refreshes the label if currently chosen element is on the list. + * + * @param objs + * list of changed object + */ + private void refresh(Object[] objs) { + if (objs == null || getInput() == null) { + return; + } + Object input = getInput(); + for (int i = 0; i < objs.length; i++) { + if (objs[i].equals(input)) { + refresh(); + break; + } + } + } + } + + /** + * Compares items according to the history. + */ + private class HistoryComparator implements Comparator { + + /* + * (non-Javadoc) + * + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(Object o1, Object o2) { + boolean h1 = isHistoryElement(o1); + boolean h2 = isHistoryElement(o2); + if (h1 == h2) + return getItemsComparator().compare(o1, o2); + + if (h1) + return -2; + if (h2) + return +2; + + return 0; + } + + } + + + /** + * Get the control where the search pattern is entered. Any filtering should + * be done using an {@link ItemsFilter}. This control should only be + * accessed for listeners that wish to handle events that do not affect + * filtering such as custom traversal. + * + * @return Control or null if the pattern control has not + * been created. + */ + public Control getPatternControl() { + return pattern; + } + +} +