ref #10138: after parsing send event to update the details view
[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
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.TaxonEditor;
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.event.EventUtility;
66 import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
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 AbstractGroupedContainer<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 AbstractGroup group;
108
109 private Label nonEditableInfoLabel;
110 private DefaultToolTip nonEditableInfoHover;
111
112 private static AbstractGroupedContainer<?> selection;
113
114 private FocusListener focusListener;
115 private LineBreakListener lineBreakListener;
116
117 private int cursorPosition;
118
119 private long lastEventTime;
120
121 protected Composite control;
122
123 private Color backgroundColor;
124 private boolean isDirty;
125
126 public AbstractGroupedContainer(AbstractGroup 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 = (ModifyEvent e)->{
155 // mark the composite dirty
156 setDirty(true);
157 lastEventTime = System.currentTimeMillis();
158 // parse the text
159 String text = nameViewer.getTextWidget().getText();
160
161 TaxonName name = (TaxonName)parseHandler.parse(text);
162 getTaxonBase().setName(name);
163 getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
164
165
166 // show errors resulting from parsing
167 calculateAnnotations();
168 // store the position of the cursor
169 storeCursor();
170 // notify selection listener
171 setDelayedSelection();
172 //EventUtility.postAsyncEvent(WorkbenchEventConstants.REFRESH_DETAILS, true);
173
174 };
175
176 nameCompositeFocusListener = new FocusAdapter() {
177
178 @Override
179 public void focusLost(FocusEvent e) {
180 super.focusLost(e);
181 persistName();
182 EventUtility.postAsyncEvent(WorkbenchEventConstants.REFRESH_DETAILS, true);
183 }
184 };
185
186 addListener();
187 }
188
189 protected void addListener() {
190 getNameViewer().getTextWidget().addModifyListener(
191 nameCompositeModifyListener);
192 getNameViewer().getTextWidget().addFocusListener(
193 nameCompositeFocusListener);
194 }
195
196 protected void removeListener() {
197 getNameViewer().getTextWidget().removeModifyListener(
198 nameCompositeModifyListener);
199 getNameViewer().getTextWidget().removeFocusListener(
200 nameCompositeFocusListener);
201 }
202
203 /**
204 * Initialize the composite specific code
205 */
206 protected abstract void initializeComposite();
207
208 protected String getEmptyTextPrompt() {
209 return EMPTY_NAME_PROMPT;
210 }
211
212 protected void initTextViewer() {
213
214 updateIndent();
215
216 updateIcon();
217
218 String text = NameHelper.getDisplayNameWithRef(getData());
219
220 if (text.length() == 0) {
221 initEmptyText();
222 } else {
223 getNameViewer().setText(text);
224 placeCursor();
225 }
226 calculateAnnotations();
227 }
228
229 synchronized protected void calculateAnnotations() {
230 getNameViewer().clearAnnotations();
231 showAnnotations();
232 }
233
234 public void showAnnotations() {
235
236 if (getName() != null && getName().hasProblem()) {
237 showParsingProblems();
238 }
239
240 if (!isNameParsable()) {
241 getNameViewer()
242 .addAnnotation(
243 new EditorAnnotation(EditorAnnotationType.WARNING,
244 0,
245 Messages.AbstractGroupedContainer_EDIT_IN_DETAILS_VIEW));
246 }
247
248 if (isNameUsedMultipleTimes()) {
249 getNameViewer().addAnnotation(
250 new EditorAnnotation(EditorAnnotationType.WARNING, 0,
251 Messages.AbstractGroupedContainer_MULTIPLE_USE));
252 }
253
254 }
255
256 private void showParsingProblems() {
257 TaxonName name = getName();
258 if (name == null){
259 return;
260 }
261
262 List<ParserProblem> parsingProblems = name.getParsingProblems();
263
264 for (ParserProblem problem : parsingProblems) {
265 getNameViewer().addAnnotation(new EditorAnnotation(problem),
266 getParsingProblemPosition());
267 }
268 }
269
270 private Position getParsingProblemPosition() {
271 String text = getNameViewer().getTextWidget().getText();
272
273 if (getName() != null && getName().hasProblem() && text.length() > 0) {
274 int start = getName().getProblemStarts();
275 int length = getName().getProblemEnds() - start;
276
277 if (start == -1 || getName().getProblemEnds() == -1) {
278 return null;
279 }
280
281 // Don't let squigglies try to draw beyond the end of the text
282 if (text.length() < start + length) {
283 length = text.length() - start;
284 }
285 if (length<0){
286 return null;
287 }
288 return new Position(start, length);
289 }
290 return null;
291 }
292
293 protected void handleSplitText(String text) {
294 // Create a synonym in a new homotypic group using text as name
295 TaxonName synonymName = TaxonName.castAndDeproxy(
296 ParseHandler.parseReferencedName(text, null));
297
298 AbstractUtility.executeOperation(new CreateSynonymInNewGroupOperation(
299 Messages.AbstractGroupedContainer_NEW_HETERO_SYNONYM, getEditor().getUndoContext(),
300 getEditor().getTaxon(), synonymName, getEditor()), group.getContext().get(UISynchronize.class));
301 }
302
303 /**
304 * Refreshes the display with latest data from the model.
305 *
306 * Note: Will not parse the text and not calculate errors!
307 */
308 @Override
309 public void refresh() {
310 // showNameRelations();
311
312 String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
313
314 if (getNameViewer().getTextWidget() == null) {
315 // we might get here via dnd. Look slike it can be ignored
316 return;
317 }
318
319 if (text.length() == 0) {
320 initEmptyText();
321 } else if (!getNameViewer().getTextWidget().getText().equals(text)) {
322 removeListener();
323 getNameViewer().getTextWidget().setText(text);
324 addListener();
325 }
326
327 updateNonEditableInfo();
328
329 updateIcon();
330 // placeCursor();
331 updateIndent();
332 setDelayedSelection();
333 enableFreeText();
334 }
335
336 protected abstract void updateIcon();
337
338 protected abstract void updateIndent();
339
340 protected abstract void updateNonEditableInfo();
341
342 protected void enableFreeText() {
343 setEnabled(isFreetextEditingAllowed());
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 if (name.isProtectedAuthorshipCache()){
397 isParsable &= nameAuthorsSet(name);
398 }
399 if (name.isProtectedNameCache()){
400 isParsable &= nameEpithetsNotSet(name) && nameAuthorsSet(name);
401 }
402
403 return isParsable;
404 }
405
406 private boolean nameAuthorsSet(TaxonName name) {
407 boolean result = true;
408 result &= name.getBasionymAuthorship() == null && name.getCombinationAuthorship() == null && name.getExBasionymAuthorship() == null && name.getExCombinationAuthorship() == null;
409 return result;
410 }
411
412 private boolean nameEpithetsNotSet(TaxonName name) {
413 boolean result = true;
414 result &= name.getGenusOrUninomial() == null && name.getInfraGenericEpithet() == null && name.getSpecificEpithet() == null && name.getInfraSpecificEpithet() == null;
415
416 return result;
417 }
418
419 /**
420 * Parse the text and calculate errors
421 */
422 public void parseAndCalculateAnnotations() {
423 removeListener();
424 String unparsedNameString = getNameViewer().getTextWidget().getText();
425 parseHandler.parse(unparsedNameString);
426 addListener();
427 calculateAnnotations();
428 }
429
430 public T getTaxonBase() {
431 return getData();
432 }
433
434 public TaxonName getName() {
435 return CdmBase.deproxy(getTaxonBase().getName());
436 }
437
438 public void persistName() {
439 if (isDirty()) {
440 getNameViewer().getTextWidget().setEnabled(false);
441 final String unparsedNameString = getNameViewer().getTextWidget()
442 .getText();
443 final TaxonName name = (TaxonName)parseHandler
444 .parseAndResolveDuplicates(unparsedNameString);
445 getTaxonBase().setName(name);
446 getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
447 setDirty(false);
448 getNameViewer().getTextWidget().setEnabled(true);
449 }
450 }
451
452 public AbstractGroup getGroup() {
453 if (group == null) {
454 throw new IllegalStateException("Group shall not be null."); //$NON-NLS-1$
455 }
456 return group;
457 }
458
459 public void remove() {
460 getGroup().remove(this);
461 }
462
463 protected void createControl() {
464 control = getEditor().getToolkit().createComposite(
465 getGroup().getControl());
466
467 control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
468 TableWrapLayout layout = new TableWrapLayout();
469 layout.leftMargin = 0;
470 layout.rightMargin = 0;
471 layout.topMargin = 5;
472 layout.bottomMargin = 5;
473
474 layout.verticalSpacing = 0;
475 layout.horizontalSpacing = 0;
476
477 control.setLayout(layout);
478
479 }
480
481 protected TaxonEditor getEditor() {
482 return getGroup().getEditor();
483 }
484
485 public Composite getControl() {
486 return control;
487 }
488
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 getNameViewer().getTextWidget().setFocus();
546 }
547
548 public boolean isSelected() {
549 return getEditor().getSelectedContainer() == this;
550 }
551
552 public void colorSelected(int mode) {
553 if (control!=null && !control.isDisposed()) {
554 String colorString = null;
555
556 switch (mode) {
557 case SELECTED_FOCUS:
558 colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
559 break;
560 case SELECTED_NO_FOCUS:
561 colorString = Resources.COLOR_CONTROL_SELECTED;
562 break;
563 default:
564 colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
565 }
566
567 backgroundColor = AbstractUtility.getColor(colorString);
568
569 setBackground(backgroundColor);
570 }
571 }
572
573 private Delay delay = new Delay(Display.getCurrent());
574
575 private class Delay extends Thread{
576
577 private volatile boolean stop = false;
578 private Display display;
579
580 public Delay(Display display) {
581 this.display = display;
582 }
583 @Override
584 public void run() {
585
586 while (!stop) {
587 try {
588 // my code goes here
589 Thread.sleep(1000);
590 if(!stop){
591 display.syncExec(() -> {
592 getEditor().getManagedForm().setInput(selection);
593 });
594 stop = true;
595 }
596 } catch (InterruptedException ex) {
597 stop = true;
598 }
599 }
600 }
601 public void requestStop() {
602 stop = true;
603 }
604 }
605
606 protected void setDelayedSelection() {
607 selection = this;
608
609 Display display = Display.getCurrent();
610
611 delay.requestStop();
612 delay = new Delay(display);
613 delay.start();
614 }
615
616 public void setBackground(Color color) {
617 control.setBackground(color);
618
619 for (Control child : control.getChildren()) {
620 child.setBackground(color);
621 }
622
623 getNameViewer().setBackground(color);
624 }
625
626 public void setFont(Font font) {
627 getNameViewer().getTextWidget().setFont(font);
628 }
629
630 public NameViewer getNameViewer() {
631 if (nameViewer == null) {
632 throw new RuntimeException(
633 "The Name Viewer is corrupt for Name Container: " //$NON-NLS-1$
634 + getTaxonBase().getName().getTitleCache());
635 }
636 return nameViewer;
637 }
638
639 public void createEmptyViewerPrompt(final String prompt) {
640
641 Assert.isNotNull(getNameViewer());
642
643 final StyledText textControl = getNameViewer().getTextWidget();
644 final IDocument document = getNameViewer().getDocument();
645
646 setFocusListener(new FocusListener() {
647
648 @Override
649 public void focusGained(FocusEvent e) {
650 if (document.get().equals(prompt)) {
651 textControl.setFont(getViewerFont());
652 document.set(""); //$NON-NLS-1$
653 }
654 }
655
656 @Override
657 public void focusLost(FocusEvent e) {
658 if (document.getLength() == 0) {
659 initEmptyText();
660 }
661 }
662
663 });
664 textControl.addFocusListener(getFocusListener());
665
666 if (document.getLength() == 0) {
667 textControl.setFont(AbstractUtility
668 .getFont(Resources.FONT_DEFAULT_PROMPT));
669 document.set(prompt);
670 }
671 }
672
673 abstract protected Font getViewerFont();
674
675 protected void initEmptyText() {
676 Font defaultFont = AbstractUtility.getFont(Resources.FONT_DEFAULT_PROMPT);
677 getNameViewer().getTextWidget().setFont(defaultFont);
678
679 getNameViewer().getDocument().set(getEmptyTextPrompt());
680 }
681
682 protected void setFocusListener(FocusListener focusListener) {
683 this.focusListener = focusListener;
684 }
685
686 private FocusListener getFocusListener() {
687 return focusListener;
688 }
689
690 public void setDirty(boolean isDirty) {
691 if (isDirty) {
692 getEditor().getManagedForm().dirtyStateChanged();
693 }
694 this.isDirty = isDirty;
695 }
696
697 @Override
698 public boolean isDirty() {
699 return isDirty;
700 }
701
702 public void setMenu() {
703 getEditor().getMenuService().registerContextMenu(getNameViewer().getTextWidget(), "eu.etaxonomy.taxeditor.editor.popupmenu.nameeditor");
704 }
705
706 private Control[] draggableControls;
707
708 protected void setDraggableControl(Control[] controls) {
709 draggableControls = controls;
710 }
711
712 public void setIsDraggable(boolean draggable) {
713
714 if (draggable) {
715
716 if (draggableControls == null) {
717 throw new NullPointerException(
718 "Draggable controls must be set to add draggability"); //$NON-NLS-1$
719 }
720
721 Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
722 int operations = DND.DROP_MOVE;
723
724 for (Control draggableControl : draggableControls) {
725 DragSource dragSource = new DragSource(draggableControl,
726 operations);
727 dragSource.setTransfer(types);
728
729 dragSource.addDragListener(new NameEditorDragListenerE4(this));
730 dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
731 control));
732 }
733 }
734 }
735
736 private String nonEditableText;
737
738 ControlListener nonEditableResizeListener = new ControlAdapter() {
739
740 int width = 0;
741
742 @Override
743 public void controlResized(ControlEvent e) {
744 if (nonEditableInfoLabel.getBounds().width == width) {
745 return;
746 }
747 width = nonEditableInfoLabel.getBounds().width;
748 if (nonEditableInfoLabel.getBounds().width > 0) {
749 nonEditableInfoLabel.setText(Dialog.shortenText(
750 nonEditableText.toUpperCase(), nonEditableInfoLabel));
751 }
752 }
753 };
754
755 private String nonEditableHoverText;
756
757 private LabelEllipsisListener nonEditableLabelEllipsisListener;
758
759 private T data;
760
761 /**
762 * nonEditableInfo is a label displayed underneath a GroupedComposite's
763 * input field. For instance, NameComposites display things like name
764 * relations, sec. references, etc. here.
765 *
766 * @param info
767 * the text to display in the label
768 * @param append
769 * whether the string should be appended to text that is already
770 * shown in the label
771 */
772 public void setNonEditableInfo(String info, boolean append) {
773 // TODO non editable info should only be drawn once, when everything
774 // else is drawn
775 info = info.toUpperCase();
776
777 if (append) {
778 nonEditableText += ", " + info; //$NON-NLS-1$
779 nonEditableHoverText += "\n" + info; //$NON-NLS-1$
780 } else {
781 nonEditableText = info;
782 nonEditableHoverText = info;
783 }
784
785 if (nonEditableInfoLabel == null) {
786 nonEditableInfoLabel = getEditor().getToolkit().createLabel(
787 control, ""); //$NON-NLS-1$
788 TableWrapData layoutData = new TableWrapData(
789 TableWrapData.FILL_GRAB, TableWrapData.TOP);
790 // Set indent to viewer ruler's width
791 if (getNameViewer().getRulerControl() != null) {
792 // TODO right justify
793 layoutData.indent = NameViewer.RULER_WIDTH;
794 }
795 nonEditableInfoLabel.setLayoutData(layoutData);
796
797 nonEditableLabelEllipsisListener = new LabelEllipsisListener(
798 nonEditableInfoLabel) {
799 @Override
800 public String getLabelText() {
801 return nonEditableText.toUpperCase();
802 }
803 };
804 nonEditableInfoLabel
805 .addControlListener(nonEditableLabelEllipsisListener);
806
807 nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
808 nonEditableInfoHover.setRespectDisplayBounds(true);
809
810 }
811 nonEditableInfoHover.setText(nonEditableHoverText);
812 nonEditableInfoLabel.setText(nonEditableText);
813
814 calculateAnnotations();
815 }
816
817 @Override
818 public T getData() {
819 return data;
820 }
821
822 public void setData(T data) {
823 this.data = HibernateProxyHelper.deproxy(data);
824 }
825
826 /**
827 * If the user hitting carriage return should cause something to happen -
828 * i.e. the creation of a new composite - call this method and override the
829 * method handleSplitText().
830 */
831 protected void createLineBreakListener() {
832 lineBreakListener = new LineBreakListener() {
833 @Override
834 public void handleSplitText(String text) {
835 AbstractGroupedContainer.this.handleSplitText(text);
836 }
837
838 };
839
840 getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
841
842 }
843
844 abstract class LabelEllipsisListener extends ControlAdapter {
845
846 private final Label label;
847 int width = 0;
848
849 LabelEllipsisListener(Label label) {
850 this.label = label;
851 }
852
853 abstract public String getLabelText();
854
855 @Override
856 public void controlResized(ControlEvent e) {
857 if (label.getBounds().width == width) {
858 return;
859 }
860 width = label.getBounds().width;
861 if (label.getBounds().width > 0) {
862 label.setText(TextHelper.shortenText(getLabelText(), label));
863 }
864 }
865 }
866
867 public void storeCursor() {
868 this.cursorPosition = getNameViewer().getCursorPosition();
869 }
870
871 /**
872 * Puts the cursor to the position it was last seen on or to the end of line
873 * if no former position is known.
874 */
875 public void placeCursor() {
876 if (cursorPosition == 0) {
877 getNameViewer().setCursorToEOL();
878 } else {
879 getNameViewer().setCursorPosition(cursorPosition);
880 }
881 }
882
883 public void restoreColor() {
884 setBackground(backgroundColor);
885 }
886
887 @Override
888 public void initialize(IManagedForm form) {
889 // TODO Auto-generated method stub
890 }
891
892 @Override
893 public void dispose() {
894 if (getControl() != null) {
895 getControl().dispose();
896 }
897 }
898
899 @Override
900 public void commit(boolean onSave) {
901 if (isDirty()) {
902 persistName();
903 }
904 }
905
906 @Override
907 public boolean setFormInput(Object input) {
908 return false;
909 }
910
911 @Override
912 public void setFocus() {
913 getNameViewer().getControl().setFocus();
914 }
915
916 @Override
917 public boolean isStale() {
918 return false;
919 }
920
921 public void setDisabled(boolean disabled) {
922 setEnabled(!disabled);
923 }
924
925 public void setEnabled(boolean enabled) {
926 Color color = enabled ? control.getForeground() : AbstractUtility.getColor(Resources.COLOR_DISABLED_EDITOR);
927
928 getNameViewer().getTextWidget().setEditable(enabled);
929 getNameViewer().getTextWidget().setForeground(color);
930 }
931
932
933 }