KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > dialogs > ResourceListSelectionDialog


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog font
11  * should be activated and used by other components.
12  *******************************************************************************/

13 package org.eclipse.ui.dialogs;
14
15 import com.ibm.icu.text.Collator;
16 import java.util.ArrayList JavaDoc;
17 import java.util.Arrays JavaDoc;
18 import java.util.Collections JavaDoc;
19 import java.util.Comparator JavaDoc;
20
21 import org.eclipse.core.resources.IContainer;
22 import org.eclipse.core.resources.IResource;
23 import org.eclipse.core.resources.IResourceProxy;
24 import org.eclipse.core.resources.IResourceProxyVisitor;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.jface.dialogs.IDialogConstants;
27 import org.eclipse.jface.dialogs.IDialogSettings;
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.custom.BusyIndicator;
30 import org.eclipse.swt.events.KeyAdapter;
31 import org.eclipse.swt.events.KeyEvent;
32 import org.eclipse.swt.events.ModifyEvent;
33 import org.eclipse.swt.events.ModifyListener;
34 import org.eclipse.swt.events.SelectionAdapter;
35 import org.eclipse.swt.events.SelectionEvent;
36 import org.eclipse.swt.graphics.Image;
37 import org.eclipse.swt.layout.GridData;
38 import org.eclipse.swt.widgets.Button;
39 import org.eclipse.swt.widgets.Composite;
40 import org.eclipse.swt.widgets.Control;
41 import org.eclipse.swt.widgets.Display;
42 import org.eclipse.swt.widgets.Label;
43 import org.eclipse.swt.widgets.Shell;
44 import org.eclipse.swt.widgets.Table;
45 import org.eclipse.swt.widgets.TableItem;
46 import org.eclipse.swt.widgets.Text;
47 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
48 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
49 import org.eclipse.ui.internal.ide.StringMatcher;
50 import org.eclipse.ui.model.WorkbenchLabelProvider;
51
52 /**
53  * Shows a list of resources to the user with a text entry field
54  * for a string pattern used to filter the list of resources.
55  * <p>
56  *
57  * @since 2.1
58  */

59 public class ResourceListSelectionDialog extends SelectionDialog {
60     
61     private static final String JavaDoc DIALOG_SETTINGS_SECTION = "ResourceListSelectionDialogSettings"; //$NON-NLS-1$
62

63     Text pattern;
64
65     Table resourceNames;
66
67     Table folderNames;
68
69     String JavaDoc patternString;
70
71     IContainer container;
72
73     int typeMask;
74
75     private static Collator collator = Collator.getInstance();
76
77     boolean gatherResourcesDynamically = true;
78
79     StringMatcher stringMatcher;
80
81     UpdateFilterThread updateFilterThread;
82
83     UpdateGatherThread updateGatherThread;
84
85     ResourceDescriptor[] descriptors;
86
87     int descriptorsSize;
88
89     WorkbenchLabelProvider labelProvider = new WorkbenchLabelProvider();
90     
91     boolean okEnabled = false;
92
93     private boolean showDerived = false;
94
95     private Button showDerivedButton;
96
97     private boolean allowUserToToggleDerived;
98     
99     static class ResourceDescriptor implements Comparable JavaDoc {
100         String JavaDoc label;
101
102         ArrayList JavaDoc resources = new ArrayList JavaDoc();
103
104         boolean resourcesSorted = true;
105
106         public int compareTo(Object JavaDoc o) {
107             return collator.compare(label, ((ResourceDescriptor) o).label);
108         }
109     }
110
111     class UpdateFilterThread extends Thread JavaDoc {
112         boolean stop = false;
113
114         int firstMatch = 0;
115
116         int lastMatch = descriptorsSize - 1;
117
118         public void run() {
119             Display display = resourceNames.getDisplay();
120             final int itemIndex[] = { 0 };
121             final int itemCount[] = { 0 };
122             //Keep track of if the widget got disposed
123
//so that we can abort if required
124
final boolean[] disposed = { false };
125             display.syncExec(new Runnable JavaDoc() {
126                 public void run() {
127                     //Be sure the widget still exists
128
if (resourceNames.isDisposed()) {
129                         disposed[0] = true;
130                         return;
131                     }
132                     itemCount[0] = resourceNames.getItemCount();
133                 }
134             });
135
136             if (disposed[0]) {
137                 return;
138             }
139
140             int last;
141             if ((patternString.indexOf('?') == -1)
142                     && (patternString.endsWith("*")) && //$NON-NLS-1$
143
(patternString.indexOf('*') == patternString.length() - 1)) {
144                 // Use a binary search to get first and last match when the pattern
145
// string ends with "*" and has no other embedded special characters.
146
// For this case, we can be smarter about getting the first and last
147
// match since the items are in sorted order.
148
firstMatch = getFirstMatch();
149                 if (firstMatch == -1) {
150                     firstMatch = 0;
151                     lastMatch = -1;
152                 } else {
153                     lastMatch = getLastMatch();
154                 }
155                 last = lastMatch;
156                 for (int i = firstMatch; i <= lastMatch; i++) {
157                     if (i % 50 == 0) {
158                         try {
159                             Thread.sleep(10);
160                         } catch (InterruptedException JavaDoc e) {
161                             // ignore
162
}
163                     }
164                     if (stop || resourceNames.isDisposed()) {
165                         disposed[0] = true;
166                         return;
167                     }
168                     final int index = i;
169                     display.syncExec(new Runnable JavaDoc() {
170                         public void run() {
171                             if (stop || resourceNames.isDisposed()) {
172                                 return;
173                             }
174                             updateItem(index, itemIndex[0], itemCount[0]);
175                             itemIndex[0]++;
176                         }
177                     });
178                 }
179             } else {
180                 last = lastMatch;
181                 boolean setFirstMatch = true;
182                 for (int i = firstMatch; i <= lastMatch; i++) {
183                     if (i % 50 == 0) {
184                         try {
185                             Thread.sleep(10);
186                         } catch (InterruptedException JavaDoc e) {
187                             // ignore
188
}
189                     }
190                     if (stop || resourceNames.isDisposed()) {
191                         disposed[0] = true;
192                         return;
193                     }
194                     final int index = i;
195                     if (match(descriptors[index].label)) {
196                         if (setFirstMatch) {
197                             setFirstMatch = false;
198                             firstMatch = index;
199                         }
200                         last = index;
201                         display.syncExec(new Runnable JavaDoc() {
202                             public void run() {
203                                 if (stop || resourceNames.isDisposed()) {
204                                     return;
205                                 }
206                                 updateItem(index, itemIndex[0], itemCount[0]);
207                                 itemIndex[0]++;
208                             }
209                         });
210                     }
211                 }
212             }
213
214             if (disposed[0]) {
215                 return;
216             }
217
218             lastMatch = last;
219             display.syncExec(new Runnable JavaDoc() {
220                 public void run() {
221                     if (resourceNames.isDisposed()) {
222                         return;
223                     }
224                     itemCount[0] = resourceNames.getItemCount();
225                     if (itemIndex[0] < itemCount[0]) {
226                         resourceNames.setRedraw(false);
227                         resourceNames.remove(itemIndex[0], itemCount[0] - 1);
228                         resourceNames.setRedraw(true);
229                     }
230                     // If no resources, remove remaining folder entries
231
if (resourceNames.getItemCount() == 0) {
232                         folderNames.removeAll();
233                         updateOKState(false);
234                     }
235                 }
236             });
237         }
238     }
239
240     class UpdateGatherThread extends Thread JavaDoc {
241         boolean stop = false;
242
243         int lastMatch = -1;
244
245         int firstMatch = 0;
246
247         boolean refilter = false;
248
249         public void run() {
250             Display display = resourceNames.getDisplay();
251             final int itemIndex[] = { 0 };
252             final int itemCount[] = { 0 };
253             //Keep track of if the widget got disposed
254
//so that we can abort if required
255
final boolean[] disposed = { false };
256             display.syncExec(new Runnable JavaDoc() {
257                 public void run() {
258                     //Be sure the widget still exists
259
if (resourceNames.isDisposed()) {
260                         disposed[0] = true;
261                         return;
262                     }
263                     itemCount[0] = resourceNames.getItemCount();
264                 }
265             });
266
267             if (disposed[0]) {
268                 return;
269             }
270
271             if (!refilter) {
272                 for (int i = 0; i <= lastMatch; i++) {
273                     if (i % 50 == 0) {
274                         try {
275                             Thread.sleep(10);
276                         } catch (InterruptedException JavaDoc e) {
277                             // ignore
278
}
279                     }
280                     if (stop || resourceNames.isDisposed()) {
281                         disposed[0] = true;
282                         return;
283                     }
284                     final int index = i;
285                     display.syncExec(new Runnable JavaDoc() {
286                         public void run() {
287                             if (stop || resourceNames.isDisposed()) {
288                                 return;
289                             }
290                             updateItem(index, itemIndex[0], itemCount[0]);
291                             itemIndex[0]++;
292                         }
293                     });
294                 }
295             } else {
296                 // we're filtering the previous list
297
for (int i = firstMatch; i <= lastMatch; i++) {
298                     if (i % 50 == 0) {
299                         try {
300                             Thread.sleep(10);
301                         } catch (InterruptedException JavaDoc e) {
302                             // ignore
303
}
304                     }
305                     if (stop || resourceNames.isDisposed()) {
306                         disposed[0] = true;
307                         return;
308                     }
309                     final int index = i;
310                     if (match(descriptors[index].label)) {
311                         display.syncExec(new Runnable JavaDoc() {
312                             public void run() {
313                                 if (stop || resourceNames.isDisposed()) {
314                                     return;
315                                 }
316                                 updateItem(index, itemIndex[0], itemCount[0]);
317                                 itemIndex[0]++;
318                             }
319                         });
320                     }
321                 }
322             }
323
324             if (disposed[0]) {
325                 return;
326             }
327
328             display.syncExec(new Runnable JavaDoc() {
329                 public void run() {
330                     if (resourceNames.isDisposed()) {
331                         return;
332                     }
333                     itemCount[0] = resourceNames.getItemCount();
334                     if (itemIndex[0] < itemCount[0]) {
335                         resourceNames.setRedraw(false);
336                         resourceNames.remove(itemIndex[0], itemCount[0] - 1);
337                         resourceNames.setRedraw(true);
338                     }
339                     // If no resources, remove remaining folder entries
340
if (resourceNames.getItemCount() == 0) {
341                         folderNames.removeAll();
342                         updateOKState(false);
343                     }
344                 }
345             });
346         }
347     }
348
349     /**
350      * Creates a new instance of the class.
351      *
352      * @param parentShell shell to parent the dialog on
353      * @param resources resources to display in the dialog
354      */

355     public ResourceListSelectionDialog(Shell parentShell, IResource[] resources) {
356         super(parentShell);
357         setShellStyle(getShellStyle() | SWT.RESIZE);
358         gatherResourcesDynamically = false;
359         initDescriptors(resources);
360     }
361
362     /**
363      * Creates a new instance of the class. When this constructor is used to
364      * create the dialog, resources will be gathered dynamically as the pattern
365      * string is specified. Only resources of the given types that match the
366      * pattern string will be listed. To further filter the matching resources,
367      * @see #select(IResource)
368      *
369      * @param parentShell shell to parent the dialog on
370      * @param container container to get resources from
371      * @param typeMask mask containing IResource types to be considered
372      */

373     public ResourceListSelectionDialog(Shell parentShell, IContainer container,
374             int typeMask) {
375         super(parentShell);
376         this.container = container;
377         this.typeMask = typeMask;
378         setShellStyle(getShellStyle() | SWT.RESIZE);
379     }
380
381     /**
382      * Adjust the pattern string for matching.
383      */

384     protected String JavaDoc adjustPattern() {
385         String JavaDoc text = pattern.getText().trim();
386         if (text.endsWith("<")) { //$NON-NLS-1$
387
// the < character indicates an exact match search
388
return text.substring(0, text.length() - 1);
389         }
390         if (!text.equals("") && !text.endsWith("*")) { //$NON-NLS-1$ //$NON-NLS-2$
391
return text + "*"; //$NON-NLS-1$
392
}
393         return text;
394     }
395
396     /**
397      * @see org.eclipse.jface.dialogs.Dialog#cancelPressed()
398      */

399     protected void cancelPressed() {
400         setResult(null);
401         super.cancelPressed();
402     }
403
404     /**
405      * @see org.eclipse.jface.window.Window#close()
406      */

407     public boolean close() {
408         boolean result = super.close();
409         labelProvider.dispose();
410         return result;
411     }
412
413     /**
414      * @see org.eclipse.jface.window.Window#create()
415      */

416     public void create() {
417         super.create();
418         pattern.setFocus();
419         getButton(IDialogConstants.OK_ID).setEnabled(okEnabled);
420     }
421
422     /**
423      * Creates the contents of this dialog, initializes the
424      * listener and the update thread.
425      *
426      * @param parent parent to create the dialog widgets in
427      */

428     protected Control createDialogArea(Composite parent) {
429
430         Composite dialogArea = (Composite) super.createDialogArea(parent);
431         Label l = new Label(dialogArea, SWT.NONE);
432         l.setText(IDEWorkbenchMessages.ResourceSelectionDialog_label);
433         GridData data = new GridData(GridData.FILL_HORIZONTAL);
434         l.setLayoutData(data);
435
436         pattern = new Text(dialogArea, SWT.SINGLE | SWT.BORDER);
437         pattern.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
438         l = new Label(dialogArea, SWT.NONE);
439         l.setText(IDEWorkbenchMessages.ResourceSelectionDialog_matching);
440         data = new GridData(GridData.FILL_HORIZONTAL);
441         l.setLayoutData(data);
442         resourceNames = new Table(dialogArea, SWT.SINGLE | SWT.BORDER
443                 | SWT.V_SCROLL);
444         data = new GridData(GridData.FILL_BOTH);
445         data.heightHint = 12 * resourceNames.getItemHeight();
446         resourceNames.setLayoutData(data);
447
448         l = new Label(dialogArea, SWT.NONE);
449         l.setText(IDEWorkbenchMessages.ResourceSelectionDialog_folders);
450         data = new GridData(GridData.FILL_HORIZONTAL);
451         l.setLayoutData(data);
452
453         folderNames = new Table(dialogArea, SWT.SINGLE | SWT.BORDER
454                 | SWT.V_SCROLL | SWT.H_SCROLL);
455         data = new GridData(GridData.FILL_BOTH);
456         data.widthHint = 300;
457         data.heightHint = 4 * folderNames.getItemHeight();
458         folderNames.setLayoutData(data);
459
460         if (gatherResourcesDynamically) {
461             updateGatherThread = new UpdateGatherThread();
462         } else {
463             updateFilterThread = new UpdateFilterThread();
464         }
465
466         pattern.addKeyListener(new KeyAdapter() {
467             public void keyReleased(KeyEvent e) {
468                 if (e.keyCode == SWT.ARROW_DOWN) {
469                     resourceNames.setFocus();
470                 }
471             }
472         });
473
474         pattern.addModifyListener(new ModifyListener() {
475             public void modifyText(ModifyEvent e) {
476                 refresh(false);
477             }
478         });
479
480         resourceNames.addSelectionListener(new SelectionAdapter() {
481             public void widgetSelected(SelectionEvent e) {
482                 updateFolders((ResourceDescriptor) e.item.getData());
483             }
484
485             public void widgetDefaultSelected(SelectionEvent e) {
486                 okPressed();
487             }
488         });
489
490         folderNames.addSelectionListener(new SelectionAdapter() {
491             public void widgetDefaultSelected(SelectionEvent e) {
492                 okPressed();
493             }
494         });
495
496         if (getAllowUserToToggleDerived()) {
497             showDerivedButton = new Button(dialogArea, SWT.CHECK);
498             showDerivedButton.setText(IDEWorkbenchMessages.ResourceSelectionDialog_showDerived);
499             showDerivedButton.addSelectionListener(new SelectionAdapter() {
500                 public void widgetSelected(SelectionEvent e) {
501                     setShowDerived(showDerivedButton.getSelection());
502                     refresh(true);
503                 }
504             });
505             showDerivedButton.setSelection(getShowDerived());
506         }
507             
508         applyDialogFont(dialogArea);
509         return dialogArea;
510     }
511
512     /**
513      * Returns whether to include a "Show derived resources" checkbox in the dialog.
514      * The default is <code>false</code>.
515      *
516      * @return <code>true</code> to include the checkbox, <code>false</code> to omit
517      * @since 3.1
518      */

519     public boolean getAllowUserToToggleDerived() {
520         return allowUserToToggleDerived;
521     }
522
523     /**
524      * Sets whether to include a "Show derived resources" checkbox in the dialog.
525      *
526      * @param allow <code>true</code> to include the checkbox, <code>false</code> to omit
527      * @since 3.1
528      */

529     public void setAllowUserToToggleDerived(boolean allow) {
530         allowUserToToggleDerived = allow;
531     }
532     
533     /**
534      */

535     private void filterResources(boolean force) {
536         String JavaDoc oldPattern = force ? null : patternString;
537         patternString = adjustPattern();
538         if (!force && patternString.equals(oldPattern)) {
539             return;
540         }
541
542         updateFilterThread.stop = true;
543         stringMatcher = new StringMatcher(patternString, true, false);
544         UpdateFilterThread oldThread = updateFilterThread;
545         updateFilterThread = new UpdateFilterThread();
546         if (patternString.equals("")) { //$NON-NLS-1$
547
updateFilterThread.firstMatch = 0;
548             updateFilterThread.lastMatch = -1;
549             updateFilterThread.start();
550             return;
551         }
552
553         if (oldPattern != null && (oldPattern.length() != 0)
554                 && oldPattern.endsWith("*") && patternString.endsWith("*")) { //$NON-NLS-1$ //$NON-NLS-2$
555
int matchLength = oldPattern.length() - 1;
556             if (patternString.regionMatches(0, oldPattern, 0, matchLength)) {
557                 // filter the previous list of items, this is done when the
558
// new pattern is a derivative of the old pattern
559
updateFilterThread.firstMatch = oldThread.firstMatch;
560                 updateFilterThread.lastMatch = oldThread.lastMatch;
561                 updateFilterThread.start();
562                 return;
563             }
564         }
565
566         // filter the entire list
567
updateFilterThread.firstMatch = 0;
568         updateFilterThread.lastMatch = descriptorsSize - 1;
569         updateFilterThread.start();
570     }
571
572     /**
573      * Use a binary search to get the first match for the patternString.
574      * This method assumes the patternString does not contain any '?'
575      * characters and that it contains only one '*' character at the end
576      * of the string.
577      */

578     private int getFirstMatch() {
579         int high = descriptorsSize;
580         int low = -1;
581         boolean match = false;
582         ResourceDescriptor desc = new ResourceDescriptor();
583         desc.label = patternString.substring(0, patternString.length() - 1);
584         while (high - low > 1) {
585             int index = (high + low) / 2;
586             String JavaDoc label = descriptors[index].label;
587             if (match(label)) {
588                 high = index;
589                 match = true;
590             } else {
591                 int compare = descriptors[index].compareTo(desc);
592                 if (compare == -1) {
593                     low = index;
594                 } else {
595                     high = index;
596                 }
597             }
598         }
599         if (match) {
600             return high;
601         }
602         return -1;
603     }
604
605     /**
606      */

607     private void gatherResources(boolean force) {
608         String JavaDoc oldPattern = force ? null : patternString;
609         patternString = adjustPattern();
610         if (!force && patternString.equals(oldPattern)) {
611             return;
612         }
613
614         updateGatherThread.stop = true;
615         updateGatherThread = new UpdateGatherThread();
616
617         if (patternString.equals("")) { //$NON-NLS-1$
618
updateGatherThread.start();
619             return;
620         }
621         stringMatcher = new StringMatcher(patternString, true, false);
622
623         if (oldPattern != null && (oldPattern.length() != 0)
624                 && oldPattern.endsWith("*") && patternString.endsWith("*")) { //$NON-NLS-1$ //$NON-NLS-2$
625
// see if the new pattern is a derivative of the old pattern
626
int matchLength = oldPattern.length() - 1;
627             if (patternString.regionMatches(0, oldPattern, 0, matchLength)) {
628                 updateGatherThread.refilter = true;
629                 updateGatherThread.firstMatch = 0;
630                 updateGatherThread.lastMatch = descriptorsSize - 1;
631                 updateGatherThread.start();
632                 return;
633             }
634         }
635
636         final ArrayList JavaDoc resources = new ArrayList JavaDoc();
637         BusyIndicator.showWhile(getShell().getDisplay(), new Runnable JavaDoc() {
638             public void run() {
639                 getMatchingResources(resources);
640                 IResource resourcesArray[] = new IResource[resources.size()];
641                 resources.toArray(resourcesArray);
642                 initDescriptors(resourcesArray);
643             }
644         });
645
646         updateGatherThread.firstMatch = 0;
647         updateGatherThread.lastMatch = descriptorsSize - 1;
648         updateGatherThread.start();
649     }
650
651     /**
652      * Return an image for a resource descriptor.
653      *
654      * @param desc resource descriptor to return image for
655      * @return an image for a resource descriptor.
656      */

657     private Image getImage(ResourceDescriptor desc) {
658         IResource r = (IResource) desc.resources.get(0);
659         return labelProvider.getImage(r);
660     }
661
662     /**
663      * Use a binary search to get the last match for the patternString.
664      * This method assumes the patternString does not contain any '?'
665      * characters and that it contains only one '*' character at the end
666      * of the string.
667      */

668     private int getLastMatch() {
669         int high = descriptorsSize;
670         int low = -1;
671         boolean match = false;
672         ResourceDescriptor desc = new ResourceDescriptor();
673         desc.label = patternString.substring(0, patternString.length() - 1);
674         while (high - low > 1) {
675             int index = (high + low) / 2;
676             String JavaDoc label = descriptors[index].label;
677             if (match(label)) {
678                 low = index;
679                 match = true;
680             } else {
681                 int compare = descriptors[index].compareTo(desc);
682                 if (compare == -1) {
683                     low = index;
684                 } else {
685                     high = index;
686                 }
687             }
688         }
689         if (match) {
690             return low;
691         }
692         return -1;
693     }
694
695     /**
696      * Gather the resources of the specified type that match the current
697      * pattern string. Gather the resources using the proxy visitor since
698      * this is quicker than getting the entire resource.
699      *
700      * @param resources resources that match
701      */

702     private void getMatchingResources(final ArrayList JavaDoc resources) {
703         try {
704             container.accept(new IResourceProxyVisitor() {
705                 public boolean visit(IResourceProxy proxy) {
706                     // optionally exclude derived resources (bugs 38085 and 81333)
707
if (!getShowDerived() && proxy.isDerived()) {
708                         return false;
709                     }
710                     int type = proxy.getType();
711                     if ((typeMask & type) != 0) {
712                         if (match(proxy.getName())) {
713                             IResource res = proxy.requestResource();
714                             if (select(res)) {
715                                 resources.add(res);
716                                 return true;
717                             }
718                             return false;
719                         }
720                     }
721                     if (type == IResource.FILE) {
722                         return false;
723                     }
724                     return true;
725                 }
726             }, IResource.NONE);
727         } catch (CoreException e) {
728             // ignore
729
}
730     }
731
732     private Image getParentImage(IResource resource) {
733         IResource parent = resource.getParent();
734         return labelProvider.getImage(parent);
735     }
736
737     private String JavaDoc getParentLabel(IResource resource) {
738         IResource parent = resource.getParent();
739         String JavaDoc text;
740         if (parent.getType() == IResource.ROOT) {
741             // Get readable name for workspace root ("Workspace"), without duplicating language-specific string here.
742
text = labelProvider.getText(parent);
743         } else {
744             text = parent.getFullPath().makeRelative().toString();
745         }
746         if(text == null) {
747             return ""; //$NON-NLS-1$
748
}
749         return text;
750     }
751
752     /**
753      * Returns whether derived resources should be shown in the list.
754      * The default is <code>false</code>.
755     &