001: /*******************************************************************************
002: * Copyright (c) 2005, 2006 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.jdt.internal.ui.dialogs;
011:
012: import org.eclipse.core.runtime.Assert;
013:
014: import org.eclipse.swt.SWT;
015: import org.eclipse.swt.events.FocusAdapter;
016: import org.eclipse.swt.events.FocusEvent;
017: import org.eclipse.swt.events.KeyAdapter;
018: import org.eclipse.swt.events.KeyEvent;
019: import org.eclipse.swt.events.ModifyEvent;
020: import org.eclipse.swt.events.ModifyListener;
021: import org.eclipse.swt.events.MouseAdapter;
022: import org.eclipse.swt.events.MouseEvent;
023: import org.eclipse.swt.events.SelectionAdapter;
024: import org.eclipse.swt.events.SelectionEvent;
025: import org.eclipse.swt.events.TraverseEvent;
026: import org.eclipse.swt.events.TraverseListener;
027: import org.eclipse.swt.layout.FillLayout;
028: import org.eclipse.swt.widgets.Composite;
029: import org.eclipse.swt.widgets.Control;
030: import org.eclipse.swt.widgets.Listener;
031: import org.eclipse.swt.widgets.Text;
032:
033: import org.eclipse.jface.contentassist.SubjectControlContentAssistant;
034: import org.eclipse.jface.viewers.CellEditor;
035: import org.eclipse.jface.viewers.IStructuredSelection;
036: import org.eclipse.jface.viewers.TableViewer;
037:
038: import org.eclipse.jdt.internal.corext.util.Messages;
039:
040: /**
041: * <code>TableTextCellEditor</code> is a copy of TextCellEditor, with the
042: * following changes:
043: *
044: * <ul>
045: * <li> modify events are sent out as the text is changed, and not only after
046: * editing is done </li>
047: *
048: * <li>a content assistant is supported</li>
049: *
050: * <li>the <code>Control</code> from <code>getControl(Composite)</code>
051: * does not notify registered FocusListeners. This is a workaround for bug
052: * 58777. </li>
053: *
054: * <li>the user can go to the next/previous row with up and down keys</li>
055: * </ul>
056: */
057: public class TableTextCellEditor extends CellEditor {
058: public interface IActivationListener {
059: public void activate();
060: }
061:
062: private final TableViewer fTableViewer;
063: private final int fColumn;
064: private final String fProperty;
065: /**
066: * The editor's value on activation. This value is reset to the
067: * cell when the editor is left via ESC key.
068: */
069: String fOriginalValue;
070: SubjectControlContentAssistant fContentAssistant;
071: private IActivationListener fActivationListener;
072:
073: protected Text text;
074:
075: private boolean isSelection = false;
076: private boolean isDeleteable = false;
077: private boolean isSelectable = false;
078:
079: private static final int defaultStyle = SWT.SINGLE;
080: private ModifyListener fModifyListener;
081:
082: public TableTextCellEditor(TableViewer tableViewer, int column) {
083: super (tableViewer.getTable(), defaultStyle);
084: fTableViewer = tableViewer;
085: fColumn = column;
086: fProperty = (String) tableViewer.getColumnProperties()[column];
087: }
088:
089: public void activate() {
090: super .activate();
091: if (fActivationListener != null)
092: fActivationListener.activate();
093: fOriginalValue = text.getText();
094: }
095:
096: private void fireModifyEvent(Object newValue) {
097: fTableViewer.getCellModifier().modify(
098: ((IStructuredSelection) fTableViewer.getSelection())
099: .getFirstElement(), fProperty, newValue);
100: }
101:
102: protected void focusLost() {
103: if (fContentAssistant != null
104: && fContentAssistant.hasProposalPopupFocus()) {
105: // skip focus lost if it went to the content assist popup
106: } else {
107: super .focusLost();
108: }
109: }
110:
111: public void setContentAssistant(
112: SubjectControlContentAssistant assistant) {
113: fContentAssistant = assistant;
114: }
115:
116: public void setActivationListener(IActivationListener listener) {
117: fActivationListener = listener;
118: }
119:
120: public Text getText() {
121: return text;
122: }
123:
124: protected void checkDeleteable() {
125: boolean oldIsDeleteable = isDeleteable;
126: isDeleteable = isDeleteEnabled();
127: if (oldIsDeleteable != isDeleteable) {
128: fireEnablementChanged(DELETE);
129: }
130: }
131:
132: protected void checkSelectable() {
133: boolean oldIsSelectable = isSelectable;
134: isSelectable = isSelectAllEnabled();
135: if (oldIsSelectable != isSelectable) {
136: fireEnablementChanged(SELECT_ALL);
137: }
138: }
139:
140: protected void checkSelection() {
141: boolean oldIsSelection = isSelection;
142: isSelection = text.getSelectionCount() > 0;
143: if (oldIsSelection != isSelection) {
144: fireEnablementChanged(COPY);
145: fireEnablementChanged(CUT);
146: }
147: }
148:
149: private ModifyListener getModifyListener() {
150: if (fModifyListener == null) {
151: fModifyListener = new ModifyListener() {
152: public void modifyText(ModifyEvent e) {
153: editOccured(e);
154: }
155: };
156: }
157: return fModifyListener;
158: }
159:
160: /* (non-Javadoc)
161: * Method declared on CellEditor.
162: */
163: protected Control createControl(Composite parent) {
164: //workaround for bug 58777: don't accept focus listeners on the text control
165: final Control[] textControl = new Control[1];
166: Composite result = new Composite(parent, SWT.NONE) {
167: public void addListener(int eventType,
168: final Listener listener) {
169: if (eventType != SWT.FocusIn
170: && eventType != SWT.FocusOut) {
171: textControl[0].addListener(eventType, listener);
172: }
173: }
174: };
175: result.setFont(parent.getFont());
176: result.setBackground(parent.getBackground());
177: result.setLayout(new FillLayout());
178:
179: text = new Text(result, getStyle());
180: textControl[0] = text;
181: text.addSelectionListener(new SelectionAdapter() {
182: public void widgetDefaultSelected(SelectionEvent e) {
183: handleDefaultSelection(e);
184: }
185: });
186: text.addKeyListener(new KeyAdapter() {
187: public void keyPressed(KeyEvent e) {
188: // support switching rows while editing:
189: if (e.stateMask == SWT.MOD1 || e.stateMask == SWT.MOD2) {
190: if (e.keyCode == SWT.ARROW_UP
191: || e.keyCode == SWT.ARROW_DOWN) {
192: // allow starting multi-selection even if in edit mode
193: deactivate();
194: e.doit = false;
195: return;
196: }
197: }
198:
199: if (e.stateMask != SWT.NONE)
200: return;
201:
202: switch (e.keyCode) {
203: case SWT.ARROW_DOWN:
204: e.doit = false;
205: int nextRow = fTableViewer.getTable()
206: .getSelectionIndex() + 1;
207: if (nextRow >= fTableViewer.getTable()
208: .getItemCount())
209: break;
210: editRow(nextRow);
211: break;
212:
213: case SWT.ARROW_UP:
214: e.doit = false;
215: int prevRow = fTableViewer.getTable()
216: .getSelectionIndex() - 1;
217: if (prevRow < 0)
218: break;
219: editRow(prevRow);
220: break;
221:
222: case SWT.F2:
223: e.doit = false;
224: deactivate();
225: break;
226: }
227: }
228:
229: private void editRow(int row) {
230: fTableViewer.getTable().setSelection(row);
231: IStructuredSelection newSelection = (IStructuredSelection) fTableViewer
232: .getSelection();
233: if (newSelection.size() == 1)
234: fTableViewer.editElement(newSelection
235: .getFirstElement(), fColumn);
236: }
237: });
238: text.addKeyListener(new KeyAdapter() {
239: // hook key pressed - see PR 14201
240: public void keyPressed(KeyEvent e) {
241: keyReleaseOccured(e);
242:
243: // as a result of processing the above call, clients may have
244: // disposed this cell editor
245: if ((getControl() == null) || getControl().isDisposed())
246: return;
247: checkSelection(); // see explaination below
248: checkDeleteable();
249: checkSelectable();
250: }
251: });
252: text.addTraverseListener(new TraverseListener() {
253: public void keyTraversed(TraverseEvent e) {
254: if (e.detail == SWT.TRAVERSE_ESCAPE
255: || e.detail == SWT.TRAVERSE_RETURN) {
256: e.doit = false;
257: }
258: }
259: });
260: // We really want a selection listener but it is not supported so we
261: // use a key listener and a mouse listener to know when selection changes
262: // may have occured
263: text.addMouseListener(new MouseAdapter() {
264: public void mouseUp(MouseEvent e) {
265: checkSelection();
266: checkDeleteable();
267: checkSelectable();
268: }
269: });
270: text.addFocusListener(new FocusAdapter() {
271: public void focusLost(FocusEvent e) {
272: TableTextCellEditor.this .focusLost();
273: }
274: });
275: text.setFont(parent.getFont());
276: text.setBackground(parent.getBackground());
277: text.setText("");//$NON-NLS-1$
278: text.addModifyListener(getModifyListener());
279:
280: return result;
281: }
282:
283: protected void fireCancelEditor() {
284: /* bug 58540: change signature refactoring interaction: validate as you type [refactoring] */
285: text.setText(fOriginalValue);
286: super .fireApplyEditorValue();
287: }
288:
289: /**
290: * The <code>TextCellEditor</code> implementation of
291: * this <code>CellEditor</code> framework method returns
292: * the text string.
293: *
294: * @return the text string
295: */
296: protected Object doGetValue() {
297: return text.getText();
298: }
299:
300: protected void doSetFocus() {
301: if (text != null) {
302: text.selectAll();
303: text.setFocus();
304: checkSelection();
305: checkDeleteable();
306: checkSelectable();
307: }
308: }
309:
310: /**
311: * The <code>TextCellEditor2</code> implementation of
312: * this <code>CellEditor</code> framework method accepts
313: * a text string (type <code>String</code>).
314: *
315: * @param value a text string (type <code>String</code>)
316: */
317: protected void doSetValue(Object value) {
318: Assert.isTrue(text != null && (value instanceof String));
319: text.removeModifyListener(getModifyListener());
320: text.setText((String) value);
321: text.addModifyListener(getModifyListener());
322: }
323:
324: /**
325: * Processes a modify event that occurred in this text cell editor.
326: * This framework method performs validation and sets the error message
327: * accordingly, and then reports a change via <code>fireEditorValueChanged</code>.
328: * Subclasses should call this method at appropriate times. Subclasses
329: * may extend or reimplement.
330: *
331: * @param e the SWT modify event
332: */
333: protected void editOccured(ModifyEvent e) {
334: String value = text.getText();
335: boolean oldValidState = isValueValid();
336: boolean newValidState = isCorrect(value);
337: if (!newValidState) {
338: // try to insert the current value into the error message.
339: setErrorMessage(Messages.format(getErrorMessage(),
340: new Object[] { value }));
341: }
342: valueChanged(oldValidState, newValidState);
343: fireModifyEvent(text.getText()); // update model on-the-fly
344: }
345:
346: public LayoutData getLayoutData() {
347: return new LayoutData();
348: }
349:
350: protected void handleDefaultSelection(SelectionEvent event) {
351: // same with enter-key handling code in keyReleaseOccured(e);
352: fireApplyEditorValue();
353: deactivate();
354: }
355:
356: public boolean isCopyEnabled() {
357: if (text == null || text.isDisposed())
358: return false;
359: return text.getSelectionCount() > 0;
360: }
361:
362: public boolean isCutEnabled() {
363: if (text == null || text.isDisposed())
364: return false;
365: return text.getSelectionCount() > 0;
366: }
367:
368: public boolean isDeleteEnabled() {
369: if (text == null || text.isDisposed())
370: return false;
371: return text.getSelectionCount() > 0
372: || text.getCaretPosition() < text.getCharCount();
373: }
374:
375: public boolean isPasteEnabled() {
376: if (text == null || text.isDisposed())
377: return false;
378: return true;
379: }
380:
381: public boolean isSelectAllEnabled() {
382: if (text == null || text.isDisposed())
383: return false;
384: return text.getCharCount() > 0;
385: }
386:
387: protected void keyReleaseOccured(KeyEvent keyEvent) {
388: if (keyEvent.character == '\r') { // Return key
389: // Enter is handled in handleDefaultSelection.
390: // Do not apply the editor value in response to an Enter key event
391: // since this can be received from the IME when the intent is -not-
392: // to apply the value.
393: // See bug 39074 [CellEditors] [DBCS] canna input mode fires bogus event from Text Control
394: //
395: // An exception is made for Ctrl+Enter for multi-line texts, since
396: // a default selection event is not sent in this case.
397: if (text != null && !text.isDisposed()
398: && (text.getStyle() & SWT.MULTI) != 0) {
399: if ((keyEvent.stateMask & SWT.CTRL) != 0) {
400: super .keyReleaseOccured(keyEvent);
401: }
402: }
403: return;
404: }
405: super .keyReleaseOccured(keyEvent);
406: }
407:
408: public void performCopy() {
409: text.copy();
410: }
411:
412: public void performCut() {
413: text.cut();
414: checkSelection();
415: checkDeleteable();
416: checkSelectable();
417: }
418:
419: public void performDelete() {
420: if (text.getSelectionCount() > 0)
421: // remove the contents of the current selection
422: text.insert(""); //$NON-NLS-1$
423: else {
424: // remove the next character
425: int pos = text.getCaretPosition();
426: if (pos < text.getCharCount()) {
427: text.setSelection(pos, pos + 1);
428: text.insert(""); //$NON-NLS-1$
429: }
430: }
431: checkSelection();
432: checkDeleteable();
433: checkSelectable();
434: }
435:
436: public void performPaste() {
437: text.paste();
438: checkSelection();
439: checkDeleteable();
440: checkSelectable();
441: }
442:
443: public void performSelectAll() {
444: text.selectAll();
445: checkSelection();
446: checkDeleteable();
447: }
448:
449: }
|