1cd5f562fc7b905d067452adac0cc85e641a27bd
[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 public void focusGained(FocusEvent e) {
531 for(AbstractGroupedContainer container : getEditor().getGroupedContainers()){
532 container.colorSelected(NOT_SELECTED);
533 }
534 getEditor().getManagedForm().setInput(AbstractGroupedContainer.this);
535 placeCursor();
536 colorSelected(SELECTED_FOCUS);
537 }
538 };
539 nameViewer.getTextWidget().addFocusListener(focusListener);
540
541 //
542 MouseAdapter mouseListener = new MouseAdapter() {
543 public void mouseDown(MouseEvent e) {
544 storeCursor();
545 }
546 };
547 control.addMouseListener(mouseListener);
548 nameViewer.getRulerControl().addMouseListener(mouseListener);
549 nameViewer.getTextWidget().addMouseListener(mouseListener);
550 }
551
552 /**
553 * <p>setIcon</p>
554 *
555 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
556 */
557 public void setIcon(Image icon) {
558 getNameViewer().setIcon(icon);
559 }
560
561 /**
562 * <p>setIndent</p>
563 *
564 * @param indent a int.
565 */
566 public void setIndent(int indent) {
567 if (control.getLayout() instanceof TableWrapLayout) {
568 TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
569 layout.leftMargin = indent;
570 layout.rightMargin = ACCEPTED_INDENT;
571 control.setLayout(layout);
572 control.layout();
573 } else {
574 new RuntimeException("Couldn't indent - composite's layout must be TableWrapLayout.");
575 }
576 }
577
578 /**
579 * <p>setSelected</p>
580 */
581 public void setSelected() {
582 getNameViewer().getTextWidget().setFocus();
583 }
584
585 /**
586 * <p>isSelected</p>
587 *
588 * @return a boolean.
589 */
590 public boolean isSelected(){
591 return getEditor().getSelectedContainer() == this;
592 }
593
594 /**
595 * <p>colorSelected</p>
596 *
597 * @param mode a int.
598 */
599 public void colorSelected(int mode){
600 if(!control.isDisposed()){
601 String colorString = null;
602
603 switch(mode){
604 case SELECTED_FOCUS:
605 colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
606 break;
607 case SELECTED_NO_FOCUS:
608 colorString = Resources.COLOR_CONTROL_SELECTED;
609 break;
610 default:
611 colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
612 }
613
614 backgroundColor = EditorUtil.getColor(colorString);
615
616 setBackground(backgroundColor);
617 }
618 }
619
620
621 /**
622 * <p>setDelayedSelection</p>
623 */
624 protected void setDelayedSelection(){
625 //TODO this might be done better
626 // this is the quickest solution i could come up with and it improves performance
627 // please reimplement if you know better.
628 selection = this;
629
630 // start timer
631 Display display = Display.getCurrent();
632 Runnable runnable = new Runnable() {
633
634 public void run() {
635 getEditor().getManagedForm().setInput(selection);
636 }
637 };
638 display.timerExec(1000, runnable);
639
640 }
641
642 /**
643 * <p>setBackground</p>
644 *
645 * @param color a {@link org.eclipse.swt.graphics.Color} object.
646 */
647 public void setBackground(Color color) {
648 control.setBackground(color);
649
650 for(Control child : control.getChildren()){
651 child.setBackground(color);
652 }
653
654 getNameViewer().setBackground(color);
655 }
656
657 /* (non-Javadoc)
658 * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
659 */
660 /**
661 * <p>setFont</p>
662 *
663 * @param font a {@link org.eclipse.swt.graphics.Font} object.
664 */
665 public void setFont(Font font) {
666 getNameViewer().getTextWidget().setFont(font);
667 }
668
669 /**
670 * <p>Getter for the field <code>nameViewer</code>.</p>
671 *
672 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.NameViewer} object.
673 */
674 public NameViewer getNameViewer() {
675 if (nameViewer == null){
676 throw new RuntimeException("The Name Viewer is corrupt for Name Container: " + getTaxonBase().getName().getTitleCache());
677 }
678 return nameViewer;
679 }
680
681 /**
682 * If <code>textViewer</code> has already been set, it will show a
683 * <code>prompt</code> along the lines of "Click here to start entering data"
684 * when empty.
685 *
686 * @param prompt a {@link java.lang.String} object.
687 */
688 public void createEmptyViewerPrompt(final String prompt) {
689
690 Assert.isNotNull(getNameViewer());
691
692 final StyledText textControl = getNameViewer().getTextWidget();
693 final IDocument document = getNameViewer().getDocument();
694
695 setFocusListener(new FocusListener() {
696
697
698 public void focusGained(FocusEvent e) {
699 if (document.get().equals(prompt)) {
700 textControl.setFont(getViewerFont());
701 document.set("");
702 }
703 }
704
705
706 public void focusLost(FocusEvent e) {
707 if (document.getLength() == 0) {
708 initEmptyText();
709 }
710 }
711
712 });
713 textControl.addFocusListener(getFocusListener());
714
715 if (document.getLength() == 0) {
716 textControl.setFont(EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT));
717 document.set(prompt);
718 }
719 }
720
721 /**
722 * <p>getViewerFont</p>
723 *
724 * @return a {@link org.eclipse.swt.graphics.Font} object.
725 */
726 abstract protected Font getViewerFont();
727
728 /**
729 * <p>initEmptyText</p>
730 */
731 protected void initEmptyText() {
732 Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
733 getNameViewer().getTextWidget().setFont(defaultFont);
734
735 getNameViewer().getDocument().set(getEmptyTextPrompt());
736 placeCursor();
737 }
738
739 /**
740 * <p>Setter for the field <code>focusListener</code>.</p>
741 *
742 * @param focusListener a {@link org.eclipse.swt.events.FocusListener} object.
743 */
744 protected void setFocusListener(FocusListener focusListener) {
745 this.focusListener = focusListener;
746 }
747
748 private FocusListener getFocusListener() {
749 return focusListener;
750 }
751
752 /**
753 * <p>setDirty</p>
754 *
755 * @param isDirty a boolean.
756 */
757 public void setDirty(boolean isDirty) {
758 if(isDirty){
759 getEditor().getManagedForm().dirtyStateChanged();
760 }
761 this.isDirty = isDirty;
762 }
763
764 /**
765 * <p>isDirty</p>
766 *
767 * @return a boolean.
768 */
769 public boolean isDirty(){
770 return isDirty;
771 }
772
773 /**
774 * <p>setMenu</p>
775 *
776 * @param menu a {@link org.eclipse.swt.widgets.Menu} object.
777 */
778 public void setMenu (Menu menu) {
779 control.setMenu(menu);
780
781 getNameViewer().setMenu(menu);
782 }
783
784 private Control[] draggableControls;
785
786 /**
787 * <p>setDraggableControl</p>
788 *
789 * @param controls an array of {@link org.eclipse.swt.widgets.Control} objects.
790 */
791 protected void setDraggableControl(Control[] controls) {
792 draggableControls = controls;
793 }
794
795 /**
796 * <p>setIsDraggable</p>
797 *
798 * @param draggable a boolean.
799 */
800 public void setIsDraggable(boolean draggable) {
801
802 if (draggable) {
803
804 if (draggableControls == null) {
805 throw new NullPointerException(
806 "Draggable controls must be set to add draggability");
807 }
808
809 Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
810 int operations = DND.DROP_MOVE;
811
812 for(Control draggableControl : draggableControls){
813 DragSource dragSource = new DragSource(draggableControl, operations);
814 dragSource.setTransfer(types);
815
816 dragSource.addDragListener(new NameEditorDragListener(this));
817 dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(control));
818 }
819 }
820 }
821
822 private String nonEditableText;
823
824 ControlListener nonEditableResizeListener = new ControlAdapter() {
825
826 int width = 0;
827
828 public void controlResized(ControlEvent e) {
829 if (nonEditableInfoLabel.getBounds().width == width) {
830 return;
831 }
832 width = nonEditableInfoLabel.getBounds().width;
833 if (nonEditableInfoLabel.getBounds().width > 0) {
834 nonEditableInfoLabel.setText(
835 Dialog.shortenText(nonEditableText.toUpperCase(),
836 nonEditableInfoLabel));
837 }
838 }
839 };
840
841 private String nonEditableHoverText;
842
843 private LabelEllipsisListener nonEditableLabelEllipsisListener;
844
845 private T data;
846
847 /**
848 * nonEditableInfo is a label displayed underneath a GroupedComposite's
849 * input field. For instance, NameComposites display things like name relations,
850 * sec. references, etc. here.
851 *
852 * @param info the text to display in the label
853 * @param append whether the string should be appended to text that is already shown in the label
854 */
855 public void setNonEditableInfo(String info, boolean append) {
856 // TODO non editable info should only be drawn once, when everything else is drawn
857 info = info.toUpperCase();
858
859 if(append){
860 nonEditableText += ", " + info;
861 nonEditableHoverText += "\n" + info;
862 }else{
863 nonEditableText = info;
864 nonEditableHoverText = info;
865 }
866
867 if (nonEditableInfoLabel == null) {
868 nonEditableInfoLabel = getEditor().getToolkit().createLabel(control, "");
869 TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP);
870 // Set indent to viewer ruler's width
871 if (getNameViewer().getRulerControl() != null) {
872 // TODO right justify
873 layoutData.indent = NameViewer.RULER_WIDTH;
874 }
875 nonEditableInfoLabel.setLayoutData(layoutData);
876
877
878
879 nonEditableLabelEllipsisListener = new LabelEllipsisListener(nonEditableInfoLabel) {
880 @Override
881 public String getLabelText() {
882 return nonEditableText.toUpperCase();
883 }
884 };
885 nonEditableInfoLabel.addControlListener(nonEditableLabelEllipsisListener);
886
887 nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
888 nonEditableInfoHover.setRespectDisplayBounds(true);
889
890 }
891 nonEditableInfoHover.setText(nonEditableHoverText);
892 nonEditableInfoLabel.setText(nonEditableText);
893 }
894
895 /**
896 * <p>Getter for the field <code>data</code>.</p>
897 *
898 * @return a T object.
899 */
900 public T getData(){
901 return data;
902 }
903
904 /**
905 * <p>Setter for the field <code>data</code>.</p>
906 *
907 * @param data a T object.
908 */
909 public void setData(T data){
910 this.data = (T) HibernateProxyHelper.deproxy(data);
911 }
912
913 /**
914 * If the user hitting carriage return should cause something to happen -
915 * i.e. the creation of a new composite - call this method and override
916 * the method handleSplitText().
917 */
918 protected void createLineBreakListener() {
919 lineBreakListener = new LineBreakListener() {
920 @Override
921 public void handleSplitText(String text) {
922 AbstractGroupedContainer.this.handleSplitText(text);
923 }
924 };
925
926 getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
927 }
928
929 abstract class LabelEllipsisListener extends ControlAdapter {
930
931 private Label label;
932 int width = 0;
933
934 LabelEllipsisListener(Label label) {
935 this.label = label;
936 }
937
938 abstract public String getLabelText();
939
940 public void controlResized(ControlEvent e) {
941 if (label.getBounds().width == width) {
942 return;
943 }
944 width = label.getBounds().width;
945 if (label.getBounds().width > 0) {
946 label.setText(TextHelper.shortenText(getLabelText(), label));
947 }
948 }
949 }
950
951 /**
952 * <p>storeCursor</p>
953 */
954 public void storeCursor() {
955 this.cursorPosition = getNameViewer().getCursorPosition();
956 }
957
958 /**
959 * Puts the cursor to the position it was last seen on or to the end of line
960 * if no former position is known.
961 */
962 public void placeCursor(){
963 if(cursorPosition == 0){
964 getNameViewer().setCursorToEOL();
965 }else{
966 getNameViewer().setCursorPosition(cursorPosition);
967 }
968 }
969
970
971
972 /**
973 * <p>Setter for the field <code>group</code>.</p>
974 *
975 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
976 */
977 public void setGroup(AbstractGroup group) {
978 this.group = group;
979 }
980
981
982
983 /**
984 * <p>restoreColor</p>
985 */
986 public void restoreColor() {
987 setBackground(backgroundColor);
988 }
989
990
991
992 /* (non-Javadoc)
993 * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
994 */
995 @Override
996 public void initialize(IManagedForm form) {
997 // TODO Auto-generated method stub
998
999 }
1000
1001
1002 /* (non-Javadoc)
1003 * @see org.eclipse.ui.forms.IFormPart#dispose()
1004 */
1005 @Override
1006 public void dispose() {
1007 if(getControl() != null){
1008 setMenu(null);
1009 getControl().dispose();
1010 }
1011 }
1012
1013
1014 /* (non-Javadoc)
1015 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1016 */
1017 @Override
1018 public void commit(boolean onSave) {
1019 if(isDirty()){
1020 persistName();
1021 }
1022 }
1023
1024
1025 /* (non-Javadoc)
1026 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1027 */
1028 @Override
1029 public boolean setFormInput(Object input) {
1030 // TODO Auto-generated method stub
1031 return false;
1032 }
1033
1034
1035 /* (non-Javadoc)
1036 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1037 */
1038 @Override
1039 public void setFocus() {
1040 getNameViewer().getControl().setFocus();
1041 }
1042
1043
1044 /* (non-Javadoc)
1045 * @see org.eclipse.ui.forms.IFormPart#isStale()
1046 */
1047 @Override
1048 public boolean isStale() {
1049 // TODO Auto-generated method stub
1050 return false;
1051 }
1052 }