3 * Copyright (C) 2017 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.taxeditor
.ui
.dialog
.selection
;
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
;
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
;
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
;
90 public abstract class SearchDialog
<T
extends ICdmBase
> extends Dialog
{// implements IConversationEnabled{
92 private TableViewer list
;
93 private Text searchField
;
95 protected Button newButton1
;
97 protected Button newButton2
;
98 protected Button filterButton
;
99 protected Button btnCheckButton
;
100 private StructuredSelection currentSelection
;
102 // message to show user
103 private String message
= ""; //$NON-NLS-1$
105 protected final ContentProvider contentProvider
;
106 ItemsListLabelProvider itemsListLabelProvider
;
108 private final RefreshCacheJob refreshCacheJob
;
110 protected Object preferenceID
;
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
<>();
119 private static final String EMPTY_STRING
= ""; //$NON-NLS-1$
120 private GridData gd_1
;
121 protected boolean useIdentifier
;
123 public SearchDialog(Shell parent
, String title
) {
126 contentProvider
= new ContentProvider();
127 refreshCacheJob
= new RefreshCacheJob();
132 public void create() {
138 protected void configureShell(Shell shell
) {
139 super.configureShell(shell
);
140 shell
.setText(title
);
144 protected Control
createDialogArea(Composite parent
) {
145 Composite container
= (Composite
) super.createDialogArea(parent
);
147 GridData gd
= new GridData(GridData
.FILL_BOTH
);
148 container
.setLayoutData(gd
);
150 GridLayout layout
= new GridLayout();
151 layout
.numColumns
= 1;
152 layout
.marginWidth
= 0;
153 layout
.marginHeight
= 0;
154 container
.setLayout(layout
);
156 final Label headerLabel
= createHeader(container
);
158 Composite searchAndFilter
= new Composite(container
, container
.getStyle());
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() {
167 public void getName(AccessibleEvent e
) {
168 e
.result
= LegacyActionTools
.removeMnemonics(headerLabel
172 searchField
.addModifyListener(new ModifyListener() {
174 public void modifyText(ModifyEvent e
) {
176 // fillContentProvider(null);
180 gd_1
= new GridData(GridData
.GRAB_HORIZONTAL
| GridData
.HORIZONTAL_ALIGN_FILL
);
181 gd_1
.horizontalIndent
= 7;
183 searchField
.setLayoutData(gd_1
);
185 createFilterButton(searchAndFilter
);
187 addIdentifierCheckButton(searchAndFilter
);
188 // new Label(searchAndFilter, SWT.NONE);
189 setList(new TableViewer(container
, SWT
.SINGLE
190 | SWT
.BORDER
| SWT
.V_SCROLL
| SWT
.VIRTUAL
));
192 getList().setContentProvider(contentProvider
);
193 getList().setLabelProvider(getItemsListLabelProvider());
194 getList().setInput(new Object
[0]);
195 getList().setItemCount(contentProvider
.getNumberOfElements());
196 getList().addSelectionChangedListener(new ISelectionChangedListener() {
198 public void selectionChanged(SelectionChangedEvent event
) {
199 StructuredSelection selection
= (StructuredSelection
) event
201 currentSelection
= selection
;
204 getList().addDoubleClickListener(new IDoubleClickListener() {
206 public void doubleClick(DoubleClickEvent event
) {
210 searchField
.addKeyListener(new KeyAdapter() {
212 public void keyPressed(KeyEvent e
) {
213 if (e
.keyCode
== SWT
.ARROW_DOWN
) {
214 if (getList().getTable().getItemCount() > 0) {
215 getList().getTable().setFocus();
221 // createExtendedContentArea(container);
222 new Label(container
, SWT
.NONE
);
228 protected void addIdentifierCheckButton(Composite searchAndFilter
) {
229 //as default do nothing
232 abstract void createFilterButton(Composite searchAndFilter
) ;
234 protected abstract void search();
238 * Create a new header which is labelled by headerLabel.
241 * @return Label the label of the header
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
);
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() {
264 public void keyTraversed(TraverseEvent e
) {
265 if (e
.detail
== SWT
.TRAVERSE_MNEMONIC
&& e
.doit
) {
266 e
.detail
= SWT
.TRAVERSE_NONE
;
267 searchField
.setFocus();
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
);
280 protected String
getMessage() {
284 protected void setMessage(String message
){
285 this.message
= message
;
288 public Text
getSearchField() {
293 public void setSearchField(Text searchField
) {
294 this.searchField
= searchField
;
297 private ItemsListLabelProvider
getItemsListLabelProvider() {
298 if (itemsListLabelProvider
== null) {
299 itemsListLabelProvider
= new ItemsListLabelProvider(
300 new LabelProvider());
302 return itemsListLabelProvider
;
306 public TableViewer
getList() {
310 public void setList(TableViewer 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
);
319 public Button
getNewButton1() {
323 public void setNewButton1(Button newButton1
) {
324 this.newButton1
= newButton1
;
327 public Button
getNewButton2() {
331 public void setNewButton2(Button newButton2
) {
332 this.newButton2
= newButton2
;
335 public Button
getFilterButton() {
339 public void setFilterButton(Button filterButton
) {
340 this.filterButton
= filterButton
;
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.
350 * @see IWorkbenchPreferenceConstants#USE_COLORED_LABELS
352 * @param listLabelProvider
353 * the label provider for items in the list
355 public void setListLabelProvider(ILabelProvider listLabelProvider
) {
356 getItemsListLabelProvider().setProvider(listLabelProvider
);
359 protected Comparator
getItemsComparator() {
360 return new Comparator
<UuidAndTitleCache
>() {
362 public int compare(UuidAndTitleCache entity1
,
363 UuidAndTitleCache entity2
) {
364 Collator collator
= Collator
.getInstance();
365 if (entity1
.getUuid().equals(entity2
.getUuid())){
368 int result
= collator
.compare(entity1
.getTitleCache(), entity2
.getTitleCache());
370 result
= entity1
.getUuid().compareTo(entity2
.getUuid());
377 class ContentProvider
implements IStructuredContentProvider
{
382 * Creates new instance of <code>ContentProvider</code>.
384 public ContentProvider() {
385 this.items
= Collections
.synchronizedList(new ArrayList(2048));
389 * Removes all content items and resets progress message.
391 public void reset() {
395 public void add(Object item
) {
396 this.items
.add(item
);
402 // public void refresh() {
403 // scheduleRefresh();
407 // * Schedule refresh job.
409 // public void scheduleRefresh() {
410 // refreshCacheJob.cancelAll();
411 // refreshCacheJob.schedule();
415 public Object
[] getElements(Object inputElement
) {
416 return items
.toArray();
419 public int getNumberOfElements() {
424 public void dispose() {
428 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
432 public boolean isUseIdentifier() {
433 return useIdentifier
;
436 public StructuredSelection
getCurrentSelection() {
437 return currentSelection
;
441 * Fills the content provider with matching items.
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
448 protected abstract void fillContentProvider(IProgressMonitor progressMonitor
) ;
452 * Refreshes the dialog - has to be called in UI thread.
454 public void refresh() {
455 if (getList() != null && !getList().getTable().isDisposed()) {
457 List
<?
> lastRefreshSelection
= ((StructuredSelection
) getList()
458 .getSelection()).toList();
459 getList().getTable().deselectAll();
461 getList().setItemCount(contentProvider
.getNumberOfElements());
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
));
472 getList().getTable().setSelection(0);
473 getList().getTable().notifyListeners(SWT
.Selection
, new Event());
475 if ( super.getButton(IDialogConstants
.OK_ID
) != null){
476 super.getButton(IDialogConstants
.OK_ID
).setEnabled(true);
479 getList().setSelection(StructuredSelection
.EMPTY
);
485 * A job responsible for computing filtered items list presented using
486 * <code>RefreshJob</code>.
488 * @see FilteredItemsSelectionDialog.RefreshJob
490 private class RefreshCacheJob
extends Job
{
492 public RefreshCacheJob() {
493 super(WorkbenchMessages
.FilteredItemsSelectionDialog_cacheRefreshJob
);
498 * Stops the job and all sub-jobs.
500 public void cancelAll() {
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);
511 if (SearchDialog
.this != null) {
512 SearchDialog
.this.fillContentProvider(monitor
);
515 return new Status(IStatus
.OK
, PlatformUI
.PLUGIN_ID
, IStatus
.OK
,
521 protected void canceling() {
527 private class ItemsListLabelProvider
extends StyledCellLabelProvider
528 implements ILabelProviderListener
{
529 private ILabelProvider provider
;
532 * Creates a new instance of the class.
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>
539 public ItemsListLabelProvider(ILabelProvider provider
) {
540 Assert
.isNotNull(provider
);
541 this.provider
= provider
;
542 this.provider
.addListener(this);
546 * Sets new label provider.
549 * new label provider for items in the list, not
552 public void setProvider(ILabelProvider newProvider
) {
553 Assert
.isNotNull(newProvider
);
554 provider
.removeListener(this);
556 provider
= newProvider
;
558 if (provider
!= null) {
559 provider
.addListener(this);
563 private boolean isSelected(Object element
) {
564 if (element
!= null && getCurrentSelection() != null) {
565 if (element
.equals(getCurrentSelection())) {
572 private String
getText(Object element
) {
573 if (element
instanceof ItemsListSeparator
) {
574 return getSeparatorLabel(((ItemsListSeparator
) element
)
577 String str
= provider
.getText(element
);
581 private StyledString
getStyledText(Object element
,
582 IStyledLabelProvider provider
) {
583 StyledString string
= provider
.getStyledText(element
);
589 public void update(ViewerCell cell
) {
590 Object element
= cell
.getElement();
592 if (!(element
instanceof ItemsListSeparator
)
593 && provider
instanceof IStyledLabelProvider
) {
594 IStyledLabelProvider styledLabelProvider
= (IStyledLabelProvider
) provider
;
595 StyledString styledString
= getStyledText(element
,
596 styledLabelProvider
);
598 cell
.setText(styledString
.getString());
599 cell
.setStyleRanges(styledString
.getStyleRanges());
600 cell
.setImage(styledLabelProvider
.getImage(element
));
602 cell
.setText(getText(element
));
605 cell
.setFont(getFont(element
));
606 cell
.setForeground(getForeground(element
));
607 cell
.setBackground(getBackground(element
));
612 private String
getSeparatorLabel(String separatorLabel
) {
613 Rectangle rect
= getList().getTable().getBounds();
615 int borderWidth
= getList().getTable().computeTrim(0, 0, 0, 0).width
;
617 int imageWidth
= WorkbenchImages
.getImage(
618 IWorkbenchGraphicConstants
.IMG_OBJ_SEPARATOR
).getBounds().width
;
620 int width
= rect
.width
- borderWidth
- imageWidth
;
622 GC gc
= new GC(getList().getTable());
623 gc
.setFont(getList().getTable().getFont());
625 int fSeparatorWidth
= gc
.getAdvanceWidth('-');
626 int fMessageLength
= gc
.textExtent(separatorLabel
).x
;
630 StringBuffer dashes
= new StringBuffer();
631 int chars
= (((width
- fMessageLength
) / fSeparatorWidth
) / 2) - 2;
632 for (int i
= 0; i
< chars
; i
++) {
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();
644 public void addListener(ILabelProviderListener listener
) {
645 listeners
.add(listener
);
649 public void dispose() {
650 provider
.removeListener(this);
656 public boolean isLabelProperty(Object element
, String property
) {
657 if (provider
.isLabelProperty(element
, property
)) {
664 public void removeListener(ILabelProviderListener listener
) {
665 listeners
.remove(listener
);
668 private Color
getBackground(Object element
) {
669 if (element
instanceof ItemsListSeparator
) {
672 if (provider
instanceof IColorProvider
) {
673 return ((IColorProvider
) provider
).getBackground(element
);
678 private Color
getForeground(Object element
) {
679 if (element
instanceof ItemsListSeparator
) {
680 return Display
.getCurrent().getSystemColor(
681 SWT
.COLOR_WIDGET_NORMAL_SHADOW
);
683 if (provider
instanceof IColorProvider
) {
684 return ((IColorProvider
) provider
).getForeground(element
);
689 private Font
getFont(Object element
) {
690 if (element
instanceof ItemsListSeparator
) {
693 if (provider
instanceof IFontProvider
) {
694 return ((IFontProvider
) provider
).getFont(element
);
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
);
709 protected Point
getInitialSize() {
710 return new Point(593, 399);