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