-/*******************************************************************************\r
- * Copyright (c) 2000, 2010 IBM Corporation and others.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * IBM Corporation - initial API and implementation\r
- * Willian Mitsuda <wmitsuda@gmail.com>\r
- * - Fix for bug 196553 - [Dialogs] Support IColorProvider/IFontProvider in FilteredItemsSelectionDialog\r
- * Peter Friese <peter.friese@gentleware.com>\r
- * - Fix for bug 208602 - [Dialogs] Open Type dialog needs accessible labels\r
- * Simon Muschel <smuschel@gmx.de> - bug 258493\r
- *******************************************************************************/\r
-package org.simantics.utils.ui.dialogs;\r
-\r
-import java.io.IOException;\r
-import java.io.StringReader;\r
-import java.io.StringWriter;\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.LinkedHashSet;\r
-import java.util.List;\r
-import java.util.Set;\r
-\r
-import org.eclipse.core.commands.AbstractHandler;\r
-import org.eclipse.core.commands.ExecutionEvent;\r
-import org.eclipse.core.commands.IHandler;\r
-import org.eclipse.core.runtime.Assert;\r
-import org.eclipse.core.runtime.CoreException;\r
-import org.eclipse.core.runtime.IProgressMonitor;\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.ListenerList;\r
-import org.eclipse.core.runtime.NullProgressMonitor;\r
-import org.eclipse.core.runtime.ProgressMonitorWrapper;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.core.runtime.SubProgressMonitor;\r
-import org.eclipse.core.runtime.jobs.Job;\r
-import org.eclipse.jface.action.Action;\r
-import org.eclipse.jface.action.ActionContributionItem;\r
-import org.eclipse.jface.action.IAction;\r
-import org.eclipse.jface.action.IMenuListener;\r
-import org.eclipse.jface.action.IMenuManager;\r
-import org.eclipse.jface.action.LegacyActionTools;\r
-import org.eclipse.jface.action.MenuManager;\r
-import org.eclipse.jface.dialogs.IDialogSettings;\r
-import org.eclipse.jface.viewers.ColumnLabelProvider;\r
-import org.eclipse.jface.viewers.ContentViewer;\r
-import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;\r
-import org.eclipse.jface.viewers.DoubleClickEvent;\r
-import org.eclipse.jface.viewers.IColorProvider;\r
-import org.eclipse.jface.viewers.IContentProvider;\r
-import org.eclipse.jface.viewers.IDoubleClickListener;\r
-import org.eclipse.jface.viewers.IFontProvider;\r
-import org.eclipse.jface.viewers.ILabelDecorator;\r
-import org.eclipse.jface.viewers.ILabelProvider;\r
-import org.eclipse.jface.viewers.ILabelProviderListener;\r
-import org.eclipse.jface.viewers.ILazyContentProvider;\r
-import org.eclipse.jface.viewers.ISelection;\r
-import org.eclipse.jface.viewers.ISelectionChangedListener;\r
-import org.eclipse.jface.viewers.IStructuredContentProvider;\r
-import org.eclipse.jface.viewers.LabelProvider;\r
-import org.eclipse.jface.viewers.LabelProviderChangedEvent;\r
-import org.eclipse.jface.viewers.SelectionChangedEvent;\r
-import org.eclipse.jface.viewers.StructuredSelection;\r
-import org.eclipse.jface.viewers.StyledCellLabelProvider;\r
-import org.eclipse.jface.viewers.StyledString;\r
-import org.eclipse.jface.viewers.TableViewer;\r
-import org.eclipse.jface.viewers.TableViewerColumn;\r
-import org.eclipse.jface.viewers.Viewer;\r
-import org.eclipse.jface.viewers.ViewerCell;\r
-import org.eclipse.jface.viewers.ViewerFilter;\r
-import org.eclipse.osgi.util.NLS;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.accessibility.ACC;\r
-import org.eclipse.swt.accessibility.AccessibleAdapter;\r
-import org.eclipse.swt.accessibility.AccessibleEvent;\r
-import org.eclipse.swt.custom.CLabel;\r
-import org.eclipse.swt.custom.ViewForm;\r
-import org.eclipse.swt.events.KeyAdapter;\r
-import org.eclipse.swt.events.KeyEvent;\r
-import org.eclipse.swt.events.ModifyEvent;\r
-import org.eclipse.swt.events.ModifyListener;\r
-import org.eclipse.swt.events.MouseAdapter;\r
-import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.events.TraverseEvent;\r
-import org.eclipse.swt.events.TraverseListener;\r
-import org.eclipse.swt.graphics.Color;\r
-import org.eclipse.swt.graphics.Font;\r
-import org.eclipse.swt.graphics.GC;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.graphics.Rectangle;\r
-import org.eclipse.swt.layout.GridData;\r
-import org.eclipse.swt.layout.GridLayout;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Control;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.Label;\r
-import org.eclipse.swt.widgets.Menu;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.swt.widgets.Table;\r
-import org.eclipse.swt.widgets.TableColumn;\r
-import org.eclipse.swt.widgets.Text;\r
-import org.eclipse.swt.widgets.ToolBar;\r
-import org.eclipse.swt.widgets.ToolItem;\r
-import org.eclipse.ui.ActiveShellExpression;\r
-import org.eclipse.ui.IMemento;\r
-import org.eclipse.ui.IWorkbenchCommandConstants;\r
-import org.eclipse.ui.IWorkbenchPreferenceConstants;\r
-import org.eclipse.ui.PlatformUI;\r
-import org.eclipse.ui.WorkbenchException;\r
-import org.eclipse.ui.XMLMemento;\r
-import org.eclipse.ui.dialogs.SearchPattern;\r
-import org.eclipse.ui.dialogs.SelectionStatusDialog;\r
-import org.eclipse.ui.handlers.IHandlerActivation;\r
-import org.eclipse.ui.handlers.IHandlerService;\r
-import org.eclipse.ui.internal.IWorkbenchGraphicConstants;\r
-import org.eclipse.ui.internal.WorkbenchImages;\r
-import org.eclipse.ui.internal.WorkbenchMessages;\r
-import org.eclipse.ui.internal.WorkbenchPlugin;\r
-import org.eclipse.ui.progress.UIJob;\r
-import org.eclipse.ui.statushandlers.StatusManager;\r
-\r
-/**\r
- * Shows a list of items to the user with a text entry field for a string\r
- * pattern used to filter the list of items.\r
- * \r
- * This is a copy from org.eclipse.ui.dialogs.FilteredItemsSelectionDialog\r
- * with a hook for column creation and a fix to a (possible) bug that \r
- * prevented the empty pattern to be handled correctly. This version of the\r
- * dialog also has the possibility to force an update of the contents if new\r
- * elements are added.\r
- * \r
- * TODO: Clean up warnings.\r
- * \r
- * @author Janne Kauttio (modifications only)\r
- * \r
- * @since 3.3\r
- */\r
-@SuppressWarnings({ "restriction", "rawtypes", "unchecked" })\r
-public abstract class ColumnFilteredItemsSelectionDialog extends\r
- SelectionStatusDialog {\r
-\r
- private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$\r
-\r
- private static final String SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$\r
-\r
- private static final String HISTORY_SETTINGS = "History"; //$NON-NLS-1$\r
-\r
- private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$\r
-\r
- private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$\r
-\r
- /**\r
- * Represents an empty selection in the pattern input field (used only for\r
- * initial pattern).\r
- */\r
- public static final int NONE = 0;\r
-\r
- /**\r
- * Pattern input field selection where caret is at the beginning (used only\r
- * for initial pattern).\r
- */\r
- public static final int CARET_BEGINNING = 1;\r
-\r
- /**\r
- * Represents a full selection in the pattern input field (used only for\r
- * initial pattern).\r
- */\r
- public static final int FULL_SELECTION = 2;\r
-\r
- private Text pattern;\r
-\r
- private TableViewer viewer;\r
-\r
- private DetailsContentViewer details;\r
-\r
- /**\r
- * It is a duplicate of a field in the CLabel class in DetailsContentViewer.\r
- * It is maintained, because the <code>setDetailsLabelProvider()</code>\r
- * could be called before content area is created.\r
- */\r
- private ILabelProvider detailsLabelProvider;\r
-\r
- private ItemsListLabelProvider itemsListLabelProvider;\r
-\r
- private MenuManager menuManager;\r
-\r
- private MenuManager contextMenuManager;\r
-\r
- private boolean multi;\r
-\r
- private ToolBar toolBar;\r
-\r
- private ToolItem toolItem;\r
-\r
- private Label progressLabel;\r
-\r
- private ToggleStatusLineAction toggleStatusLineAction;\r
-\r
- private RemoveHistoryItemAction removeHistoryItemAction;\r
-\r
- private ActionContributionItem removeHistoryActionContributionItem;\r
-\r
- private IStatus status;\r
-\r
- private RefreshCacheJob refreshCacheJob;\r
-\r
- private RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob();\r
-\r
- private Object[] currentSelection;\r
-\r
- private ContentProvider contentProvider;\r
-\r
- private FilterHistoryJob filterHistoryJob;\r
-\r
- private FilterJob filterJob;\r
-\r
- private ItemsFilter filter;\r
-\r
- private List lastCompletedResult;\r
-\r
- private ItemsFilter lastCompletedFilter;\r
-\r
- private String initialPatternText;\r
-\r
- private int selectionMode;\r
-\r
- private ItemsListSeparator itemsListSeparator;\r
-\r
- private static final String EMPTY_STRING = ""; //$NON-NLS-1$\r
-\r
- private boolean refreshWithLastSelection = false;\r
-\r
- private IHandlerActivation showViewHandler;\r
-\r
- /**\r
- * Creates a new instance of the class.\r
- * \r
- * @param shell\r
- * shell to parent the dialog on\r
- * @param multi\r
- * indicates whether dialog allows to select more than one\r
- * position in its list of items\r
- */\r
- public ColumnFilteredItemsSelectionDialog(Shell shell, boolean multi) {\r
- super(shell);\r
- this.multi = multi;\r
- filterHistoryJob = new FilterHistoryJob();\r
- filterJob = new FilterJob();\r
- contentProvider = new ContentProvider();\r
- refreshCacheJob = new RefreshCacheJob();\r
- itemsListSeparator = new ItemsListSeparator(WorkbenchMessages.FilteredItemsSelectionDialog_separatorLabel);\r
- selectionMode = NONE;\r
- }\r
-\r
- /**\r
- * Creates a new instance of the class. Created dialog won't allow to select\r
- * more than one item.\r
- * \r
- * @param shell\r
- * shell to parent the dialog on\r
- */\r
- public ColumnFilteredItemsSelectionDialog(Shell shell) {\r
- this(shell, false);\r
- }\r
-\r
- /**\r
- * Adds viewer filter to the dialog items list.\r
- * \r
- * @param filter\r
- * the new filter\r
- */\r
- protected void addListFilter(ViewerFilter filter) {\r
- contentProvider.addFilter(filter);\r
- }\r
-\r
- /**\r
- * Sets a new label provider for items in the list. If the label provider\r
- * also implements {@link\r
- * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider\r
- * .IStyledLabelProvider}, the style text labels provided by it will be used\r
- * provided that the corresponding preference is set.\r
- * \r
- * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS\r
- * \r
- * @param listLabelProvider\r
- * the label provider for items in the list\r
- */\r
- public void setListLabelProvider(ILabelProvider listLabelProvider) {\r
- getItemsListLabelProvider().setProvider(listLabelProvider);\r
- }\r
-\r
- /**\r
- * Returns the label decorator for selected items in the list.\r
- * \r
- * @return the label decorator for selected items in the list\r
- */\r
- private ILabelDecorator getListSelectionLabelDecorator() {\r
- return getItemsListLabelProvider().getSelectionDecorator();\r
- }\r
-\r
- /**\r
- * Sets the label decorator for selected items in the list.\r
- * \r
- * @param listSelectionLabelDecorator\r
- * the label decorator for selected items in the list\r
- */\r
- public void setListSelectionLabelDecorator(\r
- ILabelDecorator listSelectionLabelDecorator) {\r
- getItemsListLabelProvider().setSelectionDecorator(\r
- listSelectionLabelDecorator);\r
- }\r
-\r
- /**\r
- * Returns the item list label provider.\r
- * \r
- * @return the item list label provider\r
- */\r
- private ItemsListLabelProvider getItemsListLabelProvider() {\r
- if (itemsListLabelProvider == null) {\r
- itemsListLabelProvider = new ItemsListLabelProvider(\r
- new LabelProvider(), null);\r
- }\r
- return itemsListLabelProvider;\r
- }\r
-\r
- /**\r
- * Sets label provider for the details field.\r
- * \r
- * For a single selection, the element sent to\r
- * {@link ILabelProvider#getImage(Object)} and\r
- * {@link ILabelProvider#getText(Object)} is the selected object, for\r
- * multiple selection a {@link String} with amount of selected items is the\r
- * element.\r
- * \r
- * @see #getSelectedItems() getSelectedItems() can be used to retrieve\r
- * selected items and get the items count.\r
- * \r
- * @param detailsLabelProvider\r
- * the label provider for the details field\r
- */\r
- public void setDetailsLabelProvider(ILabelProvider detailsLabelProvider) {\r
- this.detailsLabelProvider = detailsLabelProvider;\r
- if (details != null) {\r
- details.setLabelProvider(detailsLabelProvider);\r
- }\r
- }\r
-\r
- private ILabelProvider getDetailsLabelProvider() {\r
- if (detailsLabelProvider == null) {\r
- detailsLabelProvider = new LabelProvider();\r
- }\r
- return detailsLabelProvider;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.window.Window#create()\r
- */\r
- public void create() {\r
- super.create();\r
- pattern.setFocus();\r
- }\r
-\r
- /**\r
- * Restores dialog using persisted settings. The default implementation\r
- * restores the status of the details line and the selection history.\r
- * \r
- * @param settings\r
- * settings used to restore dialog\r
- */\r
- protected void restoreDialog(IDialogSettings settings) {\r
- boolean toggleStatusLine = true;\r
-\r
- if (settings.get(SHOW_STATUS_LINE) != null) {\r
- toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE);\r
- }\r
-\r
- toggleStatusLineAction.setChecked(toggleStatusLine);\r
-\r
- details.setVisible(toggleStatusLine);\r
-\r
- String setting = settings.get(HISTORY_SETTINGS);\r
- if (setting != null) {\r
- try {\r
- IMemento memento = XMLMemento.createReadRoot(new StringReader(setting));\r
- this.contentProvider.loadHistory(memento);\r
- } catch (WorkbenchException e) {\r
- // Simply don't restore the settings\r
- StatusManager.getManager().handle(new Status(\r
- IStatus.ERROR,\r
- PlatformUI.PLUGIN_ID,\r
- IStatus.ERROR,\r
- WorkbenchMessages.FilteredItemsSelectionDialog_restoreError,\r
- e));\r
- }\r
- }\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.window.Window#close()\r
- */\r
- public boolean close() {\r
- this.filterJob.cancel();\r
- this.refreshCacheJob.cancel();\r
- this.refreshProgressMessageJob.cancel();\r
- if (showViewHandler != null) {\r
- IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);\r
- service.deactivateHandler(showViewHandler);\r
- showViewHandler.getHandler().dispose();\r
- showViewHandler = null;\r
- }\r
- if (menuManager != null)\r
- menuManager.dispose();\r
- if (contextMenuManager != null)\r
- contextMenuManager.dispose();\r
- storeDialog(getDialogSettings());\r
- return super.close();\r
- }\r
-\r
- /**\r
- * Stores dialog settings.\r
- * \r
- * @param settings\r
- * settings used to store dialog\r
- */\r
- protected void storeDialog(IDialogSettings settings) {\r
- settings.put(SHOW_STATUS_LINE, toggleStatusLineAction.isChecked());\r
-\r
- XMLMemento memento = XMLMemento.createWriteRoot(HISTORY_SETTINGS);\r
- this.contentProvider.saveHistory(memento);\r
- StringWriter writer = new StringWriter();\r
- try {\r
- memento.save(writer);\r
- settings.put(HISTORY_SETTINGS, writer.getBuffer().toString());\r
- } catch (IOException e) {\r
- // Simply don't store the settings\r
- StatusManager.getManager().handle(new Status(\r
- IStatus.ERROR,\r
- PlatformUI.PLUGIN_ID,\r
- IStatus.ERROR,\r
- WorkbenchMessages.FilteredItemsSelectionDialog_storeError,\r
- e));\r
- }\r
- }\r
-\r
- /**\r
- * Create a new header which is labelled by headerLabel.\r
- * \r
- * @param parent\r
- * @return Label the label of the header\r
- */\r
- private Label createHeader(Composite parent) {\r
- Composite header = new Composite(parent, SWT.NONE);\r
-\r
- GridLayout layout = new GridLayout();\r
- layout.numColumns = 2;\r
- layout.marginWidth = 0;\r
- layout.marginHeight = 0;\r
- header.setLayout(layout);\r
-\r
- Label headerLabel = new Label(header, SWT.NONE);\r
- headerLabel.setText((getMessage() != null && getMessage().trim().length() > 0) ? getMessage()\r
- : WorkbenchMessages.FilteredItemsSelectionDialog_patternLabel);\r
- headerLabel.addTraverseListener(new TraverseListener() {\r
- public void keyTraversed(TraverseEvent e) {\r
- if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {\r
- e.detail = SWT.TRAVERSE_NONE;\r
- pattern.setFocus();\r
- }\r
- }\r
- });\r
-\r
- GridData gd = new GridData(GridData.FILL_HORIZONTAL);\r
- headerLabel.setLayoutData(gd);\r
-\r
- createViewMenu(header);\r
- header.setLayoutData(gd);\r
- return headerLabel;\r
- }\r
-\r
- /**\r
- * Create the labels for the list and the progress. Return the list label.\r
- * \r
- * @param parent\r
- * @return Label\r
- */\r
- private Label createLabels(Composite parent) {\r
- Composite labels = new Composite(parent, SWT.NONE);\r
-\r
- GridLayout layout = new GridLayout();\r
- layout.numColumns = 2;\r
- layout.marginWidth = 0;\r
- layout.marginHeight = 0;\r
- labels.setLayout(layout);\r
-\r
- Label listLabel = new Label(labels, SWT.NONE);\r
- listLabel.setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel);\r
-\r
- listLabel.addTraverseListener(new TraverseListener() {\r
- public void keyTraversed(TraverseEvent e) {\r
- if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {\r
- e.detail = SWT.TRAVERSE_NONE;\r
- viewer.getTable().setFocus();\r
- }\r
- }\r
- });\r
-\r
- GridData gd = new GridData(GridData.FILL_HORIZONTAL);\r
- listLabel.setLayoutData(gd);\r
-\r
- progressLabel = new Label(labels, SWT.RIGHT);\r
- progressLabel.setLayoutData(gd);\r
-\r
- labels.setLayoutData(gd);\r
- return listLabel;\r
- }\r
-\r
- private void createViewMenu(Composite parent) {\r
- toolBar = new ToolBar(parent, SWT.FLAT);\r
- toolItem = new ToolItem(toolBar, SWT.PUSH, 0);\r
-\r
- GridData data = new GridData();\r
- data.horizontalAlignment = GridData.END;\r
- toolBar.setLayoutData(data);\r
-\r
- toolBar.addMouseListener(new MouseAdapter() {\r
- public void mouseDown(MouseEvent e) {\r
- showViewMenu();\r
- }\r
- });\r
-\r
- toolItem.setImage(WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU));\r
- toolItem.setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu);\r
- toolItem.addSelectionListener(new SelectionAdapter() {\r
- public void widgetSelected(SelectionEvent e) {\r
- showViewMenu();\r
- }\r
- });\r
-\r
- menuManager = new MenuManager();\r
-\r
- fillViewMenu(menuManager);\r
-\r
- IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);\r
- IHandler handler = new AbstractHandler() {\r
- public Object execute(ExecutionEvent event) {\r
- showViewMenu();\r
- return null;\r
- }\r
- };\r
- showViewHandler = service.activateHandler(\r
- IWorkbenchCommandConstants.WINDOW_SHOW_VIEW_MENU, handler,\r
- new ActiveShellExpression(getShell()));\r
- }\r
-\r
- /**\r
- * Fills the menu of the dialog.\r
- * \r
- * @param menuManager\r
- * the menu manager\r
- */\r
- protected void fillViewMenu(IMenuManager menuManager) {\r
- toggleStatusLineAction = new ToggleStatusLineAction();\r
- menuManager.add(toggleStatusLineAction);\r
- }\r
-\r
- private void showViewMenu() {\r
- Menu menu = menuManager.createContextMenu(getShell());\r
- Rectangle bounds = toolItem.getBounds();\r
- Point topLeft = new Point(bounds.x, bounds.y + bounds.height);\r
- topLeft = toolBar.toDisplay(topLeft);\r
- menu.setLocation(topLeft.x, topLeft.y);\r
- menu.setVisible(true);\r
- }\r
-\r
- /**\r
- * Hook that allows to add actions to the context menu.\r
- * <p>\r
- * Subclasses may extend in order to add other actions.</p>\r
- * \r
- * @param menuManager the context menu manager\r
- * @since 3.5\r
- */\r
- protected void fillContextMenu(IMenuManager menuManager) {\r
- List selectedElements= ((StructuredSelection)viewer.getSelection()).toList();\r
-\r
- Object item= null;\r
-\r
- for (Iterator it= selectedElements.iterator(); it.hasNext();) {\r
- item= it.next();\r
- if (item instanceof ItemsListSeparator || !isHistoryElement(item)) {\r
- return;\r
- }\r
- }\r
-\r
- if (selectedElements.size() > 0) {\r
- removeHistoryItemAction.setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);\r
-\r
- menuManager.add(removeHistoryActionContributionItem);\r
-\r
- }\r
- }\r
-\r
- private void createPopupMenu() {\r
- removeHistoryItemAction = new RemoveHistoryItemAction();\r
- removeHistoryActionContributionItem = new ActionContributionItem(removeHistoryItemAction);\r
-\r
- contextMenuManager = new MenuManager();\r
- contextMenuManager.setRemoveAllWhenShown(true);\r
- contextMenuManager.addMenuListener(new IMenuListener() {\r
- public void menuAboutToShow(IMenuManager manager) {\r
- fillContextMenu(manager);\r
- }\r
- });\r
-\r
- final Table table = viewer.getTable();\r
- Menu menu= contextMenuManager.createContextMenu(table);\r
- table.setMenu(menu);\r
- }\r
-\r
- /**\r
- * Creates an extra content area, which will be located above the details.\r
- * \r
- * @param parent\r
- * parent to create the dialog widgets in\r
- * @return an extra content area\r
- */\r
- protected abstract Control createExtendedContentArea(Composite parent);\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)\r
- */\r
- protected Control createDialogArea(Composite parent) {\r
- Composite dialogArea = (Composite) super.createDialogArea(parent);\r
-\r
- Composite content = new Composite(dialogArea, SWT.NONE);\r
- GridData gd = new GridData(GridData.FILL_BOTH);\r
- content.setLayoutData(gd);\r
-\r
- GridLayout layout = new GridLayout();\r
- layout.numColumns = 1;\r
- layout.marginWidth = 0;\r
- layout.marginHeight = 0;\r
- content.setLayout(layout);\r
-\r
- final Label headerLabel = createHeader(content);\r
-\r
- pattern = new Text(content, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);\r
- pattern.getAccessible().addAccessibleListener(new AccessibleAdapter() {\r
- public void getName(AccessibleEvent e) {\r
- e.result = LegacyActionTools.removeMnemonics(headerLabel.getText());\r
- }\r
- });\r
- gd = new GridData(GridData.FILL_HORIZONTAL);\r
- pattern.setLayoutData(gd);\r
-\r
- final Label listLabel = createLabels(content);\r
-\r
- viewer = new TableViewer(content, (multi ? SWT.MULTI : SWT.SINGLE)\r
- | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION);\r
- viewer.getTable().getAccessible().addAccessibleListener(\r
- new AccessibleAdapter() {\r
- public void getName(AccessibleEvent e) {\r
- if (e.childID == ACC.CHILDID_SELF) {\r
- e.result = LegacyActionTools.removeMnemonics(listLabel.getText());\r
- }\r
- }\r
- });\r
- viewer.setContentProvider(contentProvider);\r
- \r
- // added column creation hook\r
- createColumns(viewer);\r
- \r
- // show headers etc. if columns were added by the subclass\r
- if (viewer.getTable().getColumnCount() > 0) {\r
- viewer.getTable().setLinesVisible(true);\r
- viewer.getTable().setHeaderVisible(true);\r
- }\r
- \r
- viewer.setInput(new Object[0]);\r
- viewer.setItemCount(contentProvider.getNumberOfElements());\r
- gd = new GridData(GridData.FILL_BOTH);\r
- applyDialogFont(viewer.getTable());\r
- gd.heightHint= viewer.getTable().getItemHeight() * 15;\r
- viewer.getTable().setLayoutData(gd);\r
-\r
- createPopupMenu();\r
-\r
- pattern.addModifyListener(new ModifyListener() {\r
- public void modifyText(ModifyEvent e) {\r
- applyFilter();\r
- }\r
- });\r
-\r
- pattern.addKeyListener(new KeyAdapter() {\r
- public void keyPressed(KeyEvent e) {\r
- if (e.keyCode == SWT.ARROW_DOWN) {\r
- if (viewer.getTable().getItemCount() > 0) {\r
- viewer.getTable().setFocus();\r
- }\r
- }\r
- }\r
- });\r
-\r
- viewer.addSelectionChangedListener(new ISelectionChangedListener() {\r
- public void selectionChanged(SelectionChangedEvent event) {\r
- StructuredSelection selection = (StructuredSelection) event.getSelection();\r
- handleSelected(selection);\r
- }\r
- });\r
-\r
- viewer.addDoubleClickListener(new IDoubleClickListener() {\r
- public void doubleClick(DoubleClickEvent event) {\r
- handleDoubleClick();\r
- }\r
- });\r
-\r
- viewer.getTable().addKeyListener(new KeyAdapter() {\r
- public void keyPressed(KeyEvent e) {\r
-\r
- if (e.keyCode == SWT.DEL) {\r
-\r
- List selectedElements = ((StructuredSelection) viewer.getSelection()).toList();\r
-\r
- Object item = null;\r
- boolean isSelectedHistory = true;\r
-\r
- for (Iterator it = selectedElements.iterator(); it.hasNext();) {\r
- item = it.next();\r
- if (item instanceof ItemsListSeparator || !isHistoryElement(item)) {\r
- isSelectedHistory = false;\r
- break;\r
- }\r
- }\r
- if (isSelectedHistory)\r
- removeSelectedItems(selectedElements);\r
-\r
- }\r
-\r
- if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) != 0\r
- && (e.stateMask & SWT.CTRL) != 0) {\r
- StructuredSelection selection = (StructuredSelection) viewer.getSelection();\r
-\r
- if (selection.size() == 1) {\r
- Object element = selection.getFirstElement();\r
- if (element.equals(viewer.getElementAt(0))) {\r
- pattern.setFocus();\r
- }\r
- if (viewer.getElementAt(viewer.getTable().getSelectionIndex() - 1) instanceof ItemsListSeparator)\r
- viewer.getTable().setSelection(viewer.getTable().getSelectionIndex() - 1);\r
- viewer.getTable().notifyListeners(SWT.Selection, new Event());\r
-\r
- }\r
- }\r
-\r
- if (e.keyCode == SWT.ARROW_DOWN\r
- && (e.stateMask & SWT.SHIFT) != 0\r
- && (e.stateMask & SWT.CTRL) != 0) {\r
-\r
- if (viewer.getElementAt(viewer.getTable().getSelectionIndex() + 1) instanceof ItemsListSeparator)\r
- viewer.getTable().setSelection(viewer.getTable().getSelectionIndex() + 1);\r
- viewer.getTable().notifyListeners(SWT.Selection, new Event());\r
- }\r
- }\r
- });\r
-\r
- createExtendedContentArea(content);\r
-\r
- details = new DetailsContentViewer(content, SWT.BORDER | SWT.FLAT);\r
- details.setVisible(toggleStatusLineAction.isChecked());\r
- details.setContentProvider(new NullContentProvider());\r
- details.setLabelProvider(getDetailsLabelProvider());\r
-\r
- applyDialogFont(content);\r
-\r
- restoreDialog(getDialogSettings());\r
-\r
- if (initialPatternText != null) {\r
- pattern.setText(initialPatternText);\r
- }\r
-\r
- switch (selectionMode) {\r
- case CARET_BEGINNING:\r
- pattern.setSelection(0, 0);\r
- break;\r
- case FULL_SELECTION:\r
- pattern.setSelection(0, initialPatternText.length());\r
- break;\r
- }\r
-\r
- // apply filter even if pattern is empty (display history)\r
- applyFilter();\r
-\r
- return dialogArea;\r
- }\r
- \r
- /**\r
- * Override this method to add columns to the content area of this dialog.\r
- * \r
- * Subclass implementation of this method should NOT call the super \r
- * implementation as this will break the individual column label providers\r
- * \r
- * @param viewer\r
- */\r
- protected void createColumns(TableViewer viewer) {\r
- // no columns are added by default, just set the label provider for the viewer\r
- viewer.setLabelProvider(getItemsListLabelProvider());\r
- }\r
- \r
- /**\r
- * An utility method for adding a column to the TableViewer, should be called\r
- * from inside createColumns.\r
- * \r
- * @param viewer\r
- * @param label\r
- * @param width\r
- * @param labelProvider\r
- */\r
- protected void createColumn(TableViewer viewer, String label, int width, ColumnLabelProvider labelProvider) {\r
- TableViewerColumn viewercol = new TableViewerColumn(viewer, SWT.LEFT);\r
- \r
- TableColumn col = viewercol.getColumn();\r
- col.setText(label);\r
- col.setWidth(width);\r
- col.setResizable(true);\r
- col.setMoveable(true);\r
- \r
- // TODO: should use the local label provider class instead but it \r
- // should be made compatible with multiple columns first\r
- viewercol.setLabelProvider(labelProvider);\r
- }\r
-\r
- /**\r
- * This method is a hook for subclasses to override default dialog behavior.\r
- * The <code>handleDoubleClick()</code> method handles double clicks on\r
- * the list of filtered elements.\r
- * <p>\r
- * Current implementation makes double-clicking on the list do the same as\r
- * pressing <code>OK</code> button on the dialog.\r
- */\r
- protected void handleDoubleClick() {\r
- okPressed();\r
- }\r
-\r
- /**\r
- * Refreshes the details field according to the current selection in the\r
- * items list.\r
- */\r
- private void refreshDetails() {\r
- StructuredSelection selection = getSelectedItems();\r
-\r
- switch (selection.size()) {\r
- case 0:\r
- details.setInput(null);\r
- break;\r
- case 1:\r
- details.setInput(selection.getFirstElement());\r
- break;\r
- default:\r
- details.setInput(NLS.bind(\r
- WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected,\r
- new Integer(selection.size())));\r
- break;\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Handle selection in the items list by updating labels of selected and\r
- * unselected items and refresh the details field using the selection.\r
- * \r
- * @param selection\r
- * the new selection\r
- */\r
- protected void handleSelected(StructuredSelection selection) {\r
- IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,\r
- IStatus.OK, EMPTY_STRING, null);\r
-\r
- Object[] lastSelection = currentSelection;\r
-\r
- currentSelection = selection.toArray();\r
-\r
- if (selection.size() == 0) {\r
- status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,\r
- IStatus.ERROR, EMPTY_STRING, null);\r
-\r
- if (lastSelection != null && getListSelectionLabelDecorator() != null) {\r
- viewer.update(lastSelection, null);\r
- }\r
-\r
- currentSelection = null;\r
-\r
- } else {\r
- status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,\r
- IStatus.ERROR, EMPTY_STRING, null);\r
-\r
- List items = selection.toList();\r
-\r
- Object item = null;\r
- IStatus tempStatus = null;\r
-\r
- for (Iterator it = items.iterator(); it.hasNext();) {\r
- Object o = it.next();\r
-\r
- if (o instanceof ItemsListSeparator) {\r
- continue;\r
- }\r
-\r
- item = o;\r
- tempStatus = validateItem(item);\r
-\r
- if (tempStatus.isOK()) {\r
- status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,\r
- IStatus.OK, EMPTY_STRING, null);\r
- } else {\r
- status = tempStatus;\r
- // if any selected element is not valid status is set to\r
- // ERROR\r
- break;\r
- }\r
- }\r
-\r
- if (lastSelection != null && getListSelectionLabelDecorator() != null) {\r
- viewer.update(lastSelection, null);\r
- }\r
-\r
- if (getListSelectionLabelDecorator() != null) {\r
- viewer.update(currentSelection, null);\r
- }\r
- }\r
-\r
- refreshDetails();\r
- updateStatus(status);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()\r
- */\r
- protected IDialogSettings getDialogBoundsSettings() {\r
- IDialogSettings settings = getDialogSettings();\r
- IDialogSettings section = settings.getSection(DIALOG_BOUNDS_SETTINGS);\r
- if (section == null) {\r
- section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS);\r
- section.put(DIALOG_HEIGHT, 500);\r
- section.put(DIALOG_WIDTH, 600);\r
- }\r
- return section;\r
- }\r
-\r
- /**\r
- * Returns the dialog settings. Returned object can't be null.\r
- * \r
- * @return return dialog settings for this dialog\r
- */\r
- protected abstract IDialogSettings getDialogSettings();\r
-\r
- /**\r
- * Refreshes the dialog - has to be called in UI thread.\r
- */\r
- public void refresh() {\r
- if (viewer != null && !viewer.getTable().isDisposed()) {\r
-\r
- List lastRefreshSelection = ((StructuredSelection) viewer.getSelection()).toList();\r
- viewer.getTable().deselectAll();\r
-\r
- viewer.setItemCount(contentProvider.getNumberOfElements());\r
- viewer.refresh();\r
-\r
- if (viewer.getTable().getItemCount() > 0) {\r
- // preserve previous selection\r
- if (refreshWithLastSelection && lastRefreshSelection != null\r
- && lastRefreshSelection.size() > 0) {\r
- viewer.setSelection(new StructuredSelection(\r
- lastRefreshSelection));\r
- } else {\r
- refreshWithLastSelection = true;\r
- viewer.getTable().setSelection(0);\r
- viewer.getTable().notifyListeners(SWT.Selection, new Event());\r
- }\r
- } else {\r
- viewer.setSelection(StructuredSelection.EMPTY);\r
- }\r
-\r
- }\r
-\r
- scheduleProgressMessageRefresh();\r
- }\r
-\r
- /**\r
- * Updates the progress label.\r
- * \r
- * @deprecated\r
- */\r
- public void updateProgressLabel() {\r
- scheduleProgressMessageRefresh();\r
- }\r
-\r
- /**\r
- * Notifies the content provider - fires filtering of content provider\r
- * elements. During the filtering, a separator between history and workspace\r
- * matches is added.\r
- * <p>\r
- * This is a long running operation and should be called in a job.\r
- * \r
- * @param checkDuplicates\r
- * <code>true</code> if data concerning elements duplication\r
- * should be computed - it takes much more time than the standard\r
- * filtering\r
- * @param monitor\r
- * a progress monitor or <code>null</code> if no monitor is\r
- * available\r
- */\r
- public void reloadCache(boolean checkDuplicates, IProgressMonitor monitor) {\r
- if (viewer != null && !viewer.getTable().isDisposed() && contentProvider != null) {\r
- contentProvider.reloadCache(checkDuplicates, monitor);\r
- }\r
- }\r
-\r
- /**\r
- * Schedule refresh job.\r
- */\r
- public void scheduleRefresh() {\r
- refreshCacheJob.cancelAll();\r
- refreshCacheJob.schedule();\r
- }\r
-\r
- /**\r
- * Schedules progress message refresh.\r
- */\r
- public void scheduleProgressMessageRefresh() {\r
- if (filterJob.getState() != Job.RUNNING && refreshProgressMessageJob.getState() != Job.RUNNING)\r
- refreshProgressMessageJob.scheduleProgressRefresh(null);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()\r
- */\r
- protected void computeResult() {\r
-\r
- List selectedElements = ((StructuredSelection) viewer.getSelection())\r
- .toList();\r
-\r
- List objectsToReturn = new ArrayList();\r
-\r
- Object item = null;\r
-\r
- for (Iterator it = selectedElements.iterator(); it.hasNext();) {\r
- item = it.next();\r
-\r
- if (!(item instanceof ItemsListSeparator)) {\r
- accessedHistoryItem(item);\r
- objectsToReturn.add(item);\r
- }\r
- }\r
-\r
- setResult(objectsToReturn);\r
- }\r
-\r
- /*\r
- * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)\r
- */\r
- protected void updateStatus(IStatus status) {\r
- this.status = status;\r
- super.updateStatus(status);\r
- }\r
-\r
- /*\r
- * @see Dialog#okPressed()\r
- */\r
- protected void okPressed() {\r
- if (status != null && (status.isOK() || status.getCode() == IStatus.INFO)) {\r
- super.okPressed();\r
- }\r
- }\r
-\r
- /**\r
- * Sets the initial pattern used by the filter. This text is copied into the\r
- * selection input on the dialog. A full selection is used in the pattern\r
- * input field.\r
- * \r
- * @param text\r
- * initial pattern for the filter\r
- * @see ColumnFilteredItemsSelectionDialog#FULL_SELECTION\r
- */\r
- public void setInitialPattern(String text) {\r
- setInitialPattern(text, FULL_SELECTION);\r
- }\r
-\r
- /**\r
- * Sets the initial pattern used by the filter. This text is copied into the\r
- * selection input on the dialog. The <code>selectionMode</code> is used\r
- * to choose selection type for the input field.\r
- * \r
- * @param text\r
- * initial pattern for the filter\r
- * @param selectionMode\r
- * one of: {@link ColumnFilteredItemsSelectionDialog#NONE},\r
- * {@link ColumnFilteredItemsSelectionDialog#CARET_BEGINNING},\r
- * {@link ColumnFilteredItemsSelectionDialog#FULL_SELECTION}\r
- */\r
- public void setInitialPattern(String text, int selectionMode) {\r
- this.initialPatternText = text;\r
- this.selectionMode = selectionMode;\r
- }\r
-\r
- /**\r
- * Gets initial pattern.\r
- * \r
- * @return initial pattern, or <code>null</code> if initial pattern is not\r
- * set\r
- */\r
- protected String getInitialPattern() {\r
- return this.initialPatternText;\r
- }\r
-\r
- /**\r
- * Returns the current selection.\r
- * \r
- * @return the current selection\r
- */\r
- protected StructuredSelection getSelectedItems() {\r
-\r
- StructuredSelection selection = (StructuredSelection) viewer.getSelection();\r
-\r
- List selectedItems = selection.toList();\r
- Object itemToRemove = null;\r
-\r
- for (Iterator it = selection.iterator(); it.hasNext();) {\r
- Object item = it.next();\r
- if (item instanceof ItemsListSeparator) {\r
- itemToRemove = item;\r
- break;\r
- }\r
- }\r
-\r
- if (itemToRemove == null)\r
- return new StructuredSelection(selectedItems);\r
- // Create a new selection without the collision\r
- List newItems = new ArrayList(selectedItems);\r
- newItems.remove(itemToRemove);\r
- return new StructuredSelection(newItems);\r
-\r
- }\r
-\r
- /**\r
- * Validates the item. When items on the items list are selected or\r
- * deselected, it validates each item in the selection and the dialog status\r
- * depends on all validations.\r
- * \r
- * @param item\r
- * an item to be checked\r
- * @return status of the dialog to be set\r
- */\r
- protected abstract IStatus validateItem(Object item);\r
-\r
- /**\r
- * Creates an instance of a filter.\r
- * \r
- * @return a filter for items on the items list. Can be <code>null</code>,\r
- * no filtering will be applied then, causing no item to be shown in\r
- * the list.\r
- */\r
- protected abstract ItemsFilter createFilter();\r
-\r
- /**\r
- * Applies the filter created by <code>createFilter()</code> method to the\r
- * items list. When new filter is different than previous one it will cause\r
- * refiltering.\r
- */\r
- protected void applyFilter() {\r
- ItemsFilter newFilter = createFilter();\r
-\r
- // don't apply filtering for patterns which mean the same, for example:\r
- // *a**b and ***a*b\r
- if (filter != null && filter.equalsFilter(newFilter)) {\r
- return;\r
- }\r
- \r
- filterHistoryJob.cancel();\r
- filterJob.cancel();\r
-\r
- this.filter = newFilter;\r
- \r
- if (this.filter != null) {\r
- filterHistoryJob.schedule();\r
- }\r
- \r
- }\r
-\r
- /**\r
- * Returns comparator to sort items inside content provider. Returned object\r
- * will be probably created as an anonymous class. Parameters passed to the\r
- * <code>compare(java.lang.Object, java.lang.Object)</code> are going to\r
- * be the same type as the one used in the content provider.\r
- * \r
- * @return comparator to sort items content provider\r
- */\r
- protected abstract Comparator getItemsComparator();\r
-\r
- /**\r
- * Fills the content provider with matching items.\r
- * \r
- * @param contentProvider\r
- * collector to add items to.\r
- * {@link ColumnFilteredItemsSelectionDialog.AbstractContentProvider#add(Object, ColumnFilteredItemsSelectionDialog.ItemsFilter)}\r
- * only adds items that pass the given <code>itemsFilter</code>.\r
- * @param itemsFilter\r
- * the items filter\r
- * @param progressMonitor\r
- * must be used to report search progress. The state of this\r
- * progress monitor reflects the state of the filtering process.\r
- * @throws CoreException\r
- */\r
- protected abstract void fillContentProvider(\r
- AbstractContentProvider contentProvider, ItemsFilter itemsFilter,\r
- IProgressMonitor progressMonitor) throws CoreException;\r
- \r
- /**\r
- * Force a refresh of the content provider.\r
- */\r
- protected void forceRefresh() {\r
- lastCompletedFilter = null;\r
- lastCompletedResult = null;\r
- filterHistoryJob.schedule();\r
- }\r
-\r
- /**\r
- * Removes selected items from history.\r
- * \r
- * @param items\r
- * items to be removed\r
- */\r
- private void removeSelectedItems(List items) {\r
- for (Iterator iter = items.iterator(); iter.hasNext();) {\r
- Object item = iter.next();\r
- removeHistoryItem(item);\r
- }\r
- refreshWithLastSelection = false;\r
- contentProvider.refresh();\r
- }\r
-\r
- /**\r
- * Removes an item from history.\r
- * \r
- * @param item\r
- * an item to remove\r
- * @return removed item\r
- */\r
- protected Object removeHistoryItem(Object item) {\r
- return contentProvider.removeHistoryElement(item);\r
- }\r
-\r
- /**\r
- * Adds item to history.\r
- * \r
- * @param item\r
- * the item to be added\r
- */\r
- protected void accessedHistoryItem(Object item) {\r
- contentProvider.addHistoryElement(item);\r
- }\r
-\r
- /**\r
- * Returns a history comparator.\r
- * \r
- * @return decorated comparator\r
- */\r
- private Comparator getHistoryComparator() {\r
- return new HistoryComparator();\r
- }\r
-\r
- /**\r
- * Returns the history of selected elements.\r
- * \r
- * @return history of selected elements, or <code>null</code> if it is not\r
- * set\r
- */\r
- protected SelectionHistory getSelectionHistory() {\r
- return this.contentProvider.getSelectionHistory();\r
- }\r
-\r
- /**\r
- * Sets new history.\r
- * \r
- * @param selectionHistory\r
- * the history\r
- */\r
- protected void setSelectionHistory(SelectionHistory selectionHistory) {\r
- if (this.contentProvider != null)\r
- this.contentProvider.setSelectionHistory(selectionHistory);\r
- }\r
-\r
- /**\r
- * Indicates whether the given item is a history item.\r
- * \r
- * @param item\r
- * the item to be investigated\r
- * @return <code>true</code> if the given item exists in history,\r
- * <code>false</code> otherwise\r
- */\r
- public boolean isHistoryElement(Object item) {\r
- return this.contentProvider.isHistoryElement(item);\r
- }\r
-\r
- /**\r
- * Indicates whether the given item is a duplicate.\r
- * \r
- * @param item\r
- * the item to be investigated\r
- * @return <code>true</code> if the item is duplicate, <code>false</code>\r
- * otherwise\r
- */\r
- public boolean isDuplicateElement(Object item) {\r
- return this.contentProvider.isDuplicateElement(item);\r
- }\r
-\r
- /**\r
- * Sets separator label\r
- * \r
- * @param separatorLabel\r
- * the label showed on separator\r
- */\r
- public void setSeparatorLabel(String separatorLabel) {\r
- this.itemsListSeparator = new ItemsListSeparator(separatorLabel);\r
- }\r
-\r
- /**\r
- * Returns name for then given object.\r
- * \r
- * @param item\r
- * an object from the content provider. Subclasses should pay\r
- * attention to the passed argument. They should either only pass\r
- * objects of a known type (one used in content provider) or make\r
- * sure that passed parameter is the expected one (by type\r
- * checking like <code>instanceof</code> inside the method).\r
- * @return name of the given item\r
- */\r
- public abstract String getElementName(Object item);\r
-\r
- private class ToggleStatusLineAction extends Action {\r
-\r
- /**\r
- * Creates a new instance of the class.\r
- */\r
- public ToggleStatusLineAction() {\r
- super(WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction, IAction.AS_CHECK_BOX);\r
- }\r
-\r
- public void run() {\r
- details.setVisible(isChecked());\r
- }\r
- }\r
-\r
- /**\r
- * Only refreshes UI on the basis of an already sorted and filtered set of\r
- * items.\r
- * <p>\r
- * Standard invocation scenario:\r
- * <ol>\r
- * <li>filtering job (<code>FilterJob</code> class extending\r
- * <code>Job</code> class)</li>\r
- * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>\r
- * class extending <code>Job</code> class)</li>\r
- * <li>UI refresh (<code>RefreshJob</code> class extending\r
- * <code>UIJob</code> class)</li>\r
- * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>\r
- * class extending <code>Job</code> class)</li>\r
- * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>\r
- * class)</li>\r
- * </ol>\r
- * The scenario is rather complicated, but it had to be applied, because:\r
- * <ul>\r
- * <li> refreshing cache is rather a long action and cannot be run in the UI -\r
- * cannot be run in a UIJob</li>\r
- * <li> refreshing cache checking for duplicates is twice as long as\r
- * refreshing cache without checking for duplicates; results of the search\r
- * could be displayed earlier</li>\r
- * <li> refreshing the UI have to be run in a UIJob</li>\r
- * </ul>\r
- * \r
- * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob\r
- * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob\r
- * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob\r
- */\r
- private class RefreshJob extends UIJob {\r
-\r
- /**\r
- * Creates a new instance of the class.\r
- */\r
- public RefreshJob() {\r
- super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(),\r
- WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob);\r
- setSystem(true);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)\r
- */\r
- public IStatus runInUIThread(IProgressMonitor monitor) {\r
- if (monitor.isCanceled())\r
- return new Status(IStatus.OK, WorkbenchPlugin.PI_WORKBENCH,\r
- IStatus.OK, EMPTY_STRING, null);\r
-\r
- if (ColumnFilteredItemsSelectionDialog.this != null) {\r
- ColumnFilteredItemsSelectionDialog.this.refresh();\r
- }\r
-\r
- return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,\r
- EMPTY_STRING, null);\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Refreshes the progress message cyclically with 500 milliseconds delay.\r
- * <code>RefreshProgressMessageJob</code> is strictly connected with\r
- * <code>GranualProgressMonitor</code> and use it to to get progress\r
- * message and to decide about break of cyclical refresh.\r
- */\r
- private class RefreshProgressMessageJob extends UIJob {\r
-\r
- private GranualProgressMonitor progressMonitor;\r
-\r
- /**\r
- * Creates a new instance of the class.\r
- */\r
- public RefreshProgressMessageJob() {\r
- super(ColumnFilteredItemsSelectionDialog.this.getParentShell().getDisplay(),\r
- WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob);\r
- setSystem(true);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)\r
- */\r
- public IStatus runInUIThread(IProgressMonitor monitor) {\r
-\r
- if (!progressLabel.isDisposed())\r
- progressLabel.setText(progressMonitor != null ? progressMonitor.getMessage() : EMPTY_STRING);\r
-\r
- if (progressMonitor == null || progressMonitor.isDone()) {\r
- return new Status(IStatus.CANCEL, PlatformUI.PLUGIN_ID,\r
- IStatus.CANCEL, EMPTY_STRING, null);\r
- }\r
-\r
- // Schedule cyclical with 500 milliseconds delay\r
- schedule(500);\r
-\r
- return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,\r
- EMPTY_STRING, null);\r
- }\r
-\r
- /**\r
- * Schedule progress refresh job.\r
- * \r
- * @param progressMonitor\r
- * used during refresh progress label\r
- */\r
- public void scheduleProgressRefresh(\r
- GranualProgressMonitor progressMonitor) {\r
- this.progressMonitor = progressMonitor;\r
- // Schedule with initial delay to avoid flickering when the user\r
- // types quickly\r
- schedule(200);\r
- }\r
-\r
- }\r
-\r
- /**\r
- * A job responsible for computing filtered items list presented using\r
- * <code>RefreshJob</code>.\r
- * \r
- * @see ColumnFilteredItemsSelectionDialog.RefreshJob\r
- * \r
- */\r
- private class RefreshCacheJob extends Job {\r
-\r
- private RefreshJob refreshJob = new RefreshJob();\r
-\r
- /**\r
- * Creates a new instance of the class.\r
- */\r
- public RefreshCacheJob() {\r
- super(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob);\r
- setSystem(true);\r
- }\r
-\r
- /**\r
- * Stops the job and all sub-jobs.\r
- */\r
- public void cancelAll() {\r
- cancel();\r
- refreshJob.cancel();\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)\r
- */\r
- protected IStatus run(IProgressMonitor monitor) {\r
- if (monitor.isCanceled()) {\r
- return new Status(IStatus.CANCEL, WorkbenchPlugin.PI_WORKBENCH,\r
- IStatus.CANCEL, EMPTY_STRING, null);\r
- }\r
-\r
- if (ColumnFilteredItemsSelectionDialog.this != null) {\r
- GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(monitor);\r
- ColumnFilteredItemsSelectionDialog.this.reloadCache(true, wrappedMonitor);\r
- }\r
-\r
- if (!monitor.isCanceled()) {\r
- refreshJob.schedule();\r
- }\r
-\r
- return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,\r
- EMPTY_STRING, null);\r
-\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.jobs.Job#canceling()\r
- */\r
- protected void canceling() {\r
- super.canceling();\r
- contentProvider.stopReloadingCache();\r
- }\r
-\r
- }\r
-\r
- private class RemoveHistoryItemAction extends Action {\r
-\r
- /**\r
- * Creates a new instance of the class.\r
- */\r
- public RemoveHistoryItemAction() {\r
- super(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.action.Action#run()\r
- */\r
- public void run() {\r
- List selectedElements = ((StructuredSelection) viewer.getSelection()).toList();\r
- removeSelectedItems(selectedElements);\r
- }\r
- }\r
-\r
- private static boolean showColoredLabels() {\r
- return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS);\r
- }\r
-\r
- private class ItemsListLabelProvider extends StyledCellLabelProvider\r
- implements ILabelProviderListener {\r
- private ILabelProvider provider;\r
-\r
- private ILabelDecorator selectionDecorator;\r
-\r
- // Need to keep our own list of listeners\r
- private ListenerList listeners = new ListenerList();\r
-\r
- /**\r
- * Creates a new instance of the class.\r
- * \r
- * @param provider\r
- * the label provider for all items, not <code>null</code>\r
- * @param selectionDecorator\r
- * the decorator for selected items, can be <code>null</code>\r
- */\r
- public ItemsListLabelProvider(ILabelProvider provider,\r
- ILabelDecorator selectionDecorator) {\r
- Assert.isNotNull(provider);\r
- this.provider = provider;\r
- this.selectionDecorator = selectionDecorator;\r
-\r
- setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);\r
-\r
- provider.addListener(this);\r
-\r
- if (selectionDecorator != null) {\r
- selectionDecorator.addListener(this);\r
- }\r
- }\r
-\r
- /**\r
- * Sets new selection decorator.\r
- * \r
- * @param newSelectionDecorator\r
- * new label decorator for selected items in the list\r
- */\r
- public void setSelectionDecorator(ILabelDecorator newSelectionDecorator) {\r
- if (selectionDecorator != null) {\r
- selectionDecorator.removeListener(this);\r
- selectionDecorator.dispose();\r
- }\r
-\r
- selectionDecorator = newSelectionDecorator;\r
-\r
- if (selectionDecorator != null) {\r
- selectionDecorator.addListener(this);\r
- }\r
- }\r
-\r
- /**\r
- * Gets selection decorator.\r
- * \r
- * @return the label decorator for selected items in the list\r
- */\r
- public ILabelDecorator getSelectionDecorator() {\r
- return selectionDecorator;\r
- }\r
-\r
- /**\r
- * Sets new label provider.\r
- * \r
- * @param newProvider\r
- * new label provider for items in the list, not\r
- * <code>null</code>\r
- */\r
- public void setProvider(ILabelProvider newProvider) {\r
- Assert.isNotNull(newProvider);\r
- provider.removeListener(this);\r
- provider.dispose();\r
-\r
- provider = newProvider;\r
-\r
- if (provider != null) {\r
- provider.addListener(this);\r
- }\r
-\r
- setOwnerDrawEnabled(showColoredLabels() && provider instanceof IStyledLabelProvider);\r
- }\r
-\r
- private Image getImage(Object element) {\r
- if (element instanceof ItemsListSeparator) {\r
- return WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR);\r
- }\r
-\r
- return provider.getImage(element);\r
- }\r
-\r
- private boolean isSelected(Object element) {\r
- if (element != null && currentSelection != null) {\r
- for (int i = 0; i < currentSelection.length; i++) {\r
- if (element.equals(currentSelection[i]))\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)\r
- */\r
- private String getText(Object element) {\r
- if (element instanceof ItemsListSeparator) {\r
- return getSeparatorLabel(((ItemsListSeparator) element).getName());\r
- }\r
-\r
- String str = provider.getText(element);\r
- if (selectionDecorator != null && isSelected(element)) {\r
- return selectionDecorator.decorateText(str.toString(), element);\r
- }\r
-\r
- return str;\r
- }\r
-\r
- private StyledString getStyledText(Object element,\r
- IStyledLabelProvider provider) {\r
- StyledString string = provider.getStyledText(element);\r
-\r
- if (selectionDecorator != null && isSelected(element)) {\r
- String decorated = selectionDecorator.decorateText(string.getString(), element);\r
- return StyledCellLabelProvider.styleDecoratedString(decorated, null, string);\r
- // no need to add colors when element is selected\r
- }\r
- return string;\r
- }\r
-\r
- public void update(ViewerCell cell) {\r
- Object element = cell.getElement();\r
-\r
- if (!(element instanceof ItemsListSeparator) && provider instanceof IStyledLabelProvider) {\r
- IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) provider;\r
- StyledString styledString = getStyledText(element, styledLabelProvider);\r
-\r
- cell.setText(styledString.getString());\r
- cell.setStyleRanges(styledString.getStyleRanges());\r
- cell.setImage(styledLabelProvider.getImage(element));\r
- } else {\r
- cell.setText(getText(element));\r
- cell.setImage(getImage(element));\r
- }\r
- cell.setFont(getFont(element));\r
- cell.setForeground(getForeground(element));\r
- cell.setBackground(getBackground(element));\r
-\r
- super.update(cell);\r
- }\r
-\r
- private String getSeparatorLabel(String separatorLabel) {\r
- Rectangle rect = viewer.getTable().getBounds();\r
-\r
- int borderWidth = viewer.getTable().computeTrim(0, 0, 0, 0).width;\r
-\r
- int imageWidth = WorkbenchImages.getImage(\r
- IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width;\r
-\r
- int width = rect.width - borderWidth - imageWidth;\r
-\r
- GC gc = new GC(viewer.getTable());\r
- gc.setFont(viewer.getTable().getFont());\r
-\r
- int fSeparatorWidth = gc.getAdvanceWidth('-');\r
- int fMessageLength = gc.textExtent(separatorLabel).x;\r
-\r
- gc.dispose();\r
-\r
- StringBuffer dashes = new StringBuffer();\r
- int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2;\r
- for (int i = 0; i < chars; i++) {\r
- dashes.append('-');\r
- }\r
-\r
- StringBuffer result = new StringBuffer();\r
- result.append(dashes);\r
- result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$\r
- result.append(dashes);\r
- return result.toString().trim();\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)\r
- */\r
- public void addListener(ILabelProviderListener listener) {\r
- listeners.add(listener);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()\r
- */\r
- public void dispose() {\r
- provider.removeListener(this);\r
- provider.dispose();\r
-\r
- if (selectionDecorator != null) {\r
- selectionDecorator.removeListener(this);\r
- selectionDecorator.dispose();\r
- }\r
-\r
- super.dispose();\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,\r
- * java.lang.String)\r
- */\r
- public boolean isLabelProperty(Object element, String property) {\r
- if (provider.isLabelProperty(element, property)) {\r
- return true;\r
- }\r
- if (selectionDecorator != null\r
- && selectionDecorator.isLabelProperty(element, property)) {\r
- return true;\r
- }\r
- return false;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)\r
- */\r
- public void removeListener(ILabelProviderListener listener) {\r
- listeners.remove(listener);\r
- }\r
-\r
- private Color getBackground(Object element) {\r
- if (element instanceof ItemsListSeparator) {\r
- return null;\r
- }\r
- if (provider instanceof IColorProvider) {\r
- return ((IColorProvider) provider).getBackground(element);\r
- }\r
- return null;\r
- }\r
-\r
- private Color getForeground(Object element) {\r
- if (element instanceof ItemsListSeparator) {\r
- return Display.getCurrent().getSystemColor(\r
- SWT.COLOR_WIDGET_NORMAL_SHADOW);\r
- }\r
- if (provider instanceof IColorProvider) {\r
- return ((IColorProvider) provider).getForeground(element);\r
- }\r
- return null;\r
- }\r
-\r
- private Font getFont(Object element) {\r
- if (element instanceof ItemsListSeparator) {\r
- return null;\r
- }\r
- if (provider instanceof IFontProvider) {\r
- return ((IFontProvider) provider).getFont(element);\r
- }\r
- return null;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)\r
- */\r
- public void labelProviderChanged(LabelProviderChangedEvent event) {\r
- Object[] l = listeners.getListeners();\r
- for (int i = 0; i < listeners.size(); i++) {\r
- ((ILabelProviderListener) l[i]).labelProviderChanged(event);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Used in ItemsListContentProvider, separates history and non-history\r
- * items.\r
- */\r
- private class ItemsListSeparator {\r
-\r
- private String name;\r
-\r
- /**\r
- * Creates a new instance of the class.\r
- * \r
- * @param name\r
- * the name of the separator\r
- */\r
- public ItemsListSeparator(String name) {\r
- this.name = name;\r
- }\r
-\r
- /**\r
- * Returns the name of this separator.\r
- * \r
- * @return the name of the separator\r
- */\r
- public String getName() {\r
- return name;\r
- }\r
- }\r
-\r
- /**\r
- * GranualProgressMonitor is used for monitoring progress of filtering\r
- * process. It is used by <code>RefreshProgressMessageJob</code> to\r
- * refresh progress message. State of this monitor illustrates state of\r
- * filtering or cache refreshing process.\r
- * \r
- */\r
- private class GranualProgressMonitor extends ProgressMonitorWrapper {\r
-\r
- private String name;\r
-\r
- private String subName;\r
-\r
- private int totalWork;\r
-\r
- private double worked;\r
-\r
- private boolean done;\r
-\r
- /**\r
- * Creates instance of <code>GranualProgressMonitor</code>.\r
- * \r
- * @param monitor\r
- * progress to be wrapped\r
- */\r
- public GranualProgressMonitor(IProgressMonitor monitor) {\r
- super(monitor);\r
- }\r
-\r
- /**\r
- * Checks if filtering has been done\r
- * \r
- * @return true if filtering work has been done false in other way\r
- */\r
- public boolean isDone() {\r
- return done;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)\r
- */\r
- public void setTaskName(String name) {\r
- super.setTaskName(name);\r
- this.name = name;\r
- this.subName = null;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)\r
- */\r
- public void subTask(String name) {\r
- super.subTask(name);\r
- this.subName = name;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,\r
- * int)\r
- */\r
- public void beginTask(String name, int totalWork) {\r
- super.beginTask(name, totalWork);\r
- if (this.name == null)\r
- this.name = name;\r
- this.totalWork = totalWork;\r
- refreshProgressMessageJob.scheduleProgressRefresh(this);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)\r
- */\r
- public void worked(int work) {\r
- super.worked(work);\r
- internalWorked(work);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()\r
- */\r
- public void done() {\r
- done = true;\r
- super.done();\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)\r
- */\r
- public void setCanceled(boolean b) {\r
- done = b;\r
- super.setCanceled(b);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)\r
- */\r
- public void internalWorked(double work) {\r
- worked = worked + work;\r
- }\r
-\r
- private String getMessage() {\r
- if (done)\r
- return ""; //$NON-NLS-1$\r
-\r
- String message;\r
-\r
- if (name == null) {\r
- message = subName == null ? "" : subName; //$NON-NLS-1$\r
- } else {\r
- message = subName == null ? name\r
- : NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage,\r
- new Object[] { name, subName });\r
- }\r
- if (totalWork == 0)\r
- return message;\r
-\r
- return NLS.bind(WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage,\r
- new Object[] { message, new Integer((int) ((worked * 100) / totalWork)) });\r
-\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Filters items history and schedule filter job.\r
- */\r
- private class FilterHistoryJob extends Job {\r
-\r
- /**\r
- * Filter used during the filtering process.\r
- */\r
- private ItemsFilter itemsFilter;\r
-\r
- /**\r
- * Creates new instance of receiver.\r
- */\r
- public FilterHistoryJob() {\r
- super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);\r
- setSystem(true);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)\r
- */\r
- protected IStatus run(IProgressMonitor monitor) {\r
- this.itemsFilter = filter;\r
-\r
- contentProvider.reset();\r
-\r
- refreshWithLastSelection = false;\r
-\r
- contentProvider.addHistoryItems(itemsFilter);\r
-\r
- if (!(lastCompletedFilter != null && lastCompletedFilter.isSubFilter(this.itemsFilter)))\r
- contentProvider.refresh();\r
-\r
- filterJob.schedule();\r
-\r
- return Status.OK_STATUS;\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Filters items in indicated set and history. During filtering, it\r
- * refreshes the dialog (progress monitor and elements list).\r
- * \r
- * Depending on the filter, <code>FilterJob</code> decides which kind of\r
- * search will be run inside <code>filterContent</code>. If the last\r
- * filtering is done (last completed filter), is not null, and the new\r
- * filter is a sub-filter ({@link ColumnFilteredItemsSelectionDialog.ItemsFilter#isSubFilter(ColumnFilteredItemsSelectionDialog.ItemsFilter)})\r
- * of the last, then <code>FilterJob</code> only filters in the cache. If\r
- * it is the first filtering or the new filter isn't a sub-filter of the\r
- * last one, a full search is run.\r
- */\r
- private class FilterJob extends Job {\r
-\r
- /**\r
- * Filter used during the filtering process.\r
- */\r
- protected ItemsFilter itemsFilter;\r
-\r
- /**\r
- * Creates new instance of FilterJob\r
- */\r
- public FilterJob() {\r
- super(WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);\r
- setSystem(true);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)\r
- */\r
- protected final IStatus run(IProgressMonitor parent) {\r
- GranualProgressMonitor monitor = new GranualProgressMonitor(parent);\r
- return doRun(monitor);\r
- }\r
-\r
- /**\r
- * Executes job using the given filtering progress monitor. A hook for\r
- * subclasses.\r
- * \r
- * @param monitor\r
- * progress monitor\r
- * @return result of the execution\r
- */\r
- protected IStatus doRun(GranualProgressMonitor monitor) {\r
- try {\r
- internalRun(monitor);\r
- } catch (CoreException e) {\r
- cancel();\r
- return new Status(\r
- IStatus.ERROR,\r
- PlatformUI.PLUGIN_ID,\r
- IStatus.ERROR,\r
- WorkbenchMessages.FilteredItemsSelectionDialog_jobError,\r
- e);\r
- }\r
- return Status.OK_STATUS;\r
- }\r
-\r
- /**\r
- * Main method for the job.\r
- * \r
- * @param monitor\r
- * @throws CoreException\r
- */\r
- private void internalRun(GranualProgressMonitor monitor)\r
- throws CoreException {\r
- try {\r
- if (monitor.isCanceled())\r
- return;\r
-\r
- this.itemsFilter = filter;\r
-\r
- // why is the content not filtered if the patter is empty? \r
- // this makes no sense since the search pattern is able to\r
- // handle the empty pattern just fine, and even has settings\r
- // for what to do in this case\r
- //if (filter.getPattern().length() != 0) {\r
- // filterContent(monitor);\r
- //}\r
- \r
- filterContent(monitor);\r
-\r
- if (monitor.isCanceled())\r
- return;\r
-\r
- contentProvider.refresh();\r
- } finally {\r
- monitor.done();\r
- }\r
- }\r
-\r
- /**\r
- * Filters items.\r
- * \r
- * @param monitor\r
- * for monitoring progress\r
- * @throws CoreException\r
- */\r
- protected void filterContent(GranualProgressMonitor monitor)\r
- throws CoreException {\r
-\r
- if (lastCompletedFilter != null\r
- && lastCompletedFilter.isSubFilter(this.itemsFilter)) {\r
-\r
- int length = lastCompletedResult.size() / 500;\r
- monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName, length);\r
-\r
- for (int pos = 0; pos < lastCompletedResult.size(); pos++) {\r
-\r
- Object item = lastCompletedResult.get(pos);\r
- if (monitor.isCanceled())\r
- break;\r
- contentProvider.add(item, itemsFilter);\r
-\r
- if ((pos % 500) == 0) {\r
- monitor.worked(1);\r
- }\r
- }\r
-\r
- } else {\r
-\r
- lastCompletedFilter = null;\r
- lastCompletedResult = null;\r
-\r
- SubProgressMonitor subMonitor = null;\r
- if (monitor != null) {\r
- monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_searchJob_taskName, 100);\r
- subMonitor = new SubProgressMonitor(monitor, 95);\r
-\r
- }\r
-\r
- fillContentProvider(contentProvider, itemsFilter, subMonitor);\r
-\r
- if (monitor != null && !monitor.isCanceled()) {\r
- monitor.worked(2);\r
- contentProvider.rememberResult(itemsFilter);\r
- monitor.worked(3);\r
- }\r
- }\r
-\r
- }\r
-\r
- }\r
-\r
- /**\r
- * History stores a list of key, object pairs. The list is bounded at a\r
- * certain size. If the list exceeds this size the oldest element is removed\r
- * from the list. An element can be added/renewed with a call to\r
- * <code>accessed(Object)</code>.\r
- * <p>\r
- * The history can be stored to/loaded from an XML file.\r
- */\r
- protected static abstract class SelectionHistory {\r
-\r
- private static final String DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$\r
-\r
- private static final String DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$\r
-\r
- private static final int MAX_HISTORY_SIZE = 60;\r
-\r
- private final Set historyList;\r
-\r
- private final String rootNodeName;\r
-\r
- private final String infoNodeName;\r
-\r
- private SelectionHistory(String rootNodeName, String infoNodeName) {\r
-\r
- historyList = Collections.synchronizedSet(new LinkedHashSet() {\r
-\r
- private static final long serialVersionUID = 0L;\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see java.util.LinkedList#add(java.lang.Object)\r
- */\r
- public boolean add(Object arg0) {\r
- if (this.size() >= MAX_HISTORY_SIZE) {\r
- Iterator iterator = this.iterator();\r
- iterator.next();\r
- iterator.remove();\r
- }\r
- return super.add(arg0);\r
- }\r
-\r
- });\r
-\r
- this.rootNodeName = rootNodeName;\r
- this.infoNodeName = infoNodeName;\r
- }\r
-\r
- /**\r
- * Creates new instance of <code>SelectionHistory</code>.\r
- */\r
- public SelectionHistory() {\r
- this(DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME);\r
- }\r
-\r
- /**\r
- * Adds object to history.\r
- * \r
- * @param object\r
- * the item to be added to the history\r
- */\r
- public synchronized void accessed(Object object) {\r
- historyList.remove(object);\r
- historyList.add(object);\r
- }\r
-\r
- /**\r
- * Returns <code>true</code> if history contains object.\r
- * \r
- * @param object\r
- * the item for which check will be executed\r
- * @return <code>true</code> if history contains object\r
- * <code>false</code> in other way\r
- */\r
- public synchronized boolean contains(Object object) {\r
- return historyList.contains(object);\r
- }\r
-\r
- /**\r
- * Returns <code>true</code> if history is empty.\r
- * \r
- * @return <code>true</code> if history is empty\r
- */\r
- public synchronized boolean isEmpty() {\r
- return historyList.isEmpty();\r
- }\r
-\r
- /**\r
- * Remove element from history.\r
- * \r
- * @param element\r
- * to remove form the history\r
- * @return <code>true</code> if this list contained the specified\r
- * element\r
- */\r
- public synchronized boolean remove(Object element) {\r
- return historyList.remove(element);\r
- }\r
-\r
- /**\r
- * Load history elements from memento.\r
- * \r
- * @param memento\r
- * memento from which the history will be retrieved\r
- */\r
- public void load(IMemento memento) {\r
-\r
- XMLMemento historyMemento = (XMLMemento) memento\r
- .getChild(rootNodeName);\r
-\r
- if (historyMemento == null) {\r
- return;\r
- }\r
-\r
- IMemento[] mementoElements = historyMemento\r
- .getChildren(infoNodeName);\r
- for (int i = 0; i < mementoElements.length; ++i) {\r
- IMemento mementoElement = mementoElements[i];\r
- Object object = restoreItemFromMemento(mementoElement);\r
- if (object != null) {\r
- historyList.add(object);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Save history elements to memento.\r
- * \r
- * @param memento\r
- * memento to which the history will be added\r
- */\r
- public void save(IMemento memento) {\r
-\r
- IMemento historyMemento = memento.createChild(rootNodeName);\r
-\r
- Object[] items = getHistoryItems();\r
- for (int i = 0; i < items.length; i++) {\r
- Object item = items[i];\r
- IMemento elementMemento = historyMemento\r
- .createChild(infoNodeName);\r
- storeItemToMemento(item, elementMemento);\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Gets array of history items.\r
- * \r
- * @return array of history elements\r
- */\r
- public synchronized Object[] getHistoryItems() {\r
- return historyList.toArray();\r
- }\r
-\r
- /**\r
- * Creates an object using given memento.\r
- * \r
- * @param memento\r
- * memento used for creating new object\r
- * \r
- * @return the restored object\r
- */\r
- protected abstract Object restoreItemFromMemento(IMemento memento);\r
-\r
- /**\r
- * Store object in <code>IMemento</code>.\r
- * \r
- * @param item\r
- * the item to store\r
- * @param memento\r
- * the memento to store to\r
- */\r
- protected abstract void storeItemToMemento(Object item, IMemento memento);\r
-\r
- }\r
-\r
- /**\r
- * Filters elements using SearchPattern by comparing the names of items with\r
- * the filter pattern.\r
- */\r
- protected abstract class ItemsFilter {\r
-\r
- protected SearchPattern patternMatcher;\r
-\r
- /**\r
- * Creates new instance of ItemsFilter.\r
- */\r
- public ItemsFilter() {\r
- this(new SearchPattern());\r
- }\r
-\r
- /**\r
- * Creates new instance of ItemsFilter.\r
- * \r
- * @param searchPattern\r
- * the pattern to be used when filtering\r
- */\r
- public ItemsFilter(SearchPattern searchPattern) {\r
- patternMatcher = searchPattern;\r
- String stringPattern = ""; //$NON-NLS-1$\r
- if (pattern != null && !pattern.getText().equals("*")) { //$NON-NLS-1$\r
- stringPattern = pattern.getText();\r
- }\r
- patternMatcher.setPattern(stringPattern);\r
- }\r
-\r
- /**\r
- * Check if the given filter is a sub-filter of this filter. The default\r
- * implementation checks if the <code>SearchPattern</code> from the\r
- * given filter is a sub-pattern of the one from this filter.\r
- * <p>\r
- * <i>WARNING: This method is <b>not</b> defined in reading order, i.e.\r
- * <code>a.isSubFilter(b)</code> is <code>true</code> iff\r
- * <code>b</code> is a sub-filter of <code>a</code>, and not\r
- * vice-versa. </i>\r
- * </p>\r
- * \r
- * @param filter\r
- * the filter to be checked, or <code>null</code>\r
- * @return <code>true</code> if the given filter is sub-filter of this\r
- * filter, <code>false</code> if the given filter isn't a\r
- * sub-filter or is <code>null</code>\r
- * \r
- * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)\r
- */\r
- public boolean isSubFilter(ItemsFilter filter) {\r
- if (filter != null) {\r
- return this.patternMatcher.isSubPattern(filter.patternMatcher);\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Checks whether the provided filter is equal to the current filter.\r
- * The default implementation checks if <code>SearchPattern</code>\r
- * from current filter is equal to the one from provided filter.\r
- * \r
- * @param filter\r
- * filter to be checked, or <code>null</code>\r
- * @return <code>true</code> if the given filter is equal to current\r
- * filter, <code>false</code> if given filter isn't equal to\r
- * current one or if it is <code>null</code>\r
- * \r
- * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)\r
- */\r
- public boolean equalsFilter(ItemsFilter filter) {\r
- if (filter != null\r
- && filter.patternMatcher.equalsPattern(this.patternMatcher)) {\r
- return true;\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Checks whether the pattern's match rule is camel case.\r
- * \r
- * @return <code>true</code> if pattern's match rule is camel case,\r
- * <code>false</code> otherwise\r
- */\r
- public boolean isCamelCasePattern() {\r
- return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH;\r
- }\r
-\r
- /**\r
- * Returns the pattern string.\r
- * \r
- * @return pattern for this filter\r
- * \r
- * @see SearchPattern#getPattern()\r
- */\r
- public String getPattern() {\r
- return patternMatcher.getPattern();\r
- }\r
-\r
- /**\r
- * Returns the rule to apply for matching keys.\r
- * \r
- * @return an implementation-specific match rule\r
- * \r
- * @see SearchPattern#getMatchRule() for match rules returned by the\r
- * default implementation\r
- */\r
- public int getMatchRule() {\r
- return patternMatcher.getMatchRule();\r
- }\r
-\r
- /**\r
- * Matches text with filter.\r
- * \r
- * @param text\r
- * the text to match with the filter\r
- * @return <code>true</code> if text matches with filter pattern,\r
- * <code>false</code> otherwise\r
- */\r
- protected boolean matches(String text) {\r
- return patternMatcher.matches(text);\r
- }\r
-\r
- /**\r
- * General method for matching raw name pattern. Checks whether current\r
- * pattern is prefix of name provided item.\r
- * \r
- * @param item\r
- * item to check\r
- * @return <code>true</code> if current pattern is a prefix of name\r
- * provided item, <code>false</code> if item's name is shorter\r
- * than prefix or sequences of characters don't match.\r
- */\r
- public boolean matchesRawNamePattern(Object item) {\r
- String prefix = patternMatcher.getPattern();\r
- String text = getElementName(item);\r
-\r
- if (text == null)\r
- return false;\r
-\r
- int textLength = text.length();\r
- int prefixLength = prefix.length();\r
- if (textLength < prefixLength) {\r
- return false;\r
- }\r
- for (int i = prefixLength - 1; i >= 0; i--) {\r
- if (Character.toLowerCase(prefix.charAt(i)) != Character\r
- .toLowerCase(text.charAt(i)))\r
- return false;\r
- }\r
- return true;\r
- }\r
-\r
- /**\r
- * Matches an item against filter conditions.\r
- * \r
- * @param item\r
- * @return <code>true<code> if item matches against filter conditions, <code>false</code>\r
- * otherwise\r
- */\r
- public abstract boolean matchItem(Object item);\r
-\r
- /**\r
- * Checks consistency of an item. Item is inconsistent if was changed or\r
- * removed.\r
- * \r
- * @param item\r
- * @return <code>true</code> if item is consistent, <code>false</code>\r
- * if item is inconsistent\r
- */\r
- public abstract boolean isConsistentItem(Object item);\r
-\r
- }\r
-\r
- /**\r
- * An interface to content providers for\r
- * <code>FilterItemsSelectionDialog</code>.\r
- */\r
- protected abstract class AbstractContentProvider {\r
- /**\r
- * Adds the item to the content provider iff the filter matches the\r
- * item. Otherwise does nothing.\r
- * \r
- * @param item\r
- * the item to add\r
- * @param itemsFilter\r
- * the filter\r
- * \r
- * @see ColumnFilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)\r
- */\r
- public abstract void add(Object item, ItemsFilter itemsFilter);\r
- }\r
-\r
- /**\r
- * Collects filtered elements. Contains one synchronized, sorted set for\r
- * collecting filtered elements. All collected elements are sorted using\r
- * comparator. Comparator is returned by getElementComparator() method.\r
- * Implementation of <code>ItemsFilter</code> is used to filter elements.\r
- * The key function of filter used in to filtering is\r
- * <code>matchElement(Object item)</code>.\r
- * <p>\r
- * The <code>ContentProvider</code> class also provides item filtering\r
- * methods. The filtering has been moved from the standard TableView\r
- * <code>getFilteredItems()</code> method to content provider, because\r
- * <code>ILazyContentProvider</code> and virtual tables are used. This\r
- * class is responsible for adding a separator below history items and\r
- * marking each items as duplicate if its name repeats more than once on the\r
- * filtered list.\r
- */\r
- private class ContentProvider extends AbstractContentProvider implements\r
- IStructuredContentProvider, ILazyContentProvider {\r
-\r
- private SelectionHistory selectionHistory;\r
-\r
- /**\r
- * Raw result of the searching (unsorted, unfiltered).\r
- * <p>\r
- * Standard object flow:\r
- * <code>items -> lastSortedItems -> lastFilteredItems</code>\r
- */\r
- private Set items;\r
-\r
- /**\r
- * Items that are duplicates.\r
- */\r
- private Set duplicates;\r
-\r
- /**\r
- * List of <code>ViewerFilter</code>s to be used during filtering\r
- */\r
- private List filters;\r
-\r
- /**\r
- * Result of the last filtering.\r
- * <p>\r
- * Standard object flow:\r
- * <code>items -> lastSortedItems -> lastFilteredItems</code>\r
- */\r
- private List lastFilteredItems;\r
-\r
- /**\r
- * Result of the last sorting.\r
- * <p>\r
- * Standard object flow:\r
- * <code>items -> lastSortedItems -> lastFilteredItems</code>\r
- */\r
- private List lastSortedItems;\r
-\r
- /**\r
- * Used for <code>getFilteredItems()</code> method canceling (when the\r
- * job that invoked the method was canceled).\r
- * <p>\r
- * Method canceling could be based (only) on monitor canceling\r
- * unfortunately sometimes the method <code>getFilteredElements()</code>\r
- * could be run with a null monitor, the <code>reset</code> flag have\r
- * to be left intact.\r
- */\r
- private boolean reset;\r
-\r
- /**\r
- * Creates new instance of <code>ContentProvider</code>.\r
- */\r
- public ContentProvider() {\r
- this.items = Collections.synchronizedSet(new HashSet(2048));\r
- this.duplicates = Collections.synchronizedSet(new HashSet(256));\r
- this.lastFilteredItems = new ArrayList();\r
- this.lastSortedItems = Collections.synchronizedList(new ArrayList(2048));\r
- }\r
-\r
- /**\r
- * Sets selection history.\r
- * \r
- * @param selectionHistory\r
- * The selectionHistory to set.\r
- */\r
- public void setSelectionHistory(SelectionHistory selectionHistory) {\r
- this.selectionHistory = selectionHistory;\r
- }\r
-\r
- /**\r
- * @return Returns the selectionHistory.\r
- */\r
- public SelectionHistory getSelectionHistory() {\r
- return selectionHistory;\r
- }\r
-\r
- /**\r
- * Removes all content items and resets progress message.\r
- */\r
- public void reset() {\r
- reset = true;\r
- this.items.clear();\r
- this.duplicates.clear();\r
- this.lastSortedItems.clear();\r
- }\r
-\r
- /**\r
- * Stops reloading cache - <code>getFilteredItems()</code> method.\r
- */\r
- public void stopReloadingCache() {\r
- reset = true;\r
- }\r
-\r
- /**\r
- * Adds filtered item.\r
- * \r
- * @param item\r
- * @param itemsFilter\r
- */\r
- public void add(Object item, ItemsFilter itemsFilter) {\r
- if (itemsFilter == filter) {\r
- if (itemsFilter != null) {\r
- if (itemsFilter.matchItem(item)) {\r
- this.items.add(item);\r
- }\r
- } else {\r
- this.items.add(item);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Add all history items to <code>contentProvider</code>.\r
- * \r
- * @param itemsFilter\r
- */\r
- public void addHistoryItems(ItemsFilter itemsFilter) {\r
- if (this.selectionHistory != null) {\r
- Object[] items = this.selectionHistory.getHistoryItems();\r
- for (int i = 0; i < items.length; i++) {\r
- Object item = items[i];\r
- if (itemsFilter == filter) {\r
- if (itemsFilter != null) {\r
- if (itemsFilter.matchItem(item)) {\r
- if (itemsFilter.isConsistentItem(item)) {\r
- this.items.add(item);\r
- } else {\r
- this.selectionHistory.remove(item);\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Refresh dialog.\r
- */\r
- public void refresh() {\r
- scheduleRefresh();\r
- }\r
-\r
- /**\r
- * Removes items from history and refreshes the view.\r
- * \r
- * @param item\r
- * to remove\r
- * \r
- * @return removed item\r
- */\r
- public Object removeHistoryElement(Object item) {\r
- if (this.selectionHistory != null)\r
- this.selectionHistory.remove(item);\r
- if (filter == null || filter.getPattern().length() == 0) {\r
- items.remove(item);\r
- duplicates.remove(item);\r
- this.lastSortedItems.remove(item);\r
- }\r
-\r
- synchronized (lastSortedItems) {\r
- Collections.sort(lastSortedItems, getHistoryComparator());\r
- }\r
- return item;\r
- }\r
-\r
- /**\r
- * Adds item to history and refresh view.\r
- * \r
- * @param item\r
- * to add\r
- */\r
- public void addHistoryElement(Object item) {\r
- if (this.selectionHistory != null)\r
- this.selectionHistory.accessed(item);\r
- if (filter == null || !filter.matchItem(item)) {\r
- this.items.remove(item);\r
- this.duplicates.remove(item);\r
- this.lastSortedItems.remove(item);\r
- }\r
- synchronized (lastSortedItems) {\r
- Collections.sort(lastSortedItems, getHistoryComparator());\r
- }\r
- this.refresh();\r
- }\r
-\r
- /**\r
- * @param item\r
- * @return <code>true</code> if given item is part of the history\r
- */\r
- public boolean isHistoryElement(Object item) {\r
- if (this.selectionHistory != null) {\r
- return this.selectionHistory.contains(item);\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Sets/unsets given item as duplicate.\r
- * \r
- * @param item\r
- * item to change\r
- * \r
- * @param isDuplicate\r
- * duplicate flag\r
- */\r
- public void setDuplicateElement(Object item, boolean isDuplicate) {\r
- if (this.items.contains(item)) {\r
- if (isDuplicate)\r
- this.duplicates.add(item);\r
- else\r
- this.duplicates.remove(item);\r
- }\r
- }\r
-\r
- /**\r
- * Indicates whether given item is a duplicate.\r
- * \r
- * @param item\r
- * item to check\r
- * @return <code>true</code> if item is duplicate\r
- */\r
- public boolean isDuplicateElement(Object item) {\r
- return duplicates.contains(item);\r
- }\r
-\r
- /**\r
- * Load history from memento.\r
- * \r
- * @param memento\r
- * memento from which the history will be retrieved\r
- */\r
- public void loadHistory(IMemento memento) {\r
- if (this.selectionHistory != null)\r
- this.selectionHistory.load(memento);\r
- }\r
-\r
- /**\r
- * Save history to memento.\r
- * \r
- * @param memento\r
- * memento to which the history will be added\r
- */\r
- public void saveHistory(IMemento memento) {\r
- if (this.selectionHistory != null)\r
- this.selectionHistory.save(memento);\r
- }\r
-\r
- /**\r
- * Gets sorted items.\r
- * \r
- * @return sorted items\r
- */\r
- private Object[] getSortedItems() {\r
- if (lastSortedItems.size() != items.size()) {\r
- synchronized (lastSortedItems) {\r
- lastSortedItems.clear();\r
- lastSortedItems.addAll(items);\r
- Collections.sort(lastSortedItems, getHistoryComparator());\r
- }\r
- }\r
- return lastSortedItems.toArray();\r
- }\r
-\r
- /**\r
- * Remember result of filtering.\r
- * \r
- * @param itemsFilter\r
- */\r
- public void rememberResult(ItemsFilter itemsFilter) {\r
- List itemsList = Collections.synchronizedList(Arrays\r
- .asList(getSortedItems()));\r
- // synchronization\r
- if (itemsFilter == filter) {\r
- lastCompletedFilter = itemsFilter;\r
- lastCompletedResult = itemsList;\r
- }\r
-\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)\r
- */\r
- public Object[] getElements(Object inputElement) {\r
- return lastFilteredItems.toArray();\r
- }\r
-\r
- public int getNumberOfElements() {\r
- return lastFilteredItems.size();\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IContentProvider#dispose()\r
- */\r
- public void dispose() {\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,\r
- * java.lang.Object, java.lang.Object)\r
- */\r
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)\r
- */\r
- public void updateElement(int index) {\r
-\r
- ColumnFilteredItemsSelectionDialog.this.viewer.replace((lastFilteredItems\r
- .size() > index) ? lastFilteredItems.get(index) : null,\r
- index);\r
-\r
- }\r
-\r
- /**\r
- * Main method responsible for getting the filtered items and checking\r
- * for duplicates. It is based on the\r
- * {@link ColumnFilteredItemsSelectionDialog.ContentProvider#getFilteredItems(Object, IProgressMonitor)}.\r
- * \r
- * @param checkDuplicates\r
- * <code>true</code> if data concerning elements\r
- * duplication should be computed - it takes much more time\r
- * than standard filtering\r
- * \r
- * @param monitor\r
- * progress monitor\r
- */\r
- public void reloadCache(boolean checkDuplicates,\r
- IProgressMonitor monitor) {\r
-\r
- reset = false;\r
-\r
- if (monitor != null) {\r
- // the work is divided into two actions of the same length\r
- int totalWork = checkDuplicates ? 200 : 100;\r
-\r
- monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob, totalWork);\r
- }\r
-\r
- // the TableViewer's root (the input) is treated as parent\r
-\r
- lastFilteredItems = Arrays.asList(getFilteredItems(viewer.getInput(),\r
- monitor != null ? new SubProgressMonitor(monitor, 100) : null));\r
-\r
- if (reset || (monitor != null && monitor.isCanceled())) {\r
- if (monitor != null)\r
- monitor.done();\r
- return;\r
- }\r
-\r
- if (checkDuplicates) {\r
- checkDuplicates(monitor);\r
- }\r
- if (monitor != null)\r
- monitor.done();\r
- }\r
-\r
- private void checkDuplicates(IProgressMonitor monitor) {\r
- synchronized (lastFilteredItems) {\r
- IProgressMonitor subMonitor = null;\r
- int reportEvery = lastFilteredItems.size() / 20;\r
- if (monitor != null) {\r
- subMonitor = new SubProgressMonitor(monitor, 100);\r
- subMonitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates, 5);\r
- }\r
- HashMap helperMap = new HashMap();\r
- for (int i = 0; i < lastFilteredItems.size(); i++) {\r
- if (reset || (subMonitor != null && subMonitor.isCanceled()))\r
- return;\r
- Object item = lastFilteredItems.get(i);\r
-\r
- if (!(item instanceof ItemsListSeparator)) {\r
- Object previousItem = helperMap.put(getElementName(item), item);\r
- if (previousItem != null) {\r
- setDuplicateElement(previousItem, true);\r
- setDuplicateElement(item, true);\r
- } else {\r
- setDuplicateElement(item, false);\r
- }\r
- }\r
-\r
- if (subMonitor != null && reportEvery != 0\r
- && (i + 1) % reportEvery == 0)\r
- subMonitor.worked(1);\r
- }\r
- helperMap.clear();\r
- }\r
- }\r
-\r
- /**\r
- * Returns an array of items filtered using the provided\r
- * <code>ViewerFilter</code>s with a separator added.\r
- * \r
- * @param parent\r
- * the parent\r
- * @param monitor\r
- * progress monitor, can be <code>null</code>\r
- * @return an array of filtered items\r
- */\r
- protected Object[] getFilteredItems(Object parent,\r
- IProgressMonitor monitor) {\r
- int ticks = 100;\r
- if (monitor == null) {\r
- monitor = new NullProgressMonitor();\r
- }\r
-\r
- monitor.beginTask(WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements, ticks);\r
- if (filters != null) {\r
- ticks /= (filters.size() + 2);\r
- } else {\r
- ticks /= 2;\r
- }\r
-\r
- // get already sorted array\r
- Object[] filteredElements = getSortedItems();\r
-\r
- monitor.worked(ticks);\r
-\r
- // filter the elements using provided ViewerFilters\r
- if (filters != null && filteredElements != null) {\r
- for (Iterator iter = filters.iterator(); iter.hasNext();) {\r
- ViewerFilter f = (ViewerFilter) iter.next();\r
- filteredElements = f.filter(viewer, parent, filteredElements);\r
- monitor.worked(ticks);\r
- }\r
- }\r
-\r
- if (filteredElements == null || monitor.isCanceled()) {\r
- monitor.done();\r
- return new Object[0];\r
- }\r
-\r
- ArrayList preparedElements = new ArrayList();\r
- boolean hasHistory = false;\r
-\r
- if (filteredElements.length > 0) {\r
- if (isHistoryElement(filteredElements[0])) {\r
- hasHistory = true;\r
- }\r
- }\r
-\r
- int reportEvery = filteredElements.length / ticks;\r
-\r
- // add separator\r
- for (int i = 0; i < filteredElements.length; i++) {\r
- Object item = filteredElements[i];\r
-\r
- if (hasHistory && !isHistoryElement(item)) {\r
- preparedElements.add(itemsListSeparator);\r
- hasHistory = false;\r
- }\r
-\r
- preparedElements.add(item);\r
-\r
- if (reportEvery != 0 && ((i + 1) % reportEvery == 0)) {\r
- monitor.worked(1);\r
- }\r
- }\r
-\r
- monitor.done();\r
-\r
- return preparedElements.toArray();\r
- }\r
-\r
- /**\r
- * Adds a filter to this content provider. For an example usage of such\r
- * filters look at the project <code>org.eclipse.ui.ide</code>, class\r
- * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.\r
- * \r
- * \r
- * @param filter\r
- * the filter to be added\r
- */\r
- public void addFilter(ViewerFilter filter) {\r
- if (filters == null) {\r
- filters = new ArrayList();\r
- }\r
- filters.add(filter);\r
- // currently filters are only added when dialog is restored\r
- // if it is changed, refreshing the whole TableViewer should be\r
- // added\r
- }\r
-\r
- }\r
-\r
- /**\r
- * A content provider that does nothing.\r
- */\r
- private class NullContentProvider implements IContentProvider {\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IContentProvider#dispose()\r
- */\r
- public void dispose() {\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,\r
- * java.lang.Object, java.lang.Object)\r
- */\r
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
- }\r
-\r
- }\r
-\r
- /**\r
- * DetailsContentViewer objects are wrappers for labels.\r
- * DetailsContentViewer provides means to change label's image and text when\r
- * the attached LabelProvider is updated.\r
- */\r
- private class DetailsContentViewer extends ContentViewer {\r
-\r
- private CLabel label;\r
-\r
- /**\r
- * Unfortunately, it was impossible to delegate displaying border to\r
- * label. The <code>ViewForm</code> is used because\r
- * <code>CLabel</code> displays shadow when border is present.\r
- */\r
- private ViewForm viewForm;\r
-\r
- /**\r
- * Constructs a new instance of this class given its parent and a style\r
- * value describing its behavior and appearance.\r
- * \r
- * @param parent\r
- * the parent component\r
- * @param style\r
- * SWT style bits\r
- */\r
- public DetailsContentViewer(Composite parent, int style) {\r
- viewForm = new ViewForm(parent, style);\r
- GridData gd = new GridData(GridData.FILL_HORIZONTAL);\r
- gd.horizontalSpan = 2;\r
- viewForm.setLayoutData(gd);\r
- label = new CLabel(viewForm, SWT.FLAT);\r
- label.setFont(parent.getFont());\r
- viewForm.setContent(label);\r
- hookControl(label);\r
- }\r
-\r
- /**\r
- * Shows/hides the content viewer.\r
- * \r
- * @param visible\r
- * if the content viewer should be visible.\r
- */\r
- public void setVisible(boolean visible) {\r
- GridData gd = (GridData) viewForm.getLayoutData();\r
- gd.exclude = !visible;\r
- viewForm.getParent().layout();\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,\r
- * java.lang.Object)\r
- */\r
- protected void inputChanged(Object input, Object oldInput) {\r
- if (oldInput == null) {\r
- if (input == null) {\r
- return;\r
- }\r
- refresh();\r
- return;\r
- }\r
-\r
- refresh();\r
-\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)\r
- */\r
- protected void handleLabelProviderChanged(\r
- LabelProviderChangedEvent event) {\r
- if (event != null) {\r
- refresh(event.getElements());\r
- }\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.Viewer#getControl()\r
- */\r
- public Control getControl() {\r
- return label;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.Viewer#getSelection()\r
- */\r
- public ISelection getSelection() {\r
- // not supported\r
- return null;\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.Viewer#refresh()\r
- */\r
- public void refresh() {\r
- Object input = this.getInput();\r
- if (input != null) {\r
- ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();\r
- doRefresh(labelProvider.getText(input), labelProvider\r
- .getImage(input));\r
- } else {\r
- doRefresh(null, null);\r
- }\r
- }\r
-\r
- /**\r
- * Sets the given text and image to the label.\r
- * \r
- * @param text\r
- * the new text or null\r
- * @param image\r
- * the new image\r
- */\r
- private void doRefresh(String text, Image image) {\r
- if ( text != null ) {\r
- text = LegacyActionTools.escapeMnemonics(text);\r
- }\r
- label.setText(text);\r
- label.setImage(image);\r
- }\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,\r
- * boolean)\r
- */\r
- public void setSelection(ISelection selection, boolean reveal) {\r
- // not supported\r
- }\r
-\r
- /**\r
- * Refreshes the label if currently chosen element is on the list.\r
- * \r
- * @param objs\r
- * list of changed object\r
- */\r
- private void refresh(Object[] objs) {\r
- if (objs == null || getInput() == null) {\r
- return;\r
- }\r
- Object input = getInput();\r
- for (int i = 0; i < objs.length; i++) {\r
- if (objs[i].equals(input)) {\r
- refresh();\r
- break;\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Compares items according to the history.\r
- */\r
- private class HistoryComparator implements Comparator {\r
-\r
- /*\r
- * (non-Javadoc)\r
- * \r
- * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)\r
- */\r
- public int compare(Object o1, Object o2) {\r
- boolean h1 = isHistoryElement(o1);\r
- boolean h2 = isHistoryElement(o2);\r
- if (h1 == h2)\r
- return getItemsComparator().compare(o1, o2);\r
-\r
- if (h1)\r
- return -2;\r
- if (h2)\r
- return +2;\r
-\r
- return 0;\r
- }\r
-\r
- }\r
- \r
-\r
- /**\r
- * Get the control where the search pattern is entered. Any filtering should\r
- * be done using an {@link ItemsFilter}. This control should only be\r
- * accessed for listeners that wish to handle events that do not affect\r
- * filtering such as custom traversal.\r
- * \r
- * @return Control or <code>null</code> if the pattern control has not\r
- * been created.\r
- */\r
- public Control getPatternControl() {\r
- return pattern;\r
- }\r
-\r
-}\r
-\r
+/*******************************************************************************
+ * 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 <wmitsuda@gmail.com>
+ * - Fix for bug 196553 - [Dialogs] Support IColorProvider/IFontProvider in FilteredItemsSelectionDialog
+ * Peter Friese <peter.friese@gentleware.com>
+ * - Fix for bug 208602 - [Dialogs] Open Type dialog needs accessible labels
+ * Simon Muschel <smuschel@gmx.de> - 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 <code>setDetailsLabelProvider()</code>
+ * 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.
+ * <p>
+ * Subclasses may extend in order to add other actions.</p>
+ *
+ * @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 <code>handleDoubleClick()</code> method handles double clicks on
+ * the list of filtered elements.
+ * <p>
+ * Current implementation makes double-clicking on the list do the same as
+ * pressing <code>OK</code> 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.
+ * <p>
+ * This is a long running operation and should be called in a job.
+ *
+ * @param checkDuplicates
+ * <code>true</code> if data concerning elements duplication
+ * should be computed - it takes much more time than the standard
+ * filtering
+ * @param monitor
+ * a progress monitor or <code>null</code> 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 <code>selectionMode</code> 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 <code>null</code> 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 <code>null</code>,
+ * no filtering will be applied then, causing no item to be shown in
+ * the list.
+ */
+ protected abstract ItemsFilter createFilter();
+
+ /**
+ * Applies the filter created by <code>createFilter()</code> 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
+ * <code>compare(java.lang.Object, java.lang.Object)</code> 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 <code>itemsFilter</code>.
+ * @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 <code>null</code> 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 <code>true</code> if the given item exists in history,
+ * <code>false</code> 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 <code>true</code> if the item is duplicate, <code>false</code>
+ * 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 <code>instanceof</code> 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.
+ * <p>
+ * Standard invocation scenario:
+ * <ol>
+ * <li>filtering job (<code>FilterJob</code> class extending
+ * <code>Job</code> class)</li>
+ * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>
+ * class extending <code>Job</code> class)</li>
+ * <li>UI refresh (<code>RefreshJob</code> class extending
+ * <code>UIJob</code> class)</li>
+ * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>
+ * class extending <code>Job</code> class)</li>
+ * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>
+ * class)</li>
+ * </ol>
+ * The scenario is rather complicated, but it had to be applied, because:
+ * <ul>
+ * <li> refreshing cache is rather a long action and cannot be run in the UI -
+ * cannot be run in a UIJob</li>
+ * <li> refreshing cache checking for duplicates is twice as long as
+ * refreshing cache without checking for duplicates; results of the search
+ * could be displayed earlier</li>
+ * <li> refreshing the UI have to be run in a UIJob</li>
+ * </ul>
+ *
+ * @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.
+ * <code>RefreshProgressMessageJob</code> is strictly connected with
+ * <code>GranualProgressMonitor</code> 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
+ * <code>RefreshJob</code>.
+ *
+ * @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 <code>null</code>
+ * @param selectionDecorator
+ * the decorator for selected items, can be <code>null</code>
+ */
+ 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
+ * <code>null</code>
+ */
+ 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 <code>RefreshProgressMessageJob</code> 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 <code>GranualProgressMonitor</code>.
+ *
+ * @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, <code>FilterJob</code> decides which kind of
+ * search will be run inside <code>filterContent</code>. 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 <code>FilterJob</code> 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
+ * <code>accessed(Object)</code>.
+ * <p>
+ * 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 <code>SelectionHistory</code>.
+ */
+ 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 <code>true</code> if history contains object.
+ *
+ * @param object
+ * the item for which check will be executed
+ * @return <code>true</code> if history contains object
+ * <code>false</code> in other way
+ */
+ public synchronized boolean contains(Object object) {
+ return historyList.contains(object);
+ }
+
+ /**
+ * Returns <code>true</code> if history is empty.
+ *
+ * @return <code>true</code> if history is empty
+ */
+ public synchronized boolean isEmpty() {
+ return historyList.isEmpty();
+ }
+
+ /**
+ * Remove element from history.
+ *
+ * @param element
+ * to remove form the history
+ * @return <code>true</code> 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 <code>IMemento</code>.
+ *
+ * @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 <code>SearchPattern</code> from the
+ * given filter is a sub-pattern of the one from this filter.
+ * <p>
+ * <i>WARNING: This method is <b>not</b> defined in reading order, i.e.
+ * <code>a.isSubFilter(b)</code> is <code>true</code> iff
+ * <code>b</code> is a sub-filter of <code>a</code>, and not
+ * vice-versa. </i>
+ * </p>
+ *
+ * @param filter
+ * the filter to be checked, or <code>null</code>
+ * @return <code>true</code> if the given filter is sub-filter of this
+ * filter, <code>false</code> if the given filter isn't a
+ * sub-filter or is <code>null</code>
+ *
+ * @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 <code>SearchPattern</code>
+ * from current filter is equal to the one from provided filter.
+ *
+ * @param filter
+ * filter to be checked, or <code>null</code>
+ * @return <code>true</code> if the given filter is equal to current
+ * filter, <code>false</code> if given filter isn't equal to
+ * current one or if it is <code>null</code>
+ *
+ * @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 <code>true</code> if pattern's match rule is camel case,
+ * <code>false</code> 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 <code>true</code> if text matches with filter pattern,
+ * <code>false</code> 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 <code>true</code> if current pattern is a prefix of name
+ * provided item, <code>false</code> 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 <code>true<code> if item matches against filter conditions, <code>false</code>
+ * otherwise
+ */
+ public abstract boolean matchItem(Object item);
+
+ /**
+ * Checks consistency of an item. Item is inconsistent if was changed or
+ * removed.
+ *
+ * @param item
+ * @return <code>true</code> if item is consistent, <code>false</code>
+ * if item is inconsistent
+ */
+ public abstract boolean isConsistentItem(Object item);
+
+ }
+
+ /**
+ * An interface to content providers for
+ * <code>FilterItemsSelectionDialog</code>.
+ */
+ 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 <code>ItemsFilter</code> is used to filter elements.
+ * The key function of filter used in to filtering is
+ * <code>matchElement(Object item)</code>.
+ * <p>
+ * The <code>ContentProvider</code> class also provides item filtering
+ * methods. The filtering has been moved from the standard TableView
+ * <code>getFilteredItems()</code> method to content provider, because
+ * <code>ILazyContentProvider</code> 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).
+ * <p>
+ * Standard object flow:
+ * <code>items -> lastSortedItems -> lastFilteredItems</code>
+ */
+ private Set items;
+
+ /**
+ * Items that are duplicates.
+ */
+ private Set duplicates;
+
+ /**
+ * List of <code>ViewerFilter</code>s to be used during filtering
+ */
+ private List filters;
+
+ /**
+ * Result of the last filtering.
+ * <p>
+ * Standard object flow:
+ * <code>items -> lastSortedItems -> lastFilteredItems</code>
+ */
+ private List lastFilteredItems;
+
+ /**
+ * Result of the last sorting.
+ * <p>
+ * Standard object flow:
+ * <code>items -> lastSortedItems -> lastFilteredItems</code>
+ */
+ private List lastSortedItems;
+
+ /**
+ * Used for <code>getFilteredItems()</code> method canceling (when the
+ * job that invoked the method was canceled).
+ * <p>
+ * Method canceling could be based (only) on monitor canceling
+ * unfortunately sometimes the method <code>getFilteredElements()</code>
+ * could be run with a null monitor, the <code>reset</code> flag have
+ * to be left intact.
+ */
+ private boolean reset;
+
+ /**
+ * Creates new instance of <code>ContentProvider</code>.
+ */
+ 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 - <code>getFilteredItems()</code> 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 <code>contentProvider</code>.
+ *
+ * @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 <code>true</code> 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 <code>true</code> 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
+ * <code>true</code> 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
+ * <code>ViewerFilter</code>s with a separator added.
+ *
+ * @param parent
+ * the parent
+ * @param monitor
+ * progress monitor, can be <code>null</code>
+ * @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 <code>org.eclipse.ui.ide</code>, class
+ * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.
+ *
+ *
+ * @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 <code>ViewForm</code> is used because
+ * <code>CLabel</code> 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 <code>null</code> if the pattern control has not
+ * been created.
+ */
+ public Control getPatternControl() {
+ return pattern;
+ }
+
+}
+