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