KickJava   Java API By Example, From Geeks To Geeks.

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


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> - initial API and implementation; bug 153993
11  * fix in bug 163317, 151295, 167323, 167858, 184346, 187826, 200558,201002
12  *******************************************************************************/

13
14 package org.eclipse.jface.viewers;
15
16
17 import org.eclipse.core.runtime.Assert;
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.core.runtime.Status;
20 import org.eclipse.jface.internal.InternalPolicy;
21 import org.eclipse.jface.util.Policy;
22 import org.eclipse.swt.events.MouseAdapter;
23 import org.eclipse.swt.events.MouseEvent;
24 import org.eclipse.swt.graphics.Point;
25 import org.eclipse.swt.widgets.Control;
26 import org.eclipse.swt.widgets.Item;
27 import org.eclipse.swt.widgets.Widget;
28
29 /**
30  * The ColumnViewer is the abstract superclass of viewers that have columns
31  * (e.g., AbstractTreeViewer and AbstractTableViewer). Concrete subclasses of
32  * {@link ColumnViewer} should implement a matching concrete subclass of
33  * {@link ViewerColumn}.
34  *
35  * <strong> This class is not intended to be subclassed outside of the JFace
36  * viewers framework.</strong>
37  *
38  * @since 3.3
39  *
40  */

41 public abstract class ColumnViewer extends StructuredViewer {
42     private CellEditor[] cellEditors;
43
44     private ICellModifier cellModifier;
45
46     private String JavaDoc[] columnProperties;
47
48     /**
49      * The cell is a cached viewer cell used for refreshing.
50      */

51     private ViewerCell cell = new ViewerCell(null, 0, null);
52
53     private ColumnViewerEditor viewerEditor;
54
55     /* package */ boolean busy;
56     /* package */ boolean logWhenBusy = true; // initially true, set to false after logging for the first time
57

58     /**
59      * Create a new instance of the receiver.
60      */

61     public ColumnViewer() {
62
63     }
64
65     /* package */ boolean isBusy() {
66         if (busy) {
67             if (logWhenBusy) {
68                 String JavaDoc message = "Ignored reentrant call while viewer is busy."; //$NON-NLS-1$
69
if (!InternalPolicy.DEBUG_LOG_REENTRANT_VIEWER_CALLS) {
70                     // stop logging after the first
71
logWhenBusy = false;
72                     message += " This is only logged once per viewer instance," + //$NON-NLS-1$
73
" but similar calls will still be ignored."; //$NON-NLS-1$
74
}
75                 Policy.getLog().log(
76                     new Status(
77                         IStatus.WARNING,
78                         Policy.JFACE,
79                         message, new RuntimeException JavaDoc()));
80             }
81             return true;
82         }
83         return false;
84     }
85
86     protected void hookControl(Control control) {
87         super.hookControl(control);
88         viewerEditor = createViewerEditor();
89         hookEditingSupport(control);
90     }
91
92     /**
93      * Hook up the editing support. Subclasses may override.
94      *
95      * @param control
96      * the control you want to hook on
97      */

98     protected void hookEditingSupport(Control control) {
99         // Needed for backwards comp with AbstractTreeViewer and TableTreeViewer
100
// who are not hooked this way others may already overwrite and provide
101
// their
102
// own impl
103
if (viewerEditor != null) {
104             control.addMouseListener(new MouseAdapter() {
105                 public void mouseDown(MouseEvent e) {
106                     // Workaround for bug 185817
107
if( e.count != 2 ) {
108                         handleMouseDown(e);
109                     }
110                 }
111
112                 public void mouseDoubleClick(MouseEvent e) {
113                     handleMouseDown(e);
114                 }
115             });
116         }
117     }
118
119     /**
120      * Creates the viewer editor used for editing cell contents. To be
121      * implemented by subclasses.
122      *
123      * @return the editor, or <code>null</code> if this viewer does not
124      * support editing cell contents.
125      */

126     protected abstract ColumnViewerEditor createViewerEditor();
127
128     /**
129      * Returns the viewer cell at the given widget-relative coordinates, or
130      * <code>null</code> if there is no cell at that location
131      *
132      * @param point
133      * the widget-relative coordinates
134      * @return the cell or <code>null</code> if no cell is found at the given
135      * point
136      */

137     ViewerCell getCell(Point point) {
138         ViewerRow row = getViewerRow(point);
139         if (row != null) {
140             return row.getCell(point);
141         }
142
143         return null;
144     }
145
146     /**
147      * Returns the viewer row at the given widget-relative coordinates.
148      *
149      * @param point
150      * the widget-relative coordinates of the viewer row
151      * @return ViewerRow the row or <code>null</code> if no row is found at
152      * the given coordinates
153      */

154     protected ViewerRow getViewerRow(Point point) {
155         Item item = getItemAt(point);
156
157         if (item != null) {
158             return getViewerRowFromItem(item);
159         }
160
161         return null;
162     }
163
164
165
166     /**
167      * Returns a {@link ViewerRow} associated with the given row widget. Implementations
168      * may re-use the same instance for different row widgets; callers can only use the viewer
169      * row locally and until the next call to this method.
170      *
171      * @param item the row widget
172      * @return ViewerRow a viewer row object
173      */

174     protected abstract ViewerRow getViewerRowFromItem(Widget item);
175
176     /**
177      * Returns the column widget at the given column index.
178      *
179      * @param columnIndex
180      * the column index
181      * @return Widget the column widget
182      */

183     protected abstract Widget getColumnViewerOwner(int columnIndex);
184
185     /**
186      * Returns the viewer column for the given column index.
187      *
188      * @param columnIndex
189      * the column index
190      * @return the viewer column at the given index, or <code>null</code> if
191      * there is none for the given index
192      */

193     /* package */ViewerColumn getViewerColumn(final int columnIndex) {
194
195         ViewerColumn viewer;
196         Widget columnOwner = getColumnViewerOwner(columnIndex);
197
198         if (columnOwner == null) {
199             return null;
200         }
201
202         viewer = (ViewerColumn) columnOwner
203                 .getData(ViewerColumn.COLUMN_VIEWER_KEY);
204
205         if (viewer == null) {
206             viewer = createViewerColumn(columnOwner, CellLabelProvider
207                     .createViewerLabelProvider(this, getLabelProvider()));
208             setupEditingSupport(columnIndex, viewer);
209         }
210
211         if (viewer.getEditingSupport() == null && getCellModifier() != null) {
212             setupEditingSupport(columnIndex, viewer);
213         }
214
215         return viewer;
216     }
217
218     /**
219      * Sets up editing support for the given column based on the "old" cell
220      * editor API.
221      *
222      * @param columnIndex
223      * @param viewer
224      */

225     private void setupEditingSupport(final int columnIndex, ViewerColumn viewer) {
226         if (getCellModifier() != null) {
227             viewer.setEditingSupport(new EditingSupport(this) {
228
229                 /*
230                  * (non-Javadoc)
231                  *
232                  * @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object)
233                  */

234                 public boolean canEdit(Object JavaDoc element) {
235                     Object JavaDoc[] properties = getColumnProperties();
236
237                     if( columnIndex < properties.length ) {
238                         return getCellModifier().canModify(element,
239                                 (String JavaDoc) getColumnProperties()[columnIndex]);
240                     }
241
242                     return false;
243                 }
244
245                 /*
246                  * (non-Javadoc)
247                  *
248                  * @see org.eclipse.jface.viewers.EditingSupport#getCellEditor(java.lang.Object)
249                  */

250                 public CellEditor getCellEditor(Object JavaDoc element) {
251                     CellEditor[] editors = getCellEditors();
252                     if( columnIndex < editors.length ) {
253                         return getCellEditors()[columnIndex];
254                     }
255                     return null;
256                 }
257
258                 /*
259                  * (non-Javadoc)
260                  *
261                  * @see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object)
262                  */

263                 public Object JavaDoc getValue(Object JavaDoc element) {
264                     Object JavaDoc[] properties = getColumnProperties();
265
266                     if( columnIndex < properties.length ) {
267                         return getCellModifier().getValue(element,
268                                 (String JavaDoc) getColumnProperties()[columnIndex]);
269                     }
270
271                     return null;
272                 }
273
274                 /*
275                  * (non-Javadoc)
276                  *
277                  * @see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object,
278                  * java.lang.Object)
279                  */

280                 public void setValue(Object JavaDoc element, Object JavaDoc value) {
281                     Object JavaDoc[] properties = getColumnProperties();
282
283                     if( columnIndex < properties.length ) {
284                         getCellModifier().modify(findItem(element),
285                                 (String JavaDoc) getColumnProperties()[columnIndex], value);
286                     }
287                 }
288
289                 boolean isLegacySupport() {
290                     return true;
291                 }
292             });
293         }
294     }
295
296     /**
297      * Creates a generic viewer column for the given column widget, based on the
298      * given label provider.
299      *
300      * @param columnOwner
301      * the column widget
302      * @param labelProvider
303      * the label provider to use for the column
304      * @return ViewerColumn the viewer column
305      */

306     private ViewerColumn createViewerColumn(Widget columnOwner,
307             CellLabelProvider labelProvider) {
308         ViewerColumn column = new ViewerColumn(this, columnOwner) {
309         };
310         column.setLabelProvider(labelProvider, false);
311         return column;
312     }
313
314     /**
315      * Update the cached cell object with the given row and column. Be careful not
316      * to hold on to element objects longer than required. It is good practice to
317      * call updateCell(null, 0, null) to clear references immediately after using
318      * the cached cell object. (See bug 201280 for an example case where this happened.)
319      *
320      * @param rowItem
321      * @param column
322      * @return ViewerCell
323      */

324     /* package */ViewerCell updateCell(ViewerRow rowItem, int column, Object JavaDoc element) {
325         cell.update(rowItem, column, element);
326         return cell;
327     }
328
329     /**
330      * Returns the {@link Item} at the given widget-relative coordinates, or
331      * <code>null</code> if there is no item at the given coordinates.
332      *
333      * @param point
334      * the widget-relative coordinates
335      * @return the {@link Item} at the coordinates or <code>null</code> if
336      * there is no item at the given coordinates
337      */

338     protected abstract Item getItemAt(Point point);
339
340     /*
341      * (non-Javadoc)
342      *
343      * @see org.eclipse.jface.viewers.StructuredViewer#getItem(int, int)
344      */

345     protected Item getItem(int x, int y) {
346         return getItemAt(getControl().toControl(x, y));
347     }
348
349     /**
350      * The column viewer implementation of this <code>Viewer</code> framework
351      * method ensures that the given label provider is an instance of
352      * <code>ITableLabelProvider</code>, <code>ILabelProvider</code>, or
353      * <code>CellLabelProvider</code>.
354      * <p>
355      * If the label provider is an {@link ITableLabelProvider}, then it
356      * provides a separate label text and image for each column. Implementers of
357      * <code>ITableLabelProvider</code> may also implement
358      * {@link ITableColorProvider} and/or {@link ITableFontProvider} to provide
359      * colors and/or fonts.
360      * </p>
361      * <p>
362      * If the label provider is an <code>ILabelProvider</code>, then it
363      * provides only the label text and image for the first column, and any
364      * remaining columns are blank. Implementers of <code>ILabelProvider</code>
365      * may also implement {@link IColorProvider} and/or {@link IFontProvider} to
366      * provide colors and/or fonts.
367      * </p>
368      *
369      */

370     public void setLabelProvider(IBaseLabelProvider labelProvider) {
371         Assert.isTrue(labelProvider instanceof ITableLabelProvider
372                 || labelProvider instanceof ILabelProvider
373                 || labelProvider instanceof CellLabelProvider);
374         updateColumnParts(labelProvider);// Reset the label providers in the
375
// columns
376
super.setLabelProvider(labelProvider);
377     }
378
379     /**
380      * Clear the viewer parts for the columns
381      */

382     private void updateColumnParts(IBaseLabelProvider labelProvider) {
383         ViewerColumn column;
384         int i = 0;
385
386         while ((column = getViewerColumn(i++)) != null) {
387             column.setLabelProvider(CellLabelProvider
388                     .createViewerLabelProvider(this, labelProvider), false);
389         }
390     }
391
392     /**
393      * Cancels a currently active cell editor if one is active. All changes
394      * already done in the cell editor are lost.
395      *
396      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
397      */

398     public void cancelEditing() {
399         if (viewerEditor != null) {
400             viewerEditor.cancelEditing();
401         }
402     }
403
404     /**
405      * Apply the value of the active cell editor if one is active.
406      *
407      * @since 3.3
408      */

409     protected void applyEditorValue() {
410         if (viewerEditor != null) {
411             viewerEditor.applyEditorValue();
412         }
413     }
414
415     /**
416      * Starts editing the given element at the given column index.
417      *
418      * @param element
419      * the model element
420      * @param column
421      * the column index
422      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
423      */

424     public void editElement(Object JavaDoc element, int column) {
425         if (viewerEditor != null) {
426             try {
427                 getControl().setRedraw(false);
428                 // Set the selection at first because in Tree's
429
// the element might not be materialized
430
setSelection(new StructuredSelection(element),true);
431
432                 Widget item = findItem(element);
433                 if (item != null) {
434                     ViewerRow row = getViewerRowFromItem(item);
435                     if (row != null) {
436                         ViewerCell cell = row.getCell(column);
437                         if (cell != null) {
438                             triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(
439                                     cell));
440                         }
441                     }
442                 }
443             } finally {
444                 getControl().setRedraw(true);
445             }
446         }
447     }
448
449     /**
450      * Return the CellEditors for the receiver, or <code>null</code> if no
451      * cell editors are set.
452      * <p>
453      * Since 3.3, an alternative API is available, see
454      * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
455      * flexible way of editing values in a column viewer.
456      * </p>
457      *
458      * @return CellEditor[]
459      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
460      * @see ViewerColumn#setEditingSupport(EditingSupport)
461      * @see EditingSupport
462      */

463     public CellEditor[] getCellEditors() {
464         return cellEditors;
465     }
466
467     /**
468      * Returns the cell modifier of this viewer, or <code>null</code> if none
469      * has been set.
470      *
471      * <p>
472      * Since 3.3, an alternative API is available, see
473      * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
474      * flexible way of editing values in a column viewer.
475      * </p>
476      *
477      * @return the cell modifier, or <code>null</code>
478      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
479      * @see ViewerColumn#setEditingSupport(EditingSupport)
480      * @see EditingSupport
481      */

482     public ICellModifier getCellModifier() {
483         return cellModifier;
484     }
485
486     /**
487      * Returns the column properties of this table viewer. The properties must
488      * correspond with the columns of the table control. They are used to
489      * identify the column in a cell modifier.
490      *
491      * <p>
492      * Since 3.3, an alternative API is available, see
493      * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
494      * flexible way of editing values in a column viewer.
495      * </p>
496      *
497      * @return the list of column properties
498      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
499      * @see ViewerColumn#setEditingSupport(EditingSupport)
500      * @see EditingSupport
501      */

502     public Object JavaDoc[] getColumnProperties() {
503         return columnProperties;
504     }
505
506     /**
507      * Returns whether there is an active cell editor.
508      *
509      * <p>
510      * Since 3.3, an alternative API is available, see
511      * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
512      * flexible way of editing values in a column viewer.
513      * </p>
514      *
515      * @return <code>true</code> if there is an active cell editor, and
516      * <code>false</code> otherwise
517      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
518      * @see ViewerColumn#setEditingSupport(EditingSupport)
519      * @see EditingSupport
520      */

521     public boolean isCellEditorActive() {
522         if (viewerEditor != null) {
523             return viewerEditor.isCellEditorActive();
524         }
525         return false;
526     }
527
528     public void refresh(Object JavaDoc element) {
529         if (isBusy())
530             return;
531
532         if( isCellEditorActive() ) {
533             cancelEditing();
534         }
535
536         super.refresh(element);
537     }
538
539     public void refresh(Object JavaDoc element, boolean updateLabels) {
540         if (isBusy())
541             return;
542
543         if( isCellEditorActive() ) {
544             cancelEditing();
545         }
546
547         super.refresh(element, updateLabels);
548     }
549
550     public void update(Object JavaDoc element, String JavaDoc[] properties) {
551         if (isBusy())
552             return;
553         super.update(element, properties);
554     }
555
556     /**
557      * Sets the cell editors of this column viewer. If editing is not supported
558      * by this viewer the call simply has no effect.
559      *
560      * <p>
561      * Since 3.3, an alternative API is available, see
562      * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
563      * flexible way of editing values in a column viewer.
564      * </p>
565      *
566      * @param editors
567      * the list of cell editors
568      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
569      * @see ViewerColumn#setEditingSupport(EditingSupport)
570      * @see EditingSupport
571      */

572     public void setCellEditors(CellEditor[] editors) {
573         this.cellEditors = editors;
574     }
575
576     /**
577      * Sets the cell modifier for this column viewer. This method does nothing
578      * if editing is not supported by this viewer.
579      *
580      * <p>
581      * Since 3.3, an alternative API is available, see
582      * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
583      * flexible way of editing values in a column viewer.
584      * </p>
585      *
586      * @param modifier
587      * the cell modifier
588      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
589      * @see ViewerColumn#setEditingSupport(EditingSupport)
590      * @see EditingSupport
591      */

592     public void setCellModifier(ICellModifier modifier) {
593         this.cellModifier = modifier;
594     }
595
596     /**
597      * Sets the column properties of this column viewer. The properties must
598      * correspond with the columns of the control. They are used to identify the
599      * column in a cell modifier. If editing is not supported by this viewer the
600      * call simply has no effect.
601      *
602      * <p>
603      * Since 3.3, an alternative API is available, see
604      * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
605      * flexible way of editing values in a column viewer.
606      * </p>
607      *
608      * @param columnProperties
609      * the list of column properties
610      * @since 3.1 (in subclasses, added in 3.3 to abstract class)
611      * @see ViewerColumn#setEditingSupport(EditingSupport)
612      * @see EditingSupport
613      */

614     public void setColumnProperties(String JavaDoc[] columnProperties) {
615         this.columnProperties = columnProperties;
616     }
617
618     /**
619      * Returns the number of columns contained in the receiver. If no columns
620      * were created by the programmer, this value is zero, despite the fact that
621      * visually, one column of items may be visible. This occurs when the
622      * programmer uses the column viewer like a list, adding elements but never
623      * creating a column.
624      *
625      * @return the number of columns
626      *
627      * @since 3.3
628      */

629     protected abstract int doGetColumnCount();
630
631     /**
632      * Returns the label provider associated with the column at the given index
633      * or <code>null</code> if no column with this index is known.
634      *
635      * @param columnIndex
636      * the column index
637      * @return the label provider associated with the column or
638      * <code>null</code> if no column with this index is known
639      *
640      * @since 3.3
641      */

642     public CellLabelProvider getLabelProvider(int columnIndex) {
643         ViewerColumn column = getViewerColumn(columnIndex);
644         if (column != null) {
645             return column.getLabelProvider();
646         }
647         return null;
648     }
649
650     private void handleMouseDown(MouseEvent e) {
651         ViewerCell cell = getCell(new Point(e.x, e.y));
652
653         if (cell != null) {
654             triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(
655                     cell, e));
656         }
657     }
658
659     /**
660      * Invoking this method fires an editor activation event which tries to
661      * enable the editor but before this event is passed to
662      * {@link ColumnViewerEditorActivationStrategy} to see if this event should
663      * really trigger editor activation
664      *
665      * @param event
666      * the activation event
667      */

668     protected void triggerEditorActivationEvent(
669             ColumnViewerEditorActivationEvent event) {
670         viewerEditor.handleEditorActivationEvent(event);
671     }
672
673     /**
674      * @param columnViewerEditor
675      * the new column viewer editor
676      */

677     public void setColumnViewerEditor(ColumnViewerEditor columnViewerEditor) {
678         Assert.isNotNull(viewerEditor);
679         this.viewerEditor = columnViewerEditor;
680     }
681
682     /**
683      * @return the currently attached viewer editor
684      */

685     public ColumnViewerEditor getColumnViewerEditor() {
686         return viewerEditor;
687     }
688
689     protected Object JavaDoc[] getRawChildren(Object JavaDoc parent) {
690         boolean oldBusy = busy;
691         busy = true;
692         try {
693             return super.getRawChildren(parent);
694         } finally {
695             busy = oldBusy;
696         }
697     }
698     
699     /**
700      * Clear all cell-editors setup for backwards compatibility in
701      * {@link #setupEditingSupport(int, ViewerColumn)}. This has to be done
702      * whenever a column is disposed because the index cached when the anonymous
703      * class is created has to be readjusted
704      */

705     void clearLegacyEditingSetup() {
706         int count = doGetColumnCount();
707
708         for( int i = 0; i < count || i == 0; i++ ) {
709             Widget owner = getColumnViewerOwner(i);
710
711             if( owner != null && ! owner.isDisposed() ) {
712                 ViewerColumn column = (ViewerColumn) owner.getData(ViewerColumn.COLUMN_VIEWER_KEY);
713                 if( column != null ) {
714                     EditingSupport e = column.getEditingSupport();
715                     // Ensure that only EditingSupports are wiped that are setup
716
// for Legacy reasons
717
if (e != null && e.isLegacySupport()) {
718                         column.setEditingSupport(null);
719                     }
720                 }
721             }
722         }
723     }
724 }
Popular Tags