smaller changes in selection dialog, use always the service for searching with pattern
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / ui / dialog / selection / SearchDialog.java
1 // $Id$
2 /**
3 * Copyright (C) 2017 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10 package eu.etaxonomy.taxeditor.ui.dialog.selection;
11
12
13
14 import java.text.Collator;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.List;
19
20 import org.eclipse.core.runtime.Assert;
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.ListenerList;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.core.runtime.jobs.Job;
27 import org.eclipse.jface.action.LegacyActionTools;
28 import org.eclipse.jface.dialogs.Dialog;
29 import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
30 import org.eclipse.jface.viewers.DoubleClickEvent;
31 import org.eclipse.jface.viewers.IColorProvider;
32 import org.eclipse.jface.viewers.IDoubleClickListener;
33 import org.eclipse.jface.viewers.IFontProvider;
34 import org.eclipse.jface.viewers.ILabelProvider;
35 import org.eclipse.jface.viewers.ILabelProviderListener;
36 import org.eclipse.jface.viewers.ISelectionChangedListener;
37 import org.eclipse.jface.viewers.IStructuredContentProvider;
38 import org.eclipse.jface.viewers.LabelProvider;
39 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
40 import org.eclipse.jface.viewers.SelectionChangedEvent;
41 import org.eclipse.jface.viewers.StructuredSelection;
42 import org.eclipse.jface.viewers.StyledCellLabelProvider;
43 import org.eclipse.jface.viewers.StyledString;
44 import org.eclipse.jface.viewers.TableViewer;
45 import org.eclipse.jface.viewers.Viewer;
46 import org.eclipse.jface.viewers.ViewerCell;
47 import org.eclipse.swt.SWT;
48 import org.eclipse.swt.accessibility.AccessibleAdapter;
49 import org.eclipse.swt.accessibility.AccessibleEvent;
50 import org.eclipse.swt.events.ModifyEvent;
51 import org.eclipse.swt.events.ModifyListener;
52 import org.eclipse.swt.events.TraverseEvent;
53 import org.eclipse.swt.events.TraverseListener;
54 import org.eclipse.swt.graphics.Color;
55 import org.eclipse.swt.graphics.Font;
56 import org.eclipse.swt.graphics.GC;
57 import org.eclipse.swt.graphics.Point;
58 import org.eclipse.swt.graphics.Rectangle;
59 import org.eclipse.swt.layout.GridData;
60 import org.eclipse.swt.layout.GridLayout;
61 import org.eclipse.swt.widgets.Button;
62 import org.eclipse.swt.widgets.Composite;
63 import org.eclipse.swt.widgets.Control;
64 import org.eclipse.swt.widgets.Display;
65 import org.eclipse.swt.widgets.Event;
66 import org.eclipse.swt.widgets.Label;
67 import org.eclipse.swt.widgets.Shell;
68 import org.eclipse.swt.widgets.Table;
69 import org.eclipse.swt.widgets.Text;
70 import org.eclipse.ui.IWorkbenchPreferenceConstants;
71 import org.eclipse.ui.PlatformUI;
72 import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;
73 import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
74 import org.eclipse.ui.internal.WorkbenchImages;
75 import org.eclipse.ui.internal.WorkbenchMessages;
76 import org.eclipse.ui.internal.WorkbenchPlugin;
77
78 import eu.etaxonomy.cdm.model.common.ICdmBase;
79 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
80 import eu.etaxonomy.taxeditor.l10n.Messages;
81 import eu.etaxonomy.taxeditor.ui.dialog.selection.CdmFilteredItemsSelectionDialog.ItemsListSeparator;
82
83 /**
84 * @author k.luther
85 * @date 15.11.2017
86 *
87 */
88 public abstract class SearchDialog<T extends ICdmBase> extends Dialog{// implements IConversationEnabled{
89
90 private TableViewer list;
91 private Text searchField;
92 private String title;
93 protected Button newButton1;
94
95
96 protected Button newButton2;
97 protected Button filterButton;
98 private StructuredSelection currentSelection;
99
100 // message to show user
101 private String message = ""; //$NON-NLS-1$
102
103 protected final ContentProvider contentProvider;
104 ItemsListLabelProvider itemsListLabelProvider;
105
106 private final RefreshCacheJob refreshCacheJob;
107
108 protected Object preferenceID;
109
110 protected final int new_id = 4;
111 protected final int new_id2 = 5;
112 // Need to keep our own list of listeners
113 private final ListenerList listeners = new ListenerList();
114
115
116 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
117 private GridData gd_1;
118
119 public SearchDialog(Shell parent, boolean multi) {
120 super(parent);
121 contentProvider = new ContentProvider();
122 refreshCacheJob = new RefreshCacheJob();
123
124
125 }
126
127
128 @Override
129 protected Control createDialogArea(Composite parent) {
130 Composite container = (Composite) super.createDialogArea(parent);
131
132 GridData gd = new GridData(GridData.FILL_BOTH);
133 container.setLayoutData(gd);
134
135 GridLayout layout = new GridLayout();
136 layout.numColumns = 1;
137 layout.marginWidth = 0;
138 layout.marginHeight = 0;
139 container.setLayout(layout);
140
141 final Label headerLabel = createHeader(container);
142
143 Composite searchAndFilter = new Composite(container, container.getStyle());
144 GridData gd_searchAndFilter =new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
145
146 searchAndFilter.setLayoutData(gd_searchAndFilter);
147 GridLayout searchAndFilterLayout = new GridLayout();
148 searchAndFilterLayout.numColumns = 2;
149 searchAndFilter.setLayout(searchAndFilterLayout);
150 searchField = new Text(searchAndFilter, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);
151 searchField.getAccessible().addAccessibleListener(new AccessibleAdapter() {
152 @Override
153 public void getName(AccessibleEvent e) {
154 e.result = LegacyActionTools.removeMnemonics(headerLabel
155 .getText());
156 }
157 });
158 searchField.addModifyListener(new ModifyListener() {
159 @Override
160 public void modifyText(ModifyEvent e) {
161 search();
162 fillContentProvider(null);
163
164 }
165 });
166 gd_1 = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
167
168 searchField.setLayoutData(gd_1);
169
170 createFilterButton(searchAndFilter);
171 new Label(searchAndFilter, SWT.NONE);
172 setList(new TableViewer(container, SWT.SINGLE
173 | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL));
174
175 getList().setContentProvider(contentProvider);
176 getList().setLabelProvider(getItemsListLabelProvider());
177 getList().setInput(new Object[0]);
178 getList().setItemCount(contentProvider.getNumberOfElements());
179 getList().addSelectionChangedListener(new ISelectionChangedListener() {
180 @Override
181 public void selectionChanged(SelectionChangedEvent event) {
182 StructuredSelection selection = (StructuredSelection) event
183 .getSelection();
184 currentSelection = selection;
185 }
186 });
187 getList().addDoubleClickListener(new IDoubleClickListener() {
188 @Override
189 public void doubleClick(DoubleClickEvent event) {
190 okPressed();
191 }
192 });
193 // createExtendedContentArea(container);
194 new Label(container, SWT.NONE);
195
196 return container;
197 }
198
199
200 abstract void createFilterButton(Composite searchAndFilter) ;
201
202 protected abstract void search();
203
204
205 /**
206 * Sets the title for this dialog.
207 *
208 * @param title
209 * the title
210 */
211 public void setTitle(String title) {
212 this.title = title;
213 }
214
215 /**
216 * Create a new header which is labelled by headerLabel.
217 *
218 * @param parent
219 * @return Label the label of the header
220 */
221 private Label createHeader(Composite parent) {
222 Composite header = new Composite(parent, SWT.NONE);
223 GridData gd_header = new GridData(SWT.CENTER, SWT.CENTER, false, false, 2, 1);
224 gd_header.horizontalIndent = 5;
225 gd_header.widthHint = 575;
226 header.setLayoutData(gd_header);
227
228 GridLayout layout = new GridLayout();
229 layout.marginLeft = 5;
230 layout.horizontalSpacing = 0;
231 layout.verticalSpacing = 1;
232 layout.marginWidth = 0;
233 layout.marginHeight = 0;
234 header.setLayout(layout);
235 new Label(header, SWT.NONE);
236 Label headerLabel = new Label(header, SWT.NONE);
237 headerLabel.setText((getMessage() != null && getMessage().trim()
238 .length() > 0) ? getMessage()
239 : Messages.SearchDialog_patternLabel);
240 headerLabel.addTraverseListener(new TraverseListener() {
241 @Override
242 public void keyTraversed(TraverseEvent e) {
243 if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
244 e.detail = SWT.TRAVERSE_NONE;
245 searchField.setFocus();
246 }
247 }
248 });
249 GridData gd_headerLabel = new GridData(GridData.FILL_HORIZONTAL);
250 gd_headerLabel.horizontalAlignment = SWT.LEFT;
251 gd_headerLabel.grabExcessHorizontalSpace = false;
252 gd_headerLabel.verticalAlignment = SWT.BOTTOM;
253 gd_headerLabel.minimumHeight = 10;
254 headerLabel.setLayoutData(gd_headerLabel);
255 return headerLabel;
256 }
257
258 protected String getMessage() {
259 return message;
260 }
261
262 protected void setMessage(String message){
263 this.message = message;
264 }
265
266 public Text getSearchField() {
267 return searchField;
268 }
269
270
271 public void setSearchField(Text searchField) {
272 this.searchField = searchField;
273 }
274
275 private ItemsListLabelProvider getItemsListLabelProvider() {
276 if (itemsListLabelProvider == null) {
277 itemsListLabelProvider = new ItemsListLabelProvider(
278 new LabelProvider());
279 }
280 return itemsListLabelProvider;
281 }
282
283
284 public TableViewer getList() {
285 return list;
286 }
287
288 public void setList(TableViewer list) {
289 this.list = list;
290 Table table = list.getTable();
291 GridData gd_table = new GridData(SWT.CENTER, SWT.CENTER, true, true, 2, 1);
292 gd_table.heightHint = 231;
293 gd_table.widthHint = 543;
294 table.setLayoutData(gd_table);
295 }
296
297 public Button getNewButton1() {
298 return newButton1;
299 }
300
301
302 public void setNewButton1(Button newButton1) {
303 this.newButton1 = newButton1;
304 }
305
306
307 public Button getNewButton2() {
308 return newButton2;
309 }
310
311
312 public void setNewButton2(Button newButton2) {
313 this.newButton2 = newButton2;
314 }
315
316
317 public Button getFilterButton() {
318 return filterButton;
319 }
320
321
322 public void setFilterButton(Button filterButton) {
323 this.filterButton = filterButton;
324 }
325
326 /**
327 * Sets a new label provider for items in the list. If the label provider
328 * also implements {@link
329 * org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider
330 * .IStyledLabelProvider}, the style text labels provided by it will be used
331 * provided that the corresponding preference is set.
332 *
333 * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS
334 *
335 * @param listLabelProvider
336 * the label provider for items in the list
337 */
338 public void setListLabelProvider(ILabelProvider listLabelProvider) {
339 getItemsListLabelProvider().setProvider(listLabelProvider);
340 }
341 protected Comparator getItemsComparator() {
342 return new Comparator<UuidAndTitleCache>() {
343 @Override
344 public int compare(UuidAndTitleCache entity1,
345 UuidAndTitleCache entity2) {
346 Collator collator = Collator.getInstance();
347 if (entity1.getUuid().equals(entity2.getUuid())){
348 return 0;
349 }
350 int result = collator.compare(entity1.getTitleCache(), entity2.getTitleCache());
351 if (result == 0){
352 result = entity1.getUuid().compareTo(entity2.getUuid());
353 }
354 return result;
355 }
356 };
357 }
358
359
360 class ContentProvider implements
361 IStructuredContentProvider {
362
363 private List items;
364
365 /**
366 * Creates new instance of <code>ContentProvider</code>.
367 */
368 public ContentProvider() {
369 this.items = Collections.synchronizedList(new ArrayList(2048));
370 }
371
372 /**
373 * Removes all content items and resets progress message.
374 */
375 public void reset() {
376 this.items.clear();
377
378 }
379
380 public void add(Object item) {
381 this.items.add(item);
382 }
383
384 /**
385 * Refresh dialog.
386 */
387 public void refresh() {
388 scheduleRefresh();
389 }
390
391 /**
392 * Schedule refresh job.
393 */
394 public void scheduleRefresh() {
395 refreshCacheJob.cancelAll();
396 refreshCacheJob.schedule();
397 }
398
399 /*
400 * (non-Javadoc)
401 *
402 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
403 */
404 @Override
405 public Object[] getElements(Object inputElement) {
406 return items.toArray();
407 }
408
409 public int getNumberOfElements() {
410 return items.size();
411 }
412
413 /*
414 * (non-Javadoc)
415 *
416 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
417 */
418 @Override
419 public void dispose() {
420 }
421
422 /*
423 * (non-Javadoc)
424 *
425 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
426 * java.lang.Object, java.lang.Object)
427 */
428 @Override
429 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
430 }
431
432
433 }
434
435
436
437 public StructuredSelection getCurrentSelection() {
438 return currentSelection;
439 }
440
441 /**
442 * Fills the content provider with matching items.
443 *
444 * @param progressMonitor
445 * must be used to report search progress. The state of this
446 * progress monitor reflects the state of the filtering process.
447 * @throws CoreException
448 */
449 protected abstract void fillContentProvider(IProgressMonitor progressMonitor) ;
450
451
452 /**
453 * Refreshes the dialog - has to be called in UI thread.
454 */
455 public void refresh() {
456 if (getList() != null && !getList().getTable().isDisposed()) {
457
458 List lastRefreshSelection = ((StructuredSelection) getList()
459 .getSelection()).toList();
460 getList().getTable().deselectAll();
461
462 getList().setItemCount(contentProvider.getNumberOfElements());
463 getList().refresh();
464
465 if (getList().getTable().getItemCount() > 0) {
466 // preserve previous selection
467 if ( lastRefreshSelection != null
468 && lastRefreshSelection.size() > 0) {
469 getList().setSelection(new StructuredSelection(
470 lastRefreshSelection));
471 } else {
472
473 getList().getTable().setSelection(0);
474 getList().getTable().notifyListeners(SWT.Selection, new Event());
475 }
476 } else {
477 getList().setSelection(StructuredSelection.EMPTY);
478 }
479
480 }
481 }
482
483 /**
484 * A job responsible for computing filtered items list presented using
485 * <code>RefreshJob</code>.
486 *
487 * @see FilteredItemsSelectionDialog.RefreshJob
488 *
489 */
490 private class RefreshCacheJob extends Job {
491
492 public RefreshCacheJob() {
493 super(
494 WorkbenchMessages.FilteredItemsSelectionDialog_cacheRefreshJob);
495 setSystem(true);
496 }
497
498 /**
499 * Stops the job and all sub-jobs.
500 */
501 public void cancelAll() {
502 cancel();
503 }
504
505 /*
506 * (non-Javadoc)
507 *
508 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
509 */
510 @Override
511 protected IStatus run(IProgressMonitor monitor) {
512 if (monitor.isCanceled()) {
513 return new Status(IStatus.CANCEL, WorkbenchPlugin.PI_WORKBENCH,
514 IStatus.CANCEL, EMPTY_STRING, null);
515 }
516
517 if (SearchDialog.this != null) {
518 SearchDialog.this.fillContentProvider(monitor);
519 }
520
521 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK,
522 EMPTY_STRING, null);
523
524 }
525
526 /*
527 * (non-Javadoc)
528 *
529 * @see org.eclipse.core.runtime.jobs.Job#canceling()
530 */
531 @Override
532 protected void canceling() {
533 super.canceling();
534 }
535
536 }
537
538 private class ItemsListLabelProvider extends StyledCellLabelProvider
539 implements ILabelProviderListener {
540 private ILabelProvider provider;
541
542 /**
543 * Creates a new instance of the class.
544 *
545 * @param provider
546 * the label provider for all items, not <code>null</code>
547 * @param selectionDecorator
548 * the decorator for selected items, can be <code>null</code>
549 */
550 public ItemsListLabelProvider(ILabelProvider provider) {
551 Assert.isNotNull(provider);
552 this.provider = provider;
553 this.provider.addListener(this);
554 }
555
556 /**
557 * Sets new label provider.
558 *
559 * @param newProvider
560 * new label provider for items in the list, not
561 * <code>null</code>
562 */
563 public void setProvider(ILabelProvider newProvider) {
564 Assert.isNotNull(newProvider);
565 provider.removeListener(this);
566 provider.dispose();
567 provider = newProvider;
568
569 if (provider != null) {
570 provider.addListener(this);
571 }
572 }
573
574
575
576 private boolean isSelected(Object element) {
577 if (element != null && getCurrentSelection() != null) {
578 if (element.equals(getCurrentSelection())) {
579 return true;
580 }
581 }
582 return false;
583 }
584
585 /*
586 * (non-Javadoc)
587 *
588 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
589 */
590 private String getText(Object element) {
591 if (element instanceof ItemsListSeparator) {
592 return getSeparatorLabel(((ItemsListSeparator) element)
593 .getName());
594 }
595 String str = provider.getText(element);
596 return str;
597 }
598
599 private StyledString getStyledText(Object element,
600 IStyledLabelProvider provider) {
601 StyledString string = provider.getStyledText(element);
602
603 return string;
604 }
605
606 @Override
607 public void update(ViewerCell cell) {
608 Object element = cell.getElement();
609
610 if (!(element instanceof ItemsListSeparator)
611 && provider instanceof IStyledLabelProvider) {
612 IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) provider;
613 StyledString styledString = getStyledText(element,
614 styledLabelProvider);
615
616 cell.setText(styledString.getString());
617 cell.setStyleRanges(styledString.getStyleRanges());
618 cell.setImage(styledLabelProvider.getImage(element));
619 } else {
620 cell.setText(getText(element));
621
622 }
623 cell.setFont(getFont(element));
624 cell.setForeground(getForeground(element));
625 cell.setBackground(getBackground(element));
626
627 super.update(cell);
628 }
629
630 private String getSeparatorLabel(String separatorLabel) {
631 Rectangle rect = getList().getTable().getBounds();
632
633 int borderWidth = getList().getTable().computeTrim(0, 0, 0, 0).width;
634
635 int imageWidth = WorkbenchImages.getImage(
636 IWorkbenchGraphicConstants.IMG_OBJ_SEPARATOR).getBounds().width;
637
638 int width = rect.width - borderWidth - imageWidth;
639
640 GC gc = new GC(getList().getTable());
641 gc.setFont(getList().getTable().getFont());
642
643 int fSeparatorWidth = gc.getAdvanceWidth('-');
644 int fMessageLength = gc.textExtent(separatorLabel).x;
645
646 gc.dispose();
647
648 StringBuffer dashes = new StringBuffer();
649 int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2;
650 for (int i = 0; i < chars; i++) {
651 dashes.append('-');
652 }
653
654 StringBuffer result = new StringBuffer();
655 result.append(dashes);
656 result.append(" " + separatorLabel + " "); //$NON-NLS-1$//$NON-NLS-2$
657 result.append(dashes);
658 return result.toString().trim();
659 }
660
661 /*
662 * (non-Javadoc)
663 *
664 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
665 */
666 @Override
667 public void addListener(ILabelProviderListener listener) {
668 listeners.add(listener);
669 }
670
671 /*
672 * (non-Javadoc)
673 *
674 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
675 */
676 @Override
677 public void dispose() {
678 provider.removeListener(this);
679 provider.dispose();
680 super.dispose();
681 }
682
683 /*
684 * (non-Javadoc)
685 *
686 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
687 * java.lang.String)
688 */
689 @Override
690 public boolean isLabelProperty(Object element, String property) {
691 if (provider.isLabelProperty(element, property)) {
692 return true;
693 }
694 return false;
695 }
696
697 /*
698 * (non-Javadoc)
699 *
700 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
701 */
702 @Override
703 public void removeListener(ILabelProviderListener listener) {
704 listeners.remove(listener);
705 }
706
707 private Color getBackground(Object element) {
708 if (element instanceof ItemsListSeparator) {
709 return null;
710 }
711 if (provider instanceof IColorProvider) {
712 return ((IColorProvider) provider).getBackground(element);
713 }
714 return null;
715 }
716
717 private Color getForeground(Object element) {
718 if (element instanceof ItemsListSeparator) {
719 return Display.getCurrent().getSystemColor(
720 SWT.COLOR_WIDGET_NORMAL_SHADOW);
721 }
722 if (provider instanceof IColorProvider) {
723 return ((IColorProvider) provider).getForeground(element);
724 }
725 return null;
726 }
727
728 private Font getFont(Object element) {
729 if (element instanceof ItemsListSeparator) {
730 return null;
731 }
732 if (provider instanceof IFontProvider) {
733 return ((IFontProvider) provider).getFont(element);
734 }
735 return null;
736 }
737
738 /*
739 * (non-Javadoc)
740 *
741 * @see org.eclipse.jface.viewers.ILabelProviderListener#labelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
742 */
743 @Override
744 public void labelProviderChanged(LabelProviderChangedEvent event) {
745 Object[] l = listeners.getListeners();
746 for (int i = 0; i < listeners.size(); i++) {
747 ((ILabelProviderListener) l[i]).labelProviderChanged(event);
748 }
749 }
750 }
751
752 @Override
753 protected Point getInitialSize() {
754 return new Point(593, 399);
755 }
756
757
758
759 }
760
761