KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > viewers > ColumnViewerEditor


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Tom Schindl <tom.schindl@bestsolution.at> - refactoring (bug 153993)
11  * fix in bug 151295,166500,200337
12  *******************************************************************************/

13
14 package org.eclipse.jface.viewers;
15
16 import org.eclipse.core.runtime.ListenerList;
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.events.FocusAdapter;
19 import org.eclipse.swt.events.FocusEvent;
20 import org.eclipse.swt.events.FocusListener;
21 import org.eclipse.swt.events.MouseAdapter;
22 import org.eclipse.swt.events.MouseEvent;
23 import org.eclipse.swt.events.MouseListener;
24 import org.eclipse.swt.events.TraverseEvent;
25 import org.eclipse.swt.events.TraverseListener;
26 import org.eclipse.swt.widgets.Control;
27 import org.eclipse.swt.widgets.Display;
28 import org.eclipse.swt.widgets.Item;
29
30 /**
31  * This is the base for all editor implementations of Viewers. ColumnViewer
32  * implementators have to subclass this class and implement the missing methods
33  *
34  * @since 3.3
35  * @see TableViewerEditor
36  * @see TreeViewerEditor
37  */

38 public abstract class ColumnViewerEditor {
39     private CellEditor cellEditor;
40
41     private ICellEditorListener cellEditorListener;
42
43     private FocusListener focusListener;
44
45     private MouseListener mouseListener;
46
47     private ColumnViewer viewer;
48
49     private TraverseListener tabeditingListener;
50
51     private int activationTime;
52
53     private ViewerCell cell;
54
55     private ColumnViewerEditorActivationEvent activationEvent;
56
57     private ListenerList editorActivationListener;
58
59     private ColumnViewerEditorActivationStrategy editorActivationStrategy;
60
61     /**
62      * Tabbing from cell to cell is turned off
63      */

64     public static final int DEFAULT = 1;
65
66     /**
67      * Should if the end of the row is reach started from the start/end of the
68      * row below/above
69      */

70     public static final int TABBING_MOVE_TO_ROW_NEIGHBOR = 1 << 1;
71
72     /**
73      * Should if the end of the row is reach started from the beginning in the
74      * same row
75      */

76     public static final int TABBING_CYCLE_IN_ROW = 1 << 2;
77
78     /**
79      * Support tabbing to Cell above/below the current cell
80      */

81     public static final int TABBING_VERTICAL = 1 << 3;
82
83     /**
84      * Should tabbing from column to column with in one row be supported
85      */

86     public static final int TABBING_HORIZONTAL = 1 << 4;
87
88     /**
89      * Style mask used to enable keyboard activation
90      */

91     public static final int KEYBOARD_ACTIVATION = 1 << 5;
92
93     private int feature;
94
95     /**
96      * @param viewer
97      * the viewer this editor is attached to
98      * @param editorActivationStrategy
99      * the strategy used to decide about editor activation
100      * @param feature
101      * bit mask controlling the editor
102      * <ul>
103      * <li>{@link ColumnViewerEditor#DEFAULT}</li>
104      * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
105      * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
106      * <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
107      * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
108      * </ul>
109      */

110     protected ColumnViewerEditor(ColumnViewer viewer,
111             ColumnViewerEditorActivationStrategy editorActivationStrategy,
112             int feature) {
113         this.viewer = viewer;
114         this.editorActivationStrategy = editorActivationStrategy;
115         if ((feature & KEYBOARD_ACTIVATION) == KEYBOARD_ACTIVATION) {
116             this.editorActivationStrategy
117                     .setEnableEditorActivationWithKeyboard(true);
118         }
119         this.feature = feature;
120         initCellEditorListener();
121     }
122
123     private void initCellEditorListener() {
124         cellEditorListener = new ICellEditorListener() {
125             public void editorValueChanged(boolean oldValidState,
126                     boolean newValidState) {
127                 // Ignore.
128
}
129
130             public void cancelEditor() {
131                 ColumnViewerEditor.this.cancelEditing();
132             }
133
134             public void applyEditorValue() {
135                 ColumnViewerEditor.this.applyEditorValue();
136             }
137         };
138     }
139
140     void activateCellEditor() {
141
142         ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex());
143         Object JavaDoc element = cell.getElement();
144
145         if (part != null && part.getEditingSupport() != null
146                 && part.getEditingSupport().canEdit(element)) {
147
148             cellEditor = part.getEditingSupport().getCellEditor(element);
149             if (cellEditor != null) {
150                 if (editorActivationListener != null
151                         && !editorActivationListener.isEmpty()) {
152                     Object JavaDoc[] ls = editorActivationListener.getListeners();
153                     for (int i = 0; i < ls.length; i++) {
154
155                         if (activationEvent.cancel) {
156                             return;
157                         }
158
159                         ((ColumnViewerEditorActivationListener) ls[i])
160                                 .beforeEditorActivated(activationEvent);
161                     }
162                 }
163
164                 updateFocusCell(cell, activationEvent);
165
166                 cellEditor.addListener(cellEditorListener);
167                 part.getEditingSupport().initializeCellEditorValue(cellEditor,
168                         cell);
169
170                 // Tricky flow of control here:
171
// activate() can trigger callback to cellEditorListener which
172
// will clear cellEditor
173
// so must get control first, but must still call activate()
174
// even if there is no control.
175
final Control control = cellEditor.getControl();
176                 cellEditor.activate(activationEvent);
177                 if (control == null) {
178                     return;
179                 }
180                 setLayoutData(cellEditor.getLayoutData());
181                 setEditor(control, (Item) cell.getItem(), cell.getColumnIndex());
182                 cellEditor.setFocus();
183
184                 if( cellEditor.dependsOnExternalFocusListener() ) {
185                     if (focusListener == null) {
186                         focusListener = new FocusAdapter() {
187                             public void focusLost(FocusEvent e) {
188                                 applyEditorValue();
189                             }
190                         };
191                     }
192                     control.addFocusListener(focusListener);
193                 }
194
195                 mouseListener = new MouseAdapter() {
196                     public void mouseDown(MouseEvent e) {
197                         // time wrap?
198
// check for expiration of doubleClickTime
199
if (e.time <= activationTime) {
200                             control.removeMouseListener(mouseListener);
201                             cancelEditing();
202                             handleDoubleClickEvent();
203                         } else if (mouseListener != null) {
204                             control.removeMouseListener(mouseListener);
205                         }
206                     }
207                 };
208                 control.addMouseListener(mouseListener);
209
210                 if (tabeditingListener == null) {
211                     tabeditingListener = new TraverseListener() {
212
213                         public void keyTraversed(TraverseEvent e) {
214                             if ((feature & DEFAULT) != DEFAULT) {
215                                 processTraverseEvent(cell.getColumnIndex(),
216                                         viewer.getViewerRowFromItem(cell
217                                                 .getItem()), e);
218                             }
219                         }
220                     };
221                 }
222
223                 control.addTraverseListener(tabeditingListener);
224
225                 if (editorActivationListener != null
226                         && !editorActivationListener.isEmpty()) {
227                     Object JavaDoc[] ls = editorActivationListener.getListeners();
228                     for (int i = 0; i < ls.length; i++) {
229                         ((ColumnViewerEditorActivationListener) ls[i])
230                                 .afterEditorActivated(activationEvent);
231                     }
232                 }
233             }
234         }
235     }
236
237     /**
238      * Applies the current value and deactivates the currently active cell
239      * editor.
240      */

241     void applyEditorValue() {
242         CellEditor c = this.cellEditor;
243         if (c != null && this.cell != null) {
244             // null out cell editor before calling save
245
// in case save results in applyEditorValue being re-entered
246
// see 1GAHI8Z: ITPUI:ALL - How to code event notification when
247
// using cell editor ?
248
ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent(
249                     cell);
250             if (editorActivationListener != null
251                     && !editorActivationListener.isEmpty()) {
252                 Object JavaDoc[] ls = editorActivationListener.getListeners();
253                 for (int i = 0; i < ls.length; i++) {
254
255                     ((ColumnViewerEditorActivationListener) ls[i])
256                             .beforeEditorDeactivated(tmp);
257                 }
258             }
259
260             Item t = (Item) this.cell.getItem();
261
262             // don't null out table item -- same item is still selected
263
if (t != null && !t.isDisposed()) {
264                 saveEditorValue(c);
265             }
266                         
267             setEditor(null, null, 0);
268             c.removeListener(cellEditorListener);
269             Control control = c.getControl();
270             if (control != null) {
271                 if (mouseListener != null) {
272                     control.removeMouseListener(mouseListener);
273                     // Clear the instance not needed any more
274
mouseListener = null;
275                 }
276                 if (focusListener != null) {
277                     control.removeFocusListener(focusListener);
278                 }
279
280                 if (tabeditingListener != null) {
281                     control.removeTraverseListener(tabeditingListener);
282                 }
283             }
284             c.deactivate();
285
286             if (editorActivationListener != null
287                     && !editorActivationListener.isEmpty()) {
288                 Object JavaDoc[] ls = editorActivationListener.getListeners();
289                 for (int i = 0; i < ls.length; i++) {
290                     ((ColumnViewerEditorActivationListener) ls[i])
291                             .afterEditorDeactivated(tmp);
292                 }
293             }
294             
295             this.cellEditor = null;
296             this.activationEvent = null;
297             this.cell = null;
298         }
299     }
300
301     /**
302      * Cancel editing
303      */

304     void cancelEditing() {
305         if (cellEditor != null) {
306             ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent(
307                     cell);
308             if (editorActivationListener != null
309                     && !editorActivationListener.isEmpty()) {
310                 Object JavaDoc[] ls = editorActivationListener.getListeners();
311                 for (int i = 0; i < ls.length; i++) {
312
313                     ((ColumnViewerEditorActivationListener) ls[i])
314                             .beforeEditorDeactivated(tmp);
315                 }
316             }
317
318             setEditor(null, null, 0);
319             cellEditor.removeListener(cellEditorListener);
320
321             Control control = cellEditor.getControl();
322             if (control != null) {
323                 if (mouseListener != null) {
324                     control.removeMouseListener(mouseListener);
325                     // Clear the instance not needed any more
326
mouseListener = null;
327                 }
328                 if (focusListener != null) {
329                     control.removeFocusListener(focusListener);
330                 }
331
332                 if (tabeditingListener != null) {
333                     control.removeTraverseListener(tabeditingListener);
334                 }
335             }
336
337             CellEditor oldEditor = cellEditor;
338             oldEditor.deactivate();
339
340             if (editorActivationListener != null
341                     && !editorActivationListener.isEmpty()) {
342                 Object JavaDoc[] ls = editorActivationListener.getListeners();
343                 for (int i = 0; i < ls.length; i++) {
344                     ((ColumnViewerEditorActivationListener) ls[i])
345                             .afterEditorDeactivated(tmp);
346                 }
347             }
348             
349             this.cellEditor = null;
350             this.activationEvent = null;
351             this.cell = null;
352         }
353     }
354
355     /**
356      * Enable the editor by mouse down
357      *
358      * @param event
359      */

360     void handleEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
361         if (editorActivationStrategy.isEditorActivationEvent(event)) {
362             if (cellEditor != null) {
363                 applyEditorValue();
364             }
365
366             this.cell = (ViewerCell) event.getSource();
367
368             activationEvent = event;
369             activationTime = event.time
370                     + Display.getCurrent().getDoubleClickTime();
371
372             activateCellEditor();
373         }
374     }
375
376     private void saveEditorValue(CellEditor cellEditor) {
377         ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex());
378
379         if (part != null && part.getEditingSupport() != null) {
380             part.getEditingSupport().saveCellEditorValue(cellEditor, cell);
381         }
382     }
383
384     /**
385      * Return whether there is an active cell editor.
386      *
387      * @return <code>true</code> if there is an active cell editor; otherwise
388      * <code>false</code> is returned.
389      */

390     boolean isCellEditorActive() {
391         return cellEditor != null;
392     }
393
394     void handleDoubleClickEvent() {
395         viewer.fireDoubleClick(new DoubleClickEvent(viewer, viewer
396                 .getSelection()));
397         viewer.fireOpen(new OpenEvent(viewer, viewer.getSelection()));
398     }
399
400     /**
401      * Adds the given listener, it is to be notified when the cell editor is
402      * activated or deactivated.
403      *
404      * @param listener
405      * the listener to add
406      */

407     public void addEditorActivationListener(
408             ColumnViewerEditorActivationListener listener) {
409         if (editorActivationListener == null) {
410             editorActivationListener = new ListenerList();
411         }
412         editorActivationListener.add(listener);
413     }
414
415     /**
416      * Removes the given listener.
417      *
418      * @param listener
419      * the listener to remove
420      */

421     public void removeEditorActivationListener(
422             ColumnViewerEditorActivationListener listener) {
423         if (editorActivationListener != null) {
424             editorActivationListener.remove(listener);
425         }
426     }
427
428     /**
429      * Process the traverse event and opens the next available editor depending
430      * of the implemented strategy. The default implementation uses the style
431      * constants
432      * <ul>
433      * <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li>
434      * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li>
435      * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li>
436      * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li>
437      * </ul>
438      *
439      * <p>
440      * Subclasses may overwrite to implement their custom logic to edit the next
441      * cell
442      * </p>
443      *
444      * @param columnIndex
445      * the index of the current column
446      * @param row
447      * the current row - may only be used for the duration of this
448      * method call
449      * @param event
450      * the traverse event
451      */

452     protected void processTraverseEvent(int columnIndex, ViewerRow row,
453             TraverseEvent event) {
454
455         ViewerCell cell2edit = null;
456
457         if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
458             event.doit = false;
459
460             if ((event.stateMask & SWT.CTRL) == SWT.CTRL
461                     && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
462                 cell2edit = searchCellAboveBelow(row, viewer, columnIndex, true);
463             } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
464                 cell2edit = searchPreviousCell(row, viewer, columnIndex,
465                         columnIndex);
466             }
467         } else if (event.detail == SWT.TRAVERSE_TAB_NEXT) {
468             event.doit = false;
469
470             if ((event.stateMask & SWT.CTRL) == SWT.CTRL
471                     && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
472                 cell2edit = searchCellAboveBelow(row, viewer, columnIndex,
473                         false);
474             } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
475                 cell2edit = searchNextCell(row, viewer, columnIndex,
476                         columnIndex);
477             }
478         }
479
480         if (cell2edit != null) {
481
482             viewer.getControl().setRedraw(false);
483             ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent(
484                     cell2edit, event);
485             viewer.triggerEditorActivationEvent(acEvent);
486             viewer.getControl().setRedraw(true);
487         }
488     }
489
490     private ViewerCell searchCellAboveBelow(ViewerRow row, ColumnViewer viewer,
491             int columnIndex, boolean above) {
492         ViewerCell rv = null;
493
494         ViewerRow newRow = null;
495
496         if (above) {
497             newRow = row.getNeighbor(ViewerRow.ABOVE, false);
498         } else {
499             newRow = row.getNeighbor(ViewerRow.BELOW, false);
500         }
501
502         if (newRow != null) {
503             ViewerColumn column = viewer.getViewerColumn(columnIndex);
504             if (column != null
505                     && column.getEditingSupport() != null
506                     && column.getEditingSupport().canEdit(
507                             newRow.getItem().getData())) {
508                 rv = newRow.getCell(columnIndex);
509             } else {
510                 rv = searchCellAboveBelow(newRow, viewer, columnIndex, above);
511             }
512         }
513
514         return rv;
515     }
516
517     private ViewerCell searchPreviousCell(ViewerRow row, ColumnViewer viewer,
518             int columnIndex, int startIndex) {
519         ViewerCell rv = null;
520
521         if (columnIndex - 1 >= 0) {
522             ViewerColumn column = viewer.getViewerColumn(columnIndex - 1);
523             if (column != null
524                     && column.getEditingSupport() != null
525                     && column.getEditingSupport().canEdit(
526                             row.getItem().getData())) {
527                 rv = row.getCell(columnIndex - 1);
528             } else {
529                 rv = searchPreviousCell(row, viewer, columnIndex - 1,
530                         startIndex);
531             }
532         } else {
533             if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
534                 // Check that we don't get into endless loop
535
if (columnIndex - 1 != startIndex) {
536                     // Don't subtract -1 from getColumnCount() we need to
537
// start in the virtual column
538
// next to it
539
rv = searchPreviousCell(row, viewer, row.getColumnCount(),
540                             startIndex);
541                 }
542             } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
543                 ViewerRow rowAbove = row.getNeighbor(ViewerRow.ABOVE, false);
544                 if (rowAbove != null) {
545                     rv = searchPreviousCell(rowAbove, viewer, rowAbove
546                             .getColumnCount(), startIndex);
547                 }
548             }
549         }
550
551         return rv;
552     }
553
554     private ViewerCell searchNextCell(ViewerRow row, ColumnViewer viewer,
555             int columnIndex, int startIndex) {
556         ViewerCell rv = null;
557
558         if (columnIndex + 1 < row.getColumnCount()) {
559             ViewerColumn column = viewer.getViewerColumn(columnIndex + 1);
560             if (column != null
561                     && column.getEditingSupport() != null
562                     && column.getEditingSupport().canEdit(
563                             row.getItem().getData())) {
564                 rv = row.getCell(columnIndex + 1);
565             } else {
566                 rv = searchNextCell(row, viewer, columnIndex + 1, startIndex);
567             }
568         } else {
569             if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
570                 // Check that we don't get into endless loop
571
if (columnIndex + 1 != startIndex) {
572                     // Start from -1 from the virtual column before the
573
// first one
574
rv = searchNextCell(row, viewer, -1, startIndex);
575                 }
576             } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
577                 ViewerRow rowBelow = row.getNeighbor(ViewerRow.BELOW, false);
578                 if (rowBelow != null) {
579                     rv = searchNextCell(rowBelow, viewer, -1, startIndex);
580                 }
581             }
582         }
583
584         return rv;
585     }
586
587     /**
588      * Position the editor inside the control
589      *
590      * @param w
591      * the editor control
592      * @param item
593      * the item (row) in which the editor is drawn in
594      * @param fColumnNumber
595      * the column number in which the editor is shown
596      */

597     protected abstract void setEditor(Control w, Item item, int fColumnNumber);
598
599     /**
600      * set the layout data for the editor
601      *
602      * @param layoutData
603      * the layout data used when editor is displayed
604      */

605     protected abstract void setLayoutData(CellEditor.LayoutData layoutData);
606
607     /**
608      * @param focusCell
609      * updates the cell with the current input focus
610      * @param event
611      * the event requesting to update the focusCell
612      */

613     protected abstract void updateFocusCell(ViewerCell focusCell,
614             ColumnViewerEditorActivationEvent event);
615
616     /**
617      * @return the cell currently holding the focus if no cell has the focus or
618      * the viewer implementation doesn't support <code>null</code> is
619      * returned
620      *
621      */

622     public ViewerCell getFocusCell() {
623         return null;
624     }
625
626     /**
627      * @return the viewer working for
628      */

629     protected ColumnViewer getViewer() {
630         return viewer;
631     }
632 }
Popular Tags