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