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