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