]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/dialogs/ColumnFilteredItemsSelectionDialog.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / dialogs / ColumnFilteredItemsSelectionDialog.java
diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/dialogs/ColumnFilteredItemsSelectionDialog.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/dialogs/ColumnFilteredItemsSelectionDialog.java
new file mode 100644 (file)
index 0000000..e9be6d8
--- /dev/null
@@ -0,0 +1,3314 @@
+/*******************************************************************************\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