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