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