b482c056a6b632d927353c84145c8257e2157008
[taxeditor.git] / eclipseprojects / eu.etaxonomy.taxeditor / src / eu / etaxonomy / taxeditor / editor / GroupedComposite.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;
11
12 import org.apache.log4j.Logger;
13 import org.eclipse.core.runtime.Assert;
14 import org.eclipse.jface.text.IDocument;
15 import org.eclipse.jface.text.TextViewer;
16 import org.eclipse.jface.viewers.ISelection;
17 import org.eclipse.jface.viewers.ISelectionChangedListener;
18 import org.eclipse.jface.viewers.ISelectionProvider;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.custom.StyledText;
21 import org.eclipse.swt.dnd.DND;
22 import org.eclipse.swt.dnd.DragSource;
23 import org.eclipse.swt.dnd.DragSourceAdapter;
24 import org.eclipse.swt.dnd.DragSourceEvent;
25 import org.eclipse.swt.dnd.DragSourceListener;
26 import org.eclipse.swt.dnd.Transfer;
27 import org.eclipse.swt.events.FocusAdapter;
28 import org.eclipse.swt.events.FocusEvent;
29 import org.eclipse.swt.events.FocusListener;
30 import org.eclipse.swt.events.ModifyListener;
31 import org.eclipse.swt.graphics.Color;
32 import org.eclipse.swt.graphics.Font;
33 import org.eclipse.swt.graphics.Image;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.swt.widgets.Label;
37 import org.eclipse.ui.forms.IManagedForm;
38 import org.eclipse.ui.forms.widgets.TableWrapData;
39 import org.eclipse.ui.forms.widgets.TableWrapLayout;
40
41 import eu.etaxonomy.cdm.model.taxon.Taxon;
42 import eu.etaxonomy.taxeditor.ITaxEditorConstants;
43 import eu.etaxonomy.taxeditor.TaxEditorPlugin;
44 import eu.etaxonomy.taxeditor.actions.WidgetTransfer;
45 import eu.etaxonomy.taxeditor.editor.name.NameViewer;
46
47 /**
48 * Formats <code>GroupedComposite</code> with cosmetic and layout properties specific to the
49 * Editor. This should be used to maintain a consistent look and feel for all Editor
50 * freetext area components, such as DescriptionElementComposite.
51 * <p>
52 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the contents
53 * of {@link #getData()} when the <code>GroupedComposite</code> gets focus, i.e. to
54 * populate the property sheet with the data.
55 * </p>
56 * <p>
57 * The <code>IManagedForm</code> is also required to have a <code>Taxon</code> in its
58 * own <code>getData()</code>.
59 * </p>
60 * <p>
61 * The <code>IManagedForm</code> can also used for drawing borders by calling the method
62 * <code>createBorderSupport()</code>.
63 * </p>
64 * @author p.ciardelli
65 * @created 02.06.2008
66 * @version 1.0
67 */
68 abstract public class GroupedComposite extends Composite implements IHasPropertySource {
69 private static final Logger logger = Logger.getLogger(GroupedComposite.class);
70
71 protected NameViewer textViewer;
72 protected IManagedForm managedForm;
73 private Label nonEditableInfo;
74
75 protected Taxon taxon;
76
77 private CompositeBorderDecorator borderDecorator;
78 private FocusListener focusListener;
79 private LineBreakListener lineBreakListener;
80 private ParseListener parseListener;
81
82 /**
83 * @param parent
84 * @param managedForm
85 */
86 public GroupedComposite(Composite parent, IManagedForm managedForm) {
87 super(parent, SWT.NONE);
88
89 createContent();
90
91 this.managedForm = managedForm;
92
93 Object formData = managedForm.getForm().getBody().getData();
94 Assert.isTrue(formData instanceof Taxon,
95 "Managed form must have a Taxon in its data field.");
96 taxon = (Taxon) formData;
97 }
98
99 protected void createLineWrapSupport() {
100 if (textViewer instanceof NameViewer) {
101 new LineWrapSupport(textViewer, managedForm);
102 } else {
103 logger.warn("Can't create line wrap support because textViewer has not been initialized.");
104 }
105 }
106
107 protected void createContent() {
108 setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
109 TableWrapLayout layout = new TableWrapLayout();
110 layout.leftMargin = 0;
111 layout.topMargin = 0;
112 layout.bottomMargin = 0;
113 layout.verticalSpacing = 0;
114 setLayout(layout);
115
116 Color groupBackgroundColor = TaxEditorPlugin.getDefault().
117 getColor(ITaxEditorConstants.GROUP_GRAY_BKG_COLOR);
118 setBackground(groupBackgroundColor);
119 }
120
121 public Taxon getTaxon() {
122 return taxon;
123 }
124
125 protected void createTextViewer() {
126 textViewer = new NameViewer(this);
127
128 focusListener = new FocusAdapter() {
129 public void focusGained(FocusEvent e) {
130 setFocus();
131 }
132 };
133
134 textViewer.getTextWidget().addFocusListener(focusListener);
135 }
136
137 /**
138 * @param icon
139 */
140 public void setIcon(Image icon) {
141 if (textViewer instanceof NameViewer) {
142 textViewer.setIcon(icon);
143 } else {
144 logger.warn("Can't set icon because textViewer has not been initialized.");
145 }
146 }
147
148 /**
149 * @param indent
150 */
151 public void setIndent(int indent) {
152 if (getLayout() instanceof TableWrapLayout) {
153 TableWrapLayout layout = ((TableWrapLayout) getLayout());
154 layout.leftMargin = indent;
155 this.setLayout(layout);
156 } else {
157 logger.warn("Couldn't indent - composite's layout must be TableWrapLayout.");
158 }
159 }
160
161 /* (non-Javadoc)
162 * @see org.eclipse.swt.widgets.Composite#setFocus()
163 */
164 public boolean setFocus() {
165 setSelection();
166 return true;
167 }
168
169 /**
170 *
171 */
172 protected void setSelection() {
173 managedForm.setInput(this);
174 }
175
176 /* (non-Javadoc)
177 * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
178 */
179 public void setFont(Font font) {
180 if (textViewer != null) {
181 textViewer.getTextWidget().setFont(font);
182 } else {
183 logger.warn("Can't set font because textViewer has not been initalized.");
184 }
185 }
186
187 /**
188 * If <code>textViewer</code> has already been set, it will show a
189 * <code>prompt</code> along the lines of "Click here to start entering data"
190 * when empty.
191 *
192 * @param prompt
193 */
194 public void createEmptyViewerPrompt(final String prompt) {
195 Assert.isNotNull(textViewer);
196 // new EmptyTextViewerPrompt(getTextViewer(), prompt);
197
198 final StyledText textControl = textViewer.getTextWidget();
199 final IDocument document = textViewer.getDocument();
200 final Font promptFont = TaxEditorPlugin.getDefault()
201 .getFont(ITaxEditorConstants.DEFAULT_PROMPT_FONT);
202 setFocusListener(new FocusListener() {
203
204 @Override
205 public void focusGained(FocusEvent e) {
206 if (document.get().equals(prompt)) {
207 textControl.setFont(getViewerFont());
208 document.set("");
209 }
210 }
211
212 @Override
213 public void focusLost(FocusEvent e) {
214 if (document.getLength() == 0) {
215 initEmptyText();
216 }
217 }
218
219 });
220 textControl.addFocusListener(getFocusListener());
221
222 if (document.getLength() == 0) {
223 textControl.setFont(promptFont);
224 document.set(prompt);
225 }
226 }
227
228 abstract protected Font getViewerFont();
229
230 protected void initEmptyText() {
231 textViewer.getTextWidget().setFont(
232 TaxEditorPlugin.getDefault()
233 .getFont(ITaxEditorConstants.DEFAULT_PROMPT_FONT));
234
235 textViewer.getDocument().set(getEmptyTextPrompt());
236 }
237
238 protected String getEmptyTextPrompt() {
239 return "Click to edit";
240 }
241
242 private void setFocusListener(FocusListener focusListener) {
243 this.focusListener = focusListener;
244 }
245
246 private FocusListener getFocusListener() {
247 return focusListener;
248 }
249
250 /**
251 *
252 */
253 public void createBorderSupport() {
254
255 if (textViewer == null) {
256 logger.warn("Could not create border support - getTextViewer() returned null.");
257 } else {
258 borderDecorator = new CompositeBorderDecorator(
259 textViewer.getTextWidget(), managedForm);
260 borderDecorator.setLoseFocus(false);
261 textViewer.getTextWidget().addFocusListener(borderDecorator);
262 }
263 }
264
265 public void drawBorder() {
266 borderDecorator.paintBorder();
267 }
268
269 /**
270 * @param isDirty
271 */
272 protected void setDirty(boolean isDirty) {
273 managedForm.dirtyStateChanged();
274 }
275
276 protected ContextMenu createContextMenu() {
277 if (textViewer != null) {
278 ContextMenu contextMenu = new ContextMenu(textViewer.getRulerControl());
279 textViewer.getTextWidget().setMenu(contextMenu.getMenu());
280 return contextMenu;
281 } else {
282 logger.warn("Can't create menu because textViewer has not been initalized.");
283 return null;
284 }
285 }
286
287 public void unpaintBorder() {
288 if (borderDecorator != null) {
289 borderDecorator.unpaintBorder();
290 }
291 }
292
293 private Control draggableControl;
294 private DragSource dragSource;
295
296 protected void setDraggableControl(Control control) {
297 draggableControl = control;
298 }
299
300 public DragSource getDragSource() {
301 return dragSource;
302 }
303
304 public void setIsDraggable(boolean draggable) {
305
306 if (draggable) {
307
308 if (dragSource != null) {
309 // Already initialized
310 return;
311 }
312
313 if (draggableControl == null) {
314 throw new NullPointerException(
315 "Draggable control must be set to add draggability");
316 }
317
318 Transfer[] types = new Transfer[] { WidgetTransfer.getInstance() };
319 int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK;
320
321 dragSource = new DragSource(draggableControl, operations);
322 dragSource.setTransfer(types);
323 dragSource.addDragListener(dragSourceListener);
324
325 } else {
326 dragSource = null;
327 }
328 }
329
330 /**
331 * Drag listener which passes the Composite as the data in a drag event.
332 */
333 DragSourceListener dragSourceListener = new DragSourceAdapter() {
334
335 public void dragStart(DragSourceEvent event) {
336 if (textViewer != null) {
337 textViewer.getTextWidget().setFocus();
338 }
339 event.doit = true;
340 }
341
342 public void dragSetData(DragSourceEvent event) {
343 WidgetTransfer.getInstance().setWidget(GroupedComposite.this);
344 }
345 };
346
347 /**
348 * nonEditableInfo is a label displayed underneath a GroupedComposite's
349 * input field. For instance, NameComposites display things like name relations,
350 * sec. references, etc. here.
351 *
352 * @param info
353 */
354 public void setNonEditableInfo(String info) {
355 if (nonEditableInfo == null) {
356 nonEditableInfo = new Label(this, SWT.WRAP);
357 nonEditableInfo.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP));
358 } else {
359 info = nonEditableInfo.getText() + ", " + info;
360 }
361 nonEditableInfo.setText(info.toUpperCase());
362 }
363
364 /**
365 * If the user entering text requires parsing, call this method and override
366 * the method parse().
367 */
368 protected void createParser() {
369 if (textViewer != null) {
370 parseListener = new ParseListener() {
371 @Override
372 public void parse(String text) {
373 GroupedComposite.this.parse(text);
374 }
375 };
376 textViewer.getTextWidget().addModifyListener(parseListener);
377 } else {
378 logger.warn("Can't create parser because textViewer has not been initalized.");
379 }
380 }
381
382 protected void parse(String text) {
383 logger.warn("No parse method defined for this composite.");
384 }
385
386 /**
387 * If the user hitting carriage return should cause something to happen -
388 * i.e. the creation of a new composite - call this method and override
389 * the method handleSplitText().
390 */
391 protected void createLineBreakListener() {
392 if (textViewer != null) {
393 lineBreakListener = new LineBreakListener() {
394 @Override
395 public void handleSplitText(String text) {
396 GroupedComposite.this.handleSplitText(text);
397 }
398 };
399
400 textViewer.getTextWidget().addVerifyListener(lineBreakListener);
401 textViewer.getTextWidget().addKeyListener(lineBreakListener);
402 } else {
403 logger.warn("Can't create line break listener because textViewer has not been initalized.");
404 }
405 }
406
407 protected void handleSplitText(String text) {
408 logger.warn("No handleSplitText method defined for this composite.");
409 }
410
411 /* (non-Javadoc)
412 * @see eu.etaxonomy.taxeditor.editor.name.GroupedComposite#dispose()
413 */
414 public void dispose () {
415
416 if (dragSource != null) {
417 dragSource.removeDragListener(dragSourceListener);
418 }
419
420 if (textViewer != null) {
421 StyledText textWidget = textViewer.getTextWidget();
422
423 if (focusListener != null) {
424 textWidget.removeFocusListener(focusListener);
425 }
426
427 if (parseListener != null) {
428 textWidget.removeModifyListener(parseListener);
429 }
430
431 if (lineBreakListener != null) {
432 textWidget.removeVerifyListener(lineBreakListener);
433 textWidget.removeKeyListener(lineBreakListener);
434 }
435
436 if (borderDecorator != null) {
437 textWidget.removeFocusListener(borderDecorator);
438 }
439 }
440
441 super.dispose();
442 }
443 }