0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2006 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: * Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog font
0011: * should be activated and used by other components.
0012: *******************************************************************************/package org.eclipse.ui.dialogs;
0013:
0014: import com.ibm.icu.text.Collator;
0015: import java.util.ArrayList;
0016: import java.util.Arrays;
0017: import java.util.Collections;
0018: import java.util.Comparator;
0019:
0020: import org.eclipse.core.resources.IContainer;
0021: import org.eclipse.core.resources.IResource;
0022: import org.eclipse.core.resources.IResourceProxy;
0023: import org.eclipse.core.resources.IResourceProxyVisitor;
0024: import org.eclipse.core.runtime.CoreException;
0025: import org.eclipse.jface.dialogs.IDialogConstants;
0026: import org.eclipse.jface.dialogs.IDialogSettings;
0027: import org.eclipse.swt.SWT;
0028: import org.eclipse.swt.custom.BusyIndicator;
0029: import org.eclipse.swt.events.KeyAdapter;
0030: import org.eclipse.swt.events.KeyEvent;
0031: import org.eclipse.swt.events.ModifyEvent;
0032: import org.eclipse.swt.events.ModifyListener;
0033: import org.eclipse.swt.events.SelectionAdapter;
0034: import org.eclipse.swt.events.SelectionEvent;
0035: import org.eclipse.swt.graphics.Image;
0036: import org.eclipse.swt.layout.GridData;
0037: import org.eclipse.swt.widgets.Button;
0038: import org.eclipse.swt.widgets.Composite;
0039: import org.eclipse.swt.widgets.Control;
0040: import org.eclipse.swt.widgets.Display;
0041: import org.eclipse.swt.widgets.Label;
0042: import org.eclipse.swt.widgets.Shell;
0043: import org.eclipse.swt.widgets.Table;
0044: import org.eclipse.swt.widgets.TableItem;
0045: import org.eclipse.swt.widgets.Text;
0046: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
0047: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
0048: import org.eclipse.ui.internal.ide.StringMatcher;
0049: import org.eclipse.ui.model.WorkbenchLabelProvider;
0050:
0051: /**
0052: * Shows a list of resources to the user with a text entry field
0053: * for a string pattern used to filter the list of resources.
0054: * <p>
0055: *
0056: * @since 2.1
0057: */
0058: public class ResourceListSelectionDialog extends SelectionDialog {
0059:
0060: private static final String DIALOG_SETTINGS_SECTION = "ResourceListSelectionDialogSettings"; //$NON-NLS-1$
0061:
0062: Text pattern;
0063:
0064: Table resourceNames;
0065:
0066: Table folderNames;
0067:
0068: String patternString;
0069:
0070: IContainer container;
0071:
0072: int typeMask;
0073:
0074: private static Collator collator = Collator.getInstance();
0075:
0076: boolean gatherResourcesDynamically = true;
0077:
0078: StringMatcher stringMatcher;
0079:
0080: UpdateFilterThread updateFilterThread;
0081:
0082: UpdateGatherThread updateGatherThread;
0083:
0084: ResourceDescriptor[] descriptors;
0085:
0086: int descriptorsSize;
0087:
0088: WorkbenchLabelProvider labelProvider = new WorkbenchLabelProvider();
0089:
0090: boolean okEnabled = false;
0091:
0092: private boolean showDerived = false;
0093:
0094: private Button showDerivedButton;
0095:
0096: private boolean allowUserToToggleDerived;
0097:
0098: static class ResourceDescriptor implements Comparable {
0099: String label;
0100:
0101: ArrayList resources = new ArrayList();
0102:
0103: boolean resourcesSorted = true;
0104:
0105: public int compareTo(Object o) {
0106: return collator.compare(label,
0107: ((ResourceDescriptor) o).label);
0108: }
0109: }
0110:
0111: class UpdateFilterThread extends Thread {
0112: boolean stop = false;
0113:
0114: int firstMatch = 0;
0115:
0116: int lastMatch = descriptorsSize - 1;
0117:
0118: public void run() {
0119: Display display = resourceNames.getDisplay();
0120: final int itemIndex[] = { 0 };
0121: final int itemCount[] = { 0 };
0122: //Keep track of if the widget got disposed
0123: //so that we can abort if required
0124: final boolean[] disposed = { false };
0125: display.syncExec(new Runnable() {
0126: public void run() {
0127: //Be sure the widget still exists
0128: if (resourceNames.isDisposed()) {
0129: disposed[0] = true;
0130: return;
0131: }
0132: itemCount[0] = resourceNames.getItemCount();
0133: }
0134: });
0135:
0136: if (disposed[0]) {
0137: return;
0138: }
0139:
0140: int last;
0141: if ((patternString.indexOf('?') == -1)
0142: && (patternString.endsWith("*")) && //$NON-NLS-1$
0143: (patternString.indexOf('*') == patternString
0144: .length() - 1)) {
0145: // Use a binary search to get first and last match when the pattern
0146: // string ends with "*" and has no other embedded special characters.
0147: // For this case, we can be smarter about getting the first and last
0148: // match since the items are in sorted order.
0149: firstMatch = getFirstMatch();
0150: if (firstMatch == -1) {
0151: firstMatch = 0;
0152: lastMatch = -1;
0153: } else {
0154: lastMatch = getLastMatch();
0155: }
0156: last = lastMatch;
0157: for (int i = firstMatch; i <= lastMatch; i++) {
0158: if (i % 50 == 0) {
0159: try {
0160: Thread.sleep(10);
0161: } catch (InterruptedException e) {
0162: // ignore
0163: }
0164: }
0165: if (stop || resourceNames.isDisposed()) {
0166: disposed[0] = true;
0167: return;
0168: }
0169: final int index = i;
0170: display.syncExec(new Runnable() {
0171: public void run() {
0172: if (stop || resourceNames.isDisposed()) {
0173: return;
0174: }
0175: updateItem(index, itemIndex[0],
0176: itemCount[0]);
0177: itemIndex[0]++;
0178: }
0179: });
0180: }
0181: } else {
0182: last = lastMatch;
0183: boolean setFirstMatch = true;
0184: for (int i = firstMatch; i <= lastMatch; i++) {
0185: if (i % 50 == 0) {
0186: try {
0187: Thread.sleep(10);
0188: } catch (InterruptedException e) {
0189: // ignore
0190: }
0191: }
0192: if (stop || resourceNames.isDisposed()) {
0193: disposed[0] = true;
0194: return;
0195: }
0196: final int index = i;
0197: if (match(descriptors[index].label)) {
0198: if (setFirstMatch) {
0199: setFirstMatch = false;
0200: firstMatch = index;
0201: }
0202: last = index;
0203: display.syncExec(new Runnable() {
0204: public void run() {
0205: if (stop || resourceNames.isDisposed()) {
0206: return;
0207: }
0208: updateItem(index, itemIndex[0],
0209: itemCount[0]);
0210: itemIndex[0]++;
0211: }
0212: });
0213: }
0214: }
0215: }
0216:
0217: if (disposed[0]) {
0218: return;
0219: }
0220:
0221: lastMatch = last;
0222: display.syncExec(new Runnable() {
0223: public void run() {
0224: if (resourceNames.isDisposed()) {
0225: return;
0226: }
0227: itemCount[0] = resourceNames.getItemCount();
0228: if (itemIndex[0] < itemCount[0]) {
0229: resourceNames.setRedraw(false);
0230: resourceNames.remove(itemIndex[0],
0231: itemCount[0] - 1);
0232: resourceNames.setRedraw(true);
0233: }
0234: // If no resources, remove remaining folder entries
0235: if (resourceNames.getItemCount() == 0) {
0236: folderNames.removeAll();
0237: updateOKState(false);
0238: }
0239: }
0240: });
0241: }
0242: }
0243:
0244: class UpdateGatherThread extends Thread {
0245: boolean stop = false;
0246:
0247: int lastMatch = -1;
0248:
0249: int firstMatch = 0;
0250:
0251: boolean refilter = false;
0252:
0253: public void run() {
0254: Display display = resourceNames.getDisplay();
0255: final int itemIndex[] = { 0 };
0256: final int itemCount[] = { 0 };
0257: //Keep track of if the widget got disposed
0258: //so that we can abort if required
0259: final boolean[] disposed = { false };
0260: display.syncExec(new Runnable() {
0261: public void run() {
0262: //Be sure the widget still exists
0263: if (resourceNames.isDisposed()) {
0264: disposed[0] = true;
0265: return;
0266: }
0267: itemCount[0] = resourceNames.getItemCount();
0268: }
0269: });
0270:
0271: if (disposed[0]) {
0272: return;
0273: }
0274:
0275: if (!refilter) {
0276: for (int i = 0; i <= lastMatch; i++) {
0277: if (i % 50 == 0) {
0278: try {
0279: Thread.sleep(10);
0280: } catch (InterruptedException e) {
0281: // ignore
0282: }
0283: }
0284: if (stop || resourceNames.isDisposed()) {
0285: disposed[0] = true;
0286: return;
0287: }
0288: final int index = i;
0289: display.syncExec(new Runnable() {
0290: public void run() {
0291: if (stop || resourceNames.isDisposed()) {
0292: return;
0293: }
0294: updateItem(index, itemIndex[0],
0295: itemCount[0]);
0296: itemIndex[0]++;
0297: }
0298: });
0299: }
0300: } else {
0301: // we're filtering the previous list
0302: for (int i = firstMatch; i <= lastMatch; i++) {
0303: if (i % 50 == 0) {
0304: try {
0305: Thread.sleep(10);
0306: } catch (InterruptedException e) {
0307: // ignore
0308: }
0309: }
0310: if (stop || resourceNames.isDisposed()) {
0311: disposed[0] = true;
0312: return;
0313: }
0314: final int index = i;
0315: if (match(descriptors[index].label)) {
0316: display.syncExec(new Runnable() {
0317: public void run() {
0318: if (stop || resourceNames.isDisposed()) {
0319: return;
0320: }
0321: updateItem(index, itemIndex[0],
0322: itemCount[0]);
0323: itemIndex[0]++;
0324: }
0325: });
0326: }
0327: }
0328: }
0329:
0330: if (disposed[0]) {
0331: return;
0332: }
0333:
0334: display.syncExec(new Runnable() {
0335: public void run() {
0336: if (resourceNames.isDisposed()) {
0337: return;
0338: }
0339: itemCount[0] = resourceNames.getItemCount();
0340: if (itemIndex[0] < itemCount[0]) {
0341: resourceNames.setRedraw(false);
0342: resourceNames.remove(itemIndex[0],
0343: itemCount[0] - 1);
0344: resourceNames.setRedraw(true);
0345: }
0346: // If no resources, remove remaining folder entries
0347: if (resourceNames.getItemCount() == 0) {
0348: folderNames.removeAll();
0349: updateOKState(false);
0350: }
0351: }
0352: });
0353: }
0354: }
0355:
0356: /**
0357: * Creates a new instance of the class.
0358: *
0359: * @param parentShell shell to parent the dialog on
0360: * @param resources resources to display in the dialog
0361: */
0362: public ResourceListSelectionDialog(Shell parentShell,
0363: IResource[] resources) {
0364: super (parentShell);
0365: gatherResourcesDynamically = false;
0366: initDescriptors(resources);
0367: }
0368:
0369: /**
0370: * Creates a new instance of the class. When this constructor is used to
0371: * create the dialog, resources will be gathered dynamically as the pattern
0372: * string is specified. Only resources of the given types that match the
0373: * pattern string will be listed. To further filter the matching resources,
0374: * @see #select(IResource)
0375: *
0376: * @param parentShell shell to parent the dialog on
0377: * @param container container to get resources from
0378: * @param typeMask mask containing IResource types to be considered
0379: */
0380: public ResourceListSelectionDialog(Shell parentShell,
0381: IContainer container, int typeMask) {
0382: super (parentShell);
0383: this .container = container;
0384: this .typeMask = typeMask;
0385: }
0386:
0387: /**
0388: * Adjust the pattern string for matching.
0389: */
0390: protected String adjustPattern() {
0391: String text = pattern.getText().trim();
0392: if (text.endsWith("<")) { //$NON-NLS-1$
0393: // the < character indicates an exact match search
0394: return text.substring(0, text.length() - 1);
0395: }
0396: if (!text.equals("") && !text.endsWith("*")) { //$NON-NLS-1$ //$NON-NLS-2$
0397: return text + "*"; //$NON-NLS-1$
0398: }
0399: return text;
0400: }
0401:
0402: /**
0403: * @see org.eclipse.jface.dialogs.Dialog#cancelPressed()
0404: */
0405: protected void cancelPressed() {
0406: setResult(null);
0407: super .cancelPressed();
0408: }
0409:
0410: /**
0411: * @see org.eclipse.jface.window.Window#close()
0412: */
0413: public boolean close() {
0414: boolean result = super .close();
0415: labelProvider.dispose();
0416: return result;
0417: }
0418:
0419: /**
0420: * @see org.eclipse.jface.window.Window#create()
0421: */
0422: public void create() {
0423: super .create();
0424: pattern.setFocus();
0425: getButton(IDialogConstants.OK_ID).setEnabled(okEnabled);
0426: }
0427:
0428: /**
0429: * Creates the contents of this dialog, initializes the
0430: * listener and the update thread.
0431: *
0432: * @param parent parent to create the dialog widgets in
0433: */
0434: protected Control createDialogArea(Composite parent) {
0435:
0436: Composite dialogArea = (Composite) super
0437: .createDialogArea(parent);
0438: Label l = new Label(dialogArea, SWT.NONE);
0439: l.setText(IDEWorkbenchMessages.ResourceSelectionDialog_label);
0440: GridData data = new GridData(GridData.FILL_HORIZONTAL);
0441: l.setLayoutData(data);
0442:
0443: pattern = new Text(dialogArea, SWT.SINGLE | SWT.BORDER);
0444: pattern.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
0445: l = new Label(dialogArea, SWT.NONE);
0446: l
0447: .setText(IDEWorkbenchMessages.ResourceSelectionDialog_matching);
0448: data = new GridData(GridData.FILL_HORIZONTAL);
0449: l.setLayoutData(data);
0450: resourceNames = new Table(dialogArea, SWT.SINGLE | SWT.BORDER
0451: | SWT.V_SCROLL);
0452: data = new GridData(GridData.FILL_BOTH);
0453: data.heightHint = 12 * resourceNames.getItemHeight();
0454: resourceNames.setLayoutData(data);
0455:
0456: l = new Label(dialogArea, SWT.NONE);
0457: l.setText(IDEWorkbenchMessages.ResourceSelectionDialog_folders);
0458: data = new GridData(GridData.FILL_HORIZONTAL);
0459: l.setLayoutData(data);
0460:
0461: folderNames = new Table(dialogArea, SWT.SINGLE | SWT.BORDER
0462: | SWT.V_SCROLL | SWT.H_SCROLL);
0463: data = new GridData(GridData.FILL_BOTH);
0464: data.widthHint = 300;
0465: data.heightHint = 4 * folderNames.getItemHeight();
0466: folderNames.setLayoutData(data);
0467:
0468: if (gatherResourcesDynamically) {
0469: updateGatherThread = new UpdateGatherThread();
0470: } else {
0471: updateFilterThread = new UpdateFilterThread();
0472: }
0473:
0474: pattern.addKeyListener(new KeyAdapter() {
0475: public void keyReleased(KeyEvent e) {
0476: if (e.keyCode == SWT.ARROW_DOWN) {
0477: resourceNames.setFocus();
0478: }
0479: }
0480: });
0481:
0482: pattern.addModifyListener(new ModifyListener() {
0483: public void modifyText(ModifyEvent e) {
0484: refresh(false);
0485: }
0486: });
0487:
0488: resourceNames.addSelectionListener(new SelectionAdapter() {
0489: public void widgetSelected(SelectionEvent e) {
0490: updateFolders((ResourceDescriptor) e.item.getData());
0491: }
0492:
0493: public void widgetDefaultSelected(SelectionEvent e) {
0494: okPressed();
0495: }
0496: });
0497:
0498: folderNames.addSelectionListener(new SelectionAdapter() {
0499: public void widgetDefaultSelected(SelectionEvent e) {
0500: okPressed();
0501: }
0502: });
0503:
0504: if (getAllowUserToToggleDerived()) {
0505: showDerivedButton = new Button(dialogArea, SWT.CHECK);
0506: showDerivedButton
0507: .setText(IDEWorkbenchMessages.ResourceSelectionDialog_showDerived);
0508: showDerivedButton
0509: .addSelectionListener(new SelectionAdapter() {
0510: public void widgetSelected(SelectionEvent e) {
0511: setShowDerived(showDerivedButton
0512: .getSelection());
0513: refresh(true);
0514: }
0515: });
0516: showDerivedButton.setSelection(getShowDerived());
0517: }
0518:
0519: applyDialogFont(dialogArea);
0520: return dialogArea;
0521: }
0522:
0523: /**
0524: * Returns whether to include a "Show derived resources" checkbox in the dialog.
0525: * The default is <code>false</code>.
0526: *
0527: * @return <code>true</code> to include the checkbox, <code>false</code> to omit
0528: * @since 3.1
0529: */
0530: public boolean getAllowUserToToggleDerived() {
0531: return allowUserToToggleDerived;
0532: }
0533:
0534: /**
0535: * Sets whether to include a "Show derived resources" checkbox in the dialog.
0536: *
0537: * @param allow <code>true</code> to include the checkbox, <code>false</code> to omit
0538: * @since 3.1
0539: */
0540: public void setAllowUserToToggleDerived(boolean allow) {
0541: allowUserToToggleDerived = allow;
0542: }
0543:
0544: /**
0545: */
0546: private void filterResources(boolean force) {
0547: String oldPattern = force ? null : patternString;
0548: patternString = adjustPattern();
0549: if (!force && patternString.equals(oldPattern)) {
0550: return;
0551: }
0552:
0553: updateFilterThread.stop = true;
0554: stringMatcher = new StringMatcher(patternString, true, false);
0555: UpdateFilterThread oldThread = updateFilterThread;
0556: updateFilterThread = new UpdateFilterThread();
0557: if (patternString.equals("")) { //$NON-NLS-1$
0558: updateFilterThread.firstMatch = 0;
0559: updateFilterThread.lastMatch = -1;
0560: updateFilterThread.start();
0561: return;
0562: }
0563:
0564: if (oldPattern != null
0565: && (oldPattern.length() != 0)
0566: && oldPattern.endsWith("*") && patternString.endsWith("*")) { //$NON-NLS-1$ //$NON-NLS-2$
0567: int matchLength = oldPattern.length() - 1;
0568: if (patternString.regionMatches(0, oldPattern, 0,
0569: matchLength)) {
0570: // filter the previous list of items, this is done when the
0571: // new pattern is a derivative of the old pattern
0572: updateFilterThread.firstMatch = oldThread.firstMatch;
0573: updateFilterThread.lastMatch = oldThread.lastMatch;
0574: updateFilterThread.start();
0575: return;
0576: }
0577: }
0578:
0579: // filter the entire list
0580: updateFilterThread.firstMatch = 0;
0581: updateFilterThread.lastMatch = descriptorsSize - 1;
0582: updateFilterThread.start();
0583: }
0584:
0585: /**
0586: * Use a binary search to get the first match for the patternString.
0587: * This method assumes the patternString does not contain any '?'
0588: * characters and that it contains only one '*' character at the end
0589: * of the string.
0590: */
0591: private int getFirstMatch() {
0592: int high = descriptorsSize;
0593: int low = -1;
0594: boolean match = false;
0595: ResourceDescriptor desc = new ResourceDescriptor();
0596: desc.label = patternString.substring(0,
0597: patternString.length() - 1);
0598: while (high - low > 1) {
0599: int index = (high + low) / 2;
0600: String label = descriptors[index].label;
0601: if (match(label)) {
0602: high = index;
0603: match = true;
0604: } else {
0605: int compare = descriptors[index].compareTo(desc);
0606: if (compare == -1) {
0607: low = index;
0608: } else {
0609: high = index;
0610: }
0611: }
0612: }
0613: if (match) {
0614: return high;
0615: }
0616: return -1;
0617: }
0618:
0619: /**
0620: */
0621: private void gatherResources(boolean force) {
0622: String oldPattern = force ? null : patternString;
0623: patternString = adjustPattern();
0624: if (!force && patternString.equals(oldPattern)) {
0625: return;
0626: }
0627:
0628: updateGatherThread.stop = true;
0629: updateGatherThread = new UpdateGatherThread();
0630:
0631: if (patternString.equals("")) { //$NON-NLS-1$
0632: updateGatherThread.start();
0633: return;
0634: }
0635: stringMatcher = new StringMatcher(patternString, true, false);
0636:
0637: if (oldPattern != null
0638: && (oldPattern.length() != 0)
0639: && oldPattern.endsWith("*") && patternString.endsWith("*")) { //$NON-NLS-1$ //$NON-NLS-2$
0640: // see if the new pattern is a derivative of the old pattern
0641: int matchLength = oldPattern.length() - 1;
0642: if (patternString.regionMatches(0, oldPattern, 0,
0643: matchLength)) {
0644: updateGatherThread.refilter = true;
0645: updateGatherThread.firstMatch = 0;
0646: updateGatherThread.lastMatch = descriptorsSize - 1;
0647: updateGatherThread.start();
0648: return;
0649: }
0650: }
0651:
0652: final ArrayList resources = new ArrayList();
0653: BusyIndicator.showWhile(getShell().getDisplay(),
0654: new Runnable() {
0655: public void run() {
0656: getMatchingResources(resources);
0657: IResource resourcesArray[] = new IResource[resources
0658: .size()];
0659: resources.toArray(resourcesArray);
0660: initDescriptors(resourcesArray);
0661: }
0662: });
0663:
0664: updateGatherThread.firstMatch = 0;
0665: updateGatherThread.lastMatch = descriptorsSize - 1;
0666: updateGatherThread.start();
0667: }
0668:
0669: /**
0670: * Return an image for a resource descriptor.
0671: *
0672: * @param desc resource descriptor to return image for
0673: * @return an image for a resource descriptor.
0674: */
0675: private Image getImage(ResourceDescriptor desc) {
0676: IResource r = (IResource) desc.resources.get(0);
0677: return labelProvider.getImage(r);
0678: }
0679:
0680: /**
0681: * Use a binary search to get the last match for the patternString.
0682: * This method assumes the patternString does not contain any '?'
0683: * characters and that it contains only one '*' character at the end
0684: * of the string.
0685: */
0686: private int getLastMatch() {
0687: int high = descriptorsSize;
0688: int low = -1;
0689: boolean match = false;
0690: ResourceDescriptor desc = new ResourceDescriptor();
0691: desc.label = patternString.substring(0,
0692: patternString.length() - 1);
0693: while (high - low > 1) {
0694: int index = (high + low) / 2;
0695: String label = descriptors[index].label;
0696: if (match(label)) {
0697: low = index;
0698: match = true;
0699: } else {
0700: int compare = descriptors[index].compareTo(desc);
0701: if (compare == -1) {
0702: low = index;
0703: } else {
0704: high = index;
0705: }
0706: }
0707: }
0708: if (match) {
0709: return low;
0710: }
0711: return -1;
0712: }
0713:
0714: /**
0715: * Gather the resources of the specified type that match the current
0716: * pattern string. Gather the resources using the proxy visitor since
0717: * this is quicker than getting the entire resource.
0718: *
0719: * @param resources resources that match
0720: */
0721: private void getMatchingResources(final ArrayList resources) {
0722: try {
0723: container.accept(new IResourceProxyVisitor() {
0724: public boolean visit(IResourceProxy proxy) {
0725: // optionally exclude derived resources (bugs 38085 and 81333)
0726: if (!getShowDerived() && proxy.isDerived()) {
0727: return false;
0728: }
0729: int type = proxy.getType();
0730: if ((typeMask & type) != 0) {
0731: if (match(proxy.getName())) {
0732: IResource res = proxy.requestResource();
0733: if (select(res)) {
0734: resources.add(res);
0735: return true;
0736: }
0737: return false;
0738: }
0739: }
0740: if (type == IResource.FILE) {
0741: return false;
0742: }
0743: return true;
0744: }
0745: }, IResource.NONE);
0746: } catch (CoreException e) {
0747: // ignore
0748: }
0749: }
0750:
0751: private Image getParentImage(IResource resource) {
0752: IResource parent = resource.getParent();
0753: return labelProvider.getImage(parent);
0754: }
0755:
0756: private String getParentLabel(IResource resource) {
0757: IResource parent = resource.getParent();
0758: String text;
0759: if (parent.getType() == IResource.ROOT) {
0760: // Get readable name for workspace root ("Workspace"), without duplicating language-specific string here.
0761: text = labelProvider.getText(parent);
0762: } else {
0763: text = parent.getFullPath().makeRelative().toString();
0764: }
0765: if (text == null) {
0766: return ""; //$NON-NLS-1$
0767: }
0768: return text;
0769: }
0770:
0771: /**
0772: * Returns whether derived resources should be shown in the list.
0773: * The default is <code>false</code>.
0774: *
0775: * @return <code>true</code> to show derived resources, <code>false</code> to hide them
0776: * @since 3.1
0777: */
0778: protected boolean getShowDerived() {
0779: return showDerived;
0780: }
0781:
0782: /**
0783: * Sets whether derived resources should be shown in the list.
0784: *
0785: * @param show <code>true</code> to show derived resources, <code>false</code> to hide them
0786: * @since 3.1
0787: */
0788: protected void setShowDerived(boolean show) {
0789: showDerived = show;
0790: }
0791:
0792: /**
0793: * Creates a ResourceDescriptor for each IResource,
0794: * sorts them and removes the duplicated ones.
0795: *
0796: * @param resources resources to create resource descriptors for
0797: */
0798: private void initDescriptors(final IResource resources[]) {
0799: BusyIndicator.showWhile(null, new Runnable() {
0800: public void run() {
0801: descriptors = new ResourceDescriptor[resources.length];
0802: for (int i = 0; i < resources.length; i++) {
0803: IResource r = resources[i];
0804: ResourceDescriptor d = new ResourceDescriptor();
0805: //TDB: Should use the label provider and compare performance.
0806: d.label = r.getName();
0807: d.resources.add(r);
0808: descriptors[i] = d;
0809: }
0810: Arrays.sort(descriptors);
0811: descriptorsSize = descriptors.length;
0812:
0813: //Merge the resource descriptor with the same label and type.
0814: int index = 0;
0815: if (descriptorsSize < 2) {
0816: return;
0817: }
0818: ResourceDescriptor current = descriptors[index];
0819: IResource currentResource = (IResource) current.resources
0820: .get(0);
0821: for (int i = 1; i < descriptorsSize; i++) {
0822: ResourceDescriptor next = descriptors[i];
0823: IResource nextResource = (IResource) next.resources
0824: .get(0);
0825: if (nextResource.getType() == currentResource
0826: .getType()
0827: && next.label.equals(current.label)) {
0828: current.resources.add(nextResource);
0829: // If we are merging resources with the same name, into a single descriptor,
0830: // then we must mark the descriptor unsorted so that we will sort the folder
0831: // names.
0832: // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=76496
0833: current.resourcesSorted = false;
0834: } else {
0835: if (current.resources.size() > 1) {
0836: current.resourcesSorted = false;
0837: }
0838: descriptors[index + 1] = descriptors[i];
0839: index++;
0840: current = descriptors[index];
0841: currentResource = (IResource) current.resources
0842: .get(0);
0843: }
0844: }
0845: descriptorsSize = index + 1;
0846: }
0847: });
0848: }
0849:
0850: /**
0851: * Returns true if the label matches the chosen pattern.
0852: *
0853: * @param label label to match with the current pattern
0854: * @return true if the label matches the chosen pattern.
0855: * false otherwise.
0856: */
0857: private boolean match(String label) {
0858: if ((patternString == null)
0859: || (patternString.equals("")) || (patternString.equals("*"))) { //$NON-NLS-1$ //$NON-NLS-2$
0860: return true;
0861: }
0862: return stringMatcher.match(label);
0863: }
0864:
0865: /**
0866: * The user has selected a resource and the dialog is closing.
0867: * Set the selected resource as the dialog result.
0868: */
0869: protected void okPressed() {
0870: TableItem items[] = folderNames.getSelection();
0871: if (items.length == 1) {
0872: ArrayList result = new ArrayList();
0873: result.add(items[0].getData());
0874: setResult(result);
0875: }
0876: super .okPressed();
0877: }
0878:
0879: /**
0880: * Use this method to further filter resources. As resources are gathered,
0881: * if a resource matches the current pattern string, this method will be called.
0882: * If this method answers false, the resource will not be included in the list
0883: * of matches and the resource's children will NOT be considered for matching.
0884: */
0885: protected boolean select(IResource resource) {
0886: return true;
0887: }
0888:
0889: /**
0890: * Refreshes the filtered list of resources.
0891: * Called when the text in the pattern text entry has changed.
0892: *
0893: * @param force if <code>true</code> a refresh is forced, if <code>false</code> a refresh only
0894: * occurs if the pattern has changed
0895: *
0896: * @since 3.1
0897: */
0898: protected void refresh(boolean force) {
0899: if (gatherResourcesDynamically) {
0900: gatherResources(force);
0901: } else {
0902: filterResources(force);
0903: }
0904: }
0905:
0906: /**
0907: * A new resource has been selected. Change the contents
0908: * of the folder names list.
0909: *
0910: * @desc resource descriptor of the selected resource
0911: */
0912: private void updateFolders(final ResourceDescriptor desc) {
0913: BusyIndicator.showWhile(getShell().getDisplay(),
0914: new Runnable() {
0915: public void run() {
0916: if (!desc.resourcesSorted) {
0917: // sort the folder names
0918: Collections.sort(desc.resources,
0919: new Comparator() {
0920: public int compare(Object o1,
0921: Object o2) {
0922: String s1 = getParentLabel((IResource) o1);
0923: String s2 = getParentLabel((IResource) o2);
0924: return collator.compare(s1,
0925: s2);
0926: }
0927: });
0928: desc.resourcesSorted = true;
0929: }
0930: folderNames.removeAll();
0931: for (int i = 0; i < desc.resources.size(); i++) {
0932: TableItem newItem = new TableItem(
0933: folderNames, SWT.NONE);
0934: IResource r = (IResource) desc.resources
0935: .get(i);
0936: newItem.setText(getParentLabel(r));
0937: newItem.setImage(getParentImage(r));
0938: newItem.setData(r);
0939: }
0940: folderNames.setSelection(0);
0941: }
0942: });
0943: }
0944:
0945: /**
0946: * Update the specified item with the new info from the resource
0947: * descriptor.
0948: * Create a new table item if there is no item.
0949: *
0950: * @param index index of the resource descriptor
0951: * @param itemPos position of the existing item to update
0952: * @param itemCount number of items in the resources table widget
0953: */
0954: private void updateItem(int index, int itemPos, int itemCount) {
0955: ResourceDescriptor desc = descriptors[index];
0956: TableItem item;
0957: if (itemPos < itemCount) {
0958: item = resourceNames.getItem(itemPos);
0959: if (item.getData() != desc) {
0960: item.setText(desc.label);
0961: item.setData(desc);
0962: item.setImage(getImage(desc));
0963: if (itemPos == 0) {
0964: resourceNames.setSelection(0);
0965: updateFolders(desc);
0966: }
0967: }
0968: } else {
0969: item = new TableItem(resourceNames, SWT.NONE);
0970: item.setText(desc.label);
0971: item.setData(desc);
0972: item.setImage(getImage(desc));
0973: if (itemPos == 0) {
0974: resourceNames.setSelection(0);
0975: updateFolders(desc);
0976: }
0977: }
0978: updateOKState(true);
0979: }
0980:
0981: /**
0982: * Update the enabled state of the OK button. To be called when
0983: * the resource list is updated.
0984: * @param state the new enabled state of the button
0985: */
0986: protected void updateOKState(boolean state) {
0987: Button okButton = getButton(IDialogConstants.OK_ID);
0988: if (okButton != null && !okButton.isDisposed()
0989: && state != okEnabled) {
0990: okButton.setEnabled(state);
0991: okEnabled = state;
0992: }
0993: }
0994:
0995: /* (non-Javadoc)
0996: * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
0997: *
0998: * @since 3.2
0999: */
1000: protected IDialogSettings getDialogBoundsSettings() {
1001: IDialogSettings settings = IDEWorkbenchPlugin.getDefault()
1002: .getDialogSettings();
1003: IDialogSettings section = settings
1004: .getSection(DIALOG_SETTINGS_SECTION);
1005: if (section == null) {
1006: section = settings.addNewSection(DIALOG_SETTINGS_SECTION);
1007: }
1008: return section;
1009: }
1010: }
|