Halfway through refactoring upwards from NameComposite, DescriptionElementComposite...
[taxeditor.git] / eclipseprojects / eu.etaxonomy.taxeditor / src / eu / etaxonomy / taxeditor / editor / name / NameComposite.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.beans.PropertyChangeEvent;
13 import java.beans.PropertyChangeListener;
14
15 import org.apache.log4j.Logger;
16 import org.eclipse.core.commands.operations.IUndoContext;
17 import org.eclipse.core.commands.operations.IUndoableOperation;
18 import org.eclipse.core.runtime.Assert;
19 import org.eclipse.jface.action.Action;
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.custom.StyledText;
22 import org.eclipse.swt.events.FocusAdapter;
23 import org.eclipse.swt.events.FocusEvent;
24 import org.eclipse.swt.events.KeyAdapter;
25 import org.eclipse.swt.events.KeyEvent;
26 import org.eclipse.swt.graphics.Color;
27 import org.eclipse.swt.graphics.Font;
28 import org.eclipse.swt.graphics.Image;
29 import org.eclipse.swt.widgets.Composite;
30 import org.eclipse.swt.widgets.Label;
31 import org.eclipse.ui.forms.IManagedForm;
32 import org.eclipse.ui.forms.widgets.TableWrapData;
33 import org.eclipse.ui.forms.widgets.TableWrapLayout;
34 import org.eclipse.ui.views.properties.IPropertySource;
35
36 import eu.etaxonomy.cdm.model.name.BotanicalName;
37 import eu.etaxonomy.cdm.model.name.NonViralName;
38 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
39 import eu.etaxonomy.cdm.model.name.ZoologicalName;
40 import eu.etaxonomy.cdm.model.reference.StrictReferenceBase;
41 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
42 import eu.etaxonomy.taxeditor.ITaxEditorConstants;
43 import eu.etaxonomy.taxeditor.TaxEditorPlugin;
44 import eu.etaxonomy.taxeditor.controller.EditorController;
45 import eu.etaxonomy.taxeditor.controller.GlobalController;
46 import eu.etaxonomy.taxeditor.editor.ContextMenu;
47 import eu.etaxonomy.taxeditor.editor.GroupedComposite;
48 import eu.etaxonomy.taxeditor.editor.LineBreakListener;
49 import eu.etaxonomy.taxeditor.editor.ParseListener;
50 import eu.etaxonomy.taxeditor.model.CdmUtil;
51 import eu.etaxonomy.taxeditor.operations.name.CreateSynonymInNewGroupOperation;
52 import eu.etaxonomy.taxeditor.propertysheet.name.BotanicalNamePropertySource;
53 import eu.etaxonomy.taxeditor.propertysheet.name.NonViralNamePropertySource;
54 import eu.etaxonomy.taxeditor.propertysheet.name.ZoologicalNamePropertySource;
55
56 /**
57 * Formats an <code>GroupedComposite</code> to display <code>TaxonNameBase</code> elements
58 * in a <code>NameViewer</code>.
59 *
60 * @author p.ciardelli
61 * @created 02.06.2008
62 * @version 1.0
63 */
64 public abstract class NameComposite extends GroupedComposite {
65 private static final Logger logger = Logger.getLogger(NameComposite.class);
66
67 /**
68 * ************ COMPOSITE TYPES ************
69 */
70 public String compositeType;
71 public static final String ACCEPTED_TAXON = "accepted_name_composite";
72 public static final String HOMOTYPIC_SYNONYM = "homotypic_name_composite";
73 public static final String HETEROTYPIC_SYNONYM = "heterotypic_name_composite";
74 public static final String MISAPPLIED_NAME = "misappliedname_name_composite";
75 public static final String CONCEPTRELATION = "concept_name_comcposite";
76
77 /**
78 * ************ INDENTATIONS ************
79 */
80 public static final int ACCEPTED_INDENT = 0;
81 public static final int SYNONYM_INDENT = 15;
82 public static final int MISAPPLIEDNAME_INDENT = 15;
83 public static final int CONCEPT_INDENT = 15;
84
85 /**
86 * ************ FONTS ************
87 */
88 public static final Font ACCEPTED_FONT = TaxEditorPlugin.getDefault()
89 .getFont(ITaxEditorConstants.ACCEPTED_TAXON_FONT);
90 public static final Font SYNONYM_FONT = TaxEditorPlugin.getDefault()
91 .getFont(ITaxEditorConstants.SYNONYM_FONT);
92 public static final Font MISAPPLIEDNAME_FONT = TaxEditorPlugin.getDefault()
93 .getFont(ITaxEditorConstants.MISAPPLIEDNAME_FONT);
94 public static final Font CONCEPT_FONT = TaxEditorPlugin.getDefault()
95 .getFont(ITaxEditorConstants.CONCEPT_FONT);
96
97 /**
98 * ************ ICONS ************
99 */
100 public static final Image ACCEPTED_ICON = TaxEditorPlugin.getDefault()
101 .getImage(ITaxEditorConstants.BLACK_SQUARE_ICON);
102 public static final Image HOMOTYPIC_SYNONYM_ICON = TaxEditorPlugin
103 .getDefault().getImage(ITaxEditorConstants.HOMOTYPIC_SYN_ICON);
104 public static final Image HOMOTYPIC_SYNONYM_ORIGINAL_COMBINATION_ICON = TaxEditorPlugin
105 .getDefault().getImage(
106 ITaxEditorConstants.HOMOTYPIC_SYN_ORIGINAL_ICON);
107 public static final Image HETEROTYPIC_SYNONYM_ICON = TaxEditorPlugin
108 .getDefault().getImage(ITaxEditorConstants.HETEROTYPIC_SYN_ICON);
109 public static final Image HETEROTYPIC_SYNONYM_ORIGINAL_COMBINATION_ICON = TaxEditorPlugin
110 .getDefault().getImage(
111 ITaxEditorConstants.HETEROTYPIC_SYN_ORIGINAL_ICON);
112 public static final Image MISAPPLIEDNAME_ICON = TaxEditorPlugin
113 .getDefault().getImage(ITaxEditorConstants.MISAPPLIED_NAME_ICON);
114 public static final Image CONCEPT_ICON = TaxEditorPlugin
115 .getDefault().getImage(ITaxEditorConstants.CONCEPT_ICON);
116 public static final Image AUTONYM_ICON = TaxEditorPlugin.getDefault()
117 .getImage(ITaxEditorConstants.AUTONYM_ICON);
118 public static final Image BASIONYM_ICON = TaxEditorPlugin.getDefault()
119 .getImage(ITaxEditorConstants.BASIONYM_ICON);
120 public static final Image MOVE = TaxEditorPlugin.getDefault().getImage(
121 ITaxEditorConstants.MOVE_ICON);
122
123 /**
124 * ************ TRANSFORMATIONS ************
125 */
126 public static final String ADD_GROUP_BASIONYM = "add_group_basionym";
127 public static final String REMOVE_GROUP_BASIONYM = "remove_group_basionym";
128
129 /**
130 * ************ MENU ACTIONS ************
131 */
132 public Action CHANGE_TAXON_TO_SYNONYM_ACTION;
133
134 private static final String EMPTY_NAME_PROMPT = "Click to add name";
135
136 /**
137 * Used to turn parser on and off.
138 *
139 * @see activateParser
140 * @see deactivateParser
141 */
142 private boolean isUseParser = false;
143
144 protected boolean isParsing;
145
146 /**
147 * The constructor for a DescriptionElementComposite. Takes a parent Composite on which to
148 * create itself, and an IManagedForm for Composite life cycle methods, i.e.
149 * drawing borders, creating other Composites, creating line wrap support,
150 * etc.
151 *
152 * @param parent
153 * @param managedForm
154 */
155 public NameComposite(Composite parent, IManagedForm managedForm,
156 String compositeType, TaxonBase taxonBase) {
157 super(parent, managedForm);
158
159 createTextViewer();
160 createBorderSupport();
161 createLineWrapSupport();
162
163 setDraggableControl(textViewer.getRulerControl());
164
165 createParser();
166 createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
167 setFocus();
168
169 // NOTE: placing this after setFocus() inexplicably solved a strange bug where if the first action
170 // during a session was to open a new taxon, the empty name prompt didn't work
171 createNameListener(taxonBase.getName());
172 }
173
174 protected String getEmptyTextPrompt() {
175 return EMPTY_NAME_PROMPT;
176 }
177
178 /**
179 * Listens for changes to this name's <code>fullTitleCache</code>.
180 *
181 * @param data
182 */
183 private void createNameListener(final TaxonNameBase name) {
184
185 if (name == null) {
186 return;
187 }
188
189 final PropertyChangeListener listener = new PropertyChangeListener() {
190 public void propertyChange(PropertyChangeEvent evt) {
191
192 if (isParsing) {
193 return;
194 }
195
196 if (EditorController.isSaving()) {
197 return;
198 }
199
200 deactivateParser();
201 ((NameViewer) getTextViewer()).setText(name.getFullTitleCache());
202 activateParser();
203 }
204 };
205
206 // TODO clean this part up
207 name.addPropertyChangeListener("fullTitleCache", listener);
208 name.addPropertyChangeListener("nomenclaturalMicroReference", listener);
209
210 // name.addPropertyChangeListener(ITaxEditorConstants.REFRESH_NAMEVIEWER, listener);
211
212 StrictReferenceBase reference = (StrictReferenceBase) name.getNomenclaturalReference();
213 if (reference != null) {
214 reference.addPropertyChangeListener("titleCache", listener);
215 }
216 }
217
218 protected void initNameViewer(TaxonBase taxonBase) {
219 String text = CdmUtil.getDisplayNameWithRef(taxonBase);
220 if (text.length() == 0) {
221 initEmptyText();
222 } else {
223 getTextViewer().getTextWidget().setText(text);
224
225 if (getTextViewer() instanceof NameViewer) {
226
227 ((NameViewer) getTextViewer()).setCursorToEOL();
228
229 TaxonNameBase name = taxonBase.getName();
230 if (name != null) {
231 calculateErrors();
232 }
233 }
234 }
235 activateParser();
236 }
237
238 private void createParser() {
239 ((NameViewer) getTextViewer()).addParseListener(new ParseListener() {
240
241 @Override
242 public void parse(String text) {
243
244 // Either composite is not yet fully built, or
245 // the property sheet is writing to it
246 if (!isUseParser) {
247 return;
248 }
249
250 // Let others know the parser is active
251 isParsing = true;
252
253 // Parse the name and paint the text field w any errors
254 if (getName() != null) {
255 CdmParserController.parseFullReference((NonViralName) getName(), text);
256 }
257
258 // Any entry of text means the taxon has been changed
259 setDirty(true);
260
261 // The parser is no longer active
262 isParsing = false;
263
264 // Manually refresh the property sheet to reflect changes
265 setSelection();
266
267 // Show any errors in the name viewer
268 calculateErrors();
269 }
270 });
271 }
272
273 public void activateParser() {
274 isUseParser = true;
275 }
276
277 public void deactivateParser() {
278 isUseParser = false;
279 }
280
281 protected abstract TaxonNameBase getName();
282
283 protected void calculateErrors() {
284 textViewer.clearErrors();
285 textViewer.setShowParsingError(getName());
286 }
287
288 private NameViewer createTextViewer() {
289
290 textViewer = new NameViewer(this);
291
292 StyledText styledText = textViewer.getTextWidget();
293 // textViewer.setLineBreakListener(new LineBreakListener() {
294
295 setTextViewer(textViewer);
296
297 LineBreakListener lineBreakListener = new LineBreakListener() {
298 @Override
299 public void handleSplitText(String text) {
300
301 // Create a synonym in a new homotypic group using text as name
302 IUndoContext undoContext = EditorController.getUndoContext(taxon);
303 IUndoableOperation operation = new CreateSynonymInNewGroupOperation
304 ("new heterotypic synonym", undoContext, taxon, text); //$NON-NLS-1$
305 GlobalController.executeOperation(operation);
306 }
307 };
308
309 styledText.addVerifyListener(lineBreakListener);
310 styledText.addKeyListener(lineBreakListener);
311
312 styledText.addFocusListener(new FocusAdapter() {
313 public void focusGained(FocusEvent e) {
314 setFocus();
315 }
316 });
317
318 // createLineWrapSupport(textViewer);
319
320 return textViewer;
321 }
322
323 private Label nonEditableInfo;
324
325 public void setText(String text) {
326 Assert.isNotNull(getTextViewer(),
327 "Cannot set text for a TextViewer that has not yet been initialized.");
328 Assert.isNotNull(getTextViewer().getDocument(),
329 "Cannot set text for a TextViewer whose Document has not yet been initialized.");
330 getTextViewer().getDocument().set(text);
331 }
332
333 public void setNonEditableInfo(String info) {
334 if (nonEditableInfo == null) {
335 nonEditableInfo = new Label(this, SWT.WRAP);
336 nonEditableInfo.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP));
337 } else {
338 info = nonEditableInfo.getText() + ", " + info;
339 }
340 nonEditableInfo.setText(info.toUpperCase());
341 }
342
343 public void createBorderSupport() {
344 super.createBorderSupport();
345 if (textViewer != null) {
346 borderDecorator.setBorderedComposite(textViewer.getTextWidget());
347 }
348 }
349
350 protected IPropertySource getPropertySourceByName(TaxonNameBase name) {
351 if (name == null) {
352 return null;
353 }
354
355 if (name.getClass() == BotanicalName.class) {
356 return new BotanicalNamePropertySource((BotanicalName) name);
357 }
358 if (name.getClass() == ZoologicalName.class) {
359 return new ZoologicalNamePropertySource((ZoologicalName) name);
360 }
361 if (name instanceof NonViralName) {
362 return new NonViralNamePropertySource((NonViralName) name);
363 }
364
365 return null;
366 }
367
368 protected ContextMenu createContextMenu() {
369 if (textViewer != null) {
370 ContextMenu contextMenu = new ContextMenu(textViewer.getRulerControl());
371 textViewer.getTextWidget().setMenu(contextMenu.getMenu());
372 return contextMenu;
373 } else {
374 logger.warn("Can't create menu because textViewer has not been initalized.");
375 return null;
376 }
377 }
378 }