001: /*******************************************************************************
002: * Copyright (c) 2006, 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: * Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation; bug 153993
011: * fix in bug 163317, 151295, 167323, 167858, 184346, 187826, 201905
012: *******************************************************************************/package org.eclipse.jface.viewers;
013:
014: import org.eclipse.core.runtime.Assert;
015: import org.eclipse.core.runtime.IStatus;
016: import org.eclipse.core.runtime.Status;
017: import org.eclipse.jface.internal.InternalPolicy;
018: import org.eclipse.jface.util.Policy;
019: import org.eclipse.swt.events.MouseAdapter;
020: import org.eclipse.swt.events.MouseEvent;
021: import org.eclipse.swt.graphics.Point;
022: import org.eclipse.swt.widgets.Control;
023: import org.eclipse.swt.widgets.Item;
024: import org.eclipse.swt.widgets.Widget;
025:
026: /**
027: * The ColumnViewer is the abstract superclass of viewers that have columns
028: * (e.g., AbstractTreeViewer and AbstractTableViewer). Concrete subclasses of
029: * {@link ColumnViewer} should implement a matching concrete subclass of
030: * {@link ViewerColumn}.
031: *
032: * <strong> This class is not intended to be subclassed outside of the JFace
033: * viewers framework.</strong>
034: *
035: * @since 3.3
036: *
037: */
038: public abstract class ColumnViewer extends StructuredViewer {
039: private CellEditor[] cellEditors;
040:
041: private ICellModifier cellModifier;
042:
043: private String[] columnProperties;
044:
045: /**
046: * The cell is a cached viewer cell used for refreshing.
047: */
048: private ViewerCell cell = new ViewerCell(null, 0, null);
049:
050: private ColumnViewerEditor viewerEditor;
051:
052: /* package */boolean busy;
053: /* package */boolean logWhenBusy = true; // initially true, set to false
054:
055: // after logging for the first
056: // time
057:
058: /**
059: * Create a new instance of the receiver.
060: */
061: public ColumnViewer() {
062:
063: }
064:
065: /* package */boolean isBusy() {
066: if (busy) {
067: if (logWhenBusy) {
068: String message = "Ignored reentrant call while viewer is busy."; //$NON-NLS-1$
069: if (!InternalPolicy.DEBUG_LOG_REENTRANT_VIEWER_CALLS) {
070: // stop logging after the first
071: logWhenBusy = false;
072: message += " This is only logged once per viewer instance," + //$NON-NLS-1$
073: " but similar calls will still be ignored."; //$NON-NLS-1$
074: }
075: Policy.getLog().log(
076: new Status(IStatus.WARNING, Policy.JFACE,
077: message, new RuntimeException()));
078: }
079: return true;
080: }
081: return false;
082: }
083:
084: protected void hookControl(Control control) {
085: super .hookControl(control);
086: viewerEditor = createViewerEditor();
087: hookEditingSupport(control);
088: }
089:
090: /**
091: * Hook up the editing support. Subclasses may override.
092: *
093: * @param control
094: * the control you want to hook on
095: */
096: protected void hookEditingSupport(Control control) {
097: // Needed for backwards comp with AbstractTreeViewer and TableTreeViewer
098: // who are not hooked this way others may already overwrite and provide
099: // their
100: // own impl
101: if (viewerEditor != null) {
102: control.addMouseListener(new MouseAdapter() {
103: public void mouseDown(MouseEvent e) {
104: // Workaround for bug 185817
105: if (e.count != 2) {
106: handleMouseDown(e);
107: }
108: }
109:
110: public void mouseDoubleClick(MouseEvent e) {
111: handleMouseDown(e);
112: }
113: });
114: }
115: }
116:
117: /**
118: * Creates the viewer editor used for editing cell contents. To be
119: * implemented by subclasses.
120: *
121: * @return the editor, or <code>null</code> if this viewer does not
122: * support editing cell contents.
123: */
124: protected abstract ColumnViewerEditor createViewerEditor();
125:
126: /**
127: * Returns the viewer cell at the given widget-relative coordinates, or
128: * <code>null</code> if there is no cell at that location
129: *
130: * @param point
131: * the widget-relative coordinates
132: * @return the cell or <code>null</code> if no cell is found at the given
133: * point
134: */
135: ViewerCell getCell(Point point) {
136: ViewerRow row = getViewerRow(point);
137: if (row != null) {
138: return row.getCell(point);
139: }
140:
141: return null;
142: }
143:
144: /**
145: * Returns the viewer row at the given widget-relative coordinates.
146: *
147: * @param point
148: * the widget-relative coordinates of the viewer row
149: * @return ViewerRow the row or <code>null</code> if no row is found at
150: * the given coordinates
151: */
152: protected ViewerRow getViewerRow(Point point) {
153: Item item = getItemAt(point);
154:
155: if (item != null) {
156: return getViewerRowFromItem(item);
157: }
158:
159: return null;
160: }
161:
162: /**
163: * Returns a {@link ViewerRow} associated with the given row widget.
164: * Implementations may re-use the same instance for different row widgets;
165: * callers can only use the viewer row locally and until the next call to
166: * this method.
167: *
168: * @param item
169: * the row widget
170: * @return ViewerRow a viewer row object
171: */
172: protected abstract ViewerRow getViewerRowFromItem(Widget item);
173:
174: /**
175: * Returns the column widget at the given column index.
176: *
177: * @param columnIndex
178: * the column index
179: * @return Widget the column widget
180: */
181: protected abstract Widget getColumnViewerOwner(int columnIndex);
182:
183: /**
184: * Returns the viewer column for the given column index.
185: *
186: * @param columnIndex
187: * the column index
188: * @return the viewer column at the given index, or <code>null</code> if
189: * there is none for the given index
190: */
191: /* package */ViewerColumn getViewerColumn(final int columnIndex) {
192:
193: ViewerColumn viewer;
194: Widget columnOwner = getColumnViewerOwner(columnIndex);
195:
196: if (columnOwner == null) {
197: return null;
198: }
199:
200: viewer = (ViewerColumn) columnOwner
201: .getData(ViewerColumn.COLUMN_VIEWER_KEY);
202:
203: if (viewer == null) {
204: viewer = createViewerColumn(columnOwner,
205: CellLabelProvider.createViewerLabelProvider(this ,
206: getLabelProvider()));
207: setupEditingSupport(columnIndex, viewer);
208: }
209:
210: if (viewer.getEditingSupport() == null
211: && 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,
226: ViewerColumn viewer) {
227: if (getCellModifier() != null) {
228: viewer.setEditingSupport(new EditingSupport(this ) {
229:
230: /*
231: * (non-Javadoc)
232: *
233: * @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object)
234: */
235: public boolean canEdit(Object element) {
236: Object[] properties = getColumnProperties();
237:
238: if (columnIndex < properties.length) {
239: return getCellModifier()
240: .canModify(
241: element,
242: (String) getColumnProperties()[columnIndex]);
243: }
244:
245: return false;
246: }
247:
248: /*
249: * (non-Javadoc)
250: *
251: * @see org.eclipse.jface.viewers.EditingSupport#getCellEditor(java.lang.Object)
252: */
253: public CellEditor getCellEditor(Object element) {
254: CellEditor[] editors = getCellEditors();
255: if (columnIndex < editors.length) {
256: return getCellEditors()[columnIndex];
257: }
258: return null;
259: }
260:
261: /*
262: * (non-Javadoc)
263: *
264: * @see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object)
265: */
266: public Object getValue(Object element) {
267: Object[] properties = getColumnProperties();
268:
269: if (columnIndex < properties.length) {
270: return getCellModifier()
271: .getValue(
272: element,
273: (String) getColumnProperties()[columnIndex]);
274: }
275:
276: return null;
277: }
278:
279: /*
280: * (non-Javadoc)
281: *
282: * @see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object,
283: * java.lang.Object)
284: */
285: public void setValue(Object element, Object value) {
286: Object[] properties = getColumnProperties();
287:
288: if (columnIndex < properties.length) {
289: getCellModifier()
290: .modify(
291: findItem(element),
292: (String) getColumnProperties()[columnIndex],
293: value);
294: }
295: }
296:
297: boolean isLegacySupport() {
298: return true;
299: }
300: });
301: }
302: }
303:
304: /**
305: * Creates a generic viewer column for the given column widget, based on the
306: * given label provider.
307: *
308: * @param columnOwner
309: * the column widget
310: * @param labelProvider
311: * the label provider to use for the column
312: * @return ViewerColumn the viewer column
313: */
314: private ViewerColumn createViewerColumn(Widget columnOwner,
315: CellLabelProvider labelProvider) {
316: ViewerColumn column = new ViewerColumn(this , columnOwner) {
317: };
318: column.setLabelProvider(labelProvider, false);
319: return column;
320: }
321:
322: /**
323: * Update the cached cell object with the given row and column.
324: *
325: * @param rowItem
326: * @param column
327: * @return ViewerCell
328: */
329: /* package */ViewerCell updateCell(ViewerRow rowItem, int column,
330: Object element) {
331: cell.update(rowItem, column, element);
332: return cell;
333: }
334:
335: /**
336: * Returns the {@link Item} at the given widget-relative coordinates, or
337: * <code>null</code> if there is no item at the given coordinates.
338: *
339: * @param point
340: * the widget-relative coordinates
341: * @return the {@link Item} at the coordinates or <code>null</code> if
342: * there is no item at the given coordinates
343: */
344: protected abstract Item getItemAt(Point point);
345:
346: /*
347: * (non-Javadoc)
348: *
349: * @see org.eclipse.jface.viewers.StructuredViewer#getItem(int, int)
350: */
351: protected Item getItem(int x, int y) {
352: return getItemAt(getControl().toControl(x, y));
353: }
354:
355: /**
356: * The column viewer implementation of this <code>Viewer</code> framework
357: * method ensures that the given label provider is an instance of
358: * <code>ITableLabelProvider</code>, <code>ILabelProvider</code>, or
359: * <code>CellLabelProvider</code>.
360: * <p>
361: * If the label provider is an {@link ITableLabelProvider}, then it
362: * provides a separate label text and image for each column. Implementers of
363: * <code>ITableLabelProvider</code> may also implement
364: * {@link ITableColorProvider} and/or {@link ITableFontProvider} to provide
365: * colors and/or fonts.
366: * </p>
367: * <p>
368: * If the label provider is an <code>ILabelProvider</code>, then it
369: * provides only the label text and image for the first column, and any
370: * remaining columns are blank. Implementers of <code>ILabelProvider</code>
371: * may also implement {@link IColorProvider} and/or {@link IFontProvider} to
372: * provide colors and/or fonts.
373: * </p>
374: *
375: */
376: public void setLabelProvider(IBaseLabelProvider labelProvider) {
377: Assert.isTrue(labelProvider instanceof ITableLabelProvider
378: || labelProvider instanceof ILabelProvider
379: || labelProvider instanceof CellLabelProvider);
380: updateColumnParts(labelProvider);// Reset the label providers in the
381: // columns
382: super .setLabelProvider(labelProvider);
383: }
384:
385: /**
386: * Clear the viewer parts for the columns
387: */
388: private void updateColumnParts(IBaseLabelProvider labelProvider) {
389: ViewerColumn column;
390: int i = 0;
391:
392: while ((column = getViewerColumn(i++)) != null) {
393: column.setLabelProvider(CellLabelProvider
394: .createViewerLabelProvider(this , labelProvider),
395: false);
396: }
397: }
398:
399: /**
400: * Cancels a currently active cell editor if one is active. All changes
401: * already done in the cell editor are lost.
402: *
403: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
404: */
405: public void cancelEditing() {
406: if (viewerEditor != null) {
407: viewerEditor.cancelEditing();
408: }
409: }
410:
411: /**
412: * Apply the value of the active cell editor if one is active.
413: *
414: * @since 3.3
415: */
416: protected void applyEditorValue() {
417: if (viewerEditor != null) {
418: viewerEditor.applyEditorValue();
419: }
420: }
421:
422: /**
423: * Starts editing the given element at the given column index.
424: *
425: * @param element
426: * the model element
427: * @param column
428: * the column index
429: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
430: */
431: public void editElement(Object element, int column) {
432: if (viewerEditor != null) {
433: try {
434: getControl().setRedraw(false);
435: // Set the selection at first because in Tree's
436: // the element might not be materialized
437: setSelection(new StructuredSelection(element), true);
438:
439: Widget item = findItem(element);
440: if (item != null) {
441: ViewerRow row = getViewerRowFromItem(item);
442: if (row != null) {
443: ViewerCell cell = row.getCell(column);
444: if (cell != null) {
445: triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(
446: cell));
447: }
448: }
449: }
450: } finally {
451: getControl().setRedraw(true);
452: }
453: }
454: }
455:
456: /**
457: * Return the CellEditors for the receiver, or <code>null</code> if no
458: * cell editors are set.
459: * <p>
460: * Since 3.3, an alternative API is available, see
461: * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
462: * flexible way of editing values in a column viewer.
463: * </p>
464: *
465: * @return CellEditor[]
466: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
467: * @see ViewerColumn#setEditingSupport(EditingSupport)
468: * @see EditingSupport
469: */
470: public CellEditor[] getCellEditors() {
471: return cellEditors;
472: }
473:
474: /**
475: * Returns the cell modifier of this viewer, or <code>null</code> if none
476: * has been set.
477: *
478: * <p>
479: * Since 3.3, an alternative API is available, see
480: * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
481: * flexible way of editing values in a column viewer.
482: * </p>
483: *
484: * @return the cell modifier, or <code>null</code>
485: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
486: * @see ViewerColumn#setEditingSupport(EditingSupport)
487: * @see EditingSupport
488: */
489: public ICellModifier getCellModifier() {
490: return cellModifier;
491: }
492:
493: /**
494: * Returns the column properties of this table viewer. The properties must
495: * correspond with the columns of the table control. They are used to
496: * identify the column in a cell modifier.
497: *
498: * <p>
499: * Since 3.3, an alternative API is available, see
500: * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
501: * flexible way of editing values in a column viewer.
502: * </p>
503: *
504: * @return the list of column properties
505: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
506: * @see ViewerColumn#setEditingSupport(EditingSupport)
507: * @see EditingSupport
508: */
509: public Object[] getColumnProperties() {
510: return columnProperties;
511: }
512:
513: /**
514: * Returns whether there is an active cell editor.
515: *
516: * <p>
517: * Since 3.3, an alternative API is available, see
518: * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
519: * flexible way of editing values in a column viewer.
520: * </p>
521: *
522: * @return <code>true</code> if there is an active cell editor, and
523: * <code>false</code> otherwise
524: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
525: * @see ViewerColumn#setEditingSupport(EditingSupport)
526: * @see EditingSupport
527: */
528: public boolean isCellEditorActive() {
529: if (viewerEditor != null) {
530: return viewerEditor.isCellEditorActive();
531: }
532: return false;
533: }
534:
535: public void refresh(Object element) {
536: if (isBusy())
537: return;
538:
539: if (isCellEditorActive()) {
540: cancelEditing();
541: }
542:
543: super .refresh(element);
544: }
545:
546: public void refresh(Object element, boolean updateLabels) {
547: if (isBusy())
548: return;
549:
550: if (isCellEditorActive()) {
551: cancelEditing();
552: }
553:
554: super .refresh(element, updateLabels);
555: }
556:
557: public void update(Object element, String[] properties) {
558: if (isBusy())
559: return;
560: super .update(element, properties);
561: }
562:
563: /**
564: * Sets the cell editors of this column viewer. If editing is not supported
565: * by this viewer the call simply has no effect.
566: *
567: * <p>
568: * Since 3.3, an alternative API is available, see
569: * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
570: * flexible way of editing values in a column viewer.
571: * </p>
572: *
573: * @param editors
574: * the list of cell editors
575: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
576: * @see ViewerColumn#setEditingSupport(EditingSupport)
577: * @see EditingSupport
578: */
579: public void setCellEditors(CellEditor[] editors) {
580: this .cellEditors = editors;
581: }
582:
583: /**
584: * Sets the cell modifier for this column viewer. This method does nothing
585: * if editing is not supported by this viewer.
586: *
587: * <p>
588: * Since 3.3, an alternative API is available, see
589: * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
590: * flexible way of editing values in a column viewer.
591: * </p>
592: *
593: * @param modifier
594: * the cell modifier
595: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
596: * @see ViewerColumn#setEditingSupport(EditingSupport)
597: * @see EditingSupport
598: */
599: public void setCellModifier(ICellModifier modifier) {
600: this .cellModifier = modifier;
601: }
602:
603: /**
604: * Sets the column properties of this column viewer. The properties must
605: * correspond with the columns of the control. They are used to identify the
606: * column in a cell modifier. If editing is not supported by this viewer the
607: * call simply has no effect.
608: *
609: * <p>
610: * Since 3.3, an alternative API is available, see
611: * {@link ViewerColumn#setEditingSupport(EditingSupport)} for a more
612: * flexible way of editing values in a column viewer.
613: * </p>
614: *
615: * @param columnProperties
616: * the list of column properties
617: * @since 3.1 (in subclasses, added in 3.3 to abstract class)
618: * @see ViewerColumn#setEditingSupport(EditingSupport)
619: * @see EditingSupport
620: */
621: public void setColumnProperties(String[] columnProperties) {
622: this .columnProperties = columnProperties;
623: }
624:
625: /**
626: * Returns the number of columns contained in the receiver. If no columns
627: * were created by the programmer, this value is zero, despite the fact that
628: * visually, one column of items may be visible. This occurs when the
629: * programmer uses the column viewer like a list, adding elements but never
630: * creating a column.
631: *
632: * @return the number of columns
633: *
634: * @since 3.3
635: */
636: protected abstract int doGetColumnCount();
637:
638: /**
639: * Returns the label provider associated with the column at the given index
640: * or <code>null</code> if no column with this index is known.
641: *
642: * @param columnIndex
643: * the column index
644: * @return the label provider associated with the column or
645: * <code>null</code> if no column with this index is known
646: *
647: * @since 3.3
648: */
649: public CellLabelProvider getLabelProvider(int columnIndex) {
650: ViewerColumn column = getViewerColumn(columnIndex);
651: if (column != null) {
652: return column.getLabelProvider();
653: }
654: return null;
655: }
656:
657: private void handleMouseDown(MouseEvent e) {
658: ViewerCell cell = getCell(new Point(e.x, e.y));
659:
660: if (cell != null) {
661: triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(
662: cell, e));
663: }
664: }
665:
666: /**
667: * Invoking this method fires an editor activation event which tries to
668: * enable the editor but before this event is passed to
669: * {@link ColumnViewerEditorActivationStrategy} to see if this event should
670: * really trigger editor activation
671: *
672: * @param event
673: * the activation event
674: */
675: protected void triggerEditorActivationEvent(
676: ColumnViewerEditorActivationEvent event) {
677: viewerEditor.handleEditorActivationEvent(event);
678: }
679:
680: /**
681: * @param columnViewerEditor
682: * the new column viewer editor
683: */
684: public void setColumnViewerEditor(
685: ColumnViewerEditor columnViewerEditor) {
686: Assert.isNotNull(viewerEditor);
687: this .viewerEditor = columnViewerEditor;
688: }
689:
690: /**
691: * @return the currently attached viewer editor
692: */
693: public ColumnViewerEditor getColumnViewerEditor() {
694: return viewerEditor;
695: }
696:
697: protected Object[] getRawChildren(Object parent) {
698: boolean oldBusy = busy;
699: busy = true;
700: try {
701: return super .getRawChildren(parent);
702: } finally {
703: busy = oldBusy;
704: }
705: }
706:
707: void clearLegacyEditingSetup() {
708: if (getCellEditors() != null) {
709: ViewerColumn column;
710: int i = 0;
711: while ((column = getViewerColumn(i++)) != null) {
712: EditingSupport e = column.getEditingSupport();
713: // Ensure that only EditingSupports are wiped that are setup
714: // for Legacy reasons
715: if (e != null && e.isLegacySupport()) {
716: column.setEditingSupport(null);
717: }
718: }
719: }
720: }
721: }
|