1 12 package org.eclipse.ui.dialogs; 13 14 import java.util.Comparator ; 15 import java.util.HashSet ; 16 import java.util.Set ; 17 import java.util.Vector ; 18 19 import org.eclipse.core.runtime.Assert; 20 import org.eclipse.core.runtime.IProgressMonitor; 21 import org.eclipse.core.runtime.IStatus; 22 import org.eclipse.core.runtime.Status; 23 import org.eclipse.jface.viewers.ILabelProvider; 24 import org.eclipse.swt.SWT; 25 import org.eclipse.swt.SWTException; 26 import org.eclipse.swt.accessibility.Accessible; 27 import org.eclipse.swt.events.DisposeEvent; 28 import org.eclipse.swt.events.DisposeListener; 29 import org.eclipse.swt.events.SelectionListener; 30 import org.eclipse.swt.graphics.Image; 31 import org.eclipse.swt.layout.GridData; 32 import org.eclipse.swt.layout.GridLayout; 33 import org.eclipse.swt.widgets.Composite; 34 import org.eclipse.swt.widgets.Event; 35 import org.eclipse.swt.widgets.Table; 36 import org.eclipse.swt.widgets.TableItem; 37 import org.eclipse.ui.internal.WorkbenchMessages; 38 import org.eclipse.ui.internal.misc.StringMatcher; 39 import org.eclipse.ui.internal.util.Util; 40 import org.eclipse.ui.progress.WorkbenchJob; 41 42 49 public class FilteredList extends Composite { 50 53 public interface FilterMatcher { 54 66 void setFilter(String pattern, boolean ignoreCase, 67 boolean ignoreWildCards); 68 69 77 boolean match(Object element); 78 } 79 80 private class DefaultFilterMatcher implements FilterMatcher { 81 private StringMatcher fMatcher; 82 83 public void setFilter(String pattern, boolean ignoreCase, 84 boolean ignoreWildCards) { 85 fMatcher = new StringMatcher(pattern + '*', ignoreCase, 86 ignoreWildCards); 87 } 88 89 public boolean match(Object element) { 90 return fMatcher.match(fLabelProvider.getText(element)); 91 } 92 } 93 94 private Table fList; 95 96 ILabelProvider fLabelProvider; 97 98 private boolean fMatchEmptyString = true; 99 100 private boolean fIgnoreCase; 101 102 private boolean fAllowDuplicates; 103 104 private String fFilter = ""; 106 private TwoArrayQuickSorter fSorter; 107 108 Object [] fElements = new Object [0]; 109 110 Label[] fLabels; 111 112 Vector fImages = new Vector (); 113 114 int[] fFoldedIndices; 115 116 int fFoldedCount; 117 118 int[] fFilteredIndices; 119 120 int fFilteredCount; 121 122 private FilterMatcher fFilterMatcher = new DefaultFilterMatcher(); 123 124 Comparator fComparator; 125 126 TableUpdateJob fUpdateJob; 127 128 131 private static class Label { 132 135 public final String string; 136 137 140 public final Image image; 141 142 148 public Label(String newString, Image image) { 149 if (newString == null) { 150 this.string = Util.ZERO_LENGTH_STRING; 151 } else { 152 this.string = newString; 153 } 154 this.image = image; 155 } 156 157 163 public boolean equals(Label label) { 164 if (label == null) { 165 return false; 166 } 167 if (string == null && label.string != null) { 170 return false; 171 } 172 if ((string != null) && (!string.equals(label.string))) { 173 return false; 174 } 175 if (image == null) { 176 return label.image == null; 177 } 178 return image.equals(label.image); 179 } 180 } 181 182 private final class LabelComparator implements Comparator { 183 private boolean labelIgnoreCase; 184 185 LabelComparator(boolean ignoreCase) { 186 labelIgnoreCase = ignoreCase; 187 } 188 189 public int compare(Object left, Object right) { 190 Label leftLabel = (Label) left; 191 Label rightLabel = (Label) right; 192 int value; 193 if (fComparator == null) { 194 value = labelIgnoreCase ? leftLabel.string 195 .compareToIgnoreCase(rightLabel.string) 196 : leftLabel.string.compareTo(rightLabel.string); 197 } else { 198 value = fComparator 199 .compare(leftLabel.string, rightLabel.string); 200 } 201 if (value != 0) { 202 return value; 203 } 204 if (leftLabel.image == null) { 206 return (rightLabel.image == null) ? 0 : -1; 207 } else if (rightLabel.image == null) { 208 return +1; 209 } else { 210 return fImages.indexOf(leftLabel.image) 211 - fImages.indexOf(rightLabel.image); 212 } 213 } 214 } 215 216 233 public FilteredList(Composite parent, int style, 234 ILabelProvider labelProvider, boolean ignoreCase, 235 boolean allowDuplicates, boolean matchEmptyString) { 236 super(parent, SWT.NONE); 237 GridLayout layout = new GridLayout(); 238 layout.marginHeight = 0; 239 layout.marginWidth = 0; 240 setLayout(layout); 241 fList = new Table(this, style); 242 fList.setLayoutData(new GridData(GridData.FILL_BOTH)); 243 fList.setFont(parent.getFont()); 244 fList.addDisposeListener(new DisposeListener() { 245 public void widgetDisposed(DisposeEvent e) { 246 fLabelProvider.dispose(); 247 if (fUpdateJob != null) { 248 fUpdateJob.cancel(); 249 } 250 } 251 }); 252 fLabelProvider = labelProvider; 253 fIgnoreCase = ignoreCase; 254 fSorter = new TwoArrayQuickSorter(new LabelComparator(ignoreCase)); 255 fAllowDuplicates = allowDuplicates; 256 fMatchEmptyString = matchEmptyString; 257 } 258 259 265 public void setElements(Object [] elements) { 266 if (elements == null) { 267 fElements = new Object [0]; 268 } else { 269 fElements = new Object [elements.length]; 271 System.arraycopy(elements, 0, fElements, 0, elements.length); 272 } 273 int length = fElements.length; 274 fLabels = new Label[length]; 276 Set imageSet = new HashSet (); 277 for (int i = 0; i != length; i++) { 278 String text = fLabelProvider.getText(fElements[i]); 279 Image image = fLabelProvider.getImage(fElements[i]); 280 fLabels[i] = new Label(text, image); 281 imageSet.add(image); 282 } 283 fImages.clear(); 284 fImages.addAll(imageSet); 285 fSorter.sort(fLabels, fElements); 286 fFilteredIndices = new int[length]; 287 fFoldedIndices = new int[length]; 288 updateList(); 289 } 290 291 297 public boolean isEmpty() { 298 return (fElements == null) || (fElements.length == 0); 299 } 300 301 306 public void setFilterMatcher(FilterMatcher filterMatcher) { 307 Assert.isNotNull(filterMatcher); 308 fFilterMatcher = filterMatcher; 309 } 310 311 316 public void setComparator(Comparator comparator) { 317 Assert.isNotNull(comparator); 318 fComparator = comparator; 319 } 320 321 327 public void addSelectionListener(SelectionListener listener) { 328 fList.addSelectionListener(listener); 329 } 330 331 337 public void removeSelectionListener(SelectionListener listener) { 338 fList.removeSelectionListener(listener); 339 } 340 341 347 public void setSelection(int[] selection) { 348 if (selection == null || selection.length == 0) { 349 fList.deselectAll(); 350 } else { 351 if (fUpdateJob == null) { 354 fList.setSelection(selection); 355 fList.notifyListeners(SWT.Selection, new Event()); 356 } else { 357 fUpdateJob.updateSelection(selection); 360 } 361 } 362 } 363 364 369 public int[] getSelectionIndices() { 370 return fList.getSelectionIndices(); 371 } 372 373 379 public int getSelectionIndex() { 380 return fList.getSelectionIndex(); 381 } 382 383 389 public void setSelection(Object [] elements) { 390 if (elements == null || elements.length == 0) { 391 fList.deselectAll(); 392 return; 393 } 394 if (fElements == null) { 395 return; 396 } 397 int[] indices = new int[elements.length]; 399 for (int i = 0; i != elements.length; i++) { 400 int j; 401 for (j = 0; j != fFoldedCount; j++) { 402 int max = (j == fFoldedCount - 1) ? fFilteredCount 403 : fFoldedIndices[j + 1]; 404 int l; 405 for (l = fFoldedIndices[j]; l != max; l++) { 406 if (fElements[fFilteredIndices[l]].equals(elements[i])) { 408 indices[i] = j; 409 break; 410 } 411 } 412 if (l != max) { 413 break; 414 } 415 } 416 if (j == fFoldedCount) { 418 indices[i] = 0; 419 } 420 } 421 setSelection(indices); 422 } 423 424 432 public Object [] getSelection() { 433 if (fList.isDisposed() || (fList.getSelectionCount() == 0)) { 434 return new Object [0]; 435 } 436 int[] indices = fList.getSelectionIndices(); 437 Object [] elements = new Object [indices.length]; 438 for (int i = 0; i != indices.length; i++) { 439 elements[i] = fElements[fFilteredIndices[fFoldedIndices[indices[i]]]]; 440 } 441 return elements; 442 } 443 444 451 public void setFilter(String filter) { 452 fFilter = (filter == null) ? "" : filter; updateList(); 454 } 455 456 private void updateList() { 457 fFilteredCount = filter(); 458 fFoldedCount = fold(); 459 if (fUpdateJob != null) { 460 fUpdateJob.cancel(); 461 } 462 fUpdateJob = new TableUpdateJob(fList, fFoldedCount); 463 fUpdateJob.schedule(); 464 } 465 466 471 public String getFilter() { 472 return fFilter; 473 } 474 475 483 public Object [] getFoldedElements(int index) { 484 if ((index < 0) || (index >= fFoldedCount)) { 485 return null; 486 } 487 int start = fFoldedIndices[index]; 488 int count = (index == fFoldedCount - 1) ? fFilteredCount - start 489 : fFoldedIndices[index + 1] - start; 490 Object [] elements = new Object [count]; 491 for (int i = 0; i != count; i++) { 492 elements[i] = fElements[fFilteredIndices[start + i]]; 493 } 494 return elements; 495 } 496 497 502 private int fold() { 503 if (fAllowDuplicates) { 504 for (int i = 0; i != fFilteredCount; i++) { 505 fFoldedIndices[i] = i; } 507 return fFilteredCount; 508 } 509 int k = 0; 510 Label last = null; 511 for (int i = 0; i != fFilteredCount; i++) { 512 int j = fFilteredIndices[i]; 513 Label current = fLabels[j]; 514 if (!current.equals(last)) { 515 fFoldedIndices[k] = i; 516 k++; 517 last = current; 518 } 519 } 520 return k; 521 } 522 523 527 private int filter() { 528 if (((fFilter == null) || (fFilter.length() == 0)) 529 && !fMatchEmptyString) { 530 return 0; 531 } 532 fFilterMatcher.setFilter(fFilter.trim(), fIgnoreCase, false); 533 int k = 0; 534 for (int i = 0; i != fElements.length; i++) { 535 if (fFilterMatcher.match(fElements[i])) { 536 fFilteredIndices[k++] = i; 537 } 538 } 539 return k; 540 } 541 542 private class TableUpdateJob extends WorkbenchJob { 543 final Table fTable; 544 545 final int fCount; 546 547 private int currentIndex = 0; 548 549 552 int[] indicesToSelect; 553 554 private boolean readyForSelection = false; 555 556 563 public TableUpdateJob(Table table, int count) { 564 super(WorkbenchMessages.FilteredList_UpdateJobName); 565 setSystem(true); 566 fTable = table; 567 fCount = count; 568 } 569 570 575 public IStatus runInUIThread(IProgressMonitor monitor) { 576 if (fTable.isDisposed()) { 577 return Status.CANCEL_STATUS; 578 } 579 int itemCount = fTable.getItemCount(); 580 581 if (fCount < itemCount) { 583 fTable.setRedraw(false); 584 fTable.remove(fCount, itemCount - 1); 585 fTable.setRedraw(true); 586 itemCount = fTable.getItemCount(); 587 } 588 if (fCount == 0) { 590 fTable.notifyListeners(SWT.Selection, new Event()); 591 return Status.OK_STATUS; 592 } 593 int iterations = Math.min(10, fCount - currentIndex); 595 for (int i = 0; i < iterations; i++) { 596 if (monitor.isCanceled()) { 597 return Status.CANCEL_STATUS; 598 } 599 final TableItem item = (currentIndex < itemCount) ? fTable 600 .getItem(currentIndex) 601 : new TableItem(fTable, SWT.NONE); 602 final Label label = fLabels[fFilteredIndices[fFoldedIndices[currentIndex]]]; 603 item.setText(label.string); 604 item.setImage(label.image); 605 currentIndex++; 606 } 607 if (monitor.isCanceled()) { 608 return Status.CANCEL_STATUS; 609 } 610 if (currentIndex < fCount) { 611 schedule(100); 612 } else { 613 if (indicesToSelect == null) { 614 if (fCount > 0) { 618 if (fTable.getSelectionIndices().length == 0) { 619 defaultSelect(); 620 } else { 621 fTable.notifyListeners(SWT.Selection, new Event()); 626 } 627 } 628 } else { 629 selectAndNotify(indicesToSelect); 631 } 632 readyForSelection = true; 635 } 636 return Status.OK_STATUS; 637 } 638 643 void updateSelection(final int[] indices) { 644 indicesToSelect = indices; 645 if (readyForSelection) { 646 selectAndNotify(indices); 647 } 648 } 649 650 653 private void defaultSelect() { 654 657 selectAndNotify(new int[] { 0 }); 658 } 659 660 665 private void selectAndNotify(final int[] indices) { 666 if (fTable.isDisposed()) { 669 return; 670 } 671 fTable.setSelection(indices); 672 fTable.notifyListeners(SWT.Selection, new Event()); 673 } 674 } 675 676 681 public boolean getAllowDuplicates() { 682 return fAllowDuplicates; 683 } 684 685 692 public void setAllowDuplicates(boolean allowDuplicates) { 693 this.fAllowDuplicates = allowDuplicates; 694 } 695 696 701 public boolean getIgnoreCase() { 702 return fIgnoreCase; 703 } 704 705 712 public void setIgnoreCase(boolean ignoreCase) { 713 this.fIgnoreCase = ignoreCase; 714 } 715 716 722 public boolean getMatchEmptyString() { 723 return fMatchEmptyString; 724 } 725 726 735 public void setMatchEmptyString(boolean matchEmptyString) { 736 this.fMatchEmptyString = matchEmptyString; 737 } 738 739 744 public ILabelProvider getLabelProvider() { 745 return fLabelProvider; 746 } 747 748 755 public void setLabelProvider(ILabelProvider labelProvider) { 756 this.fLabelProvider = labelProvider; 757 } 758 759 776 public Accessible getAccessible() { 777 return fList.getAccessible(); 778 } 779 } 780 | Popular Tags |