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