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 java.text.MessageFormat; // Not using ICU to support standalone JFace scenario
013:
014: import org.eclipse.core.runtime.Assert;
015: import org.eclipse.swt.SWT;
016: import org.eclipse.swt.events.FocusAdapter;
017: import org.eclipse.swt.events.FocusEvent;
018: import org.eclipse.swt.events.KeyAdapter;
019: import org.eclipse.swt.events.KeyEvent;
020: import org.eclipse.swt.events.ModifyEvent;
021: import org.eclipse.swt.events.ModifyListener;
022: import org.eclipse.swt.events.MouseAdapter;
023: import org.eclipse.swt.events.MouseEvent;
024: import org.eclipse.swt.events.SelectionAdapter;
025: import org.eclipse.swt.events.SelectionEvent;
026: import org.eclipse.swt.events.TraverseEvent;
027: import org.eclipse.swt.events.TraverseListener;
028: import org.eclipse.swt.widgets.Composite;
029: import org.eclipse.swt.widgets.Control;
030: import org.eclipse.swt.widgets.Text;
031:
032: /**
033: * A cell editor that manages a text entry field.
034: * The cell editor's value is the text string itself.
035: * <p>
036: * This class may be instantiated; it is not intended to be subclassed.
037: * </p>
038: */
039: public class TextCellEditor extends CellEditor {
040:
041: /**
042: * The text control; initially <code>null</code>.
043: */
044: protected Text text;
045:
046: private ModifyListener modifyListener;
047:
048: /**
049: * State information for updating action enablement
050: */
051: private boolean isSelection = false;
052:
053: private boolean isDeleteable = false;
054:
055: private boolean isSelectable = false;
056:
057: /**
058: * Default TextCellEditor style
059: * specify no borders on text widget as cell outline in table already
060: * provides the look of a border.
061: */
062: private static final int defaultStyle = SWT.SINGLE;
063:
064: /**
065: * Creates a new text string cell editor with no control
066: * The cell editor value is the string itself, which is initially the empty
067: * string. Initially, the cell editor has no cell validator.
068: *
069: * @since 2.1
070: */
071: public TextCellEditor() {
072: setStyle(defaultStyle);
073: }
074:
075: /**
076: * Creates a new text string cell editor parented under the given control.
077: * The cell editor value is the string itself, which is initially the empty string.
078: * Initially, the cell editor has no cell validator.
079: *
080: * @param parent the parent control
081: */
082: public TextCellEditor(Composite parent) {
083: this (parent, defaultStyle);
084: }
085:
086: /**
087: * Creates a new text string cell editor parented under the given control.
088: * The cell editor value is the string itself, which is initially the empty string.
089: * Initially, the cell editor has no cell validator.
090: *
091: * @param parent the parent control
092: * @param style the style bits
093: * @since 2.1
094: */
095: public TextCellEditor(Composite parent, int style) {
096: super (parent, style);
097: }
098:
099: /**
100: * Checks to see if the "deletable" state (can delete/
101: * nothing to delete) has changed and if so fire an
102: * enablement changed notification.
103: */
104: private void checkDeleteable() {
105: boolean oldIsDeleteable = isDeleteable;
106: isDeleteable = isDeleteEnabled();
107: if (oldIsDeleteable != isDeleteable) {
108: fireEnablementChanged(DELETE);
109: }
110: }
111:
112: /**
113: * Checks to see if the "selectable" state (can select)
114: * has changed and if so fire an enablement changed notification.
115: */
116: private void checkSelectable() {
117: boolean oldIsSelectable = isSelectable;
118: isSelectable = isSelectAllEnabled();
119: if (oldIsSelectable != isSelectable) {
120: fireEnablementChanged(SELECT_ALL);
121: }
122: }
123:
124: /**
125: * Checks to see if the selection state (selection /
126: * no selection) has changed and if so fire an
127: * enablement changed notification.
128: */
129: private void checkSelection() {
130: boolean oldIsSelection = isSelection;
131: isSelection = text.getSelectionCount() > 0;
132: if (oldIsSelection != isSelection) {
133: fireEnablementChanged(COPY);
134: fireEnablementChanged(CUT);
135: }
136: }
137:
138: /* (non-Javadoc)
139: * Method declared on CellEditor.
140: */
141: protected Control createControl(Composite parent) {
142: text = new Text(parent, getStyle());
143: text.addSelectionListener(new SelectionAdapter() {
144: public void widgetDefaultSelected(SelectionEvent e) {
145: handleDefaultSelection(e);
146: }
147: });
148: text.addKeyListener(new KeyAdapter() {
149: // hook key pressed - see PR 14201
150: public void keyPressed(KeyEvent e) {
151: keyReleaseOccured(e);
152:
153: // as a result of processing the above call, clients may have
154: // disposed this cell editor
155: if ((getControl() == null) || getControl().isDisposed()) {
156: return;
157: }
158: checkSelection(); // see explanation below
159: checkDeleteable();
160: checkSelectable();
161: }
162: });
163: text.addTraverseListener(new TraverseListener() {
164: public void keyTraversed(TraverseEvent e) {
165: if (e.detail == SWT.TRAVERSE_ESCAPE
166: || e.detail == SWT.TRAVERSE_RETURN) {
167: e.doit = false;
168: }
169: }
170: });
171: // We really want a selection listener but it is not supported so we
172: // use a key listener and a mouse listener to know when selection changes
173: // may have occurred
174: text.addMouseListener(new MouseAdapter() {
175: public void mouseUp(MouseEvent e) {
176: checkSelection();
177: checkDeleteable();
178: checkSelectable();
179: }
180: });
181: text.addFocusListener(new FocusAdapter() {
182: public void focusLost(FocusEvent e) {
183: TextCellEditor.this .focusLost();
184: }
185: });
186: text.setFont(parent.getFont());
187: text.setBackground(parent.getBackground());
188: text.setText("");//$NON-NLS-1$
189: text.addModifyListener(getModifyListener());
190: return text;
191: }
192:
193: /**
194: * The <code>TextCellEditor</code> implementation of
195: * this <code>CellEditor</code> framework method returns
196: * the text string.
197: *
198: * @return the text string
199: */
200: protected Object doGetValue() {
201: return text.getText();
202: }
203:
204: /* (non-Javadoc)
205: * Method declared on CellEditor.
206: */
207: protected void doSetFocus() {
208: if (text != null) {
209: text.selectAll();
210: text.setFocus();
211: checkSelection();
212: checkDeleteable();
213: checkSelectable();
214: }
215: }
216:
217: /**
218: * The <code>TextCellEditor</code> implementation of
219: * this <code>CellEditor</code> framework method accepts
220: * a text string (type <code>String</code>).
221: *
222: * @param value a text string (type <code>String</code>)
223: */
224: protected void doSetValue(Object value) {
225: Assert.isTrue(text != null && (value instanceof String));
226: text.removeModifyListener(getModifyListener());
227: text.setText((String) value);
228: text.addModifyListener(getModifyListener());
229: }
230:
231: /**
232: * Processes a modify event that occurred in this text cell editor.
233: * This framework method performs validation and sets the error message
234: * accordingly, and then reports a change via <code>fireEditorValueChanged</code>.
235: * Subclasses should call this method at appropriate times. Subclasses
236: * may extend or reimplement.
237: *
238: * @param e the SWT modify event
239: */
240: protected void editOccured(ModifyEvent e) {
241: String value = text.getText();
242: if (value == null) {
243: value = "";//$NON-NLS-1$
244: }
245: Object typedValue = value;
246: boolean oldValidState = isValueValid();
247: boolean newValidState = isCorrect(typedValue);
248: if (typedValue == null && newValidState) {
249: Assert
250: .isTrue(false,
251: "Validator isn't limiting the cell editor's type range");//$NON-NLS-1$
252: }
253: if (!newValidState) {
254: // try to insert the current value into the error message.
255: setErrorMessage(MessageFormat.format(getErrorMessage(),
256: new Object[] { value }));
257: }
258: valueChanged(oldValidState, newValidState);
259: }
260:
261: /**
262: * Since a text editor field is scrollable we don't
263: * set a minimumSize.
264: */
265: public LayoutData getLayoutData() {
266: return new LayoutData();
267: }
268:
269: /**
270: * Return the modify listener.
271: */
272: private ModifyListener getModifyListener() {
273: if (modifyListener == null) {
274: modifyListener = new ModifyListener() {
275: public void modifyText(ModifyEvent e) {
276: editOccured(e);
277: }
278: };
279: }
280: return modifyListener;
281: }
282:
283: /**
284: * Handles a default selection event from the text control by applying the editor
285: * value and deactivating this cell editor.
286: *
287: * @param event the selection event
288: *
289: * @since 3.0
290: */
291: protected void handleDefaultSelection(SelectionEvent event) {
292: // same with enter-key handling code in keyReleaseOccured(e);
293: fireApplyEditorValue();
294: deactivate();
295: }
296:
297: /**
298: * The <code>TextCellEditor</code> implementation of this
299: * <code>CellEditor</code> method returns <code>true</code> if
300: * the current selection is not empty.
301: */
302: public boolean isCopyEnabled() {
303: if (text == null || text.isDisposed()) {
304: return false;
305: }
306: return text.getSelectionCount() > 0;
307: }
308:
309: /**
310: * The <code>TextCellEditor</code> implementation of this
311: * <code>CellEditor</code> method returns <code>true</code> if
312: * the current selection is not empty.
313: */
314: public boolean isCutEnabled() {
315: if (text == null || text.isDisposed()) {
316: return false;
317: }
318: return text.getSelectionCount() > 0;
319: }
320:
321: /**
322: * The <code>TextCellEditor</code> implementation of this
323: * <code>CellEditor</code> method returns <code>true</code>
324: * if there is a selection or if the caret is not positioned
325: * at the end of the text.
326: */
327: public boolean isDeleteEnabled() {
328: if (text == null || text.isDisposed()) {
329: return false;
330: }
331: return text.getSelectionCount() > 0
332: || text.getCaretPosition() < text.getCharCount();
333: }
334:
335: /**
336: * The <code>TextCellEditor</code> implementation of this
337: * <code>CellEditor</code> method always returns <code>true</code>.
338: */
339: public boolean isPasteEnabled() {
340: if (text == null || text.isDisposed()) {
341: return false;
342: }
343: return true;
344: }
345:
346: /**
347: * Check if save all is enabled
348: * @return true if it is
349: */
350: public boolean isSaveAllEnabled() {
351: if (text == null || text.isDisposed()) {
352: return false;
353: }
354: return true;
355: }
356:
357: /**
358: * Returns <code>true</code> if this cell editor is
359: * able to perform the select all action.
360: * <p>
361: * This default implementation always returns
362: * <code>false</code>.
363: * </p>
364: * <p>
365: * Subclasses may override
366: * </p>
367: * @return <code>true</code> if select all is possible,
368: * <code>false</code> otherwise
369: */
370: public boolean isSelectAllEnabled() {
371: if (text == null || text.isDisposed()) {
372: return false;
373: }
374: return text.getCharCount() > 0;
375: }
376:
377: /**
378: * Processes a key release event that occurred in this cell editor.
379: * <p>
380: * The <code>TextCellEditor</code> implementation of this framework method
381: * ignores when the RETURN key is pressed since this is handled in
382: * <code>handleDefaultSelection</code>.
383: * An exception is made for Ctrl+Enter for multi-line texts, since
384: * a default selection event is not sent in this case.
385: * </p>
386: *
387: * @param keyEvent the key event
388: */
389: protected void keyReleaseOccured(KeyEvent keyEvent) {
390: if (keyEvent.character == '\r') { // Return key
391: // Enter is handled in handleDefaultSelection.
392: // Do not apply the editor value in response to an Enter key event
393: // since this can be received from the IME when the intent is -not-
394: // to apply the value.
395: // See bug 39074 [CellEditors] [DBCS] canna input mode fires bogus event from Text Control
396: //
397: // An exception is made for Ctrl+Enter for multi-line texts, since
398: // a default selection event is not sent in this case.
399: if (text != null && !text.isDisposed()
400: && (text.getStyle() & SWT.MULTI) != 0) {
401: if ((keyEvent.stateMask & SWT.CTRL) != 0) {
402: super .keyReleaseOccured(keyEvent);
403: }
404: }
405: return;
406: }
407: super .keyReleaseOccured(keyEvent);
408: }
409:
410: /**
411: * The <code>TextCellEditor</code> implementation of this
412: * <code>CellEditor</code> method copies the
413: * current selection to the clipboard.
414: */
415: public void performCopy() {
416: text.copy();
417: }
418:
419: /**
420: * The <code>TextCellEditor</code> implementation of this
421: * <code>CellEditor</code> method cuts the
422: * current selection to the clipboard.
423: */
424: public void performCut() {
425: text.cut();
426: checkSelection();
427: checkDeleteable();
428: checkSelectable();
429: }
430:
431: /**
432: * The <code>TextCellEditor</code> implementation of this
433: * <code>CellEditor</code> method deletes the
434: * current selection or, if there is no selection,
435: * the character next character from the current position.
436: */
437: public void performDelete() {
438: if (text.getSelectionCount() > 0) {
439: // remove the contents of the current selection
440: text.insert(""); //$NON-NLS-1$
441: } else {
442: // remove the next character
443: int pos = text.getCaretPosition();
444: if (pos < text.getCharCount()) {
445: text.setSelection(pos, pos + 1);
446: text.insert(""); //$NON-NLS-1$
447: }
448: }
449: checkSelection();
450: checkDeleteable();
451: checkSelectable();
452: }
453:
454: /**
455: * The <code>TextCellEditor</code> implementation of this
456: * <code>CellEditor</code> method pastes the
457: * the clipboard contents over the current selection.
458: */
459: public void performPaste() {
460: text.paste();
461: checkSelection();
462: checkDeleteable();
463: checkSelectable();
464: }
465:
466: /**
467: * The <code>TextCellEditor</code> implementation of this
468: * <code>CellEditor</code> method selects all of the
469: * current text.
470: */
471: public void performSelectAll() {
472: text.selectAll();
473: checkSelection();
474: checkDeleteable();
475: }
476:
477: boolean dependsOnExternalFocusListener() {
478: return getClass() != TextCellEditor.class;
479: }
480: }
|