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

* Subclasses may extend in order to add other actions.

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

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

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

* Standard invocation scenario: *

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

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

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

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

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

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

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

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

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