Fixes #2338
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / name / container / AbstractGroupedContainer.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.taxeditor.editor.name.container;
11
12 import java.util.Set;
13
14 import org.eclipse.core.runtime.Assert;
15 import org.eclipse.jface.dialogs.Dialog;
16 import org.eclipse.jface.text.IDocument;
17 import org.eclipse.jface.window.DefaultToolTip;
18 import org.eclipse.swt.custom.StyledText;
19 import org.eclipse.swt.dnd.DND;
20 import org.eclipse.swt.dnd.DragSource;
21 import org.eclipse.swt.dnd.Transfer;
22 import org.eclipse.swt.events.ControlAdapter;
23 import org.eclipse.swt.events.ControlEvent;
24 import org.eclipse.swt.events.ControlListener;
25 import org.eclipse.swt.events.FocusAdapter;
26 import org.eclipse.swt.events.FocusEvent;
27 import org.eclipse.swt.events.FocusListener;
28 import org.eclipse.swt.events.ModifyEvent;
29 import org.eclipse.swt.events.ModifyListener;
30 import org.eclipse.swt.events.MouseAdapter;
31 import org.eclipse.swt.events.MouseEvent;
32 import org.eclipse.swt.graphics.Color;
33 import org.eclipse.swt.graphics.Font;
34 import org.eclipse.swt.graphics.Image;
35 import org.eclipse.swt.widgets.Composite;
36 import org.eclipse.swt.widgets.Control;
37 import org.eclipse.swt.widgets.Display;
38 import org.eclipse.swt.widgets.Label;
39 import org.eclipse.swt.widgets.Menu;
40 import org.eclipse.ui.forms.IFormPart;
41 import org.eclipse.ui.forms.IManagedForm;
42 import org.eclipse.ui.forms.widgets.TableWrapData;
43 import org.eclipse.ui.forms.widgets.TableWrapLayout;
44
45 import eu.etaxonomy.cdm.common.CdmUtils;
46 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
47 import eu.etaxonomy.cdm.model.name.NameRelationship;
48 import eu.etaxonomy.cdm.model.name.NonViralName;
49 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
50 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
51 import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
52 import eu.etaxonomy.taxeditor.editor.EditorUtil;
53 import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
54 import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragListener;
55 import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragSourceEffect;
56 import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
57 import eu.etaxonomy.taxeditor.labels.ILabelImageStrategy;
58 import eu.etaxonomy.taxeditor.labels.LabelImageProvider;
59 import eu.etaxonomy.taxeditor.model.IElementHasDetails;
60 import eu.etaxonomy.taxeditor.model.NameHelper;
61 import eu.etaxonomy.taxeditor.model.TextHelper;
62 import eu.etaxonomy.taxeditor.parser.ParseHandler;
63 import eu.etaxonomy.taxeditor.preference.Resources;
64
65 /**
66 * Formats <code>GroupedComposite</code> with cosmetic and layout properties specific to the
67 * Editor. This should be used to maintain a consistent look and feel for all Editor
68 * freetext area components, such as DescriptionElementComposite.
69 * <p>
70 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the contents
71 * of {@link #getData()} when the <code>GroupedComposite</code> gets focus, i.e. to
72 * populate the property sheet with the data.
73 * </p>
74 * <p>
75 * The <code>IManagedForm</code> is also required to have a <code>Taxon</code> in its
76 * own <code>getData()</code>.
77 * </p>
78 * <p>
79 * The <code>IManagedForm</code> can also used for drawing borders by calling the method
80 * <code>createBorderSupport()</code>.
81 * </p>
82 *
83 * @author p.ciardelli
84 * @author n.hoffmann
85 * @created 02.06.2008
86 * @version 1.0
87 */
88 abstract public class AbstractGroupedContainer<T extends TaxonBase> implements IFormPart, IContainerConstants, IElementHasDetails {
89
90 protected ParseHandler parseHandler;
91
92 private FocusListener nameCompositeFocusListener;
93 private ModifyListener nameCompositeModifyListener;
94
95 protected NameViewer nameViewer;
96
97 private AbstractGroup group;
98
99 private Label nonEditableInfoLabel;
100 private DefaultToolTip nonEditableInfoHover;
101
102 private static AbstractGroupedContainer selection;
103
104 private FocusListener focusListener;
105 private LineBreakListener lineBreakListener;
106
107 private int cursorPosition;
108
109 protected Composite control;
110
111 private Color backgroundColor;
112 private boolean isDirty;
113
114
115 /**
116 * <p>Constructor for AbstractGroupedContainer.</p>
117 *
118 * @param editor a {@link eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor} object.
119 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
120 * @param taxonBase a T object.
121 * @param <T> a T object.
122 */
123 public AbstractGroupedContainer(T taxonBase){
124 setData(taxonBase);
125 parseHandler = ParseHandler.NewInstance(taxonBase.getName());
126 }
127
128 public void createContent(){
129 createControl();
130
131 createTextViewer();
132 createLineWrapSupport();
133 createLineBreakListener();
134
135 setMenu(getEditor().getMenu());
136
137 setDraggableControl(new Control[]{getControl(), getNameViewer().getRulerControl()});
138
139 createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
140
141
142 initializeComposite();
143
144 createListener();
145
146 isFreeTextEnabled();
147 }
148
149
150
151 /**
152 * <p>createListener</p>
153 */
154 protected void createListener(){
155 nameCompositeModifyListener = new ModifyListener(){
156
157 public void modifyText(ModifyEvent e) {
158 // mark the composite dirty
159 setDirty(true);
160 // parse the text
161 String text = nameViewer.getTextWidget().getText();
162
163 NonViralName name = parseHandler.parse(text);
164 getTaxonBase().setName(name);
165 getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
166
167 // show errors resulting from parsing
168 calculateErrors();
169 // store the position of the cursor
170 storeCursor();
171 // notify selection listener
172 setDelayedSelection();
173 }
174 };
175 nameCompositeFocusListener = new FocusAdapter(){
176
177 /* (non-Javadoc)
178 * @see org.eclipse.swt.events.FocusAdapter#focusLost(org.eclipse.swt.events.FocusEvent)
179 */
180 @Override
181 public void focusLost(FocusEvent e) {
182 super.focusLost(e);
183
184 persistName();
185 }
186 };
187
188 addListener();
189 }
190
191 private void addListener(){
192 getNameViewer().getTextWidget().addModifyListener(nameCompositeModifyListener);
193 getNameViewer().getTextWidget().addFocusListener(nameCompositeFocusListener);
194 }
195
196 private void removeListener(){
197 getNameViewer().getTextWidget().removeModifyListener(nameCompositeModifyListener);
198 getNameViewer().getTextWidget().removeFocusListener(nameCompositeFocusListener);
199 }
200
201 /**
202 * Initialize the composite specific code
203 */
204 protected abstract void initializeComposite();
205
206 /**
207 * <p>getEmptyTextPrompt</p>
208 *
209 * @return a {@link java.lang.String} object.
210 */
211 protected String getEmptyTextPrompt() {
212 return EMPTY_NAME_PROMPT;
213 }
214
215 /**
216 *
217 */
218 private void showNameRelations() {
219 TaxonNameBase<?, ?> name = getName();
220 if (name == null) {
221 return;
222 }
223
224 ILabelImageStrategy strategy = LabelImageProvider.getLabelStrategy(name);
225 LabelImageProvider labelProvider = new LabelImageProvider(strategy);
226
227 Set<NameRelationship> nameRelations = name.getNameRelations();
228 if (nameRelations.size() == 0) {
229 return;
230 }
231 // for (NameRelationship nameRelation : nameRelations) {
232 // String typeLabel = null;
233 // TaxonNameBase<?, ?> relatedName = null;
234 //
235 // if (name.equals(nameRelation.getFromName())) {
236 // typeLabel = labelProvider.getNameRelationTypeLabel(
237 // nameRelation.getType());
238 // relatedName = nameRelation.getToName();
239 // } else {
240 // typeLabel = labelProvider.getNameRelationTypeInverseLabel(
241 // nameRelation.getType());
242 // relatedName = nameRelation.getFromName();
243 // }
244 //
245 // setNonEditableInfo(typeLabel + " " + NameHelper.getDisplayName(relatedName));
246 // }
247 }
248
249 /**
250 * <p>initTextViewer</p>
251 */
252 protected void initTextViewer() {
253
254 // showNameRelations();
255
256 updateIndent();
257
258 updateIcon();
259
260 String text = NameHelper.getDisplayNameWithRef(getData());
261
262 if (text.length() == 0) {
263 initEmptyText();
264 } else {
265 getNameViewer().setText(text);
266 placeCursor();
267 }
268 calculateErrors();
269 }
270
271 /**
272 * <p>calculateErrors</p>
273 */
274 synchronized protected void calculateErrors() {
275 getNameViewer().clearErrors();
276 getNameViewer().setShowParsingErrors(getName());
277 }
278
279 /**
280 * <p>handleSplitText</p>
281 *
282 * @param text a {@link java.lang.String} object.
283 */
284 protected void handleSplitText(String text) {
285 // Create a synonym in a new homotypic group using text as name
286 TaxonNameBase synonymName = ParseHandler.parseReferencedName(text, null);
287
288 EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation
289 ("New Heterotypic Synonym", getEditor().getUndoContext(), getEditor().getTaxon(), synonymName, getEditor()));
290 }
291
292 /**
293 * Refreshes the display with latest data from the model.
294 *
295 * Note: Will not parse the text and not calculate errors!
296 */
297 public void refresh() {
298 // showNameRelations();
299
300 String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
301
302 if(getNameViewer().getTextWidget() == null){
303 // we might get here via dnd. Look slike it can be ignored
304 return;
305 }
306
307 if (text.length() == 0) {
308 initEmptyText();
309 } else if(! getNameViewer().getTextWidget().getText().equals(text)) {
310 removeListener();
311 getNameViewer().getTextWidget().setText(text);
312 addListener();
313 }
314
315 updateNonEditableInfo();
316
317 updateIcon();
318 // placeCursor();
319 updateIndent();
320
321 isFreeTextEnabled();
322 }
323
324 /**
325 *
326 */
327 protected abstract void updateIcon();
328
329
330 protected abstract void updateIndent();
331
332
333 /**
334 * <p>updateNonEditableInfo</p>
335 */
336 protected abstract void updateNonEditableInfo();
337
338 /**
339 *
340 */
341 private void isFreeTextEnabled() {
342 // Decide whether editing of freetext is allowed or not.
343 if (freetextEditingAllowed()) {
344
345 // set editable
346 getNameViewer().getTextWidget().setEditable(true);
347
348 // restore foreground font color again
349 getNameViewer().getTextWidget().setForeground(control.getForeground());
350 } else {
351 // set non-editable
352 getNameViewer().getTextWidget().setEditable(false);
353
354 // grey out text as a non-editable indicator
355 getNameViewer().getTextWidget().setForeground(EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR));
356 }
357 }
358
359
360 /**
361 * Checks whether the freetext should be editable based on specific empty fields.
362 * @return
363 */
364 private boolean freetextEditingAllowed() {
365 NonViralName name = (NonViralName) HibernateProxyHelper.deproxy(getName());
366 boolean enableFreetext = true;
367
368 enableFreetext &= isNameUsedMultipleTimes(name);
369 enableFreetext &= isNameParsable(name);
370
371 if(!enableFreetext){
372 getNameViewer().setShowNameNotParsableWarning(name);
373 }
374
375 return enableFreetext;
376 }
377
378 private boolean isNameUsedMultipleTimes(NonViralName name){
379 if(name.getTaxonBases().size() != 1){
380 getNameViewer().setShowMultipleNameUsageWarning(name);
381 return false;
382 }
383 return true;
384 }
385
386 private boolean isNameParsable(NonViralName name){
387 boolean isParsable = true;
388 isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); //taxonFieldsEmpty();
389
390 isParsable &= ! name.isProtectedAuthorshipCache();
391 isParsable &= ! name.isProtectedNameCache();
392
393
394 return isParsable;
395 }
396
397 /**
398 * Parse the text and calculate errors
399 */
400 public void parseAndCalculateErrors(){
401 removeListener();
402 String unparsedNameString = getNameViewer().getTextWidget().getText();
403 parseHandler.parse(unparsedNameString);
404 addListener();
405 calculateErrors();
406 }
407
408 /**
409 * <p>getTaxonBase</p>
410 *
411 * @return the taxonBase
412 */
413 public T getTaxonBase() {
414 return getData();
415 }
416
417 /**
418 * <p>getName</p>
419 *
420 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
421 */
422 public TaxonNameBase getName(){
423 return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase().getName());
424 }
425
426 /**
427 * <p>persistName</p>
428 */
429 public void persistName(){
430 if(isDirty()){
431 getNameViewer().getTextWidget().setEnabled(false);
432 final String unparsedNameString = getNameViewer().getTextWidget().getText();
433 // Job job = new Job("Persisting Name"){
434 //
435 // @Override
436 // protected IStatus run(IProgressMonitor monitor) {
437 //
438 final NonViralName name = parseHandler.parseAndResolveDuplicates(unparsedNameString);
439 //
440 // Display.getDefault().asyncExec(new Runnable(){
441 // public void run() {
442 getTaxonBase().setName(name);
443 getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
444 setDirty(false);
445 getNameViewer().getTextWidget().setEnabled(true);
446 // };
447 // });
448 //
449 //
450 // return Status.OK_STATUS;
451 // }
452 //
453 // };
454 //
455 // job.setPriority(Job.DECORATE);
456 // job.schedule();
457 }
458 }
459
460 /**
461 * <p>Getter for the field <code>group</code>.</p>
462 *
463 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
464 */
465 public AbstractGroup getGroup(){
466 if(group == null){
467 throw new IllegalStateException("Group shall not be null.");
468 }
469 return group;
470 }
471
472 /**
473 * <p>remove</p>
474 */
475 public void remove(){
476 getGroup().remove(this);
477 }
478
479 /**
480 * <p>createControl</p>
481 */
482 protected void createControl() {
483 control = getEditor().getToolkit().createComposite(getGroup().getControl());
484
485 control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
486 TableWrapLayout layout = new TableWrapLayout();
487 layout.leftMargin = 0;
488 layout.rightMargin = 0;
489 layout.topMargin = 5;
490 layout.bottomMargin = 5;
491
492 layout.verticalSpacing = 0;
493 layout.horizontalSpacing = 0;
494
495 control.setLayout(layout);
496
497 }
498
499 /**
500 * @return
501 */
502 protected TaxonNameEditor getEditor() {
503 return getGroup().getEditor();
504 }
505
506 /**
507 * <p>Getter for the field <code>control</code>.</p>
508 *
509 * @return a {@link org.eclipse.swt.widgets.Composite} object.
510 */
511 public Composite getControl(){
512 return control;
513 }
514
515 /**
516 * <p>createLineWrapSupport</p>
517 */
518 protected void createLineWrapSupport() {
519 new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
520 }
521
522
523 /**
524 * <p>createTextViewer</p>
525 */
526 protected void createTextViewer() {
527 nameViewer = new NameViewer(control);
528
529 focusListener = new FocusAdapter() {
530 @Override
531 public void focusGained(FocusEvent e) {
532 for(AbstractGroupedContainer container : getEditor().getGroupedContainers()){
533 container.colorSelected(NOT_SELECTED);
534 }
535 getEditor().getManagedForm().setInput(AbstractGroupedContainer.this);
536 placeCursor();
537 colorSelected(SELECTED_FOCUS);
538 }
539 };
540 nameViewer.getTextWidget().addFocusListener(focusListener);
541
542 //
543 MouseAdapter mouseListener = new MouseAdapter() {
544 @Override
545 public void mouseDown(MouseEvent e) {
546 storeCursor();
547 }
548 };
549 control.addMouseListener(mouseListener);
550 nameViewer.getRulerControl().addMouseListener(mouseListener);
551 nameViewer.getTextWidget().addMouseListener(mouseListener);
552 }
553
554 /**
555 * <p>setIcon</p>
556 *
557 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
558 */
559 public void setIcon(Image icon) {
560 getNameViewer().setIcon(icon);
561 }
562
563 /**
564 * <p>setIndent</p>
565 *
566 * @param indent a int.
567 */
568 public void setIndent(int indent) {
569 if (control.getLayout() instanceof TableWrapLayout) {
570 TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
571 layout.leftMargin = indent;
572 layout.rightMargin = ACCEPTED_INDENT;
573 control.setLayout(layout);
574 control.layout();
575 } else {
576 new RuntimeException("Couldn't indent - composite's layout must be TableWrapLayout.");
577 }
578 }
579
580 /**
581 * <p>setSelected</p>
582 */
583 public void setSelected() {
584 getNameViewer().getTextWidget().setFocus();
585 }
586
587 /**
588 * <p>isSelected</p>
589 *
590 * @return a boolean.
591 */
592 public boolean isSelected(){
593 return getEditor().getSelectedContainer() == this;
594 }
595
596 /**
597 * <p>colorSelected</p>
598 *
599 * @param mode a int.
600 */
601 public void colorSelected(int mode){
602 if(!control.isDisposed()){
603 String colorString = null;
604
605 switch(mode){
606 case SELECTED_FOCUS:
607 colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
608 break;
609 case SELECTED_NO_FOCUS:
610 colorString = Resources.COLOR_CONTROL_SELECTED;
611 break;
612 default:
613 colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
614 }
615
616 backgroundColor = EditorUtil.getColor(colorString);
617
618 setBackground(backgroundColor);
619 }
620 }
621
622
623 /**
624 * <p>setDelayedSelection</p>
625 */
626 protected void setDelayedSelection(){
627 //TODO this might be done better
628 // this is the quickest solution i could come up with and it improves performance
629 // please reimplement if you know better.
630 selection = this;
631
632 // start timer
633 Display display = Display.getCurrent();
634 Runnable runnable = new Runnable() {
635
636 public void run() {
637 getEditor().getManagedForm().setInput(selection);
638 }
639 };
640 display.timerExec(1000, runnable);
641
642 }
643
644 /**
645 * <p>setBackground</p>
646 *
647 * @param color a {@link org.eclipse.swt.graphics.Color} object.
648 */
649 public void setBackground(Color color) {
650 control.setBackground(color);
651
652 for(Control child : control.getChildren()){
653 child.setBackground(color);
654 }
655
656 getNameViewer().setBackground(color);
657 }
658
659 /* (non-Javadoc)
660 * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
661 */
662 /**
663 * <p>setFont</p>
664 *
665 * @param font a {@link org.eclipse.swt.graphics.Font} object.
666 */
667 public void setFont(Font font) {
668 getNameViewer().getTextWidget().setFont(font);
669 }
670
671 /**
672 * <p>Getter for the field <code>nameViewer</code>.</p>
673 *
674 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.NameViewer} object.
675 */
676 public NameViewer getNameViewer() {
677 if (nameViewer == null){
678 throw new RuntimeException("The Name Viewer is corrupt for Name Container: " + getTaxonBase().getName().getTitleCache());
679 }
680 return nameViewer;
681 }
682
683 /**
684 * If <code>textViewer</code> has already been set, it will show a
685 * <code>prompt</code> along the lines of "Click here to start entering data"
686 * when empty.
687 *
688 * @param prompt a {@link java.lang.String} object.
689 */
690 public void createEmptyViewerPrompt(final String prompt) {
691
692 Assert.isNotNull(getNameViewer());
693
694 final StyledText textControl = getNameViewer().getTextWidget();
695 final IDocument document = getNameViewer().getDocument();
696
697 setFocusListener(new FocusListener() {
698
699
700 public void focusGained(FocusEvent e) {
701 if (document.get().equals(prompt)) {
702 textControl.setFont(getViewerFont());
703 document.set("");
704 }
705 }
706
707
708 public void focusLost(FocusEvent e) {
709 if (document.getLength() == 0) {
710 initEmptyText();
711 }
712 }
713
714 });
715 textControl.addFocusListener(getFocusListener());
716
717 if (document.getLength() == 0) {
718 textControl.setFont(EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT));
719 document.set(prompt);
720 }
721 }
722
723 /**
724 * <p>getViewerFont</p>
725 *
726 * @return a {@link org.eclipse.swt.graphics.Font} object.
727 */
728 abstract protected Font getViewerFont();
729
730 /**
731 * <p>initEmptyText</p>
732 */
733 protected void initEmptyText() {
734 Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
735 getNameViewer().getTextWidget().setFont(defaultFont);
736
737 getNameViewer().getDocument().set(getEmptyTextPrompt());
738 placeCursor();
739 }
740
741 /**
742 * <p>Setter for the field <code>focusListener</code>.</p>
743 *
744 * @param focusListener a {@link org.eclipse.swt.events.FocusListener} object.
745 */
746 protected void setFocusListener(FocusListener focusListener) {
747 this.focusListener = focusListener;
748 }
749
750 private FocusListener getFocusListener() {
751 return focusListener;
752 }
753
754 /**
755 * <p>setDirty</p>
756 *
757 * @param isDirty a boolean.
758 */
759 public void setDirty(boolean isDirty) {
760 if(isDirty){
761 getEditor().getManagedForm().dirtyStateChanged();
762 }
763 this.isDirty = isDirty;
764 }
765
766 /**
767 * <p>isDirty</p>
768 *
769 * @return a boolean.
770 */
771 public boolean isDirty(){
772 return isDirty;
773 }
774
775 /**
776 * <p>setMenu</p>
777 *
778 * @param menu a {@link org.eclipse.swt.widgets.Menu} object.
779 */
780 public void setMenu (Menu menu) {
781 control.setMenu(menu);
782
783 getNameViewer().setMenu(menu);
784 }
785
786 private Control[] draggableControls;
787
788 /**
789 * <p>setDraggableControl</p>
790 *
791 * @param controls an array of {@link org.eclipse.swt.widgets.Control} objects.
792 */
793 protected void setDraggableControl(Control[] controls) {
794 draggableControls = controls;
795 }
796
797 /**
798 * <p>setIsDraggable</p>
799 *
800 * @param draggable a boolean.
801 */
802 public void setIsDraggable(boolean draggable) {
803
804 if (draggable) {
805
806 if (draggableControls == null) {
807 throw new NullPointerException(
808 "Draggable controls must be set to add draggability");
809 }
810
811 Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
812 int operations = DND.DROP_MOVE;
813
814 for(Control draggableControl : draggableControls){
815 DragSource dragSource = new DragSource(draggableControl, operations);
816 dragSource.setTransfer(types);
817
818 dragSource.addDragListener(new NameEditorDragListener(this));
819 dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(control));
820 }
821 }
822 }
823
824 private String nonEditableText;
825
826 ControlListener nonEditableResizeListener = new ControlAdapter() {
827
828 int width = 0;
829
830 @Override
831 public void controlResized(ControlEvent e) {
832 if (nonEditableInfoLabel.getBounds().width == width) {
833 return;
834 }
835 width = nonEditableInfoLabel.getBounds().width;
836 if (nonEditableInfoLabel.getBounds().width > 0) {
837 nonEditableInfoLabel.setText(
838 Dialog.shortenText(nonEditableText.toUpperCase(),
839 nonEditableInfoLabel));
840 }
841 }
842 };
843
844 private String nonEditableHoverText;
845
846 private LabelEllipsisListener nonEditableLabelEllipsisListener;
847
848 private T data;
849
850 /**
851 * nonEditableInfo is a label displayed underneath a GroupedComposite's
852 * input field. For instance, NameComposites display things like name relations,
853 * sec. references, etc. here.
854 *
855 * @param info the text to display in the label
856 * @param append whether the string should be appended to text that is already shown in the label
857 */
858 public void setNonEditableInfo(String info, boolean append) {
859 // TODO non editable info should only be drawn once, when everything else is drawn
860 info = info.toUpperCase();
861
862 if(append){
863 nonEditableText += ", " + info;
864 nonEditableHoverText += "\n" + info;
865 }else{
866 nonEditableText = info;
867 nonEditableHoverText = info;
868 }
869
870 if (nonEditableInfoLabel == null) {
871 nonEditableInfoLabel = getEditor().getToolkit().createLabel(control, "");
872 TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP);
873 // Set indent to viewer ruler's width
874 if (getNameViewer().getRulerControl() != null) {
875 // TODO right justify
876 layoutData.indent = NameViewer.RULER_WIDTH;
877 }
878 nonEditableInfoLabel.setLayoutData(layoutData);
879
880
881
882 nonEditableLabelEllipsisListener = new LabelEllipsisListener(nonEditableInfoLabel) {
883 @Override
884 public String getLabelText() {
885 return nonEditableText.toUpperCase();
886 }
887 };
888 nonEditableInfoLabel.addControlListener(nonEditableLabelEllipsisListener);
889
890 nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
891 nonEditableInfoHover.setRespectDisplayBounds(true);
892
893 }
894 nonEditableInfoHover.setText(nonEditableHoverText);
895 nonEditableInfoLabel.setText(nonEditableText);
896
897 calculateErrors();
898 }
899
900 /**
901 * <p>Getter for the field <code>data</code>.</p>
902 *
903 * @return a T object.
904 */
905 public T getData(){
906 return data;
907 }
908
909 /**
910 * <p>Setter for the field <code>data</code>.</p>
911 *
912 * @param data a T object.
913 */
914 public void setData(T data){
915 this.data = (T) HibernateProxyHelper.deproxy(data);
916 }
917
918 /**
919 * If the user hitting carriage return should cause something to happen -
920 * i.e. the creation of a new composite - call this method and override
921 * the method handleSplitText().
922 */
923 protected void createLineBreakListener() {
924 lineBreakListener = new LineBreakListener() {
925 @Override
926 public void handleSplitText(String text) {
927 AbstractGroupedContainer.this.handleSplitText(text);
928 }
929 };
930
931 getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
932 }
933
934 abstract class LabelEllipsisListener extends ControlAdapter {
935
936 private final Label label;
937 int width = 0;
938
939 LabelEllipsisListener(Label label) {
940 this.label = label;
941 }
942
943 abstract public String getLabelText();
944
945 @Override
946 public void controlResized(ControlEvent e) {
947 if (label.getBounds().width == width) {
948 return;
949 }
950 width = label.getBounds().width;
951 if (label.getBounds().width > 0) {
952 label.setText(TextHelper.shortenText(getLabelText(), label));
953 }
954 }
955 }
956
957 /**
958 * <p>storeCursor</p>
959 */
960 public void storeCursor() {
961 this.cursorPosition = getNameViewer().getCursorPosition();
962 }
963
964 /**
965 * Puts the cursor to the position it was last seen on or to the end of line
966 * if no former position is known.
967 */
968 public void placeCursor(){
969 if(cursorPosition == 0){
970 getNameViewer().setCursorToEOL();
971 }else{
972 getNameViewer().setCursorPosition(cursorPosition);
973 }
974 }
975
976
977
978 /**
979 * <p>Setter for the field <code>group</code>.</p>
980 *
981 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
982 */
983 public void setGroup(AbstractGroup group) {
984 this.group = group;
985 }
986
987
988
989 /**
990 * <p>restoreColor</p>
991 */
992 public void restoreColor() {
993 setBackground(backgroundColor);
994 }
995
996
997
998 /* (non-Javadoc)
999 * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
1000 */
1001 @Override
1002 public void initialize(IManagedForm form) {
1003 // TODO Auto-generated method stub
1004
1005 }
1006
1007
1008 /* (non-Javadoc)
1009 * @see org.eclipse.ui.forms.IFormPart#dispose()
1010 */
1011 @Override
1012 public void dispose() {
1013 if(getControl() != null){
1014 setMenu(null);
1015 getControl().dispose();
1016 }
1017 }
1018
1019
1020 /* (non-Javadoc)
1021 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1022 */
1023 @Override
1024 public void commit(boolean onSave) {
1025 if(isDirty()){
1026 persistName();
1027 }
1028 }
1029
1030
1031 /* (non-Javadoc)
1032 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1033 */
1034 @Override
1035 public boolean setFormInput(Object input) {
1036 // TODO Auto-generated method stub
1037 return false;
1038 }
1039
1040
1041 /* (non-Javadoc)
1042 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1043 */
1044 @Override
1045 public void setFocus() {
1046 getNameViewer().getControl().setFocus();
1047 }
1048
1049
1050 /* (non-Javadoc)
1051 * @see org.eclipse.ui.forms.IFormPart#isStale()
1052 */
1053 @Override
1054 public boolean isStale() {
1055 // TODO Auto-generated method stub
1056 return false;
1057 }
1058 }