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