Mercurial > projects > dwt-addons
view dwtx/jface/viewers/ColumnViewerEditor.d @ 72:5df4896124c7
JFace and its examples do compile
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Thu, 22 May 2008 17:56:17 +0200 |
parents | 4878bef4a38e |
children | 7ffeace6c47f |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2006, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Tom Schindl <tom.schindl@bestsolution.at> - refactoring (bug 153993) * fix in bug: 151295,178946,166500,195908,201906,207676,180504,216706,218336 * Port to the D programming language: * Frank Benoit <benoit@tionex.de> *******************************************************************************/ module dwtx.jface.viewers.ColumnViewerEditor; import dwtx.jface.viewers.CellEditor; import dwtx.jface.viewers.ICellEditorListener; import dwtx.jface.viewers.ColumnViewer; import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy; import dwtx.jface.viewers.ViewerCell; import dwtx.jface.viewers.ViewerColumn; import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; import dwtx.jface.viewers.ColumnViewerEditorActivationListener; import dwtx.jface.viewers.ColumnViewerEditorDeactivationEvent; import dwtx.jface.viewers.ViewerRow; import dwtx.jface.viewers.DoubleClickEvent; import dwtx.jface.viewers.OpenEvent; import dwt.DWT; import dwt.events.DisposeEvent; import dwt.events.DisposeListener; import dwt.events.FocusAdapter; import dwt.events.FocusEvent; import dwt.events.FocusListener; import dwt.events.MouseAdapter; import dwt.events.MouseEvent; import dwt.events.MouseListener; import dwt.events.TraverseEvent; import dwt.events.TraverseListener; import dwt.widgets.Control; import dwt.widgets.Item; import dwtx.core.runtime.ListenerList; import dwt.dwthelper.utils; /** * This is the base for all editor implementations of Viewers. ColumnViewer * implementors have to subclass this class and implement the missing methods * * @since 3.3 * @see TableViewerEditor * @see TreeViewerEditor */ public abstract class ColumnViewerEditor { private CellEditor cellEditor; private ICellEditorListener cellEditorListener; private FocusListener focusListener; private MouseListener mouseListener; private ColumnViewer viewer; private TraverseListener tabeditingListener; private ViewerCell cell; private ListenerList editorActivationListener; private ColumnViewerEditorActivationStrategy editorActivationStrategy; private bool inEditorDeactivation; private DisposeListener disposeListener; /** * Tabbing from cell to cell is turned off */ public static const int DEFAULT = 1; /** * Should if the end of the row is reach started from the start/end of the * row below/above */ public static const int TABBING_MOVE_TO_ROW_NEIGHBOR = 1 << 1; /** * Should if the end of the row is reach started from the beginning in the * same row */ public static const int TABBING_CYCLE_IN_ROW = 1 << 2; /** * Support tabbing to Cell above/below the current cell */ public static const int TABBING_VERTICAL = 1 << 3; /** * Should tabbing from column to column with in one row be supported */ public static const int TABBING_HORIZONTAL = 1 << 4; /** * Style mask used to enable keyboard activation */ public static const int KEYBOARD_ACTIVATION = 1 << 5; /** * Style mask used to turn <b>off</b> the feature that an editor activation * is canceled on double click. It is also possible to turn off this feature * per cell-editor using {@link CellEditor#getDoubleClickTimeout()} */ public static final int KEEP_EDITOR_ON_DOUBLE_CLICK = 1 << 6; private int feature; /** * @param viewer * the viewer this editor is attached to * @param editorActivationStrategy * the strategy used to decide about editor activation * @param feature * bit mask controlling the editor * <ul> * <li>{@link ColumnViewerEditor#DEFAULT}</li> * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li> * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li> * <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li> * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li> * </ul> */ protected this(ColumnViewer viewer, ColumnViewerEditorActivationStrategy editorActivationStrategy, int feature) { this.viewer = viewer; this.editorActivationStrategy = editorActivationStrategy; if ((feature & KEYBOARD_ACTIVATION) is KEYBOARD_ACTIVATION) { this.editorActivationStrategy .setEnableEditorActivationWithKeyboard(true); } this.feature = feature; this.disposeListener = new class(viewer) DisposeListener { ColumnViewer viewer_; this(ColumnViewer a){ viewer_=a; } public void widgetDisposed(DisposeEvent e) { if( viewer_.isCellEditorActive() ) { cancelEditing(); } } }; initCellEditorListener(); } private void initCellEditorListener() { cellEditorListener = new class ICellEditorListener { public void editorValueChanged(bool oldValidState, bool newValidState) { // Ignore. } public void cancelEditor() { this.outer.cancelEditing(); } public void applyEditorValue() { this.outer.applyEditorValue(); } }; } private bool activateCellEditor(ColumnViewerEditorActivationEvent activationEvent) { ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex()); Object element = cell.getElement(); if (part !is null && part.getEditingSupport() !is null && part.getEditingSupport().canEdit_package(element)) { cellEditor = part.getEditingSupport().getCellEditor_package(element); if (cellEditor !is null) { int timeout = cellEditor.getDoubleClickTimeout_package(); int activationTime; if (timeout !is 0) { activationTime = activationEvent.time + timeout; } else { activationTime = 0; } if (editorActivationListener !is null && !editorActivationListener.isEmpty()) { Object[] ls = editorActivationListener.getListeners(); for (int i = 0; i < ls.length; i++) { (cast(ColumnViewerEditorActivationListener) ls[i]) .beforeEditorActivated(activationEvent); // Was the activation canceled ? if (activationEvent.cancel) { return false; } } } updateFocusCell(cell, activationEvent); cellEditor.addListener(cellEditorListener); part.getEditingSupport().initializeCellEditorValue_package(cellEditor, cell); // Tricky flow of control here: // activate() can trigger callback to cellEditorListener which // will clear cellEditor // so must get control first, but must still call activate() // even if there is no control. Control control = cellEditor.getControl(); cellEditor.activate(activationEvent); if (control is null) { return false; } setLayoutData(cellEditor.getLayoutData()); setEditor(control, cast(Item) cell.getItem(), cell.getColumnIndex()); cellEditor.setFocus(); if (cellEditor.dependsOnExternalFocusListener_package()) { if (focusListener is null) { focusListener = new class FocusAdapter { public void focusLost(FocusEvent e) { applyEditorValue(); } }; } control.addFocusListener(focusListener); } mouseListener = new class(control, activationEvent, activationTime) MouseAdapter { Control control_; ColumnViewerEditorActivationEvent activationEvent_; int activationTime_; this(Control a, ColumnViewerEditorActivationEvent b, int c){ control_=a; activationEvent_=b; activationTime_=c; } public void mouseDown(MouseEvent e) { // time wrap? // check for expiration of doubleClickTime if (shouldFireDoubleClick(activationTime_, e.time, activationEvent_) && e.button is 1) { control_.removeMouseListener(mouseListener); cancelEditing(); handleDoubleClickEvent(); } else if (mouseListener !is null) { control_.removeMouseListener(mouseListener); } } }; if (activationTime !is 0 && (feature & KEEP_EDITOR_ON_DOUBLE_CLICK) is 0) { control.addMouseListener(mouseListener); } if (tabeditingListener is null) { tabeditingListener = new class TraverseListener { public void keyTraversed(TraverseEvent e) { if ((feature & DEFAULT) !is DEFAULT) { processTraverseEvent(cell.getColumnIndex(), viewer.getViewerRowFromItem_package(cell .getItem()), e); } } }; } control.addTraverseListener(tabeditingListener); if (editorActivationListener !is null && !editorActivationListener.isEmpty()) { Object[] ls = editorActivationListener.getListeners(); for (int i = 0; i < ls.length; i++) { (cast(ColumnViewerEditorActivationListener) ls[i]) .afterEditorActivated(activationEvent); } } this.cell.getItem().addDisposeListener(disposeListener); return true; } } return false; } private bool shouldFireDoubleClick(int activationTime, int mouseTime, ColumnViewerEditorActivationEvent activationEvent) { return mouseTime <= activationTime && activationEvent.eventType !is ColumnViewerEditorActivationEvent.KEY_PRESSED && activationEvent.eventType !is ColumnViewerEditorActivationEvent.PROGRAMMATIC && activationEvent.eventType !is ColumnViewerEditorActivationEvent.TRAVERSAL; } /** * Applies the current value and deactivates the currently active cell * editor. */ void applyEditorValue() { // avoid re-entering if (!inEditorDeactivation) { try { inEditorDeactivation = true; CellEditor c = this.cellEditor; if (c !is null && this.cell !is null) { ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent( cell); tmp.eventType = ColumnViewerEditorDeactivationEvent.EDITOR_SAVED; if (editorActivationListener !is null && !editorActivationListener.isEmpty()) { Object[] ls = editorActivationListener.getListeners(); for (int i = 0; i < ls.length; i++) { (cast(ColumnViewerEditorActivationListener) ls[i]) .beforeEditorDeactivated(tmp); } } Item t = cast(Item) this.cell.getItem(); // don't null out table item -- same item is still selected if (t !is null && !t.isDisposed()) { saveEditorValue(c); } if (!viewer.getControl().isDisposed()) { setEditor(null, null, 0); } c.removeListener(cellEditorListener); Control control = c.getControl(); if (control !is null && !control.isDisposed()) { if (mouseListener !is null) { control.removeMouseListener(mouseListener); // Clear the instance not needed any more mouseListener = null; } if (focusListener !is null) { control.removeFocusListener(focusListener); } if (tabeditingListener !is null) { control.removeTraverseListener(tabeditingListener); } } c.deactivate_package(tmp); if (editorActivationListener !is null && !editorActivationListener.isEmpty()) { Object[] ls = editorActivationListener.getListeners(); for (int i = 0; i < ls.length; i++) { (cast(ColumnViewerEditorActivationListener) ls[i]) .afterEditorDeactivated(tmp); } } if( ! this.cell.getItem().isDisposed() ) { this.cell.getItem().removeDisposeListener(disposeListener); } } this.cellEditor = null; this.cell = null; } finally { inEditorDeactivation = false; } } } /** * Cancel editing */ void cancelEditing() { // avoid re-entering if (!inEditorDeactivation) { try { inEditorDeactivation = true; if (cellEditor !is null) { ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent( cell); tmp.eventType = ColumnViewerEditorDeactivationEvent.EDITOR_CANCELED; if (editorActivationListener !is null && !editorActivationListener.isEmpty()) { Object[] ls = editorActivationListener.getListeners(); for (int i = 0; i < ls.length; i++) { (cast(ColumnViewerEditorActivationListener) ls[i]) .beforeEditorDeactivated(tmp); } } if (!viewer.getControl().isDisposed()) { setEditor(null, null, 0); } cellEditor.removeListener(cellEditorListener); Control control = cellEditor.getControl(); if (control !is null && !viewer.getControl().isDisposed()) { if (mouseListener !is null) { control.removeMouseListener(mouseListener); // Clear the instance not needed any more mouseListener = null; } if (focusListener !is null) { control.removeFocusListener(focusListener); } if (tabeditingListener !is null) { control.removeTraverseListener(tabeditingListener); } } CellEditor oldEditor = cellEditor; oldEditor.deactivate_package(tmp); if (editorActivationListener !is null && !editorActivationListener.isEmpty()) { Object[] ls = editorActivationListener.getListeners(); for (int i = 0; i < ls.length; i++) { (cast(ColumnViewerEditorActivationListener) ls[i]) .afterEditorDeactivated(tmp); } } if( ! this.cell.getItem().isDisposed() ) { this.cell.getItem().addDisposeListener(disposeListener); } this.cellEditor = null; this.cell = null; } } finally { inEditorDeactivation = false; } } } /** * Enable the editor by mouse down * * @param event */ void handleEditorActivationEvent(ColumnViewerEditorActivationEvent event) { // Only activate if the event isn't tagged as canceled if (!event.cancel && editorActivationStrategy.isEditorActivationEvent_package(event)) { if (cellEditor !is null) { applyEditorValue(); } this.cell = cast(ViewerCell) event.getSource(); if( ! activateCellEditor(event) ) { this.cell = null; this.cellEditor = null; } } } private void saveEditorValue(CellEditor cellEditor) { ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex()); if (part !is null && part.getEditingSupport() !is null) { part.getEditingSupport().saveCellEditorValue_package(cellEditor, cell); } } /** * Return whether there is an active cell editor. * * @return <code>true</code> if there is an active cell editor; otherwise * <code>false</code> is returned. */ bool isCellEditorActive() { return cellEditor !is null; } void handleDoubleClickEvent() { viewer.fireDoubleClick_package(new DoubleClickEvent(viewer, viewer .getSelection())); viewer.fireOpen_package(new OpenEvent(viewer, viewer.getSelection())); } /** * Adds the given listener, it is to be notified when the cell editor is * activated or deactivated. * * @param listener * the listener to add */ public void addEditorActivationListener( ColumnViewerEditorActivationListener listener) { if (editorActivationListener is null) { editorActivationListener = new ListenerList(); } editorActivationListener.add(listener); } /** * Removes the given listener. * * @param listener * the listener to remove */ public void removeEditorActivationListener( ColumnViewerEditorActivationListener listener) { if (editorActivationListener !is null) { editorActivationListener.remove(listener); } } /** * Process the traverse event and opens the next available editor depending * of the implemented strategy. The default implementation uses the style * constants * <ul> * <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li> * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li> * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li> * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li> * </ul> * * <p> * Subclasses may overwrite to implement their custom logic to edit the next * cell * </p> * * @param columnIndex * the index of the current column * @param row * the current row - may only be used for the duration of this * method call * @param event * the traverse event */ protected void processTraverseEvent(int columnIndex, ViewerRow row, TraverseEvent event) { ViewerCell cell2edit = null; if (event.detail is DWT.TRAVERSE_TAB_PREVIOUS) { event.doit = false; if ((event.stateMask & DWT.CTRL) is DWT.CTRL && (feature & TABBING_VERTICAL) is TABBING_VERTICAL) { cell2edit = searchCellAboveBelow(row, viewer, columnIndex, true); } else if ((feature & TABBING_HORIZONTAL) is TABBING_HORIZONTAL) { cell2edit = searchPreviousCell(row, row.getCell(columnIndex), row.getCell(columnIndex), viewer); } } else if (event.detail is DWT.TRAVERSE_TAB_NEXT) { event.doit = false; if ((event.stateMask & DWT.CTRL) is DWT.CTRL && (feature & TABBING_VERTICAL) is TABBING_VERTICAL) { cell2edit = searchCellAboveBelow(row, viewer, columnIndex, false); } else if ((feature & TABBING_HORIZONTAL) is TABBING_HORIZONTAL) { cell2edit = searchNextCell(row, row.getCell(columnIndex), row .getCell(columnIndex), viewer); } } if (cell2edit !is null) { viewer.getControl().setRedraw(false); ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent( cell2edit, event); viewer.triggerEditorActivationEvent_package(acEvent); viewer.getControl().setRedraw(true); } } private ViewerCell searchCellAboveBelow(ViewerRow row, ColumnViewer viewer, int columnIndex, bool above) { ViewerCell rv = null; ViewerRow newRow = null; if (above) { newRow = row.getNeighbor(ViewerRow.ABOVE, false); } else { newRow = row.getNeighbor(ViewerRow.BELOW, false); } if (newRow !is null) { ViewerColumn column = viewer.getViewerColumn(columnIndex); if (column !is null && column.getEditingSupport() !is null && column.getEditingSupport().canEdit_package( newRow.getItem().getData())) { rv = newRow.getCell(columnIndex); } else { rv = searchCellAboveBelow(newRow, viewer, columnIndex, above); } } return rv; } private bool isCellEditable(ColumnViewer viewer, ViewerCell cell) { ViewerColumn column = viewer.getViewerColumn(cell.getColumnIndex()); return column !is null && column.getEditingSupport() !is null && column.getEditingSupport().canEdit_package(cell.getElement()); } private ViewerCell searchPreviousCell(ViewerRow row, ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer) { ViewerCell rv = null; ViewerCell previousCell; if (currentCell !is null) { previousCell = currentCell.getNeighbor(ViewerCell.LEFT, true); } else { if (row.getColumnCount() !is 0) { previousCell = row.getCell(row.getCreationIndex_package(row .getColumnCount() - 1)); } else { previousCell = row.getCell(0); } } // No endless loop if (originalCell.opEquals(previousCell)) { return null; } if (previousCell !is null) { if (isCellEditable(viewer, previousCell)) { rv = previousCell; } else { rv = searchPreviousCell(row, previousCell, originalCell, viewer); } } else { if ((feature & TABBING_CYCLE_IN_ROW) is TABBING_CYCLE_IN_ROW) { rv = searchPreviousCell(row, null, originalCell, viewer); } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) is TABBING_MOVE_TO_ROW_NEIGHBOR) { ViewerRow rowAbove = row.getNeighbor(ViewerRow.ABOVE, false); if (rowAbove !is null) { rv = searchPreviousCell(rowAbove, null, originalCell, viewer); } } } return rv; } private ViewerCell searchNextCell(ViewerRow row, ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer) { ViewerCell rv = null; ViewerCell nextCell; if (currentCell !is null) { nextCell = currentCell.getNeighbor(ViewerCell.RIGHT, true); } else { nextCell = row.getCell(row.getCreationIndex_package(0)); } // No endless loop if (originalCell.opEquals(nextCell)) { return null; } if (nextCell !is null) { if (isCellEditable(viewer, nextCell)) { rv = nextCell; } else { rv = searchNextCell(row, nextCell, originalCell, viewer); } } else { if ((feature & TABBING_CYCLE_IN_ROW) is TABBING_CYCLE_IN_ROW) { rv = searchNextCell(row, null, originalCell, viewer); } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) is TABBING_MOVE_TO_ROW_NEIGHBOR) { ViewerRow rowBelow = row.getNeighbor(ViewerRow.BELOW, false); if (rowBelow !is null) { rv = searchNextCell(rowBelow, null, originalCell, viewer); } } } return rv; } /** * Position the editor inside the control * * @param w * the editor control * @param item * the item (row) in which the editor is drawn in * @param fColumnNumber * the column number in which the editor is shown */ protected abstract void setEditor(Control w, Item item, int fColumnNumber); /** * set the layout data for the editor * * @param layoutData * the layout data used when editor is displayed */ protected abstract void setLayoutData(LayoutData layoutData); /** * @param focusCell * updates the cell with the current input focus * @param event * the event requesting to update the focusCell */ protected abstract void updateFocusCell(ViewerCell focusCell, ColumnViewerEditorActivationEvent event); /** * @return the cell currently holding the focus if no cell has the focus or * the viewer implementation doesn't support <code>null</code> is * returned * */ public ViewerCell getFocusCell() { return null; } /** * @return the viewer working for */ protected ColumnViewer getViewer() { return viewer; } }