001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jface.viewers;
011:
012: import org.eclipse.core.runtime.ListenerList;
013: import org.eclipse.core.runtime.Assert;
014: import org.eclipse.jface.util.IPropertyChangeListener;
015: import org.eclipse.jface.util.PropertyChangeEvent;
016: import org.eclipse.jface.util.SafeRunnable;
017: import org.eclipse.swt.SWT;
018: import org.eclipse.swt.events.KeyEvent;
019: import org.eclipse.swt.widgets.Composite;
020: import org.eclipse.swt.widgets.Control;
021:
022: /**
023: * Abstract base class for cell editors. Implements property change listener handling,
024: * and SWT window management.
025: * <p>
026: * Subclasses implement particular kinds of cell editors. This package contains various
027: * specialized cell editors:
028: * <ul>
029: * <li><code>TextCellEditor</code> - for simple text strings</li>
030: * <li><code>ColorCellEditor</code> - for colors</li>
031: * <li><code>ComboBoxCellEditor</code> - value selected from drop-down combo box</li>
032: * <li><code>CheckboxCellEditor</code> - boolean valued checkbox</li>
033: * <li><code>DialogCellEditor</code> - value from arbitrary dialog</li>
034: * </ul>
035: * </p>
036: */
037: public abstract class CellEditor {
038:
039: /**
040: * List of cell editor listeners (element type: <code>ICellEditorListener</code>).
041: */
042: private ListenerList listeners = new ListenerList();
043:
044: /**
045: * List of cell editor property change listeners
046: * (element type: <code>IPropertyChangeListener</code>).
047: */
048: private ListenerList propertyChangeListeners = new ListenerList();
049:
050: /**
051: * Indicates whether this cell editor's current value is valid.
052: */
053: private boolean valid = false;
054:
055: /**
056: * Optional cell editor validator; <code>null</code> if none.
057: */
058: private ICellEditorValidator validator = null;
059:
060: /**
061: * The error message string to display for invalid values;
062: * <code>null</code> if none (that is, the value is valid).
063: */
064: private String errorMessage = null;
065:
066: /**
067: * Indicates whether this cell editor has been changed recently.
068: */
069: private boolean dirty = false;
070:
071: /**
072: * This cell editor's control, or <code>null</code>
073: * if not created yet.
074: */
075: private Control control = null;
076:
077: /**
078: * Default cell editor style
079: */
080: private static final int defaultStyle = SWT.NONE;
081:
082: /**
083: * This cell editor's style
084: */
085: private int style = defaultStyle;
086:
087: /**
088: * Struct-like layout data for cell editors, with reasonable defaults
089: * for all fields.
090: */
091: public static class LayoutData {
092: /**
093: * Horizontal alignment; <code>SWT.LEFT</code> by default.
094: */
095: public int horizontalAlignment = SWT.LEFT;
096:
097: /**
098: * Indicates control grabs additional space; <code>true</code> by default.
099: */
100: public boolean grabHorizontal = true;
101:
102: /**
103: * Minimum width in pixels; <code>50</code> pixels by default.
104: */
105: public int minimumWidth = 50;
106: }
107:
108: /**
109: * Property name for the copy action
110: */
111: public static final String COPY = "copy"; //$NON-NLS-1$
112:
113: /**
114: * Property name for the cut action
115: */
116: public static final String CUT = "cut"; //$NON-NLS-1$
117:
118: /**
119: * Property name for the delete action
120: */
121: public static final String DELETE = "delete"; //$NON-NLS-1$
122:
123: /**
124: * Property name for the find action
125: */
126: public static final String FIND = "find"; //$NON-NLS-1$
127:
128: /**
129: * Property name for the paste action
130: */
131: public static final String PASTE = "paste"; //$NON-NLS-1$
132:
133: /**
134: * Property name for the redo action
135: */
136: public static final String REDO = "redo"; //$NON-NLS-1$
137:
138: /**
139: * Property name for the select all action
140: */
141: public static final String SELECT_ALL = "selectall"; //$NON-NLS-1$
142:
143: /**
144: * Property name for the undo action
145: */
146: public static final String UNDO = "undo"; //$NON-NLS-1$
147:
148: /**
149: * Creates a new cell editor with no control
150: * The cell editor has no cell validator.
151: * @since 2.1
152: */
153: protected CellEditor() {
154: }
155:
156: /**
157: * Creates a new cell editor under the given parent control.
158: * The cell editor has no cell validator.
159: *
160: * @param parent the parent control
161: */
162: protected CellEditor(Composite parent) {
163: this (parent, defaultStyle);
164: }
165:
166: /**
167: * Creates a new cell editor under the given parent control.
168: * The cell editor has no cell validator.
169: *
170: * @param parent the parent control
171: * @param style the style bits
172: * @since 2.1
173: */
174: protected CellEditor(Composite parent, int style) {
175: this .style = style;
176: create(parent);
177: }
178:
179: /**
180: * Activates this cell editor.
181: * <p>
182: * The default implementation of this framework method
183: * does nothing. Subclasses may reimplement.
184: * </p>
185: */
186: public void activate() {
187: }
188:
189: /**
190: * Adds a listener to this cell editor.
191: * Has no effect if an identical listener is already registered.
192: *
193: * @param listener a cell editor listener
194: */
195: public void addListener(ICellEditorListener listener) {
196: listeners.add(listener);
197: }
198:
199: /**
200: * Adds a property change listener to this cell editor.
201: * Has no effect if an identical property change listener
202: * is already registered.
203: *
204: * @param listener a property change listener
205: */
206: public void addPropertyChangeListener(
207: IPropertyChangeListener listener) {
208: propertyChangeListeners.add(listener);
209: }
210:
211: /**
212: * Creates the control for this cell editor under the given parent control.
213: * <p>
214: * This framework method must be implemented by concrete
215: * subclasses.
216: * </p>
217: *
218: * @param parent the parent control
219: * @return the new control, or <code>null</code> if this cell editor has no control
220: */
221: protected abstract Control createControl(Composite parent);
222:
223: /**
224: * Creates the control for this cell editor under the given parent control.
225: *
226: * @param parent the parent control
227: * @since 2.1
228: */
229: public void create(Composite parent) {
230: Assert.isTrue(control == null);
231: control = createControl(parent);
232: // See 1GD5CA6: ITPUI:ALL - TaskView.setSelection does not work
233: // Control is created with getVisible()==true by default.
234: // This causes composite.setFocus() to work incorrectly.
235: // The cell editor's control grabs focus instead, even if it is not active.
236: // Make the control invisible here by default.
237: deactivate();
238: }
239:
240: /**
241: * Hides this cell editor's control. Does nothing if this
242: * cell editor is not visible.
243: */
244: public void deactivate() {
245: if (control != null && !control.isDisposed()) {
246: control.setVisible(false);
247: }
248: }
249:
250: /**
251: * Disposes of this cell editor and frees any associated SWT resources.
252: */
253: public void dispose() {
254: if (control != null && !control.isDisposed()) {
255: control.dispose();
256: }
257: control = null;
258: }
259:
260: /**
261: * Returns this cell editor's value.
262: * <p>
263: * This framework method must be implemented by concrete subclasses.
264: * </p>
265: *
266: * @return the value of this cell editor
267: * @see #getValue
268: */
269: protected abstract Object doGetValue();
270:
271: /**
272: * Sets the focus to the cell editor's control.
273: * <p>
274: * This framework method must be implemented by concrete subclasses.
275: * </p>
276: *
277: * @see #setFocus
278: */
279: protected abstract void doSetFocus();
280:
281: /**
282: * Sets this cell editor's value.
283: * <p>
284: * This framework method must be implemented by concrete subclasses.
285: * </p>
286: *
287: * @param value the value of this cell editor
288: * @see #setValue
289: */
290: protected abstract void doSetValue(Object value);
291:
292: /**
293: * Notifies all registered cell editor listeners of an apply event.
294: * Only listeners registered at the time this method is called are notified.
295: *
296: * @see ICellEditorListener#applyEditorValue
297: */
298: protected void fireApplyEditorValue() {
299: Object[] array = listeners.getListeners();
300: for (int i = 0; i < array.length; i++) {
301: final ICellEditorListener l = (ICellEditorListener) array[i];
302: SafeRunnable.run(new SafeRunnable() {
303: public void run() {
304: l.applyEditorValue();
305: }
306: });
307: }
308: }
309:
310: /**
311: * Notifies all registered cell editor listeners that editing has been
312: * canceled.
313: *
314: * @see ICellEditorListener#cancelEditor
315: */
316: protected void fireCancelEditor() {
317: Object[] array = listeners.getListeners();
318: for (int i = 0; i < array.length; i++) {
319: final ICellEditorListener l = (ICellEditorListener) array[i];
320: SafeRunnable.run(new SafeRunnable() {
321: public void run() {
322: l.cancelEditor();
323: }
324: });
325: }
326: }
327:
328: /**
329: * Notifies all registered cell editor listeners of a value change.
330: *
331: * @param oldValidState the valid state before the end user changed the value
332: * @param newValidState the current valid state
333: * @see ICellEditorListener#editorValueChanged
334: */
335: protected void fireEditorValueChanged(final boolean oldValidState,
336: final boolean newValidState) {
337: Object[] array = listeners.getListeners();
338: for (int i = 0; i < array.length; i++) {
339: final ICellEditorListener l = (ICellEditorListener) array[i];
340: SafeRunnable.run(new SafeRunnable() {
341: public void run() {
342: l.editorValueChanged(oldValidState, newValidState);
343: }
344: });
345: }
346: }
347:
348: /**
349: * Notifies all registered property listeners
350: * of an enablement change.
351: *
352: * @param actionId the id indicating what action's enablement has changed.
353: */
354: protected void fireEnablementChanged(final String actionId) {
355: Object[] array = propertyChangeListeners.getListeners();
356: for (int i = 0; i < array.length; i++) {
357: final IPropertyChangeListener l = (IPropertyChangeListener) array[i];
358: SafeRunnable.run(new SafeRunnable() {
359: public void run() {
360: l.propertyChange(new PropertyChangeEvent(this ,
361: actionId, null, null));
362: }
363: });
364: }
365: }
366:
367: /**
368: * Sets the style bits for this cell editor.
369: *
370: * @param style the SWT style bits for this cell editor
371: * @since 2.1
372: */
373: public void setStyle(int style) {
374: this .style = style;
375: }
376:
377: /**
378: * Returns the style bits for this cell editor.
379: *
380: * @return the style for this cell editor
381: * @since 2.1
382: */
383: public int getStyle() {
384: return style;
385: }
386:
387: /**
388: * Returns the control used to implement this cell editor.
389: *
390: * @return the control, or <code>null</code> if this cell editor has no control
391: */
392: public Control getControl() {
393: return control;
394: }
395:
396: /**
397: * Returns the current error message for this cell editor.
398: *
399: * @return the error message if the cell editor is in an invalid state,
400: * and <code>null</code> if the cell editor is valid
401: */
402: public String getErrorMessage() {
403: return errorMessage;
404: }
405:
406: /**
407: * Returns a layout data object for this cell editor.
408: * This is called each time the cell editor is activated
409: * and controls the layout of the SWT table editor.
410: * <p>
411: * The default implementation of this method sets the
412: * minimum width to the control's preferred width.
413: * Subclasses may extend or reimplement.
414: * </p>
415: *
416: * @return the layout data object
417: */
418: public LayoutData getLayoutData() {
419: LayoutData result = new LayoutData();
420: Control control = getControl();
421: if (control != null) {
422: result.minimumWidth = control.computeSize(SWT.DEFAULT,
423: SWT.DEFAULT, true).x;
424: }
425: return result;
426: }
427:
428: /**
429: * Returns the input validator for this cell editor.
430: *
431: * @return the input validator, or <code>null</code> if none
432: */
433: public ICellEditorValidator getValidator() {
434: return validator;
435: }
436:
437: /**
438: * Returns this cell editor's value provided that it has a valid one.
439: *
440: * @return the value of this cell editor, or <code>null</code>
441: * if the cell editor does not contain a valid value
442: */
443: public final Object getValue() {
444: if (!valid) {
445: return null;
446: }
447:
448: return doGetValue();
449: }
450:
451: /**
452: * Returns whether this cell editor is activated.
453: *
454: * @return <code>true</code> if this cell editor's control is
455: * currently activated, and <code>false</code> if not activated
456: */
457: public boolean isActivated() {
458: // Use the state of the visible style bit (getVisible()) rather than the
459: // window's actual visibility (isVisible()) to get correct handling when
460: // an ancestor control goes invisible, see bug 85331.
461: return control != null && control.getVisible();
462: }
463:
464: /**
465: * Returns <code>true</code> if this cell editor is
466: * able to perform the copy action.
467: * <p>
468: * This default implementation always returns
469: * <code>false</code>.
470: * </p>
471: * <p>
472: * Subclasses may override
473: * </p>
474: * @return <code>true</code> if copy is possible,
475: * <code>false</code> otherwise
476: */
477: public boolean isCopyEnabled() {
478: return false;
479: }
480:
481: /**
482: * Returns whether the given value is valid for this cell editor.
483: * This cell editor's validator (if any) makes the actual determination.
484: * @param value the value to check for
485: *
486: * @return <code>true</code> if the value is valid, and <code>false</code>
487: * if invalid
488: */
489: protected boolean isCorrect(Object value) {
490: errorMessage = null;
491: if (validator == null) {
492: return true;
493: }
494:
495: errorMessage = validator.isValid(value);
496: return (errorMessage == null || errorMessage.equals(""));//$NON-NLS-1$
497: }
498:
499: /**
500: * Returns <code>true</code> if this cell editor is
501: * able to perform the cut action.
502: * <p>
503: * This default implementation always returns
504: * <code>false</code>.
505: * </p>
506: * <p>
507: * Subclasses may override
508: * </p>
509: * @return <code>true</code> if cut is possible,
510: * <code>false</code> otherwise
511: */
512: public boolean isCutEnabled() {
513: return false;
514: }
515:
516: /**
517: * Returns <code>true</code> if this cell editor is
518: * able to perform the delete action.
519: * <p>
520: * This default implementation always returns
521: * <code>false</code>.
522: * </p>
523: * <p>
524: * Subclasses may override
525: * </p>
526: * @return <code>true</code> if delete is possible,
527: * <code>false</code> otherwise
528: */
529: public boolean isDeleteEnabled() {
530: return false;
531: }
532:
533: /**
534: * Returns whether the value of this cell editor has changed since the
535: * last call to <code>setValue</code>.
536: *
537: * @return <code>true</code> if the value has changed, and <code>false</code>
538: * if unchanged
539: */
540: public boolean isDirty() {
541: return dirty;
542: }
543:
544: /**
545: * Marks this cell editor as dirty.
546: * @since 2.1
547: */
548: protected void markDirty() {
549: dirty = true;
550: }
551:
552: /**
553: * Returns <code>true</code> if this cell editor is
554: * able to perform the find action.
555: * <p>
556: * This default implementation always returns
557: * <code>false</code>.
558: * </p>
559: * <p>
560: * Subclasses may override
561: * </p>
562: * @return <code>true</code> if find is possible,
563: * <code>false</code> otherwise
564: */
565: public boolean isFindEnabled() {
566: return false;
567: }
568:
569: /**
570: * Returns <code>true</code> if this cell editor is
571: * able to perform the paste action.
572: * <p>
573: * This default implementation always returns
574: * <code>false</code>.
575: * </p>
576: * <p>
577: * Subclasses may override
578: * </p>
579: * @return <code>true</code> if paste is possible,
580: * <code>false</code> otherwise
581: */
582: public boolean isPasteEnabled() {
583: return false;
584: }
585:
586: /**
587: * Returns <code>true</code> if this cell editor is
588: * able to perform the redo action.
589: * <p>
590: * This default implementation always returns
591: * <code>false</code>.
592: * </p>
593: * <p>
594: * Subclasses may override
595: * </p>
596: * @return <code>true</code> if redo is possible,
597: * <code>false</code> otherwise
598: */
599: public boolean isRedoEnabled() {
600: return false;
601: }
602:
603: /**
604: * Returns <code>true</code> if this cell editor is
605: * able to perform the select all action.
606: * <p>
607: * This default implementation always returns
608: * <code>false</code>.
609: * </p>
610: * <p>
611: * Subclasses may override
612: * </p>
613: * @return <code>true</code> if select all is possible,
614: * <code>false</code> otherwise
615: */
616: public boolean isSelectAllEnabled() {
617: return false;
618: }
619:
620: /**
621: * Returns <code>true</code> if this cell editor is
622: * able to perform the undo action.
623: * <p>
624: * This default implementation always returns
625: * <code>false</code>.
626: * </p>
627: * <p>
628: * Subclasses may override
629: * </p>
630: * @return <code>true</code> if undo is possible,
631: * <code>false</code> otherwise
632: */
633: public boolean isUndoEnabled() {
634: return false;
635: }
636:
637: /**
638: * Returns whether this cell editor has a valid value.
639: * The default value is false.
640: *
641: * @return <code>true</code> if the value is valid, and <code>false</code>
642: * if invalid
643: *
644: * @see #setValueValid(boolean)
645: */
646: public boolean isValueValid() {
647: return valid;
648: }
649:
650: /**
651: * Processes a key release event that occurred in this cell editor.
652: * <p>
653: * The default implementation of this framework method cancels editing
654: * when the ESC key is pressed. When the RETURN key is pressed the current
655: * value is applied and the cell editor deactivates.
656: * Subclasses should call this method at appropriate times.
657: * Subclasses may also extend or reimplement.
658: * </p>
659: *
660: * @param keyEvent the key event
661: */
662: protected void keyReleaseOccured(KeyEvent keyEvent) {
663: if (keyEvent.character == '\u001b') { // Escape character
664: fireCancelEditor();
665: } else if (keyEvent.character == '\r') { // Return key
666: fireApplyEditorValue();
667: deactivate();
668: }
669: }
670:
671: /**
672: * Processes a focus lost event that occurred in this cell editor.
673: * <p>
674: * The default implementation of this framework method applies the current
675: * value and deactivates the cell editor.
676: * Subclasses should call this method at appropriate times.
677: * Subclasses may also extend or reimplement.
678: * </p>
679: */
680: protected void focusLost() {
681: if (isActivated()) {
682: fireApplyEditorValue();
683: deactivate();
684: }
685: }
686:
687: /**
688: * Performs the copy action.
689: * This default implementation does nothing.
690: * <p>
691: * Subclasses may override
692: * </p>
693: */
694: public void performCopy() {
695: }
696:
697: /**
698: * Performs the cut action.
699: * This default implementation does nothing.
700: * <p>
701: * Subclasses may override
702: * </p>
703: */
704: public void performCut() {
705: }
706:
707: /**
708: * Performs the delete action.
709: * This default implementation does nothing.
710: * <p>
711: * Subclasses may override
712: * </p>
713: */
714: public void performDelete() {
715: }
716:
717: /**
718: * Performs the find action.
719: * This default implementation does nothing.
720: * <p>
721: * Subclasses may override
722: * </p>
723: */
724: public void performFind() {
725: }
726:
727: /**
728: * Performs the paste action.
729: * This default implementation does nothing.
730: * <p>
731: * Subclasses may override
732: * </p>
733: */
734: public void performPaste() {
735: }
736:
737: /**
738: * Performs the redo action.
739: * This default implementation does nothing.
740: * <p>
741: * Subclasses may override
742: * </p>
743: */
744: public void performRedo() {
745: }
746:
747: /**
748: * Performs the select all action.
749: * This default implementation does nothing.
750: * <p>
751: * Subclasses may override
752: * </p>
753: */
754: public void performSelectAll() {
755: }
756:
757: /**
758: * Performs the undo action.
759: * This default implementation does nothing.
760: * <p>
761: * Subclasses may override
762: * </p>
763: */
764: public void performUndo() {
765: }
766:
767: /**
768: * Removes the given listener from this cell editor.
769: * Has no affect if an identical listener is not registered.
770: *
771: * @param listener a cell editor listener
772: */
773: public void removeListener(ICellEditorListener listener) {
774: listeners.remove(listener);
775: }
776:
777: /**
778: * Removes the given property change listener from this cell editor.
779: * Has no affect if an identical property change listener is not
780: * registered.
781: *
782: * @param listener a property change listener
783: */
784: public void removePropertyChangeListener(
785: IPropertyChangeListener listener) {
786: propertyChangeListeners.remove(listener);
787: }
788:
789: /**
790: * Sets or clears the current error message for this cell editor.
791: * <p>
792: * No formatting is done here, the message to be set is expected to be fully formatted
793: * before being passed in.
794: * </p>
795: * @param message the error message, or <code>null</code> to clear
796: */
797: protected void setErrorMessage(String message) {
798: errorMessage = message;
799: }
800:
801: /**
802: * Sets the focus to the cell editor's control.
803: */
804: public void setFocus() {
805: doSetFocus();
806: }
807:
808: /**
809: * Sets the input validator for this cell editor.
810: *
811: * @param validator the input validator, or <code>null</code> if none
812: */
813: public void setValidator(ICellEditorValidator validator) {
814: this .validator = validator;
815: }
816:
817: /**
818: * Sets this cell editor's value.
819: *
820: * @param value the value of this cell editor
821: */
822: public final void setValue(Object value) {
823: valid = isCorrect(value);
824: dirty = false;
825: doSetValue(value);
826: }
827:
828: /**
829: * Sets the valid state of this cell editor.
830: * The default value is false.
831: * Subclasses should call this method on construction.
832: *
833: * @param valid <code>true</code> if the current value is valid,
834: * and <code>false</code> if invalid
835: *
836: * @see #isValueValid
837: */
838: protected void setValueValid(boolean valid) {
839: this .valid = valid;
840: }
841:
842: /**
843: * The value has changed.
844: * Updates the valid state flag, marks this cell editor as dirty,
845: * and notifies all registered cell editor listeners of a value change.
846: *
847: * @param oldValidState the valid state before the end user changed the value
848: * @param newValidState the current valid state
849: * @see ICellEditorListener#editorValueChanged
850: */
851: protected void valueChanged(boolean oldValidState,
852: boolean newValidState) {
853: valid = newValidState;
854: dirty = true;
855: fireEditorValueChanged(oldValidState, newValidState);
856: }
857:
858: /**
859: * Activate the editor but also inform the editor which event triggered its activation.
860: * <b>The default implementation simply calls {@link #activate()}</b>
861: *
862: * @param activationEvent the editor activation event
863: * @since 3.3
864: */
865: public void activate(
866: ColumnViewerEditorActivationEvent activationEvent) {
867: activate();
868: }
869:
870: /**
871: * This method is for interal use in {@link ColumnViewerEditor} to not break clients
872: * who don't implement the {@link ICellEditorListener} appropiately
873: *
874: * @return <code>true</code> to indicate that a focus listener has to be attached
875: */
876: boolean dependsOnExternalFocusListener() {
877: return true;
878: }
879: }
|