0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: * Willian Mitsuda <wmitsuda@gmail.com>
0011: * - Fix for bug 196553 - [Dialogs] Support IColorProvider/IFontProvider in FilteredItemsSelectionDialog
0012:
0013: *******************************************************************************/package org.eclipse.ui.dialogs;
0014:
0015: import java.io.IOException;
0016: import java.io.StringReader;
0017: import java.io.StringWriter;
0018: import java.util.ArrayList;
0019: import java.util.Arrays;
0020: import java.util.Collections;
0021: import java.util.Comparator;
0022: import java.util.HashMap;
0023: import java.util.HashSet;
0024: import java.util.Iterator;
0025: import java.util.LinkedList;
0026: import java.util.List;
0027: import java.util.Set;
0028:
0029: import org.eclipse.core.commands.AbstractHandler;
0030: import org.eclipse.core.commands.ExecutionEvent;
0031: import org.eclipse.core.commands.IHandler;
0032: import org.eclipse.core.runtime.Assert;
0033: import org.eclipse.core.runtime.CoreException;
0034: import org.eclipse.core.runtime.IProgressMonitor;
0035: import org.eclipse.core.runtime.IStatus;
0036: import org.eclipse.core.runtime.ListenerList;
0037: import org.eclipse.core.runtime.ProgressMonitorWrapper;
0038: import org.eclipse.core.runtime.Status;
0039: import org.eclipse.core.runtime.SubProgressMonitor;
0040: import org.eclipse.core.runtime.jobs.Job;
0041: import org.eclipse.jface.action.Action;
0042: import org.eclipse.jface.action.ActionContributionItem;
0043: import org.eclipse.jface.action.IAction;
0044: import org.eclipse.jface.action.IMenuListener;
0045: import org.eclipse.jface.action.IMenuManager;
0046: import org.eclipse.jface.action.MenuManager;
0047: import org.eclipse.jface.dialogs.IDialogSettings;
0048: import org.eclipse.jface.viewers.ContentViewer;
0049: import org.eclipse.jface.viewers.DoubleClickEvent;
0050: import org.eclipse.jface.viewers.IColorProvider;
0051: import org.eclipse.jface.viewers.IContentProvider;
0052: import org.eclipse.jface.viewers.IDoubleClickListener;
0053: import org.eclipse.jface.viewers.IFontProvider;
0054: import org.eclipse.jface.viewers.ILabelDecorator;
0055: import org.eclipse.jface.viewers.ILabelProvider;
0056: import org.eclipse.jface.viewers.ILabelProviderListener;
0057: import org.eclipse.jface.viewers.ILazyContentProvider;
0058: import org.eclipse.jface.viewers.ISelection;
0059: import org.eclipse.jface.viewers.ISelectionChangedListener;
0060: import org.eclipse.jface.viewers.IStructuredContentProvider;
0061: import org.eclipse.jface.viewers.LabelProvider;
0062: import org.eclipse.jface.viewers.LabelProviderChangedEvent;
0063: import org.eclipse.jface.viewers.SelectionChangedEvent;
0064: import org.eclipse.jface.viewers.StructuredSelection;
0065: import org.eclipse.jface.viewers.TableViewer;
0066: import org.eclipse.jface.viewers.Viewer;
0067: import org.eclipse.jface.viewers.ViewerFilter;
0068: import org.eclipse.osgi.util.NLS;
0069: import org.eclipse.swt.SWT;
0070: import org.eclipse.swt.custom.CLabel;
0071: import org.eclipse.swt.custom.ViewForm;
0072: import org.eclipse.swt.events.KeyAdapter;
0073: import org.eclipse.swt.events.KeyEvent;
0074: import org.eclipse.swt.events.ModifyEvent;
0075: import org.eclipse.swt.events.ModifyListener;
0076: import org.eclipse.swt.events.MouseAdapter;
0077: import org.eclipse.swt.events.MouseEvent;
0078: import org.eclipse.swt.events.SelectionAdapter;
0079: import org.eclipse.swt.events.SelectionEvent;
0080: import org.eclipse.swt.events.TraverseEvent;
0081: import org.eclipse.swt.events.TraverseListener;
0082: import org.eclipse.swt.graphics.Color;
0083: import org.eclipse.swt.graphics.Font;
0084: import org.eclipse.swt.graphics.GC;
0085: import org.eclipse.swt.graphics.Image;
0086: import org.eclipse.swt.graphics.Point;
0087: import org.eclipse.swt.graphics.Rectangle;
0088: import org.eclipse.swt.layout.GridData;
0089: import org.eclipse.swt.layout.GridLayout;
0090: import org.eclipse.swt.widgets.Composite;
0091: import org.eclipse.swt.widgets.Control;
0092: import org.eclipse.swt.widgets.Display;
0093: import org.eclipse.swt.widgets.Event;
0094: import org.eclipse.swt.widgets.Label;
0095: import org.eclipse.swt.widgets.Menu;
0096: import org.eclipse.swt.widgets.Shell;
0097: import org.eclipse.swt.widgets.Text;
0098: import org.eclipse.swt.widgets.ToolBar;
0099: import org.eclipse.swt.widgets.ToolItem;
0100: import org.eclipse.ui.ActiveShellExpression;
0101: import org.eclipse.ui.IMemento;
0102: import org.eclipse.ui.PlatformUI;
0103: import org.eclipse.ui.WorkbenchException;
0104: import org.eclipse.ui.XMLMemento;
0105: import org.eclipse.ui.handlers.IHandlerActivation;
0106: import org.eclipse.ui.handlers.IHandlerService;
0107: import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
0108: import org.eclipse.ui.internal.WorkbenchImages;
0109: import org.eclipse.ui.internal.WorkbenchMessages;
0110: import org.eclipse.ui.internal.WorkbenchPlugin;
0111: import org.eclipse.ui.progress.UIJob;
0112: import org.eclipse.ui.statushandlers.StatusManager;
0113:
0114: /**
0115: * Shows a list of items to the user with a text entry field for a string
0116: * pattern used to filter the list of items.
0117: *
0118: * @since 3.3
0119: */
0120: public abstract class FilteredItemsSelectionDialog extends
0121: SelectionStatusDialog {
0122:
0123: private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$
0124:
0125: private static final String SHOW_STATUS_LINE = "ShowStatusLine"; //$NON-NLS-1$
0126:
0127: private static final String HISTORY_SETTINGS = "History"; //$NON-NLS-1$
0128:
0129: private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
0130:
0131: private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
0132:
0133: /**
0134: * Represents an empty selection in the pattern input field (used only for
0135: * initial pattern).
0136: */
0137: public static final int NONE = 0;
0138:
0139: /**
0140: * Pattern input field selection where caret is at the beginning (used only
0141: * for initial pattern).
0142: */
0143: public static final int CARET_BEGINNING = 1;
0144:
0145: /**
0146: * Represents a full selection in the pattern input field (used only for
0147: * initial pattern).
0148: */
0149: public static final int FULL_SELECTION = 2;
0150:
0151: private Text pattern;
0152:
0153: private TableViewer list;
0154:
0155: private DetailsContentViewer details;
0156:
0157: /**
0158: * It is a duplicate of a field in the CLabel class in DetailsContentViewer.
0159: * It is maintained, because the <code>setDetailsLabelProvider()</code>
0160: * could be called before content area is created.
0161: */
0162: private ILabelProvider detailsLabelProvider;
0163:
0164: private ItemsListLabelProvider itemsListLabelProvider;
0165:
0166: private MenuManager menuManager;
0167:
0168: private boolean multi;
0169:
0170: private ToolBar toolBar;
0171:
0172: private ToolItem toolItem;
0173:
0174: private Label progressLabel;
0175:
0176: private ToggleStatusLineAction toggleStatusLineAction;
0177:
0178: private RemoveHistoryItemAction removeHistoryItemAction;
0179:
0180: private ActionContributionItem removeHistoryActionContributionItem;
0181:
0182: private IStatus status;
0183:
0184: private RefreshCacheJob refreshCacheJob;
0185:
0186: private RefreshProgressMessageJob refreshProgressMessageJob = new RefreshProgressMessageJob();
0187:
0188: private Object[] lastSelection;
0189:
0190: private ContentProvider contentProvider;
0191:
0192: private FilterHistoryJob filterHistoryJob;
0193:
0194: private FilterJob filterJob;
0195:
0196: private ItemsFilter filter;
0197:
0198: private List lastCompletedResult;
0199:
0200: private ItemsFilter lastCompletedFilter;
0201:
0202: private String initialPatternText;
0203:
0204: private int selectionMode;
0205:
0206: private ItemsListSeparator itemsListSeparator;
0207:
0208: private static final String EMPTY_STRING = ""; //$NON-NLS-1$
0209:
0210: private boolean refreshWithLastSelection = false;
0211:
0212: private IHandlerActivation showViewHandler;
0213:
0214: /**
0215: * Creates a new instance of the class.
0216: *
0217: * @param shell
0218: * shell to parent the dialog on
0219: * @param multi
0220: * indicates whether dialog allows to select more than one
0221: * position in its list of items
0222: */
0223: public FilteredItemsSelectionDialog(Shell shell, boolean multi) {
0224: super (shell);
0225: this .multi = multi;
0226: filterHistoryJob = new FilterHistoryJob();
0227: filterJob = new FilterJob();
0228: contentProvider = new ContentProvider();
0229: refreshCacheJob = new RefreshCacheJob();
0230: itemsListSeparator = new ItemsListSeparator(
0231: WorkbenchMessages.FilteredItemsSelectionDialog_separatorLabel);
0232: selectionMode = NONE;
0233: }
0234:
0235: /**
0236: * Creates a new instance of the class. Created dialog won't allow to select
0237: * more than one item.
0238: *
0239: * @param shell
0240: * shell to parent the dialog on
0241: */
0242: public FilteredItemsSelectionDialog(Shell shell) {
0243: this (shell, false);
0244: }
0245:
0246: /**
0247: * Adds viewer filter to the dialog items list.
0248: *
0249: * @param filter
0250: * the new filter
0251: */
0252: protected void addListFilter(ViewerFilter filter) {
0253: contentProvider.addFilter(filter);
0254: }
0255:
0256: /**
0257: * Sets a new label provider for items in the list.
0258: *
0259: * @param listLabelProvider
0260: * the label provider for items in the list
0261: */
0262: public void setListLabelProvider(ILabelProvider listLabelProvider) {
0263: getItemsListLabelProvider().setProvider(listLabelProvider);
0264: }
0265:
0266: /**
0267: * Returns the label decorator for selected items in the list.
0268: *
0269: * @return the label decorator for selected items in the list
0270: */
0271: private ILabelDecorator getListSelectionLabelDecorator() {
0272: return getItemsListLabelProvider().getSelectionDecorator();
0273: }
0274:
0275: /**
0276: * Sets the label decorator for selected items in the list.
0277: *
0278: * @param listSelectionLabelDecorator
0279: * the label decorator for selected items in the list
0280: */
0281: public void setListSelectionLabelDecorator(
0282: ILabelDecorator listSelectionLabelDecorator) {
0283: getItemsListLabelProvider().setSelectionDecorator(
0284: listSelectionLabelDecorator);
0285: }
0286:
0287: /**
0288: * Returns the item list label provider.
0289: *
0290: * @return the item list label provider
0291: */
0292: private ItemsListLabelProvider getItemsListLabelProvider() {
0293: if (itemsListLabelProvider == null) {
0294: itemsListLabelProvider = new ItemsListLabelProvider(
0295: new LabelProvider(), null);
0296: }
0297: return itemsListLabelProvider;
0298: }
0299:
0300: /**
0301: * Sets label provider for the details field.
0302: *
0303: * For a single selection, the element sent to
0304: * {@link ILabelProvider#getImage(Object)} and
0305: * {@link ILabelProvider#getText(Object)} is the selected object, for
0306: * multiple selection a {@link String} with amount of selected items is the
0307: * element.
0308: *
0309: * @see #getSelectedItems() getSelectedItems() can be used to retrieve
0310: * selected items and get the items count.
0311: *
0312: * @param detailsLabelProvider
0313: * the label provider for the details field
0314: */
0315: public void setDetailsLabelProvider(
0316: ILabelProvider detailsLabelProvider) {
0317: this .detailsLabelProvider = detailsLabelProvider;
0318: if (details != null) {
0319: details.setLabelProvider(detailsLabelProvider);
0320: }
0321: }
0322:
0323: private ILabelProvider getDetailsLabelProvider() {
0324: if (detailsLabelProvider == null) {
0325: detailsLabelProvider = new LabelProvider();
0326: }
0327: return detailsLabelProvider;
0328: }
0329:
0330: /*
0331: * (non-Javadoc)
0332: *
0333: * @see org.eclipse.jface.window.Window#create()
0334: */
0335: public void create() {
0336: super .create();
0337: pattern.setFocus();
0338: }
0339:
0340: /**
0341: * Restores dialog using persisted settings. The default implementation
0342: * restores the status of the details line and the selection history.
0343: *
0344: * @param settings
0345: * settings used to restore dialog
0346: */
0347: protected void restoreDialog(IDialogSettings settings) {
0348: boolean toggleStatusLine = true;
0349:
0350: if (settings.get(SHOW_STATUS_LINE) != null) {
0351: toggleStatusLine = settings.getBoolean(SHOW_STATUS_LINE);
0352: }
0353:
0354: toggleStatusLineAction.setChecked(toggleStatusLine);
0355:
0356: details.setVisible(toggleStatusLine);
0357:
0358: String setting = settings.get(HISTORY_SETTINGS);
0359: if (setting != null) {
0360: try {
0361: IMemento memento = XMLMemento
0362: .createReadRoot(new StringReader(setting));
0363: this .contentProvider.loadHistory(memento);
0364: } catch (WorkbenchException e) {
0365: // Simply don't restore the settings
0366: StatusManager
0367: .getManager()
0368: .handle(
0369: new Status(
0370: IStatus.ERROR,
0371: PlatformUI.PLUGIN_ID,
0372: IStatus.ERROR,
0373: WorkbenchMessages.FilteredItemsSelectionDialog_restoreError,
0374: e));
0375: }
0376: }
0377: }
0378:
0379: /*
0380: * (non-Javadoc)
0381: *
0382: * @see org.eclipse.jface.window.Window#close()
0383: */
0384: public boolean close() {
0385: this .filterJob.cancel();
0386: this .refreshCacheJob.cancel();
0387: this .refreshProgressMessageJob.cancel();
0388: if (showViewHandler != null) {
0389: IHandlerService service = (IHandlerService) PlatformUI
0390: .getWorkbench().getService(IHandlerService.class);
0391: service.deactivateHandler(showViewHandler);
0392: showViewHandler.getHandler().dispose();
0393: showViewHandler = null;
0394: }
0395: storeDialog(getDialogSettings());
0396: return super .close();
0397: }
0398:
0399: /**
0400: * Stores dialog settings.
0401: *
0402: * @param settings
0403: * settings used to store dialog
0404: */
0405: protected void storeDialog(IDialogSettings settings) {
0406: settings.put(SHOW_STATUS_LINE, toggleStatusLineAction
0407: .isChecked());
0408:
0409: XMLMemento memento = XMLMemento
0410: .createWriteRoot(HISTORY_SETTINGS);
0411: this .contentProvider.saveHistory(memento);
0412: StringWriter writer = new StringWriter();
0413: try {
0414: memento.save(writer);
0415: settings.put(HISTORY_SETTINGS, writer.getBuffer()
0416: .toString());
0417: } catch (IOException e) {
0418: // Simply don't store the settings
0419: StatusManager
0420: .getManager()
0421: .handle(
0422: new Status(
0423: IStatus.ERROR,
0424: PlatformUI.PLUGIN_ID,
0425: IStatus.ERROR,
0426: WorkbenchMessages.FilteredItemsSelectionDialog_storeError,
0427: e));
0428: }
0429: }
0430:
0431: private void createHeader(Composite parent) {
0432: Composite header = new Composite(parent, SWT.NONE);
0433:
0434: GridLayout layout = new GridLayout();
0435: layout.numColumns = 2;
0436: layout.marginWidth = 0;
0437: layout.marginHeight = 0;
0438: header.setLayout(layout);
0439:
0440: Label label = new Label(header, SWT.NONE);
0441: label
0442: .setText((getMessage() != null && getMessage().trim()
0443: .length() > 0) ? getMessage()
0444: : WorkbenchMessages.FilteredItemsSelectionDialog_patternLabel);
0445: label.addTraverseListener(new TraverseListener() {
0446: public void keyTraversed(TraverseEvent e) {
0447: if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
0448: e.detail = SWT.TRAVERSE_NONE;
0449: pattern.setFocus();
0450: }
0451: }
0452: });
0453:
0454: GridData gd = new GridData(GridData.FILL_HORIZONTAL);
0455: label.setLayoutData(gd);
0456:
0457: createViewMenu(header);
0458: header.setLayoutData(gd);
0459: }
0460:
0461: private void createLabels(Composite parent) {
0462: Composite labels = new Composite(parent, SWT.NONE);
0463:
0464: GridLayout layout = new GridLayout();
0465: layout.numColumns = 2;
0466: layout.marginWidth = 0;
0467: layout.marginHeight = 0;
0468: labels.setLayout(layout);
0469:
0470: Label listLabel = new Label(labels, SWT.NONE);
0471: listLabel
0472: .setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel);
0473:
0474: listLabel.addTraverseListener(new TraverseListener() {
0475: public void keyTraversed(TraverseEvent e) {
0476: if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
0477: e.detail = SWT.TRAVERSE_NONE;
0478: list.getTable().setFocus();
0479: }
0480: }
0481: });
0482:
0483: GridData gd = new GridData(GridData.FILL_HORIZONTAL);
0484: listLabel.setLayoutData(gd);
0485:
0486: progressLabel = new Label(labels, SWT.RIGHT);
0487: progressLabel.setLayoutData(gd);
0488:
0489: labels.setLayoutData(gd);
0490: }
0491:
0492: private void createViewMenu(Composite parent) {
0493: toolBar = new ToolBar(parent, SWT.FLAT);
0494: toolItem = new ToolItem(toolBar, SWT.PUSH, 0);
0495:
0496: GridData data = new GridData();
0497: data.horizontalAlignment = GridData.END;
0498: toolBar.setLayoutData(data);
0499:
0500: toolBar.addMouseListener(new MouseAdapter() {
0501: public void mouseDown(MouseEvent e) {
0502: showViewMenu();
0503: }
0504: });
0505:
0506: toolItem
0507: .setImage(WorkbenchImages
0508: .getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU));
0509: toolItem
0510: .setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu);
0511: toolItem.addSelectionListener(new SelectionAdapter() {
0512: public void widgetSelected(SelectionEvent e) {
0513: showViewMenu();
0514: }
0515: });
0516:
0517: menuManager = new MenuManager();
0518:
0519: fillViewMenu(menuManager);
0520:
0521: IHandlerService service = (IHandlerService) PlatformUI
0522: .getWorkbench().getService(IHandlerService.class);
0523: IHandler handler = new AbstractHandler() {
0524: /* (non-Javadoc)
0525: * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
0526: */
0527: public Object execute(ExecutionEvent event) {
0528: showViewMenu();
0529: return null;
0530: }
0531: };
0532: showViewHandler = service.activateHandler(
0533: "org.eclipse.ui.window.showViewMenu", handler, //$NON-NLS-1$
0534: new ActiveShellExpression(getShell()));
0535: }
0536:
0537: /**
0538: * Fills the menu of the dialog.
0539: *
0540: * @param menuManager
0541: * the menu manager
0542: */
0543: protected void fillViewMenu(IMenuManager menuManager) {
0544: toggleStatusLineAction = new ToggleStatusLineAction();
0545: menuManager.add(toggleStatusLineAction);
0546: }
0547:
0548: private void showViewMenu() {
0549: Menu menu = menuManager.createContextMenu(getShell());
0550: Rectangle bounds = toolItem.getBounds();
0551: Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
0552: topLeft = toolBar.toDisplay(topLeft);
0553: menu.setLocation(topLeft.x, topLeft.y);
0554: menu.setVisible(true);
0555: }
0556:
0557: private void createPopupMenu() {
0558: removeHistoryItemAction = new RemoveHistoryItemAction();
0559: removeHistoryActionContributionItem = new ActionContributionItem(
0560: removeHistoryItemAction);
0561:
0562: MenuManager manager = new MenuManager();
0563: manager.add(removeHistoryActionContributionItem);
0564: manager.addMenuListener(new IMenuListener() {
0565: public void menuAboutToShow(IMenuManager manager) {
0566: List selectedElements = ((StructuredSelection) list
0567: .getSelection()).toList();
0568:
0569: Object item = null;
0570:
0571: manager.remove(removeHistoryActionContributionItem);
0572:
0573: for (Iterator it = selectedElements.iterator(); it
0574: .hasNext();) {
0575: item = it.next();
0576: if (item instanceof ItemsListSeparator
0577: || !isHistoryElement(item)) {
0578: return;
0579: }
0580: }
0581:
0582: if (selectedElements.size() > 0) {
0583: removeHistoryItemAction
0584: .setText(WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
0585:
0586: manager.add(removeHistoryActionContributionItem);
0587:
0588: }
0589: }
0590: });
0591:
0592: Menu menu = manager.createContextMenu(getShell());
0593: list.getTable().setMenu(menu);
0594: }
0595:
0596: /**
0597: * Creates an extra content area, which will be located above the details.
0598: *
0599: * @param parent
0600: * parent to create the dialog widgets in
0601: * @return an extra content area
0602: */
0603: protected abstract Control createExtendedContentArea(
0604: Composite parent);
0605:
0606: /*
0607: * (non-Javadoc)
0608: *
0609: * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
0610: */
0611: protected Control createDialogArea(Composite parent) {
0612: Composite dialogArea = (Composite) super
0613: .createDialogArea(parent);
0614:
0615: Composite content = new Composite(dialogArea, SWT.NONE);
0616: GridData gd = new GridData(GridData.FILL_BOTH);
0617: content.setLayoutData(gd);
0618:
0619: GridLayout layout = new GridLayout();
0620: layout.numColumns = 1;
0621: layout.marginWidth = 0;
0622: layout.marginHeight = 0;
0623: content.setLayout(layout);
0624:
0625: createHeader(content);
0626:
0627: pattern = new Text(content, SWT.SINGLE | SWT.BORDER);
0628: gd = new GridData(GridData.FILL_HORIZONTAL);
0629: pattern.setLayoutData(gd);
0630:
0631: createLabels(content);
0632:
0633: list = new TableViewer(content,
0634: (multi ? SWT.MULTI : SWT.SINGLE) | SWT.BORDER
0635: | SWT.V_SCROLL | SWT.VIRTUAL);
0636: list.setContentProvider(contentProvider);
0637: list.setLabelProvider(getItemsListLabelProvider());
0638: list.setInput(new Object[0]);
0639: list.setItemCount(contentProvider.getElements(null).length);
0640: gd = new GridData(GridData.FILL_BOTH);
0641: list.getTable().setLayoutData(gd);
0642:
0643: createPopupMenu();
0644:
0645: pattern.addModifyListener(new ModifyListener() {
0646: public void modifyText(ModifyEvent e) {
0647: applyFilter();
0648: }
0649: });
0650:
0651: pattern.addKeyListener(new KeyAdapter() {
0652: public void keyPressed(KeyEvent e) {
0653: if (e.keyCode == SWT.ARROW_DOWN) {
0654: if (list.getTable().getItemCount() > 0) {
0655: list.getTable().setFocus();
0656: }
0657: }
0658: }
0659: });
0660:
0661: list
0662: .addSelectionChangedListener(new ISelectionChangedListener() {
0663: public void selectionChanged(
0664: SelectionChangedEvent event) {
0665: StructuredSelection selection = (StructuredSelection) event
0666: .getSelection();
0667: handleSelected(selection);
0668: }
0669: });
0670:
0671: list.addDoubleClickListener(new IDoubleClickListener() {
0672: public void doubleClick(DoubleClickEvent event) {
0673: handleDoubleClick();
0674: }
0675: });
0676:
0677: list.getTable().addKeyListener(new KeyAdapter() {
0678: public void keyPressed(KeyEvent e) {
0679:
0680: if (e.keyCode == SWT.DEL) {
0681:
0682: List selectedElements = ((StructuredSelection) list
0683: .getSelection()).toList();
0684:
0685: Object item = null;
0686: boolean isSelectedHistory = true;
0687:
0688: for (Iterator it = selectedElements.iterator(); it
0689: .hasNext();) {
0690: item = it.next();
0691: if (item instanceof ItemsListSeparator
0692: || !isHistoryElement(item)) {
0693: isSelectedHistory = false;
0694: break;
0695: }
0696: }
0697: if (isSelectedHistory)
0698: removeSelectedItems(selectedElements);
0699:
0700: }
0701:
0702: if (e.keyCode == SWT.ARROW_UP
0703: && (e.stateMask & SWT.SHIFT) != 0
0704: && (e.stateMask & SWT.CTRL) != 0) {
0705: StructuredSelection selection = (StructuredSelection) list
0706: .getSelection();
0707:
0708: if (selection.size() == 1) {
0709: Object element = selection.getFirstElement();
0710: if (element.equals(list.getElementAt(0))) {
0711: pattern.setFocus();
0712: }
0713: if (list.getElementAt(list.getTable()
0714: .getSelectionIndex() - 1) instanceof ItemsListSeparator)
0715: list
0716: .getTable()
0717: .setSelection(
0718: list
0719: .getTable()
0720: .getSelectionIndex() - 1);
0721: list.getTable().notifyListeners(SWT.Selection,
0722: new Event());
0723:
0724: }
0725: }
0726:
0727: if (e.keyCode == SWT.ARROW_DOWN
0728: && (e.stateMask & SWT.SHIFT) != 0
0729: && (e.stateMask & SWT.CTRL) != 0) {
0730:
0731: if (list.getElementAt(list.getTable()
0732: .getSelectionIndex() + 1) instanceof ItemsListSeparator)
0733: list
0734: .getTable()
0735: .setSelection(
0736: list.getTable()
0737: .getSelectionIndex() + 1);
0738: list.getTable().notifyListeners(SWT.Selection,
0739: new Event());
0740: }
0741:
0742: }
0743: });
0744:
0745: createExtendedContentArea(content);
0746:
0747: details = new DetailsContentViewer(content, SWT.BORDER
0748: | SWT.FLAT);
0749: details.setVisible(toggleStatusLineAction.isChecked());
0750: details.setContentProvider(new NullContentProvider());
0751: details.setLabelProvider(getDetailsLabelProvider());
0752:
0753: applyDialogFont(content);
0754:
0755: restoreDialog(getDialogSettings());
0756:
0757: if (initialPatternText != null) {
0758: pattern.setText(initialPatternText);
0759: }
0760:
0761: switch (selectionMode) {
0762: case CARET_BEGINNING:
0763: pattern.setSelection(0, 0);
0764: break;
0765: case FULL_SELECTION:
0766: pattern.setSelection(0, initialPatternText.length());
0767: break;
0768: }
0769:
0770: // apply filter even if pattern is empty (display history)
0771: applyFilter();
0772:
0773: return dialogArea;
0774: }
0775:
0776: /**
0777: * This method is a hook for subclasses to override default dialog behavior.
0778: * The <code>handleDoubleClick()</code> method handles double clicks on
0779: * the list of filtered elements.
0780: * <p>
0781: * Current implementation makes double-clicking on the list do the same as
0782: * pressing <code>OK</code> button on the dialog.
0783: */
0784: protected void handleDoubleClick() {
0785: okPressed();
0786: }
0787:
0788: /**
0789: * Refreshes the details field according to the current selection in the
0790: * items list.
0791: */
0792: private void refreshDetails() {
0793: StructuredSelection selection = getSelectedItems();
0794:
0795: switch (selection.size()) {
0796: case 0:
0797: details.setInput(null);
0798: break;
0799: case 1:
0800: details.setInput(selection.getFirstElement());
0801: break;
0802: default:
0803: details
0804: .setInput(NLS
0805: .bind(
0806: WorkbenchMessages.FilteredItemsSelectionDialog_nItemsSelected,
0807: new Integer(selection.size())));
0808: break;
0809: }
0810:
0811: }
0812:
0813: /**
0814: * Handle selection in the items list by updating labels of selected and
0815: * unselected items and refresh the details field using the selection.
0816: *
0817: * @param selection
0818: * the new selection
0819: */
0820: protected void handleSelected(StructuredSelection selection) {
0821: IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
0822: IStatus.OK, EMPTY_STRING, null);
0823:
0824: if (selection.size() == 0) {
0825: status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
0826: IStatus.ERROR, EMPTY_STRING, null);
0827:
0828: if (lastSelection != null
0829: && getListSelectionLabelDecorator() != null) {
0830: list.update(lastSelection, null);
0831: }
0832:
0833: lastSelection = null;
0834:
0835: } else {
0836: status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
0837: IStatus.ERROR, EMPTY_STRING, null);
0838:
0839: List items = selection.toList();
0840:
0841: Object item = null;
0842: IStatus tempStatus = null;
0843:
0844: for (Iterator it = items.iterator(); it.hasNext();) {
0845: Object o = it.next();
0846:
0847: if (o instanceof ItemsListSeparator) {
0848: continue;
0849: }
0850:
0851: item = o;
0852: tempStatus = validateItem(item);
0853:
0854: if (tempStatus.isOK()) {
0855: status = new Status(IStatus.OK,
0856: PlatformUI.PLUGIN_ID, IStatus.OK,
0857: EMPTY_STRING, null);
0858: } else {
0859: status = tempStatus;
0860: // if any selected element is not valid status is set to
0861: // ERROR
0862: break;
0863: }
0864: }
0865:
0866: if (lastSelection != null
0867: && getListSelectionLabelDecorator() != null) {
0868: list.update(lastSelection, null);
0869: }
0870:
0871: if (getListSelectionLabelDecorator() != null) {
0872: list.update(items.toArray(), null);
0873: }
0874:
0875: lastSelection = items.toArray();
0876: }
0877:
0878: refreshDetails();
0879: updateStatus(status);
0880: }
0881:
0882: /*
0883: * (non-Javadoc)
0884: *
0885: * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
0886: */
0887: protected IDialogSettings getDialogBoundsSettings() {
0888: IDialogSettings settings = getDialogSettings();
0889: IDialogSettings section = settings
0890: .getSection(DIALOG_BOUNDS_SETTINGS);
0891: if (section == null) {
0892: section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS);
0893: section.put(DIALOG_HEIGHT, 500);
0894: section.put(DIALOG_WIDTH, 600);
0895: }
0896: return section;
0897: }
0898:
0899: /**
0900: * Returns the dialog settings. Returned object can't be null.
0901: *
0902: * @return return dialog settings for this dialog
0903: */
0904: protected abstract IDialogSettings getDialogSettings();
0905:
0906: /**
0907: * Refreshes the dialog - has to be called in UI thread.
0908: */
0909: public void refresh() {
0910: if (list != null && !list.getTable().isDisposed()) {
0911:
0912: List lastRefreshSelection = ((StructuredSelection) list
0913: .getSelection()).toList();
0914:
0915: list.setItemCount(contentProvider.getElements(null).length);
0916: list.refresh();
0917:
0918: if (list.getTable().getItemCount() > 0) {
0919: // preserve previous selection
0920: if (refreshWithLastSelection
0921: && lastRefreshSelection != null
0922: && lastRefreshSelection.size() > 0) {
0923: list.setSelection(new StructuredSelection(
0924: lastRefreshSelection));
0925: } else {
0926: refreshWithLastSelection = true;
0927: list.getTable().setSelection(0);
0928: list.getTable().notifyListeners(SWT.Selection,
0929: new Event());
0930: }
0931: } else {
0932: list.setSelection(StructuredSelection.EMPTY);
0933: }
0934:
0935: }
0936:
0937: scheduleProgressMessageRefresh();
0938: }
0939:
0940: /**
0941: * Updates the progress label.
0942: *
0943: * @deprecated
0944: */
0945: public void updateProgressLabel() {
0946: scheduleProgressMessageRefresh();
0947: }
0948:
0949: /**
0950: * Notifies the content provider - fires filtering of content provider
0951: * elements. During the filtering, a separator between history and workspace
0952: * matches is added.
0953: * <p>
0954: * This is a long running operation and should be called in a job.
0955: *
0956: * @param checkDuplicates
0957: * <code>true</code> if data concerning elements duplication
0958: * should be computed - it takes much more time than the standard
0959: * filtering
0960: * @param monitor
0961: * a progress monitor or <code>null</code> if no monitor is
0962: * available
0963: */
0964: public void reloadCache(boolean checkDuplicates,
0965: IProgressMonitor monitor) {
0966: if (list != null && !list.getTable().isDisposed()
0967: && contentProvider != null) {
0968: contentProvider.reloadCache(checkDuplicates, monitor);
0969: }
0970: }
0971:
0972: /**
0973: * Schedule refresh job.
0974: */
0975: public void scheduleRefresh() {
0976: refreshCacheJob.cancelAll();
0977: refreshCacheJob.schedule();
0978: }
0979:
0980: /**
0981: * Schedules progress message refresh.
0982: */
0983: public void scheduleProgressMessageRefresh() {
0984: if (filterJob.getState() != Job.RUNNING
0985: && refreshProgressMessageJob.getState() != Job.RUNNING)
0986: refreshProgressMessageJob.scheduleProgressRefresh(null);
0987: }
0988:
0989: /*
0990: * (non-Javadoc)
0991: *
0992: * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
0993: */
0994: protected void computeResult() {
0995:
0996: List selectedElements = ((StructuredSelection) list
0997: .getSelection()).toList();
0998:
0999: List objectsToReturn = new ArrayList();
1000:
1001: Object item = null;
1002:
1003: for (Iterator it = selectedElements.iterator(); it.hasNext();) {
1004: item = it.next();
1005:
1006: if (!(item instanceof ItemsListSeparator)) {
1007: accessedHistoryItem(item);
1008: objectsToReturn.add(item);
1009: }
1010: }
1011:
1012: setResult(objectsToReturn);
1013: }
1014:
1015: /*
1016: * @see org.eclipse.ui.dialogs.SelectionStatusDialog#updateStatus(org.eclipse.core.runtime.IStatus)
1017: */
1018: protected void updateStatus(IStatus status) {
1019: this .status = status;
1020: super .updateStatus(status);
1021: }
1022:
1023: /*
1024: * @see Dialog#okPressed()
1025: */
1026: protected void okPressed() {
1027: if (status != null
1028: && (status.isOK() || status.getCode() == IStatus.INFO)) {
1029: super .okPressed();
1030: }
1031: }
1032:
1033: /**
1034: * Sets the initial pattern used by the filter. This text is copied into the
1035: * selection input on the dialog. A full selection is used in the pattern
1036: * input field.
1037: *
1038: * @param text
1039: * initial pattern for the filter
1040: * @see FilteredItemsSelectionDialog#FULL_SELECTION
1041: */
1042: public void setInitialPattern(String text) {
1043: setInitialPattern(text, FULL_SELECTION);
1044: }
1045:
1046: /**
1047: * Sets the initial pattern used by the filter. This text is copied into the
1048: * selection input on the dialog. The <code>selectionMode</code> is used
1049: * to choose selection type for the input field.
1050: *
1051: * @param text
1052: * initial pattern for the filter
1053: * @param selectionMode
1054: * one of: {@link FilteredItemsSelectionDialog#NONE},
1055: * {@link FilteredItemsSelectionDialog#CARET_BEGINNING},
1056: * {@link FilteredItemsSelectionDialog#FULL_SELECTION}
1057: */
1058: public void setInitialPattern(String text, int selectionMode) {
1059: this .initialPatternText = text;
1060: this .selectionMode = selectionMode;
1061: }
1062:
1063: /**
1064: * Gets initial pattern.
1065: *
1066: * @return initial pattern, or <code>null</code> if initial pattern is not
1067: * set
1068: */
1069: protected String getInitialPattern() {
1070: return this .initialPatternText;
1071: }
1072:
1073: /**
1074: * Returns the current selection.
1075: *
1076: * @return the current selection
1077: */
1078: protected StructuredSelection getSelectedItems() {
1079:
1080: StructuredSelection selection = (StructuredSelection) list
1081: .getSelection();
1082:
1083: List selectedItems = selection.toList();
1084: Object itemToRemove = null;
1085:
1086: for (Iterator it = selection.iterator(); it.hasNext();) {
1087: Object item = it.next();
1088: if (item instanceof ItemsListSeparator) {
1089: itemToRemove = item;
1090: break;
1091: }
1092: }
1093:
1094: if (itemToRemove == null)
1095: return new StructuredSelection(selectedItems);
1096: // Create a new selection without the collision
1097: List newItems = new ArrayList(selectedItems);
1098: newItems.remove(itemToRemove);
1099: return new StructuredSelection(newItems);
1100:
1101: }
1102:
1103: /**
1104: * Validates the item. When items on the items list are selected or
1105: * deselected, it validates each item in the selection and the dialog status
1106: * depends on all validations.
1107: *
1108: * @param item
1109: * an item to be checked
1110: * @return status of the dialog to be set
1111: */
1112: protected abstract IStatus validateItem(Object item);
1113:
1114: /**
1115: * Creates an instance of a filter.
1116: *
1117: * @return a filter for items on the items list. Can be <code>null</code>,
1118: * no filtering will be applied then, causing no item to be shown in
1119: * the list.
1120: */
1121: protected abstract ItemsFilter createFilter();
1122:
1123: /**
1124: * Applies the filter created by <code>createFilter()</code> method to the
1125: * items list. When new filter is different than previous one it will cause
1126: * refiltering.
1127: */
1128: protected void applyFilter() {
1129:
1130: ItemsFilter newFilter = createFilter();
1131:
1132: // don't apply filtering for patterns which mean the same, for example:
1133: // *a**b and ***a*b
1134: if (filter != null && filter.equalsFilter(newFilter)) {
1135: return;
1136: }
1137:
1138: filterHistoryJob.cancel();
1139: filterJob.cancel();
1140:
1141: this .filter = newFilter;
1142:
1143: if (this .filter != null) {
1144: filterHistoryJob.schedule();
1145: }
1146: }
1147:
1148: /**
1149: * Returns comparator to sort items inside content provider. Returned object
1150: * will be probably created as an anonymous class. Parameters passed to the
1151: * <code>compare(java.lang.Object, java.lang.Object)</code> are going to
1152: * be the same type as the one used in the content provider.
1153: *
1154: * @return comparator to sort items content provider
1155: */
1156: protected abstract Comparator getItemsComparator();
1157:
1158: /**
1159: * Fills the content provider with matching items.
1160: *
1161: * @param contentProvider
1162: * collector to add items to.
1163: * {@link FilteredItemsSelectionDialog.AbstractContentProvider#add(Object, FilteredItemsSelectionDialog.ItemsFilter)}
1164: * only adds items that pass the given <code>itemsFilter</code>.
1165: * @param itemsFilter
1166: * the items filter
1167: * @param progressMonitor
1168: * must be used to report search progress. The state of this
1169: * progress monitor reflects the state of the filtering process.
1170: * @throws CoreException
1171: */
1172: protected abstract void fillContentProvider(
1173: AbstractContentProvider contentProvider,
1174: ItemsFilter itemsFilter, IProgressMonitor progressMonitor)
1175: throws CoreException;
1176:
1177: /**
1178: * Removes selected items from history.
1179: *
1180: * @param items
1181: * items to be removed
1182: */
1183: private void removeSelectedItems(List items) {
1184: for (Iterator iter = items.iterator(); iter.hasNext();) {
1185: Object item = iter.next();
1186: removeHistoryItem(item);
1187: }
1188: refreshWithLastSelection = false;
1189: contentProvider.refresh();
1190: }
1191:
1192: /**
1193: * Removes an item from history.
1194: *
1195: * @param item
1196: * an item to remove
1197: * @return removed item
1198: */
1199: protected Object removeHistoryItem(Object item) {
1200: return contentProvider.removeHistoryElement(item);
1201: }
1202:
1203: /**
1204: * Adds item to history.
1205: *
1206: * @param item
1207: * the item to be added
1208: */
1209: protected void accessedHistoryItem(Object item) {
1210: contentProvider.addHistoryElement(item);
1211: }
1212:
1213: /**
1214: * Returns a history comparator.
1215: *
1216: * @return decorated comparator
1217: */
1218: private Comparator getHistoryComparator() {
1219: return new HistoryComparator();
1220: }
1221:
1222: /**
1223: * Returns the history of selected elements.
1224: *
1225: * @return history of selected elements, or <code>null</code> if it is not
1226: * set
1227: */
1228: protected SelectionHistory getSelectionHistory() {
1229: return this .contentProvider.getSelectionHistory();
1230: }
1231:
1232: /**
1233: * Sets new history.
1234: *
1235: * @param selectionHistory
1236: * the history
1237: */
1238: protected void setSelectionHistory(SelectionHistory selectionHistory) {
1239: if (this .contentProvider != null)
1240: this .contentProvider.setSelectionHistory(selectionHistory);
1241: }
1242:
1243: /**
1244: * Indicates whether the given item is a history item.
1245: *
1246: * @param item
1247: * the item to be investigated
1248: * @return <code>true</code> if the given item exists in history,
1249: * <code>false</code> otherwise
1250: */
1251: public boolean isHistoryElement(Object item) {
1252: return this .contentProvider.isHistoryElement(item);
1253: }
1254:
1255: /**
1256: * Indicates whether the given item is a duplicate.
1257: *
1258: * @param item
1259: * the item to be investigated
1260: * @return <code>true</code> if the item is duplicate, <code>false</code>
1261: * otherwise
1262: */
1263: public boolean isDuplicateElement(Object item) {
1264: return this .contentProvider.isDuplicateElement(item);
1265: }
1266:
1267: /**
1268: * Sets separator label
1269: *
1270: * @param separatorLabel
1271: * the label showed on separator
1272: */
1273: public void setSeparatorLabel(String separatorLabel) {
1274: this .itemsListSeparator = new ItemsListSeparator(separatorLabel);
1275: }
1276:
1277: /**
1278: * Returns name for then given object.
1279: *
1280: * @param item
1281: * an object from the content provider. Subclasses should pay
1282: * attention to the passed argument. They should either only pass
1283: * objects of a known type (one used in content provider) or make
1284: * sure that passed parameter is the expected one (by type
1285: * checking like <code>instanceof</code> inside the method).
1286: * @return name of the given item
1287: */
1288: public abstract String getElementName(Object item);
1289:
1290: private class ToggleStatusLineAction extends Action {
1291:
1292: /**
1293: * Creates a new instance of the class.
1294: */
1295: public ToggleStatusLineAction() {
1296: super (
1297: WorkbenchMessages.FilteredItemsSelectionDialog_toggleStatusAction,
1298: IAction.AS_CHECK_BOX);
1299: }
1300:
1301: public void run() {
1302: details.setVisible(isChecked());
1303: }
1304: }
1305:
1306: /**
1307: * Only refreshes UI on the basis of an already sorted and filtered set of
1308: * items.
1309: * <p>
1310: * Standard invocation scenario:
1311: * <ol>
1312: * <li>filtering job (<code>FilterJob</code> class extending
1313: * <code>Job</code> class)</li>
1314: * <li>cache refresh without checking for duplicates (<code>RefreshCacheJob</code>
1315: * class extending <code>Job</code> class)</li>
1316: * <li>UI refresh (<code>RefreshJob</code> class extending
1317: * <code>UIJob</code> class)</li>
1318: * <li>cache refresh with checking for duplicates (<cod>CacheRefreshJob</code>
1319: * class extending <code>Job</code> class)</li>
1320: * <li>UI refresh (<code>RefreshJob</code> class extending <code>UIJob</code>
1321: * class)</li>
1322: * </ol>
1323: * The scenario is rather complicated, but it had to be applied, because:
1324: * <ul>
1325: * <li> refreshing cache is rather a long action and cannot be run in the UI -
1326: * cannot be run in a UIJob</li>
1327: * <li> refreshing cache checking for duplicates is twice as long as
1328: * refreshing cache without checking for duplicates; results of the search
1329: * could be displayed earlier</li>
1330: * <li> refreshing the UI have to be run in a UIJob</li>
1331: * </ul>
1332: *
1333: * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.FilterJob
1334: * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshJob
1335: * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.RefreshCacheJob
1336: */
1337: private class RefreshJob extends UIJob {
1338:
1339: /**
1340: * Creates a new instance of the class.
1341: */
1342: public RefreshJob() {
1343: super (
1344: FilteredItemsSelectionDialog.this .getParentShell()
1345: .getDisplay(),
1346: WorkbenchMessages.FilteredItemsSelectionDialog_refreshJob);
1347: setSystem(true);
1348: }
1349:
1350: /*
1351: * (non-Javadoc)
1352: *
1353: * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1354: */
1355: public IStatus runInUIThread(IProgressMonitor monitor) {
1356: if (monitor.isCanceled())
1357: return new Status(IStatus.OK,
1358: WorkbenchPlugin.PI_WORKBENCH, IStatus.OK,
1359: EMPTY_STRING, null);
1360:
1361: if (FilteredItemsSelectionDialog.this != null) {
1362: FilteredItemsSelectionDialog.this .refresh();
1363: }
1364:
1365: return new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
1366: IStatus.OK, EMPTY_STRING, null);
1367: }
1368:
1369: }
1370:
1371: /**
1372: * Refreshes the progress message cyclically with 500 milliseconds delay.
1373: * <code>RefreshProgressMessageJob</code> is strictly connected with
1374: * <code>GranualProgressMonitor</code> and use it to to get progress
1375: * message and to decide about break of cyclical refresh.
1376: */
1377: private class RefreshProgressMessageJob extends UIJob {
1378:
1379: private GranualProgressMonitor progressMonitor;
1380:
1381: /**
1382: * Creates a new instance of the class.
1383: */
1384: public RefreshProgressMessageJob() {
1385: super (
1386: FilteredItemsSelectionDialog.this .getParentShell()
1387: .getDisplay(),
1388: WorkbenchMessages.FilteredItemsSelectionDialog_progressRefreshJob);
1389: setSystem(true);
1390: }
1391:
1392: /*
1393: * (non-Javadoc)
1394: *
1395: * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
1396: */
1397: public IStatus runInUIThread(IProgressMonitor monitor) {
1398:
1399: if (!progressLabel.isDisposed())
1400: progressLabel
1401: .setText(progressMonitor != null ? progressMonitor
1402: .getMessage()
1403: : EMPTY_STRING);
1404:
1405: if (progressMonitor == null || progressMonitor.isDone()) {
1406: return new Status(IStatus.CANCEL, PlatformUI.PLUGIN_ID,
1407: IStatus.CANCEL, EMPTY_STRING, null);
1408: }
1409:
1410: // Schedule cyclical with 500 milliseconds delay
1411: schedule(500);
1412:
1413: return new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
1414: IStatus.OK, EMPTY_STRING, null);
1415: }
1416:
1417: /**
1418: * Schedule progress refresh job.
1419: *
1420: * @param progressMonitor
1421: * used during refresh progress label
1422: */
1423: public void scheduleProgressRefresh(
1424: GranualProgressMonitor progressMonitor) {
1425: this .progressMonitor = progressMonitor;
1426: // Schedule with initial delay to avoid flickering when the user
1427: // types quickly
1428: schedule(200);
1429: }
1430:
1431: }
1432:
1433: /**
1434: * A job responsible for computing filtered items list presented using
1435: * <code>RefreshJob</code>.
1436: *
1437: * @see RefreshJob
1438: *
1439: */
1440: private class RefreshCacheJob extends Job {
1441:
1442: private RefreshJob refreshJob = new RefreshJob();
1443:
1444: /**
1445: * Creates a new instance of the class.
1446: */
1447: public RefreshCacheJob() {
1448: super (
1449: WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob);
1450: setSystem(true);
1451: }
1452:
1453: /**
1454: * Stops the job and all sub-jobs.
1455: */
1456: public void cancelAll() {
1457: cancel();
1458: refreshJob.cancel();
1459: }
1460:
1461: /*
1462: * (non-Javadoc)
1463: *
1464: * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1465: */
1466: protected IStatus run(IProgressMonitor monitor) {
1467: if (monitor.isCanceled()) {
1468: return new Status(IStatus.CANCEL,
1469: WorkbenchPlugin.PI_WORKBENCH, IStatus.CANCEL,
1470: EMPTY_STRING, null);
1471: }
1472:
1473: if (FilteredItemsSelectionDialog.this != null) {
1474: GranualProgressMonitor wrappedMonitor = new GranualProgressMonitor(
1475: monitor);
1476: FilteredItemsSelectionDialog.this .reloadCache(true,
1477: wrappedMonitor);
1478: }
1479:
1480: if (!monitor.isCanceled()) {
1481: refreshJob.schedule();
1482: }
1483:
1484: return new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
1485: IStatus.OK, EMPTY_STRING, null);
1486:
1487: }
1488:
1489: /*
1490: * (non-Javadoc)
1491: *
1492: * @see org.eclipse.core.runtime.jobs.Job#canceling()
1493: */
1494: protected void canceling() {
1495: super .canceling();
1496: contentProvider.stopReloadingCache();
1497: }
1498:
1499: }
1500:
1501: private class RemoveHistoryItemAction extends Action {
1502:
1503: /**
1504: * Creates a new instance of the class.
1505: */
1506: public RemoveHistoryItemAction() {
1507: super (
1508: WorkbenchMessages.FilteredItemsSelectionDialog_removeItemsFromHistoryAction);
1509: }
1510:
1511: /*
1512: * (non-Javadoc)
1513: *
1514: * @see org.eclipse.jface.action.Action#run()
1515: */
1516: public void run() {
1517: List selectedElements = ((StructuredSelection) list
1518: .getSelection()).toList();
1519: removeSelectedItems(selectedElements);
1520: }
1521: }
1522:
1523: private class ItemsListLabelProvider extends LabelProvider
1524: implements IColorProvider, IFontProvider,
1525: ILabelProviderListener {
1526: private ILabelProvider provider;
1527:
1528: private ILabelDecorator selectionDecorator;
1529:
1530: // Need to keep our own list of listeners
1531: private ListenerList listeners = new ListenerList();
1532:
1533: /**
1534: * Creates a new instance of the class.
1535: *
1536: * @param provider
1537: * the label provider for all items, not <code>null</code>
1538: * @param selectionDecorator
1539: * the decorator for selected items, can be <code>null</code>
1540: */
1541: public ItemsListLabelProvider(ILabelProvider provider,
1542: ILabelDecorator selectionDecorator) {
1543: Assert.isNotNull(provider);
1544: this .provider = provider;
1545: this .selectionDecorator = selectionDecorator;
1546:
1547: provider.addListener(this );
1548:
1549: if (selectionDecorator != null) {
1550: selectionDecorator.addListener(this );
1551: }
1552: }
1553:
1554: /**
1555: * Sets new selection decorator.
1556: *
1557: * @param newSelectionDecorator
1558: * new label decorator for selected items in the list
1559: */
1560: public void setSelectionDecorator(
1561: ILabelDecorator newSelectionDecorator) {
1562: if (selectionDecorator != null) {
1563: selectionDecorator.removeListener(this );
1564: selectionDecorator.dispose();
1565: }
1566:
1567: selectionDecorator = newSelectionDecorator;
1568:
1569: if (selectionDecorator != null) {
1570: selectionDecorator.addListener(this );
1571: }
1572: }
1573:
1574: /**
1575: * Gets selection decorator.
1576: *
1577: * @return the label decorator for selected items in the list
1578: */
1579: public ILabelDecorator getSelectionDecorator() {
1580: return selectionDecorator;
1581: }
1582:
1583: /**
1584: * Sets new label provider.
1585: *
1586: * @param newProvider
1587: * new label provider for items in the list, not
1588: * <code>null</code>
1589: */
1590: public void setProvider(ILabelProvider newProvider) {
1591: Assert.isNotNull(newProvider);
1592: provider.removeListener(this );
1593: provider.dispose();
1594:
1595: provider = newProvider;
1596:
1597: if (provider != null) {
1598: provider.addListener(this );
1599: }
1600: }
1601:
1602: /**
1603: * Gets the label provider.
1604: *
1605: * @return the label provider for items in the list
1606: */
1607: public ILabelProvider getProvider() {
1608: return provider;
1609: }
1610:
1611: /*
1612: * (non-Javadoc)
1613: *
1614: * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
1615: */
1616: public Image getImage(Object element) {
1617: if (element instanceof ItemsListSeparator) {
1618: return WorkbenchImages
1619: .getImage(IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR);
1620: }
1621:
1622: return provider.getImage(element);
1623: }
1624:
1625: /*
1626: * (non-Javadoc)
1627: *
1628: * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
1629: */
1630: public String getText(Object element) {
1631: if (element instanceof ItemsListSeparator) {
1632: return getSeparatorLabel(((ItemsListSeparator) element)
1633: .getName());
1634: }
1635:
1636: String str = provider.getText(element);
1637:
1638: if (selectionDecorator != null && element != null) {
1639:
1640: // ((StructuredSelection)list.getSelection()).toList().contains(element))
1641: // cannot be used - virtual tables produce cycles in
1642: // update item - get selection invocation scenarios
1643:
1644: int[] selectionIndices = list.getTable()
1645: .getSelectionIndices();
1646: List elements = Arrays.asList(contentProvider
1647: .getElements(null));
1648: for (int i = 0; i < selectionIndices.length; i++) {
1649: if (elements.size() > selectionIndices[i]
1650: && element.equals(elements
1651: .get(selectionIndices[i]))) {
1652: str = selectionDecorator.decorateText(str,
1653: element);
1654: break;
1655: }
1656: }
1657: }
1658: return str;
1659: }
1660:
1661: private String getSeparatorLabel(String separatorLabel) {
1662: Rectangle rect = list.getTable().getBounds();
1663:
1664: int borderWidth = list.getTable().computeTrim(0, 0, 0, 0).width;
1665:
1666: int imageWidth = WorkbenchImages.getImage(
1667: IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR)
1668: .getBounds().width;
1669:
1670: int width = rect.width - borderWidth - imageWidth;
1671:
1672: GC gc = new GC(list.getTable());
1673: gc.setFont(list.getTable().getFont());
1674:
1675: int fSeparatorWidth = gc.getAdvanceWidth('-');
1676: int fMessageLength = gc.textExtent(separatorLabel).x;
1677:
1678: gc.dispose();
1679:
1680: StringBuffer dashes = new StringBuffer();
1681: int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2;
1682: for (int i = 0; i < chars; i++) {
1683: dashes.append('-');
1684: }
1685:
1686: StringBuffer result = new StringBuffer();
1687: result.append(dashes);
1688: result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$
1689: result.append(dashes);
1690: return result.toString().trim();
1691: }
1692:
1693: /*
1694: * (non-Javadoc)
1695: *
1696: * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
1697: */
1698: public void addListener(ILabelProviderListener listener) {
1699: listeners.add(listener);
1700: }
1701:
1702: /*
1703: * (non-Javadoc)
1704: *
1705: * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
1706: */
1707: public void dispose() {
1708: provider.removeListener(this );
1709: provider.dispose();
1710:
1711: if (selectionDecorator != null) {
1712: selectionDecorator.removeListener(this );
1713: selectionDecorator.dispose();
1714: }
1715:
1716: super .dispose();
1717: }
1718:
1719: /*
1720: * (non-Javadoc)
1721: *
1722: * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
1723: * java.lang.String)
1724: */
1725: public boolean isLabelProperty(Object element, String property) {
1726: if (provider.isLabelProperty(element, property)) {
1727: return true;
1728: }
1729: if (selectionDecorator != null
1730: && selectionDecorator.isLabelProperty(element,
1731: property)) {
1732: return true;
1733: }
1734: return false;
1735: }
1736:
1737: /*
1738: * (non-Javadoc)
1739: *
1740: * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
1741: */
1742: public void removeListener(ILabelProviderListener listener) {
1743: listeners.remove(listener);
1744: }
1745:
1746: /*
1747: * (non-Javadoc)
1748: *
1749: * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
1750: */
1751: public Color getBackground(Object element) {
1752: if (element instanceof ItemsListSeparator) {
1753: return null;
1754: }
1755: if (provider instanceof IColorProvider) {
1756: return ((IColorProvider) provider)
1757: .getBackground(element);
1758: }
1759: return null;
1760: }
1761:
1762: /*
1763: * (non-Javadoc)
1764: *
1765: * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
1766: */
1767: public Color getForeground(Object element) {
1768: if (element instanceof ItemsListSeparator) {
1769: return Display.getCurrent().getSystemColor(
1770: SWT.COLOR_WIDGET_NORMAL_SHADOW);
1771: }
1772: if (provider instanceof IColorProvider) {
1773: return ((IColorProvider) provider)
1774: .getForeground(element);
1775: }
1776: return null;
1777: }
1778:
1779: /*
1780: * (non-Javadoc)
1781: *
1782: * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object)
1783: */
1784: public Font getFont(Object element) {
1785: if (element instanceof ItemsListSeparator) {
1786: return null;
1787: }
1788: if (provider instanceof IFontProvider) {
1789: return ((IFontProvider) provider).getFont(element);
1790: }
1791: return null;
1792: }
1793:
1794: /*
1795: * (non-Javadoc)
1796: *
1797: * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
1798: */
1799: public void labelProviderChanged(LabelProviderChangedEvent event) {
1800: Object[] l = listeners.getListeners();
1801: for (int i = 0; i < listeners.size(); i++) {
1802: ((ILabelProviderListener) l[i])
1803: .labelProviderChanged(event);
1804: }
1805: }
1806: }
1807:
1808: /**
1809: * Used in ItemsListContentProvider, separates history and non-history
1810: * items.
1811: */
1812: private class ItemsListSeparator {
1813:
1814: private String name;
1815:
1816: /**
1817: * Creates a new instance of the class.
1818: *
1819: * @param name
1820: * the name of the separator
1821: */
1822: public ItemsListSeparator(String name) {
1823: this .name = name;
1824: }
1825:
1826: /**
1827: * Returns the name of this separator.
1828: *
1829: * @return the name of the separator
1830: */
1831: public String getName() {
1832: return name;
1833: }
1834: }
1835:
1836: /**
1837: * GranualProgressMonitor is used for monitoring progress of filtering
1838: * process. It is used by <code>RefreshProgressMessageJob</code> to
1839: * refresh progress message. State of this monitor illustrates state of
1840: * filtering or cache refreshing process.
1841: *
1842: * @see GranualProgressMonitor#internalWorked(double)
1843: */
1844: private class GranualProgressMonitor extends ProgressMonitorWrapper {
1845:
1846: private String name;
1847:
1848: private String subName;
1849:
1850: private int totalWork;
1851:
1852: private double worked;
1853:
1854: private boolean done;
1855:
1856: /**
1857: * Creates instance of <code>GranualProgressMonitor</code>.
1858: *
1859: * @param monitor
1860: * progress to be wrapped
1861: */
1862: public GranualProgressMonitor(IProgressMonitor monitor) {
1863: super (monitor);
1864: }
1865:
1866: /**
1867: * Checks if filtering has been done
1868: *
1869: * @return true if filtering work has been done false in other way
1870: */
1871: public boolean isDone() {
1872: return done;
1873: }
1874:
1875: /*
1876: * (non-Javadoc)
1877: *
1878: * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setTaskName(java.lang.String)
1879: */
1880: public void setTaskName(String name) {
1881: super .setTaskName(name);
1882: this .name = name;
1883: this .subName = null;
1884: }
1885:
1886: /*
1887: * (non-Javadoc)
1888: *
1889: * @see org.eclipse.core.runtime.ProgressMonitorWrapper#subTask(java.lang.String)
1890: */
1891: public void subTask(String name) {
1892: super .subTask(name);
1893: this .subName = name;
1894: }
1895:
1896: /*
1897: * (non-Javadoc)
1898: *
1899: * @see org.eclipse.core.runtime.ProgressMonitorWrapper#beginTask(java.lang.String,
1900: * int)
1901: */
1902: public void beginTask(String name, int totalWork) {
1903: super .beginTask(name, totalWork);
1904: if (this .name == null)
1905: this .name = name;
1906: this .totalWork = totalWork;
1907: refreshProgressMessageJob.scheduleProgressRefresh(this );
1908: }
1909:
1910: /*
1911: * (non-Javadoc)
1912: *
1913: * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
1914: */
1915: public void worked(int work) {
1916: super .worked(work);
1917: internalWorked(work);
1918: }
1919:
1920: /*
1921: * (non-Javadoc)
1922: *
1923: * @see org.eclipse.core.runtime.ProgressMonitorWrapper#done()
1924: */
1925: public void done() {
1926: done = true;
1927: super .done();
1928: }
1929:
1930: /*
1931: * (non-Javadoc)
1932: *
1933: * @see org.eclipse.core.runtime.ProgressMonitorWrapper#setCanceled(boolean)
1934: */
1935: public void setCanceled(boolean b) {
1936: done = b;
1937: super .setCanceled(b);
1938: }
1939:
1940: /*
1941: * (non-Javadoc)
1942: *
1943: * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
1944: */
1945: public void internalWorked(double work) {
1946: worked = worked + work;
1947: }
1948:
1949: private String getMessage() {
1950: if (done)
1951: return ""; //$NON-NLS-1$
1952:
1953: String message;
1954:
1955: if (name == null) {
1956: message = subName == null ? "" : subName; //$NON-NLS-1$
1957: } else {
1958: message = subName == null ? name
1959: : NLS
1960: .bind(
1961: WorkbenchMessages.FilteredItemsSelectionDialog_subtaskProgressMessage,
1962: new Object[] { name, subName });
1963: }
1964: if (totalWork == 0)
1965: return message;
1966:
1967: return NLS
1968: .bind(
1969: WorkbenchMessages.FilteredItemsSelectionDialog_taskProgressMessage,
1970: new Object[] {
1971: message,
1972: new Integer(
1973: (int) ((worked * 100) / totalWork)) });
1974:
1975: }
1976:
1977: }
1978:
1979: /**
1980: * Filters items history and schedule filter job.
1981: */
1982: private class FilterHistoryJob extends Job {
1983:
1984: /**
1985: * Filter used during the filtering process.
1986: */
1987: private ItemsFilter itemsFilter;
1988:
1989: /**
1990: * Creates new instance of receiver.
1991: */
1992: public FilterHistoryJob() {
1993: super (
1994: WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
1995: setSystem(true);
1996: }
1997:
1998: /*
1999: * (non-Javadoc)
2000: *
2001: * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2002: */
2003: protected IStatus run(IProgressMonitor monitor) {
2004:
2005: this .itemsFilter = filter;
2006:
2007: contentProvider.reset();
2008:
2009: refreshWithLastSelection = false;
2010:
2011: contentProvider.addHistoryItems(itemsFilter);
2012:
2013: if (!(lastCompletedFilter != null && lastCompletedFilter
2014: .isSubFilter(this .itemsFilter)))
2015: contentProvider.refresh();
2016:
2017: filterJob.schedule();
2018:
2019: return Status.OK_STATUS;
2020: }
2021:
2022: }
2023:
2024: /**
2025: * Filters items in indicated set and history. During filtering, it
2026: * refreshes the dialog (progress monitor and elements list).
2027: *
2028: * Depending on the filter, <code>FilterJob</code> decides which kind of
2029: * search will be run inside <code>filterContent</code>. If the last
2030: * filtering is done (last completed filter), is not null, and the new
2031: * filter is a sub-filter ({@link FilteredItemsSelectionDialog.ItemsFilter#isSubFilter(FilteredItemsSelectionDialog.ItemsFilter)})
2032: * of the last, then <code>FilterJob</code> only filters in the cache. If
2033: * it is the first filtering or the new filter isn't a sub-filter of the
2034: * last one, a full search is run.
2035: */
2036: private class FilterJob extends Job {
2037:
2038: /**
2039: * Filter used during the filtering process.
2040: */
2041: protected ItemsFilter itemsFilter;
2042:
2043: /**
2044: * Creates new instance of FilterJob
2045: */
2046: public FilterJob() {
2047: super (
2048: WorkbenchMessages.FilteredItemsSelectionDialog_jobLabel);
2049: setSystem(true);
2050: }
2051:
2052: /*
2053: * (non-Javadoc)
2054: *
2055: * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
2056: */
2057: protected final IStatus run(IProgressMonitor parent) {
2058: GranualProgressMonitor monitor = new GranualProgressMonitor(
2059: parent);
2060: return doRun(monitor);
2061: }
2062:
2063: /**
2064: * Executes job using the given filtering progress monitor. A hook for
2065: * subclasses.
2066: *
2067: * @param monitor
2068: * progress monitor
2069: * @return result of the execution
2070: */
2071: protected IStatus doRun(GranualProgressMonitor monitor) {
2072: try {
2073: internalRun(monitor);
2074: } catch (CoreException e) {
2075: cancel();
2076: return new Status(
2077: IStatus.ERROR,
2078: PlatformUI.PLUGIN_ID,
2079: IStatus.ERROR,
2080: WorkbenchMessages.FilteredItemsSelectionDialog_jobError,
2081: e);
2082: }
2083: return Status.OK_STATUS;
2084: }
2085:
2086: /**
2087: * Main method for the job.
2088: *
2089: * @param monitor
2090: * @throws CoreException
2091: */
2092: private void internalRun(GranualProgressMonitor monitor)
2093: throws CoreException {
2094: try {
2095: if (monitor.isCanceled())
2096: return;
2097:
2098: this .itemsFilter = filter;
2099:
2100: if (filter.getPattern().length() != 0) {
2101: filterContent(monitor);
2102: }
2103:
2104: if (monitor.isCanceled())
2105: return;
2106:
2107: contentProvider.refresh();
2108: } finally {
2109: monitor.done();
2110: }
2111: }
2112:
2113: /**
2114: * Filters items.
2115: *
2116: * @param monitor
2117: * for monitoring progress
2118: * @throws CoreException
2119: */
2120: protected void filterContent(GranualProgressMonitor monitor)
2121: throws CoreException {
2122:
2123: if (lastCompletedFilter != null
2124: && lastCompletedFilter
2125: .isSubFilter(this .itemsFilter)) {
2126:
2127: int length = lastCompletedResult.size() / 500;
2128: monitor
2129: .beginTask(
2130: WorkbenchMessages.FilteredItemsSelectionDialog_cacheSearchJob_taskName,
2131: length);
2132:
2133: for (int pos = 0; pos < lastCompletedResult.size(); pos++) {
2134:
2135: Object item = lastCompletedResult.get(pos);
2136: if (monitor.isCanceled())
2137: break;
2138: contentProvider.add(item, itemsFilter);
2139:
2140: if ((pos % 500) == 0) {
2141: monitor.worked(1);
2142: }
2143: }
2144:
2145: } else {
2146:
2147: lastCompletedFilter = null;
2148: lastCompletedResult = null;
2149:
2150: SubProgressMonitor subMonitor = null;
2151: if (monitor != null) {
2152: monitor
2153: .beginTask(
2154: WorkbenchMessages.FilteredItemsSelectionDialog_searchJob_taskName,
2155: 100);
2156: subMonitor = new SubProgressMonitor(monitor, 95);
2157:
2158: }
2159:
2160: fillContentProvider(contentProvider, itemsFilter,
2161: subMonitor);
2162:
2163: if (monitor != null && !monitor.isCanceled()) {
2164: monitor.worked(2);
2165: contentProvider.rememberResult(itemsFilter);
2166: monitor.worked(3);
2167: }
2168: }
2169:
2170: }
2171:
2172: }
2173:
2174: /**
2175: * History stores a list of key, object pairs. The list is bounded at a
2176: * certain size. If the list exceeds this size the oldest element is removed
2177: * from the list. An element can be added/renewed with a call to
2178: * <code>accessed(Object)</code>.
2179: * <p>
2180: * The history can be stored to/loaded from an XML file.
2181: */
2182: protected static abstract class SelectionHistory {
2183:
2184: private static final String DEFAULT_ROOT_NODE_NAME = "historyRootNode"; //$NON-NLS-1$
2185:
2186: private static final String DEFAULT_INFO_NODE_NAME = "infoNode"; //$NON-NLS-1$
2187:
2188: private static final int MAX_HISTORY_SIZE = 60;
2189:
2190: private final List historyList;
2191:
2192: private final String rootNodeName;
2193:
2194: private final String infoNodeName;
2195:
2196: private SelectionHistory(String rootNodeName,
2197: String infoNodeName) {
2198:
2199: historyList = Collections
2200: .synchronizedList(new LinkedList() {
2201:
2202: private static final long serialVersionUID = 0L;
2203:
2204: /*
2205: * (non-Javadoc)
2206: *
2207: * @see java.util.LinkedList#add(java.lang.Object)
2208: */
2209: public boolean add(Object arg0) {
2210: if (this .size() > MAX_HISTORY_SIZE)
2211: this .removeFirst();
2212: if (!this .contains(arg0))
2213: return super .add(arg0);
2214: return false;
2215: }
2216:
2217: });
2218:
2219: this .rootNodeName = rootNodeName;
2220: this .infoNodeName = infoNodeName;
2221: }
2222:
2223: /**
2224: * Creates new instance of <code>SelectionHistory</code>.
2225: */
2226: public SelectionHistory() {
2227: this (DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME);
2228: }
2229:
2230: /**
2231: * Adds object to history.
2232: *
2233: * @param object
2234: * the item to be added to the history
2235: */
2236: public synchronized void accessed(Object object) {
2237: historyList.add(object);
2238: }
2239:
2240: /**
2241: * Returns <code>true</code> if history contains object.
2242: *
2243: * @param object
2244: * the item for which check will be executed
2245: * @return <code>true</code> if history contains object
2246: * <code>false</code> in other way
2247: */
2248: public synchronized boolean contains(Object object) {
2249: return historyList.contains(object);
2250: }
2251:
2252: /**
2253: * Returns <code>true</code> if history is empty.
2254: *
2255: * @return <code>true</code> if history is empty
2256: */
2257: public synchronized boolean isEmpty() {
2258: return historyList.isEmpty();
2259: }
2260:
2261: /**
2262: * Remove element from history.
2263: *
2264: * @param element
2265: * to remove form the history
2266: * @return <code>true</code> if this list contained the specified
2267: * element
2268: */
2269: public synchronized boolean remove(Object element) {
2270: return historyList.remove(element);
2271: }
2272:
2273: /**
2274: * Load history elements from memento.
2275: *
2276: * @param memento
2277: * memento from which the history will be retrieved
2278: */
2279: public void load(IMemento memento) {
2280:
2281: XMLMemento historyMemento = (XMLMemento) memento
2282: .getChild(rootNodeName);
2283:
2284: if (historyMemento == null) {
2285: return;
2286: }
2287:
2288: IMemento[] mementoElements = historyMemento
2289: .getChildren(infoNodeName);
2290: for (int i = 0; i < mementoElements.length; ++i) {
2291: IMemento mementoElement = mementoElements[i];
2292: Object object = restoreItemFromMemento(mementoElement);
2293: if (object != null) {
2294: historyList.add(object);
2295: }
2296: }
2297: }
2298:
2299: /**
2300: * Save history elements to memento.
2301: *
2302: * @param memento
2303: * memento to which the history will be added
2304: */
2305: public void save(IMemento memento) {
2306:
2307: IMemento historyMemento = memento.createChild(rootNodeName);
2308:
2309: Object[] items = getHistoryItems();
2310: for (int i = 0; i < items.length; i++) {
2311: Object item = items[i];
2312: IMemento elementMemento = historyMemento
2313: .createChild(infoNodeName);
2314: storeItemToMemento(item, elementMemento);
2315: }
2316:
2317: }
2318:
2319: /**
2320: * Gets array of history items.
2321: *
2322: * @return array of history elements
2323: */
2324: public synchronized Object[] getHistoryItems() {
2325: return historyList.toArray();
2326: }
2327:
2328: /**
2329: * Creates an object using given memento.
2330: *
2331: * @param memento
2332: * memento used for creating new object
2333: *
2334: * @return the restored object
2335: */
2336: protected abstract Object restoreItemFromMemento(
2337: IMemento memento);
2338:
2339: /**
2340: * Store object in <code>IMemento</code>.
2341: *
2342: * @param item
2343: * the item to store
2344: * @param memento
2345: * the memento to store to
2346: */
2347: protected abstract void storeItemToMemento(Object item,
2348: IMemento memento);
2349:
2350: }
2351:
2352: /**
2353: * Filters elements using SearchPattern by comparing the names of items with
2354: * the filter pattern.
2355: */
2356: protected abstract class ItemsFilter {
2357:
2358: protected SearchPattern patternMatcher;
2359:
2360: /**
2361: * Creates new instance of ItemsFilter.
2362: */
2363: public ItemsFilter() {
2364: this (new SearchPattern());
2365: }
2366:
2367: /**
2368: * Creates new instance of ItemsFilter.
2369: *
2370: * @param searchPattern
2371: * the pattern to be used when filtering
2372: */
2373: public ItemsFilter(SearchPattern searchPattern) {
2374: patternMatcher = searchPattern;
2375: String stringPattern = ""; //$NON-NLS-1$
2376: if (pattern != null && !pattern.getText().equals("*")) { //$NON-NLS-1$
2377: stringPattern = pattern.getText();
2378: }
2379: patternMatcher.setPattern(stringPattern);
2380: }
2381:
2382: /**
2383: * Check if the given filter is a sub-filter of current filter. The
2384: * default implementation checks if the <code>SearchPattern</code>
2385: * from the current filter is a sub-pattern of the one from the provided
2386: * filter.
2387: *
2388: * @param filter
2389: * the filter to be checked, or <code>null</code>
2390: * @return <code>true</code> if the given filter is sub-filter of the
2391: * current, <code>false</code> if the given filter isn't a
2392: * sub-filter or is <code>null</code>
2393: *
2394: * @see org.eclipse.ui.dialogs.SearchPattern#isSubPattern(org.eclipse.ui.dialogs.SearchPattern)
2395: */
2396: public boolean isSubFilter(ItemsFilter filter) {
2397: if (filter != null) {
2398: return this .patternMatcher
2399: .isSubPattern(filter.patternMatcher);
2400: }
2401: return false;
2402: }
2403:
2404: /**
2405: * Checks whether the provided filter is equal to the current filter.
2406: * The default implementation checks if <code>SearchPattern</code>
2407: * from current filter is equal to the one from provided filter.
2408: *
2409: * @param filter
2410: * filter to be checked, or <code>null</code>
2411: * @return <code>true</code> if the given filter is equal to current
2412: * filter, <code>false</code> if given filter isn't equal to
2413: * current one or if it is <code>null</code>
2414: *
2415: * @see org.eclipse.ui.dialogs.SearchPattern#equalsPattern(org.eclipse.ui.dialogs.SearchPattern)
2416: */
2417: public boolean equalsFilter(ItemsFilter filter) {
2418: if (filter != null
2419: && filter.patternMatcher
2420: .equalsPattern(this .patternMatcher)) {
2421: return true;
2422: }
2423: return false;
2424: }
2425:
2426: /**
2427: * Checks whether the pattern's match rule is camel case.
2428: *
2429: * @return <code>true</code> if pattern's match rule is camel case,
2430: * <code>false</code> otherwise
2431: */
2432: public boolean isCamelCasePattern() {
2433: return patternMatcher.getMatchRule() == SearchPattern.RULE_CAMELCASE_MATCH;
2434: }
2435:
2436: /**
2437: * Returns the pattern string.
2438: *
2439: * @return pattern for this filter
2440: *
2441: * @see SearchPattern#getPattern()
2442: */
2443: public String getPattern() {
2444: return patternMatcher.getPattern();
2445: }
2446:
2447: /**
2448: * Returns the rule to apply for matching keys.
2449: *
2450: * @return match rule
2451: *
2452: * @see SearchPattern#getMatchRule()
2453: */
2454: public int getMatchRule() {
2455: return patternMatcher.getMatchRule();
2456: }
2457:
2458: /**
2459: * Matches text with filter.
2460: *
2461: * @param text
2462: * @return <code>true</code> if text matches with filter pattern,
2463: * <code>false</code> otherwise
2464: */
2465: protected boolean matches(String text) {
2466: return patternMatcher.matches(text);
2467: }
2468:
2469: /**
2470: * General method for matching raw name pattern. Checks whether current
2471: * pattern is prefix of name provided item.
2472: *
2473: * @param item
2474: * item to check
2475: * @return <code>true</code> if current pattern is a prefix of name
2476: * provided item, <code>false</code> if item's name is shorter
2477: * than prefix or sequences of characters don't match.
2478: */
2479: public boolean matchesRawNamePattern(Object item) {
2480: String prefix = patternMatcher.getPattern();
2481: String text = getElementName(item);
2482:
2483: if (text == null)
2484: return false;
2485:
2486: int textLength = text.length();
2487: int prefixLength = prefix.length();
2488: if (textLength < prefixLength) {
2489: return false;
2490: }
2491: for (int i = prefixLength - 1; i >= 0; i--) {
2492: if (Character.toLowerCase(prefix.charAt(i)) != Character
2493: .toLowerCase(text.charAt(i)))
2494: return false;
2495: }
2496: return true;
2497: }
2498:
2499: /**
2500: * Matches an item against filter conditions.
2501: *
2502: * @param item
2503: * @return <code>true<code> if item matches against filter conditions, <code>false</code>
2504: * otherwise
2505: */
2506: public abstract boolean matchItem(Object item);
2507:
2508: /**
2509: * Checks consistency of an item. Item is inconsistent if was changed or
2510: * removed.
2511: *
2512: * @param item
2513: * @return <code>true</code> if item is consistent, <code>false</code>
2514: * if item is inconsistent
2515: */
2516: public abstract boolean isConsistentItem(Object item);
2517:
2518: }
2519:
2520: /**
2521: * An interface to content providers for
2522: * <code>FilterItemsSelectionDialog</code>.
2523: */
2524: protected abstract class AbstractContentProvider {
2525: /**
2526: * Adds the item to the content provider iff the filter matches the
2527: * item. Otherwise does nothing.
2528: *
2529: * @param item
2530: * the item to add
2531: * @param itemsFilter
2532: * the filter
2533: *
2534: * @see FilteredItemsSelectionDialog.ItemsFilter#matchItem(Object)
2535: */
2536: public abstract void add(Object item, ItemsFilter itemsFilter);
2537: }
2538:
2539: /**
2540: * Collects filtered elements. Contains one synchronized, sorted set for
2541: * collecting filtered elements. All collected elements are sorted using
2542: * comparator. Comparator is returned by getElementComparator() method.
2543: * Implementation of <code>ItemsFilter</code> is used to filter elements.
2544: * The key function of filter used in to filtering is
2545: * <code>matchElement(Object item)</code>.
2546: * <p>
2547: * The <code>ContentProvider</code> class also provides item filtering
2548: * methods. The filtering has been moved from the standard TableView
2549: * <code>getFilteredItems()</code> method to content provider, because
2550: * <code>ILazyContentProvider</code> and virtual tables are used. This
2551: * class is responsible for adding a separator below history items and
2552: * marking each items as duplicate if its name repeats more than once on the
2553: * filtered list.
2554: */
2555: private class ContentProvider extends AbstractContentProvider
2556: implements IStructuredContentProvider, ILazyContentProvider {
2557:
2558: private SelectionHistory selectionHistory;
2559:
2560: /**
2561: * Raw result of the searching (unsorted, unfiltered).
2562: * <p>
2563: * Standard object flow:
2564: * <code>items -> lastSortedItems -> lastFilteredItems</code>
2565: */
2566: private Set items;
2567:
2568: /**
2569: * Items that are duplicates.
2570: */
2571: private Set duplicates;
2572:
2573: /**
2574: * List of <code>ViewerFilter</code>s to be used during filtering
2575: */
2576: private List filters;
2577:
2578: /**
2579: * Result of the last filtering.
2580: * <p>
2581: * Standard object flow:
2582: * <code>items -> lastSortedItems -> lastFilteredItems</code>
2583: */
2584: private List lastFilteredItems;
2585:
2586: /**
2587: * Result of the last sorting.
2588: * <p>
2589: * Standard object flow:
2590: * <code>items -> lastSortedItems -> lastFilteredItems</code>
2591: */
2592: private List lastSortedItems;
2593:
2594: /**
2595: * Used for <code>getFilteredItems()</code> method canceling (when the
2596: * job that invoked the method was canceled).
2597: * <p>
2598: * Method canceling could be based (only) on monitor canceling
2599: * unfortunately sometimes the method <code>getFilteredElements()</code>
2600: * could be run with a null monitor, the <code>reset</code> flag have
2601: * to be left intact.
2602: */
2603: private boolean reset;
2604:
2605: /**
2606: * Creates new instance of <code>ContentProvider</code>.
2607: *
2608: * @param selectionHistory
2609: */
2610: public ContentProvider(SelectionHistory selectionHistory) {
2611: this ();
2612: this .selectionHistory = selectionHistory;
2613: }
2614:
2615: /**
2616: * Creates new instance of <code>ContentProvider</code>.
2617: */
2618: public ContentProvider() {
2619: this .items = Collections.synchronizedSet(new HashSet(2048));
2620: this .duplicates = Collections.synchronizedSet(new HashSet(
2621: 256));
2622: this .lastFilteredItems = new ArrayList();
2623: this .lastSortedItems = Collections
2624: .synchronizedList(new ArrayList(2048));
2625: }
2626:
2627: /**
2628: * Sets selection history.
2629: *
2630: * @param selectionHistory
2631: * The selectionHistory to set.
2632: */
2633: public void setSelectionHistory(
2634: SelectionHistory selectionHistory) {
2635: this .selectionHistory = selectionHistory;
2636: }
2637:
2638: /**
2639: * @return Returns the selectionHistory.
2640: */
2641: public SelectionHistory getSelectionHistory() {
2642: return selectionHistory;
2643: }
2644:
2645: /**
2646: * Removes all content items and resets progress message.
2647: */
2648: public void reset() {
2649: reset = true;
2650: this .items.clear();
2651: this .duplicates.clear();
2652: this .lastSortedItems.clear();
2653: }
2654:
2655: /**
2656: * Stops reloading cache - <code>getFilteredItems()</code> method.
2657: */
2658: public void stopReloadingCache() {
2659: reset = true;
2660: }
2661:
2662: /**
2663: * Adds filtered item.
2664: *
2665: * @param item
2666: * @param itemsFilter
2667: */
2668: public void add(Object item, ItemsFilter itemsFilter) {
2669: if (itemsFilter == filter) {
2670: if (itemsFilter != null) {
2671: if (itemsFilter.matchItem(item)) {
2672: this .items.add(item);
2673: }
2674: } else {
2675: this .items.add(item);
2676: }
2677: }
2678: }
2679:
2680: /**
2681: * Add all history items to <code>contentProvider</code>.
2682: *
2683: * @param itemsFilter
2684: */
2685: public void addHistoryItems(ItemsFilter itemsFilter) {
2686: if (this .selectionHistory != null) {
2687: Object[] items = this .selectionHistory
2688: .getHistoryItems();
2689: for (int i = 0; i < items.length; i++) {
2690: Object item = items[i];
2691: if (itemsFilter == filter) {
2692: if (itemsFilter != null) {
2693: if (itemsFilter.matchItem(item)) {
2694: if (itemsFilter.isConsistentItem(item)) {
2695: this .items.add(item);
2696: } else {
2697: this .selectionHistory.remove(item);
2698: }
2699: }
2700: }
2701: }
2702: }
2703: }
2704: }
2705:
2706: /**
2707: * Refresh dialog.
2708: */
2709: public void refresh() {
2710: scheduleRefresh();
2711: }
2712:
2713: /**
2714: * Removes items from history and refreshes the view.
2715: *
2716: * @param item
2717: * to remove
2718: *
2719: * @return removed item
2720: */
2721: public Object removeHistoryElement(Object item) {
2722: if (this .selectionHistory != null)
2723: this .selectionHistory.remove(item);
2724: if (filter == null || filter.getPattern().length() == 0) {
2725: items.remove(item);
2726: duplicates.remove(item);
2727: this .lastSortedItems.remove(item);
2728: }
2729:
2730: synchronized (lastSortedItems) {
2731: Collections.sort(lastSortedItems,
2732: getHistoryComparator());
2733: }
2734: return item;
2735: }
2736:
2737: /**
2738: * Adds item to history and refresh view.
2739: *
2740: * @param item
2741: * to add
2742: */
2743: public void addHistoryElement(Object item) {
2744: if (this .selectionHistory != null)
2745: this .selectionHistory.accessed(item);
2746: if (filter == null || !filter.matchItem(item)) {
2747: this .items.remove(item);
2748: this .duplicates.remove(item);
2749: this .lastSortedItems.remove(item);
2750: }
2751: synchronized (lastSortedItems) {
2752: Collections.sort(lastSortedItems,
2753: getHistoryComparator());
2754: }
2755: this .refresh();
2756: }
2757:
2758: /**
2759: * @param item
2760: * @return <code>true</code> if given item is part of the history
2761: */
2762: public boolean isHistoryElement(Object item) {
2763: if (this .selectionHistory != null) {
2764: return this .selectionHistory.contains(item);
2765: }
2766: return false;
2767: }
2768:
2769: /**
2770: * Sets/unsets given item as duplicate.
2771: *
2772: * @param item
2773: * item to change
2774: *
2775: * @param isDuplicate
2776: * duplicate flag
2777: */
2778: public void setDuplicateElement(Object item, boolean isDuplicate) {
2779: if (this .items.contains(item)) {
2780: if (isDuplicate)
2781: this .duplicates.add(item);
2782: else
2783: this .duplicates.remove(item);
2784: }
2785: }
2786:
2787: /**
2788: * Indicates whether given item is a duplicate.
2789: *
2790: * @param item
2791: * item to check
2792: * @return <code>true</code> if item is duplicate
2793: */
2794: public boolean isDuplicateElement(Object item) {
2795: return duplicates.contains(item);
2796: }
2797:
2798: /**
2799: * Load history from memento.
2800: *
2801: * @param memento
2802: * memento from which the history will be retrieved
2803: */
2804: public void loadHistory(IMemento memento) {
2805: if (this .selectionHistory != null)
2806: this .selectionHistory.load(memento);
2807: }
2808:
2809: /**
2810: * Save history to memento.
2811: *
2812: * @param memento
2813: * memento to which the history will be added
2814: */
2815: public void saveHistory(IMemento memento) {
2816: if (this .selectionHistory != null)
2817: this .selectionHistory.save(memento);
2818: }
2819:
2820: /**
2821: * Gets sorted items.
2822: *
2823: * @return sorted items
2824: */
2825: private Object[] getSortedItems() {
2826: if (lastSortedItems.size() != items.size()) {
2827: synchronized (lastSortedItems) {
2828: lastSortedItems.clear();
2829: lastSortedItems.addAll(items);
2830: Collections.sort(lastSortedItems,
2831: getHistoryComparator());
2832: }
2833: }
2834: return lastSortedItems.toArray();
2835: }
2836:
2837: /**
2838: * Remember result of filtering.
2839: *
2840: * @param itemsFilter
2841: */
2842: public void rememberResult(ItemsFilter itemsFilter) {
2843: List itemsList = Collections.synchronizedList(Arrays
2844: .asList(getSortedItems()));
2845: // synchronization
2846: if (itemsFilter == filter) {
2847: lastCompletedFilter = itemsFilter;
2848: lastCompletedResult = itemsList;
2849: }
2850:
2851: }
2852:
2853: /*
2854: * (non-Javadoc)
2855: *
2856: * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
2857: */
2858: public Object[] getElements(Object inputElement) {
2859: return lastFilteredItems.toArray();
2860: }
2861:
2862: /*
2863: * (non-Javadoc)
2864: *
2865: * @see org.eclipse.jface.viewers.IContentProvider#dispose()
2866: */
2867: public void dispose() {
2868: }
2869:
2870: /*
2871: * (non-Javadoc)
2872: *
2873: * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
2874: * java.lang.Object, java.lang.Object)
2875: */
2876: public void inputChanged(Viewer viewer, Object oldInput,
2877: Object newInput) {
2878: }
2879:
2880: /*
2881: * (non-Javadoc)
2882: *
2883: * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
2884: */
2885: public void updateElement(int index) {
2886:
2887: FilteredItemsSelectionDialog.this .list
2888: .replace(
2889: (lastFilteredItems.size() > index) ? lastFilteredItems
2890: .get(index)
2891: : null, index);
2892:
2893: }
2894:
2895: /**
2896: * Main method responsible for getting the filtered items and checking
2897: * for duplicates. It is based on the
2898: * {@link ContentProvider#getFilteredItems(Object, IProgressMonitor)}.
2899: *
2900: * @param checkDuplicates
2901: * <code>true</code> if data concerning elements
2902: * duplication should be computed - it takes much more time
2903: * than standard filtering
2904: *
2905: * @param monitor
2906: * progress monitor
2907: */
2908: public void reloadCache(boolean checkDuplicates,
2909: IProgressMonitor monitor) {
2910:
2911: reset = false;
2912:
2913: if (monitor != null) {
2914: // the work is divided into two actions of the same length
2915: int totalWork = checkDuplicates ? 200 : 100;
2916:
2917: monitor
2918: .beginTask(
2919: WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob,
2920: totalWork);
2921: }
2922:
2923: // the TableViewer's root (the input) is treated as parent
2924:
2925: lastFilteredItems = Arrays.asList(getFilteredItems(list
2926: .getInput(),
2927: monitor != null ? new SubProgressMonitor(monitor,
2928: 100) : null));
2929:
2930: if (reset || (monitor != null && monitor.isCanceled())) {
2931: if (monitor != null)
2932: monitor.done();
2933: return;
2934: }
2935:
2936: if (checkDuplicates) {
2937: checkDuplicates(monitor);
2938: }
2939: if (monitor != null)
2940: monitor.done();
2941: }
2942:
2943: private void checkDuplicates(IProgressMonitor monitor) {
2944: synchronized (lastFilteredItems) {
2945: IProgressMonitor subMonitor = null;
2946: int reportEvery = lastFilteredItems.size() / 20;
2947: if (monitor != null) {
2948: subMonitor = new SubProgressMonitor(monitor, 100);
2949: subMonitor
2950: .beginTask(
2951: WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_checkDuplicates,
2952: 5);
2953: }
2954: HashMap helperMap = new HashMap();
2955: for (int i = 0; i < lastFilteredItems.size(); i++) {
2956: if (reset
2957: || (subMonitor != null && subMonitor
2958: .isCanceled()))
2959: return;
2960: Object item = lastFilteredItems.get(i);
2961:
2962: if (!(item instanceof ItemsListSeparator)) {
2963: Object previousItem = helperMap.put(
2964: getElementName(item), item);
2965: if (previousItem != null) {
2966: setDuplicateElement(previousItem, true);
2967: setDuplicateElement(item, true);
2968: } else {
2969: setDuplicateElement(item, false);
2970: }
2971: }
2972:
2973: if (subMonitor != null && reportEvery != 0
2974: && (i + 1) % reportEvery == 0)
2975: subMonitor.worked(1);
2976: }
2977: helperMap.clear();
2978: }
2979: }
2980:
2981: /**
2982: * Returns an array of items filtered using the provided
2983: * <code>ViewerFilter</code>s with a separator added.
2984: *
2985: * @param parent
2986: * the parent
2987: * @param monitor
2988: * progress monitor, can be <code>null</code>
2989: * @return an array of filtered items
2990: */
2991: protected Object[] getFilteredItems(Object parent,
2992: IProgressMonitor monitor) {
2993: int ticks = 100;
2994:
2995: if (monitor != null) {
2996: monitor
2997: .beginTask(
2998: WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob_getFilteredElements,
2999: ticks);
3000: if (filters != null) {
3001: ticks /= (filters.size() + 2);
3002: } else {
3003: ticks /= 2;
3004: }
3005: }
3006:
3007: // get already sorted array
3008: Object[] filteredElements = getSortedItems();
3009:
3010: if (monitor != null) {
3011: monitor.worked(ticks);
3012: }
3013:
3014: // filter the elements using provided ViewerFilters
3015: if (filters != null && filteredElements != null) {
3016: for (Iterator iter = filters.iterator(); iter.hasNext();) {
3017: ViewerFilter f = (ViewerFilter) iter.next();
3018: filteredElements = f.filter(list, parent,
3019: filteredElements);
3020: if (monitor != null)
3021: monitor.worked(ticks);
3022: }
3023: }
3024:
3025: if (filteredElements == null || monitor.isCanceled()) {
3026: if (monitor != null)
3027: monitor.done();
3028: return new Object[0];
3029: }
3030:
3031: ArrayList preparedElements = new ArrayList();
3032: boolean hasHistory = false;
3033:
3034: if (filteredElements.length > 0) {
3035: if (isHistoryElement(filteredElements[0])) {
3036: hasHistory = true;
3037: }
3038: }
3039:
3040: int reportEvery = filteredElements.length / ticks;
3041:
3042: // add separator
3043: for (int i = 0; i < filteredElements.length; i++) {
3044: Object item = filteredElements[i];
3045:
3046: if (hasHistory && !isHistoryElement(item)) {
3047: preparedElements.add(itemsListSeparator);
3048: hasHistory = false;
3049: }
3050:
3051: preparedElements.add(item);
3052:
3053: if (monitor != null && reportEvery != 0
3054: && ((i + 1) % reportEvery == 0))
3055: monitor.worked(1);
3056: }
3057:
3058: if (monitor != null)
3059: monitor.done();
3060:
3061: return preparedElements.toArray();
3062: }
3063:
3064: /**
3065: * Adds a filter to this content provider. For an example usage of such
3066: * filters look at the project <code>org.eclipse.ui.ide</code>, class
3067: * <code>org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog.CustomWorkingSetFilter</code>.
3068: *
3069: *
3070: * @param filter
3071: * the filter to be added
3072: */
3073: public void addFilter(ViewerFilter filter) {
3074: if (filters == null) {
3075: filters = new ArrayList();
3076: }
3077: filters.add(filter);
3078: // currently filters are only added when dialog is restored
3079: // if it is changed, refreshing the whole TableViewer should be
3080: // added
3081: }
3082:
3083: }
3084:
3085: /**
3086: * A content provider that does nothing.
3087: */
3088: private class NullContentProvider implements IContentProvider {
3089:
3090: /*
3091: * (non-Javadoc)
3092: *
3093: * @see org.eclipse.jface.viewers.IContentProvider#dispose()
3094: */
3095: public void dispose() {
3096: }
3097:
3098: /*
3099: * (non-Javadoc)
3100: *
3101: * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
3102: * java.lang.Object, java.lang.Object)
3103: */
3104: public void inputChanged(Viewer viewer, Object oldInput,
3105: Object newInput) {
3106: }
3107:
3108: }
3109:
3110: /**
3111: * DetailsContentViewer objects are wrappers for labels.
3112: * DetailsContentViewer provides means to change label's image and text when
3113: * the attached LabelProvider is updated.
3114: */
3115: private class DetailsContentViewer extends ContentViewer {
3116:
3117: private CLabel label;
3118:
3119: /**
3120: * Unfortunately, it was impossible to delegate displaying border to
3121: * label. The <code>ViewForm</code> is used because
3122: * <code>CLabel</code> displays shadow when border is present.
3123: */
3124: private ViewForm viewForm;
3125:
3126: /**
3127: * Constructs a new instance of this class given its parent and a style
3128: * value describing its behavior and appearance.
3129: *
3130: * @param parent
3131: * the parent component
3132: * @param style
3133: * SWT style bits
3134: */
3135: public DetailsContentViewer(Composite parent, int style) {
3136: viewForm = new ViewForm(parent, style);
3137: GridData gd = new GridData(GridData.FILL_HORIZONTAL);
3138: gd.horizontalSpan = 2;
3139: viewForm.setLayoutData(gd);
3140: label = new CLabel(viewForm, SWT.FLAT);
3141: label.setFont(parent.getFont());
3142: viewForm.setContent(label);
3143: hookControl(label);
3144: }
3145:
3146: /**
3147: * Shows/hides the content viewer.
3148: *
3149: * @param visible
3150: * if the content viewer should be visible.
3151: */
3152: public void setVisible(boolean visible) {
3153: GridData gd = (GridData) viewForm.getLayoutData();
3154: gd.exclude = !visible;
3155: viewForm.getParent().layout();
3156: }
3157:
3158: /*
3159: * (non-Javadoc)
3160: *
3161: * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object,
3162: * java.lang.Object)
3163: */
3164: protected void inputChanged(Object input, Object oldInput) {
3165: if (oldInput == null) {
3166: if (input == null) {
3167: return;
3168: }
3169: refresh();
3170: return;
3171: }
3172:
3173: refresh();
3174:
3175: }
3176:
3177: /*
3178: * (non-Javadoc)
3179: *
3180: * @see org.eclipse.jface.viewers.ContentViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
3181: */
3182: protected void handleLabelProviderChanged(
3183: LabelProviderChangedEvent event) {
3184: if (event != null) {
3185: refresh(event.getElements());
3186: }
3187: }
3188:
3189: /*
3190: * (non-Javadoc)
3191: *
3192: * @see org.eclipse.jface.viewers.Viewer#getControl()
3193: */
3194: public Control getControl() {
3195: return label;
3196: }
3197:
3198: /*
3199: * (non-Javadoc)
3200: *
3201: * @see org.eclipse.jface.viewers.Viewer#getSelection()
3202: */
3203: public ISelection getSelection() {
3204: // not supported
3205: return null;
3206: }
3207:
3208: /*
3209: * (non-Javadoc)
3210: *
3211: * @see org.eclipse.jface.viewers.Viewer#refresh()
3212: */
3213: public void refresh() {
3214: Object input = this .getInput();
3215: if (input != null) {
3216: ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();
3217: doRefresh(labelProvider.getText(input), labelProvider
3218: .getImage(input));
3219: } else {
3220: doRefresh(null, null);
3221: }
3222: }
3223:
3224: /**
3225: * Sets the given text and image to the label.
3226: *
3227: * @param text
3228: * the new text or null
3229: * @param image
3230: * the new image
3231: */
3232: private void doRefresh(String text, Image image) {
3233: label.setText(text);
3234: label.setImage(image);
3235: }
3236:
3237: /*
3238: * (non-Javadoc)
3239: *
3240: * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection,
3241: * boolean)
3242: */
3243: public void setSelection(ISelection selection, boolean reveal) {
3244: // not supported
3245: }
3246:
3247: /**
3248: * Refreshes the label if currently chosen element is on the list.
3249: *
3250: * @param objs
3251: * list of changed object
3252: */
3253: private void refresh(Object[] objs) {
3254: if (objs == null || getInput() == null) {
3255: return;
3256: }
3257: Object input = getInput();
3258: for (int i = 0; i < objs.length; i++) {
3259: if (objs[i].equals(input)) {
3260: refresh();
3261: break;
3262: }
3263: }
3264: }
3265: }
3266:
3267: /**
3268: * Compares items using camel case method.
3269: */
3270: private class CamelCaseComparator implements Comparator {
3271:
3272: /*
3273: * (non-Javadoc)
3274: *
3275: * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3276: */
3277: public int compare(Object o1, Object o2) {
3278:
3279: int leftCategory = getCamelCaseCategory(o1);
3280: int rightCategory = getCamelCaseCategory(o2);
3281: if (leftCategory < rightCategory)
3282: return -1;
3283: if (leftCategory > rightCategory)
3284: return +1;
3285:
3286: return getItemsComparator().compare(o1, o2);
3287: }
3288:
3289: private int getCamelCaseCategory(Object item) {
3290: if (filter == null)
3291: return 0;
3292: if (!filter.isCamelCasePattern())
3293: return 0;
3294: return filter.matchesRawNamePattern(item) ? 0 : 1;
3295: }
3296: }
3297:
3298: /**
3299: * Compares items according to the history.
3300: */
3301: private class HistoryComparator implements Comparator {
3302:
3303: private CamelCaseComparator camelCaseComparator;
3304:
3305: /**
3306: *
3307: */
3308: public HistoryComparator() {
3309: this .camelCaseComparator = new CamelCaseComparator();
3310: }
3311:
3312: /*
3313: * (non-Javadoc)
3314: *
3315: * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
3316: */
3317: public int compare(Object o1, Object o2) {
3318: if ((isHistoryElement(o1) && isHistoryElement(o2))
3319: || (!isHistoryElement(o1) && !isHistoryElement(o2)))
3320: return this .camelCaseComparator.compare(o1, o2);
3321:
3322: if (isHistoryElement(o1))
3323: return -2;
3324: if (isHistoryElement(o2))
3325: return +2;
3326:
3327: return 0;
3328: }
3329:
3330: }
3331:
3332: /**
3333: * Get the control where the search pattern is entered. Any filtering should
3334: * be done using an {@link ItemsFilter}. This control should only be
3335: * accessed for listeners that wish to handle events that do not affect
3336: * filtering such as custom traversal.
3337: *
3338: * @return Control or <code>null</code> if the pattern control has not
3339: * been created.
3340: */
3341: public Control getPatternControl() {
3342: return pattern;
3343: }
3344:
3345: }
|