add new class CdmFilteredSelectionDialog
authorKatja Luther <k.luther@bgbm.org>
Fri, 10 Jun 2016 12:15:44 +0000 (14:15 +0200)
committerKatja Luther <k.luther@bgbm.org>
Fri, 10 Jun 2016 12:19:21 +0000 (14:19 +0200)
eu.etaxonomy.taxeditor.store/src/main/java/eu/etaxonomy/taxeditor/ui/dialog/selection/CdmFilteredItemsSelectionDialog.java [new file with mode: 0644]

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