InternalCompositeTable.java
上传用户:hengxinggs
上传日期:2008-01-15
资源大小:212k
文件大小:44k
源码类别:
PlugIns编程
开发平台:
Java
- /*
- * Copyright (C) 2005 David Orme <djo@coconut-palm-software.com>
- *
- * 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:
- * David Orme - Initial API and implementation
- */
- package org.eclipse.jface.examples.databinding.compositetable;
- import java.lang.reflect.Constructor;
- import java.util.Iterator;
- import java.util.LinkedList;
- import org.eclipse.jface.examples.databinding.compositetable.internal.EmptyTablePlaceholder;
- import org.eclipse.jface.examples.databinding.compositetable.internal.ISelectableRegionControl;
- import org.eclipse.jface.examples.databinding.compositetable.internal.TableRow;
- import org.eclipse.jface.examples.databinding.compositetable.reflect.DuckType;
- import org.eclipse.swt.SWT;
- import org.eclipse.swt.events.FocusEvent;
- import org.eclipse.swt.events.KeyEvent;
- import org.eclipse.swt.events.PaintEvent;
- import org.eclipse.swt.events.PaintListener;
- import org.eclipse.swt.events.SelectionEvent;
- import org.eclipse.swt.events.SelectionListener;
- import org.eclipse.swt.events.TraverseEvent;
- import org.eclipse.swt.graphics.Color;
- import org.eclipse.swt.graphics.Point;
- import org.eclipse.swt.graphics.Rectangle;
- import org.eclipse.swt.layout.FillLayout;
- import org.eclipse.swt.layout.GridData;
- import org.eclipse.swt.layout.GridLayout;
- import org.eclipse.swt.widgets.Composite;
- import org.eclipse.swt.widgets.Control;
- import org.eclipse.swt.widgets.Display;
- import org.eclipse.swt.widgets.Event;
- import org.eclipse.swt.widgets.Layout;
- import org.eclipse.swt.widgets.Listener;
- import org.eclipse.swt.widgets.Slider;
- import org.eclipse.swt.widgets.Widget;
- /** (non-API)
- * Class InternalCompositeTable. This is the run-time CompositeTableControl. It gets its prototype
- * row and (optional) header objects from its SWT parent, then uses them to implement an SWT
- * virtual table control.
- *
- * @author djo
- */
- public class InternalCompositeTable extends Composite implements Listener {
- // The internal UI controls that make up this control.
- private Composite sliderHolder = null;
- private Composite controlHolder = null;
- private Slider slider = null;
- private EmptyTablePlaceholder emptyTablePlaceholder = null;
- // My parent CompositeTable
- private CompositeTable parent;
- // Property fields
- private int maxRowsVisible;
- private int numRowsInDisplay;
- private int numRowsInCollection;
- private int topRow;
- private int currentRow;
- private int currentColumn;
- // The visible/invisible row objects and bookeeping info about them
- private int currentVisibleTopRow = 0;
- private int numRowsVisible = 0;
- private LinkedList rows = new LinkedList();
- private LinkedList spareRows = new LinkedList();
- // The prototype header/row objects and Constructors so we can duplicate them
- private Constructor headerConstructor;
- private Constructor rowConstructor;
- private Control headerControl;
- private Control myHeader = null;
- private Control rowControl;
- /**
- * Constructor InternalCompositeTable. The usual SWT constructor. The same style bits are
- * allowed here as are allowed on Composite.
- *
- * @param parentControl The SWT parent.
- * @param style Style bits.
- */
- public InternalCompositeTable(Composite parentControl, int style) {
- super(parentControl, style);
- initialize();
- this.parent = (CompositeTable) parentControl;
- setBackground(parentControl.getBackground());
- controlHolder.addListener(SWT.MouseWheel, this);
- maxRowsVisible = parent.getMaxRowsVisible();
- numRowsInCollection = parent.getNumRowsInCollection();
- topRow = parent.getTopRow();
- headerConstructor = parent.getHeaderConstructor();
- rowConstructor = parent.getRowConstructor();
- headerControl = parent.getHeaderControl();
- rowControl = parent.getRowControl();
- currentVisibleTopRow = topRow;
- showHeader();
- updateVisibleRows();
- if (numRowsVisible < 1) {
- createEmptyTablePlaceholer();
- }
- }
- public void setBackground(Color color) {
- super.setBackground(color);
- controlHolder.setBackground(color);
- }
- /**
- * Initialize the overall table UI.
- */
- private void initialize() {
- GridLayout gl = new GridLayout();
- gl.numColumns = 2;
- gl.verticalSpacing = 0;
- gl.marginWidth = 0;
- gl.marginHeight = 0;
- gl.horizontalSpacing = 0;
- this.setLayout(gl);
- createControlHolder();
- createSliderHolder();
- }
- /**
- * Initialize the controlHolder, which is the holder Composite for the header object (if
- * applicable) and the row objects.
- */
- private void createControlHolder() {
- GridData gridData = new org.eclipse.swt.layout.GridData();
- gridData.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
- gridData.grabExcessHorizontalSpace = true;
- gridData.grabExcessVerticalSpace = true;
- gridData.verticalAlignment = org.eclipse.swt.layout.GridData.FILL;
- controlHolder = new Composite(this, SWT.NONE);
- controlHolder.setLayoutData(gridData);
- controlHolder.setLayout(new Layout() {
- protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
- if (rowControl != null) {
- int height = 0;
- int width = 0;
- if (headerControl != null) {
- Point headerSize = headerControl.getSize();
- width = headerSize.x;
- height = headerSize.y;
- }
- Point rowSize = rowControl.getSize();
- height += rowSize.y * 2;
- if (width < rowSize.x) {
- width = rowSize.x;
- }
- return new Point(height, width);
- }
- return new Point(50, 50);
- }
- protected void layout(Composite composite, boolean flushCache) {
- layoutControlHolder();
- }
- });
- }
- /**
- * Initialize the sliderHolder and slider. The SliderHolder is a Composite that is
- * responsible for showing and hiding the vertical slider upon request.
- */
- private void createSliderHolder() {
- GridData gd = getSliderGridData();
- sliderHolder = new Composite(this, SWT.NONE);
- slider = new Slider(sliderHolder, SWT.VERTICAL);
- slider.addSelectionListener(sliderSelectionListener);
- sliderHolder.setLayout(new FillLayout());
- sliderHolder.setLayoutData(gd);
- sliderHolder.setTabList(new Control[] {});
- }
- // Slider utility methods ---------------------------------------------------------------------
- /**
- * Returns a GridData for the SliderHolder appropriate for if the slider is visible or not.
- *
- * @return A GridData with a widthHint of 0 if the slider is not visible or with a widthHint
- * of SWT.DEFAULT otherwise.
- */
- private GridData getSliderGridData() {
- GridData gd = new org.eclipse.swt.layout.GridData();
- gd.grabExcessVerticalSpace = true;
- gd.verticalAlignment = org.eclipse.swt.layout.GridData.FILL;
- gd.verticalSpan = 1;
- if (!sliderVisible) {
- gd.widthHint = 0;
- }
- gd.horizontalAlignment = org.eclipse.swt.layout.GridData.CENTER;
- return gd;
- }
- private boolean sliderVisible = false;
- /**
- * Sets if the slider is visible or not.
- *
- * @param visible true if the slider should be visible; false otherwise.
- */
- public void setSliderVisible(boolean visible) {
- this.sliderVisible = visible;
- sliderHolder.setLayoutData(getSliderGridData());
- Display.getCurrent().asyncExec(new Runnable() {
- public void run() {
- sliderHolder.getParent().layout(true);
- sliderHolder.layout(true);
- Point sliderHolderSize = sliderHolder.getSize();
- slider.setBounds(0, 0, sliderHolderSize.x, sliderHolderSize.y);
- }
- });
- }
- /**
- * Returns if the slider is visible.
- *
- * @return true if the slider is visible; false otherwise.
- */
- public boolean isSliderVisible() {
- return sliderVisible;
- }
- /* (non-Javadoc)
- * @see org.eclipse.swt.widgets.Widget#dispose()
- */
- public void dispose() {
- disposeRows(rows);
- disposeRows(spareRows);
- super.dispose();
- }
- /**
- * Disposes all the row objects in the specified LinkedList.
- *
- * @param rowsCollection The collection containing TableRow objects to dispose.
- */
- private void disposeRows(LinkedList rowsCollection) {
- for (Iterator rowsIter = rowsCollection.iterator(); rowsIter.hasNext();) {
- TableRow row = (TableRow) rowsIter.next();
- row.dispose();
- }
- }
- // Row object layout --------------------------------------------------------------------------
- /**
- * Layout the child controls within the controlHolder Composite.
- */
- protected void layoutControlHolder() {
- if (myHeader != null)
- layoutChild(myHeader);
- for (Iterator rowsIter = rows.iterator(); rowsIter.hasNext();) {
- TableRow row = (TableRow) rowsIter.next();
- layoutChild(row.getRowControl());
- }
- updateVisibleRows();
- }
- /**
- * Layout a particular row or header control (child control of the controlHolder).
- * If the child control has a layout manager, we delegate to that layout manager.
- * Otherwise, we use the built in table layout manager.
- *
- * @param child The row or header control to layout.
- * @return height of child
- */
- private int layoutChild(Control child) {
- if (child instanceof Composite) {
- Composite composite = (Composite) child;
- if (composite.getLayout() == null) {
- return parent.layoutHeaderOrRow(composite);
- }
- composite.layout(true);
- return composite.getSize().y;
- }
- return child.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
- }
- // Table control layout -- utility methods ----------------------------------------------------
- /**
- * Construct a header or row object on demand. Logs an error and returns null on failure.
- *
- * @param parent The SWT parent.
- * @param constructor The header or row object's constructor.
- * @return The constructed control or null if none could be constructed.
- */
- private Control createInternalControl(Composite parent, Constructor constructor) {
- Control result = null;
- try {
- result = (Control) constructor.newInstance(new Object[] {parent, new Integer(SWT.NULL)});
- } catch (Exception e) {
- throw new IllegalArgumentException("Unable to construct control"); //$NON-NLS-1$
- }
- return result;
- }
- /**
- * If the header control hasn't been created yet, create and show it.
- */
- private void showHeader() {
- if (myHeader == null && headerConstructor != null) {
- myHeader = createInternalControl(controlHolder, headerConstructor);
- if (myHeader instanceof Composite) {
- Composite headerComp = (Composite) myHeader;
- if (headerComp.getLayout() == null) {
- headerComp.addPaintListener(headerPaintListener);
- }
- }
- layoutChild(myHeader);
- }
- }
- // Table control layout -- main refresh algorithm ---------------------------------------------
- /**
- * Main refresh algorithm entry point. This method refreshes everything in the table:
- *
- * <ul>
- * <li>Makes sure the correct number of rows are visible
- * <li>Makes sure each row has been refreshed with data from the underlying model
- * </ul>
- */
- void updateVisibleRows() {
- // If we don't have our prototype row object yet, bail out
- if (rowControl == null) {
- return;
- }
- // Figure out how many rows we can stack vertically
- int clientAreaHeight = controlHolder.getSize().y;
- if (clientAreaHeight <= 0) {
- return;
- }
- int topPosition = 0;
- int headerHeight = 0;
- if (myHeader != null) {
- headerHeight = headerControl.getSize().y + 3;
- clientAreaHeight -= headerHeight;
- topPosition += headerHeight;
- }
- numRowsInDisplay = clientAreaHeight / rowControl.getSize().y;
- // Make sure we have something to lay out to begin with
- if (numRowsInCollection > 0) {
- numRowsVisible = numRowsInDisplay;
- disposeEmptyTablePlaceholder();
- int displayableRows = numRowsInCollection - topRow;
- if (numRowsVisible > displayableRows) {
- numRowsVisible = displayableRows;
- }
- if (numRowsVisible > maxRowsVisible) {
- numRowsVisible = maxRowsVisible;
- }
- if (numRowsVisible < 1) {
- numRowsVisible = 1;
- }
- // Scroll the view so that the right number of row
- // objects are showing and they have the right data
- if (rows.size() - Math.abs(currentVisibleTopRow - topRow) > 0) {
- if (currentRow >= numRowsVisible) {
- deleteRowAt(0);
- ++currentVisibleTopRow;
- ++topRow;
- --currentRow;
- }
- scrollTop();
- fixNumberOfRows();
- } else {
- currentVisibleTopRow = topRow;
- // The order of number fixing/refresh is important in order to
- // minimize the number of screen redraw operations
- if (rows.size() > numRowsVisible) {
- fixNumberOfRows();
- refreshAllRows();
- } else {
- refreshAllRows();
- fixNumberOfRows();
- }
- }
- } else {
- numRowsVisible = 0;
- topRow=0;
- currentRow=0;
- currentColumn=0;
- currentVisibleTopRow = 0;
- numRowsVisible = 0;
- if (emptyTablePlaceholder == null) {
- fixNumberOfRows();
- createEmptyTablePlaceholer();
- }
- }
- // Show, hide, reset the scroll bar
- if (numRowsVisible < numRowsInCollection) {
- int extra = numRowsInCollection - numRowsVisible;
- int pageIncrement = numRowsVisible;
- if (pageIncrement > extra)
- pageIncrement = extra;
- slider.setMaximum(numRowsInCollection);
- slider.setMinimum(0);
- slider.setIncrement(1);
- slider.setPageIncrement(pageIncrement);
- slider.setThumb(numRowsInCollection - (numRowsInCollection - numRowsVisible));
- slider.setSelection(topRow);
- if (!isSliderVisible()) {
- setSliderVisible(true);
- }
- } else {
- setSliderVisible(false);
- }
- // Lay out the header and rows correctly in the display
- int width = controlHolder.getSize().x;
- // First, the header...
- if (myHeader != null) {
- myHeader.setBounds(0, 0, width, headerHeight);
- }
- // Make sure we have rows to lay out...
- if (numRowsInCollection < 1) {
- return;
- }
- // Now the rows.
- int rowHeight = 50;
- rowHeight = rowControl.getSize().y;
- for (Iterator rowsIter = rows.iterator(); rowsIter.hasNext();) {
- TableRow row = (TableRow) rowsIter.next();
- Control rowControl = row.getRowControl();
- rowControl.setBounds(0, topPosition, width, rowHeight);
- layoutChild(rowControl);
- topPosition += rowHeight;
- }
- }
- /**
- * Utility method: Makes sure that the currently visible top row is the same as the top row
- * specified in the TopRow property.
- */
- private void scrollTop() {
- while (currentVisibleTopRow < topRow) {
- deleteRowAt(0);
- ++currentVisibleTopRow;
- }
- while (currentVisibleTopRow > topRow) {
- --currentVisibleTopRow;
- insertRowAt(0);
- }
- }
- /**
- * Utility method: Makes sure that the number of rows that are visible correspond with
- * what should be visible given the table control's size, where it is scrolled, and
- * the number of rows in the underlying collection.
- */
- private void fixNumberOfRows() {
- int numRows = rows.size();
- while (numRows > numRowsVisible) {
- deleteRowAt(numRows-1);
- numRows = rows.size();
- }
- while (numRows < numRowsVisible) {
- insertRowAt(numRows);
- numRows = rows.size();
- }
- }
- /**
- * Fire the refresh event on all visible rows.
- */
- void refreshAllRows() {
- int row=0;
- for (Iterator rowsIter = rows.iterator(); rowsIter.hasNext();) {
- TableRow rowControl = (TableRow) rowsIter.next();
- fireRefreshEvent(topRow + row, rowControl.getRowControl());
- ++row;
- }
- }
- /**
- * Insert a new row object at the specified 0-based position relatve to the topmost row.
- *
- * @param position The 0-based position relative to the topmost row.
- */
- private void insertRowAt(int position) {
- TableRow newRow = getNewRow();
- if (position > rows.size()) {
- position = rows.size();
- }
- rows.add(position, newRow);
- fireRefreshEvent(currentVisibleTopRow + position, newRow.getRowControl());
- }
- /**
- * Delete the row at the specified 0-based position relative to the topmost row.
- *
- * @param position The 0-based position relative to the topmost row.
- */
- private void deleteRowAt(int position) {
- TableRow row = (TableRow) rows.remove(position);
- row.setVisible(false);
- spareRows.addLast(row);
- }
- /**
- * Utility method: Creates a new row object or recycles one that had been previously
- * created but was no longer needed.
- *
- * @return The new row object.
- */
- private TableRow getNewRow() {
- if (spareRows.size() > 0) {
- TableRow recycledRow = (TableRow) spareRows.removeFirst();
- recycledRow.setVisible(true);
- return recycledRow;
- }
- TableRow newRow = new TableRow(this, createInternalControl(controlHolder, rowConstructor));
- fireRowConstructionEvent(newRow.getRowControl());
- if (newRow.getRowControl() instanceof Composite) {
- Composite rowComp = (Composite) newRow.getRowControl();
- if (rowComp.getLayout() == null) {
- rowComp.setBackground(getBackground());
- rowComp.addPaintListener(rowPaintListener);
- }
- }
- return newRow;
- }
- // Property getters/setters --------------------------------------------------------------
- /*
- * These are internal API.
- * <p>
- * Plese refer to the JavaDoc on CompositeTable for detailed description of these property methods.
- */
- /** (non-API)
- * Method getHeaderControl. Return the prototype control being used as a header.
- *
- * @return The header control
- */
- public Control getHeaderControl() {
- return headerControl;
- }
- /**
- * Method setMaxRowsVisible. Sets the maximum number of rows that will be permitted
- * in the table at once. For example, setting this property to 1 will have the effect of
- * creating a single editing area with a scroll bar on the right allowing the user to scroll
- * through all rows using either the mouse or the PgUp/PgDn keys. The default value is
- * Integer.MAX_VALUE.
- *
- * @param maxRowsVisible the maximum number of rows that are permitted to be visible at one time, regardless
- * of the control's size.
- */
- public void setMaxRowsVisible(int maxRowsVisible) {
- this.maxRowsVisible = maxRowsVisible;
- updateVisibleRows();
- }
- /**
- * Method getNumRowsVisible. Returns the actual number of rows that are currently visible.
- * Normally CompositeTable displays as many rows as will fit vertically given the control's
- * size. This value can be clamped to a maximum using the MaxRowsVisible property.
- *
- * @return the actual number of rows that are currently visible.
- */
- public int getNumRowsVisible() {
- return numRowsVisible;
- }
- /**
- * Method setNumRowsInCollection. Sets the number of rows in the data structure that is
- * being edited.
- *
- * @param numRowsInCollection the number of rows represented by the underlying data structure.
- */
- public void setNumRowsInCollection(int numRowsInCollection) {
- this.topRow = 0;
- if (currentRow > 0) {
- currentRow = 0;
- }
- this.numRowsInCollection = numRowsInCollection;
- updateVisibleRows();
- refreshAllRows();
- }
- /**
- * Method setTopRow. Set the number of the line that is being displayed in the top row
- * of the CompositeTable editor (0-based). If the new top row is not equal to the current
- * top row, the table will automatically be scrolled to the new position. This number must
- * be greater than 0 and less than NumRowsInCollection.
- *
- * @param topRow the line number of the new top row.
- */
- public void setTopRow(int topRow) {
- fireRowDepartEvent();
- this.topRow = topRow;
- updateVisibleRows();
- fireRowArriveEvent();
- }
- /**
- * Method getTopRow. Return the number of the line that is being displayed in the top row
- * of the CompositeTable editor (0-based).
- *
- * @return the number of the top line.
- */
- public int getTopRow() {
- return topRow;
- }
- /**
- * Method getSelection. Returns the currently-selected (column, row) pair where the row
- * specifies the offset from the top of the table window. In order to get the current
- * row in the underlying data structure, use getSelection().y + getCurrentRow().
- *
- * @return the currently-selected (column, row) pair where the row specifies the offset
- * from the top of the table window.
- */
- public Point getSelection() {
- return new Point(currentColumn, currentRow);
- }
- /**
- * Method setSelection. Sets the currently-selected (column, row) pair where the row
- * specifies the offset from the top of the table window. In order to get the current
- * row in the underlying data structure, use getSelection().y + getCurrentRow().
- *
- * @param column the column to select
- * @param row the row to select
- */
- public void setSelection(int column, int row) {
- if (row == currentRow)
- internalSetSelection(column, row, false);
- else {
- if (fireRequestRowChangeEvent())
- internalSetSelection(column, row, true);
- }
- }
- /**
- * Method setWeights. Indicates that the column weights were just set and we should re-layout
- * the control holder object.
- */
- public void setWeights() {
- layoutControlHolder();
- }
- // Refresh Event API --------------------------------------------------------------------------
- /**
- * Adds the specified listener to the set of listeners that will be notified when a row
- * refresh event occurs.
- *
- * @param listener the listener to add.
- */
- public void addRefreshContentProvider(IRowContentProvider listener) {
- parent.contentProviders.add(listener);
- }
- /**
- * Remove the specified listener from the set of listeners that will be notified when a
- * row refresh event occurs.
- *
- * @param listener the listener to remove.
- */
- public void removeRefreshContentProvider(IRowContentProvider listener) {
- parent.contentProviders.remove(listener);
- }
- private void fireRefreshEvent(int positionInCollection, Control rowControl) {
- if (numRowsInCollection < 1) {
- return;
- }
- for (Iterator refreshListenersIter = parent.contentProviders.iterator(); refreshListenersIter.hasNext();) {
- IRowContentProvider listener = (IRowContentProvider) refreshListenersIter.next();
- listener.refresh(parent, positionInCollection, rowControl);
- }
- }
- // Empty table placeholder --------------------------------------------------------------------
- private void createEmptyTablePlaceholer() {
- emptyTablePlaceholder = new EmptyTablePlaceholder(controlHolder, SWT.NULL);
- if (rowControl != null)
- emptyTablePlaceholder.setBackground(rowControl.getBackground());
- emptyTablePlaceholder.setMessage(parent.getInsertHint());
- }
- private void disposeEmptyTablePlaceholder() {
- if (emptyTablePlaceholder != null) {
- emptyTablePlaceholder.dispose();
- emptyTablePlaceholder = null;
- }
- }
- // Event Handling -----------------------------------------------------------------------------
- private boolean needToRequestRC=true;
- /**
- * Handle a keyPressed event on any row control.
- *
- * @param sender The row that is sending the event
- * @param e the actual KeyEvent
- */
- public void keyPressed(TableRow sender, KeyEvent e) {
- if ((e.stateMask & SWT.CONTROL) != 0) {
- switch (e.keyCode) {
- case SWT.HOME:
- if (topRow <= 0) {
- return;
- }
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(e.widget);
- // If the focus is already in the top visible row, we will need to explicitly
- // fire an arrive event.
- boolean needToArrive = true;
- if (currentRow > 0) {
- needToArrive = false;
- }
- setTopRow(0);
- if (needToArrive) {
- internalSetSelection(currentColumn, 0, true);
- } else {
- internalSetSelection(currentColumn, 0, false);
- }
- return;
- case SWT.END:
- if (topRow + numRowsVisible < numRowsInCollection) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(e.widget);
- // If the focus is already in the last visible row, we will need to explicitly
- // fire an arrive event.
- needToArrive = true;
- if (currentRow < numRowsVisible-1) {
- needToArrive = false;
- }
- setTopRow(numRowsInCollection - numRowsVisible);
- if (needToArrive) {
- internalSetSelection(currentColumn, numRowsVisible-1, true);
- } else {
- internalSetSelection(currentColumn, numRowsVisible-1, false);
- }
- }
- return;
- case SWT.DEL:
- if (fireDeleteEvent()) {
- // We know the object is gone if we made it here, so now refresh the display...
- --numRowsInCollection;
- // If we deleted the last row in the list
- if (currentRow >= numRowsVisible-1) {
- // If that wasn't the last row in the collection, move the focus
- if (numRowsInCollection > 0) {
- // If we're only displaying one row, scroll first
- if (currentRow < 1) {
- needToRequestRC = false;
- deleteRowAt(currentRow);
- setTopRow(topRow-1);
- internalSetSelection(currentColumn, currentRow, true);
- } else {
- needToRequestRC = false;
- internalSetSelection(currentColumn, currentRow-1, false);
- Display.getCurrent().asyncExec(new Runnable() {
- public void run() {
- deleteRowAt(currentRow+1);
- updateVisibleRows();
- }
- });
- }
- } else {
- // Otherwise, show the placeholder object and give it focus
- deleteRowAt(currentRow);
- --numRowsVisible;
- createEmptyTablePlaceholer();
- emptyTablePlaceholder.setFocus();
- }
- } else {
- // else, keep the focus where it was
- deleteRowAt(currentRow);
- updateVisibleRows();
- internalSetSelection(currentColumn, currentRow, true);
- }
- }
- return;
- default:
- return;
- }
- }
- switch (e.keyCode) {
- case SWT.INSERT:
- // If no insertHandler has been registered, bail out
- if (parent.insertHandlers.size() < 1) {
- return;
- }
- // Make sure we can leave the current row
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- // Insert the new object
- int newRowPosition = fireInsertEvent();
- if (newRowPosition < 0) {
- // This should never happen, but...
- return;
- }
- disposeEmptyTablePlaceholder();
- // If the current widget has a selection, deselect it
- deselect(e.widget);
- // If the new row is in the visible space, refresh it
- if (topRow <= newRowPosition &&
- numRowsVisible > newRowPosition - topRow) {
- insertRowAt(newRowPosition - topRow);
- ++numRowsInCollection;
- updateVisibleRows();
- int newRowNumber = newRowPosition - topRow;
- if (newRowNumber != currentRow) {
- internalSetSelection(currentColumn, newRowNumber, false);
- } else {
- internalSetSelection(currentColumn, newRowNumber, true);
- }
- return;
- }
- // else...
- ++numRowsInCollection;
- // If the new row is above us, scroll up to it
- if (newRowPosition < topRow + currentRow) {
- setTopRow(newRowPosition);
- Display.getDefault().asyncExec(new Runnable() {
- public void run() {
- updateVisibleRows();
- if (currentRow != 0) {
- internalSetSelection(currentColumn, 0, false);
- } else {
- internalSetSelection(currentColumn, 0, true);
- }
- }
- });
- } else {
- // If we're appending
- if (numRowsInDisplay > numRowsVisible) {
- updateVisibleRows();
- int newRowNumber = newRowPosition - topRow;
- if (newRowNumber != currentRow) {
- internalSetSelection(currentColumn, newRowNumber, false);
- } else {
- internalSetSelection(currentColumn, newRowNumber, true);
- }
- } else {
- // It's somewhere in the middle below us; scroll down to it
- setTopRow(newRowPosition-numRowsVisible+1);
- int newRowNumber = numRowsVisible-1;
- if (newRowNumber != currentRow) {
- internalSetSelection(currentColumn, newRowNumber, false);
- } else {
- internalSetSelection(currentColumn, newRowNumber, true);
- }
- }
- }
- return;
- case SWT.ARROW_UP:
- if (maxRowsVisible <= 1)
- return;
- if (currentRow > 0) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(e.widget);
- internalSetSelection(currentColumn, currentRow-1, false);
- return;
- }
- if (topRow > 0) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(e.widget);
- setTopRow(topRow - 1);
- internalSetSelection(currentColumn, currentRow, true);
- return;
- }
- return;
- case SWT.ARROW_DOWN:
- if (maxRowsVisible <= 1)
- return;
- if (currentRow < numRowsVisible-1) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(e.widget);
- internalSetSelection(currentColumn, currentRow+1, false);
- return;
- }
- if (topRow + numRowsVisible < numRowsInCollection) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(e.widget);
- setTopRow(topRow + 1);
- internalSetSelection(currentColumn, currentRow, true);
- return;
- }
- return;
- case SWT.PAGE_UP:
- if (topRow > 0) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- int newTopRow = topRow - numRowsInDisplay;
- if (newTopRow < 0) {
- newTopRow = 0;
- }
- setTopRow(newTopRow);
- internalSetSelection(currentColumn, currentRow, true);
- }
- return;
- case SWT.PAGE_DOWN:
- if (topRow + numRowsVisible < numRowsInCollection) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- int newTopRow = topRow + numRowsVisible;
- if (newTopRow >= numRowsInCollection - 1) {
- newTopRow = numRowsInCollection - 1;
- }
- setTopRow(newTopRow);
- if (currentRow < numRowsVisible) {
- internalSetSelection(currentColumn, currentRow, true);
- } else {
- internalSetSelection(currentColumn, numRowsVisible-1, true);
- }
- }
- return;
- }
- }
- /**
- * Handle the keyTraversed event on any child control in the table.
- *
- * @param sender The row sending the event.
- * @param e The SWT TraverseEvent
- */
- public void keyTraversed(TableRow sender, TraverseEvent e) {
- if (e.detail == SWT.TRAVERSE_TAB_NEXT) {
- if (currentColumn >= sender.getNumColumns() - 1) {
- e.detail = SWT.TRAVERSE_NONE;
- handleNextRowNavigation();
- }
- } else if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
- if (currentColumn == 0) {
- e.detail = SWT.TRAVERSE_NONE;
- handlePreviousRowNavigation(sender);
- }
- } else if (e.detail == SWT.TRAVERSE_RETURN) {
- e.detail = SWT.TRAVERSE_NONE;
- if (currentColumn >= sender.getNumColumns() - 1) {
- handleNextRowNavigation();
- } else {
- deferredSetFocus(getControl(currentColumn+1, currentRow), false);
- }
- }
- }
- /**
- * The SelectionListener for the table's slider control.
- */
- private SelectionListener sliderSelectionListener = new SelectionListener() {
- public void widgetSelected(SelectionEvent e) {
- if (slider.getSelection() == topRow) {
- return;
- }
- if (!fireRequestRowChangeEvent()) {
- slider.setSelection(topRow);
- return;
- }
- deselect(getControl(currentColumn, currentRow));
- setTopRow(slider.getSelection());
- deferredSetFocus(getControl(currentColumn, currentRow), true);
- }
- public void widgetDefaultSelected(SelectionEvent e) {
- widgetSelected(e);
- }
- };
- /**
- * Scroll wheel event handling.
- */
- public void handleEvent(Event event) {
- if (event.count > 0) {
- if (topRow > 0) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- deselect(getControl(currentColumn, currentRow));
- setTopRow(topRow - 1);
- deferredSetFocus(getControl(currentColumn, currentRow), true);
- }
- } else {
- if (topRow < numRowsInCollection - numRowsVisible) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- deselect(getControl(currentColumn, currentRow));
- setTopRow(topRow + 1);
- deferredSetFocus(getControl(currentColumn, currentRow), true);
- }
- }
- }
- /**
- * Handle focusLost events on any child control. This is not currently used.
- *
- * @param sender The row containing the sending control.
- * @param e The SWT FocusEvent.
- */
- public void focusLost(TableRow sender, FocusEvent e) {
- }
- /**
- * Handle focusGained events on any child control.
- *
- * @param sender The row containing the sending control.
- * @param e The SWT FocusEvent.
- */
- public void focusGained(TableRow sender, FocusEvent e) {
- boolean rowChanged = false;
- if (getRowNumber(sender) != currentRow) {
- if (needToRequestRC) {
- if (!fireRequestRowChangeEvent()) {
- // Go back if we're not allowed to be here
- deferredSetFocus(getControl(currentColumn, currentRow), false);
- }
- } else {
- needToRequestRC = true;
- }
- rowChanged = true;
- }
- currentRow = getRowNumber(sender);
- currentColumn = sender.getColumnNumber((Control)e.widget);
- if (rowChanged)
- fireRowArriveEvent();
- }
- private PaintListener headerPaintListener = new PaintListener() {
- public void paintControl(PaintEvent e) {
- if (parent.gridLinesOn) {
- drawGridLines(e, true);
- }
- }
- };
- private PaintListener rowPaintListener = new PaintListener() {
- public void paintControl(PaintEvent e) {
- if (parent.gridLinesOn) {
- drawGridLines(e, false);
- }
- }
- };
- private void drawGridLines(PaintEvent e, boolean isHeader) {
- Color oldColor = e.gc.getForeground();
- try {
- // Get the colors we need
- Display display = Display.getCurrent();
- Color lineColor = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
- Color secondaryColor = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
- Color hilightColor = display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
- if (!isHeader) {
- lineColor = display.getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
- }
- // Get the control
- Control toPaint = (Control) e.widget;
- Point controlSize = toPaint.getSize();
- // Draw the bottom line(s)
- e.gc.setForeground(lineColor);
- e.gc.drawLine(0, controlSize.y-1, controlSize.x, controlSize.y-1);
- if (isHeader) {
- e.gc.setForeground(secondaryColor);
- e.gc.drawLine(0, controlSize.y-2, controlSize.x, controlSize.y-2);
- e.gc.setForeground(hilightColor);
- e.gc.drawLine(0, 1, controlSize.x, 1);
- }
- // Now draw lines around the child controls, if there are any
- if (toPaint instanceof Composite) {
- Composite row = (Composite) toPaint;
- Control[] children = row.getChildren();
- for (int i = 0; i < children.length; i++) {
- Rectangle childBounds = children[i].getBounds();
- // Paint the beginning lines
- if (isHeader) {
- e.gc.setForeground(hilightColor);
- e.gc.drawLine(childBounds.x-2, 1, childBounds.x-2, controlSize.y-2);
- }
- // Paint the ending lines
- e.gc.setForeground(lineColor);
- int lineLeft = childBounds.x + childBounds.width+1;
- e.gc.drawLine(lineLeft, 0, lineLeft, controlSize.y);
- if (isHeader) {
- e.gc.setForeground(secondaryColor);
- e.gc.drawLine(lineLeft-1, 0, lineLeft-1, controlSize.y-1);
- }
- }
- }
- } finally {
- e.gc.setForeground(oldColor);
- }
- }
- // Event Firing -------------------------------------------------------------------------------
- /**
- * Fire the row construction event
- *
- * @param newControl The new row's SWT control
- */
- private void fireRowConstructionEvent(Control newControl) {
- for (Iterator rowConstructionListenersIter = parent.rowConstructionListeners.iterator(); rowConstructionListenersIter.hasNext();) {
- IRowConstructionListener listener = (IRowConstructionListener) rowConstructionListenersIter.next();
- listener.rowConstructed(newControl);
- }
- }
- /**
- * Indicate to listeners that the focus is arriving on the specified row
- */
- private void fireRowArriveEvent() {
- if (rows.size() < 1) {
- return;
- }
- for (Iterator rowChangeListenersIter = parent.rowFocusListeners.iterator(); rowChangeListenersIter.hasNext();) {
- IRowFocusListener listener = (IRowFocusListener) rowChangeListenersIter.next();
- listener.arrive(parent, topRow+currentRow, currentRow().getRowControl());
- }
- }
- /**
- * Request permission from all listeners to leave the current row.
- *
- * @return true if all listeners permit the row change; false otherwise.
- */
- private boolean fireRequestRowChangeEvent() {
- if (rows.size() < 1) {
- return true;
- }
- if (currentRow > rows.size()-1) {
- // (if the other row is already gone)
- return true;
- }
- for (Iterator rowChangeListenersIter = parent.rowFocusListeners.iterator(); rowChangeListenersIter.hasNext();) {
- IRowFocusListener listener = (IRowFocusListener) rowChangeListenersIter.next();
- if (!listener.requestRowChange(parent, topRow+currentRow, currentRow().getRowControl())) {
- return false;
- }
- }
- fireRowDepartEvent();
- return true;
- }
- /**
- * Indicate to listeners that the focus is about to leave the current row.
- */
- private void fireRowDepartEvent() {
- if (rows.size() < 1) {
- return;
- }
- for (Iterator rowChangeListenersIter = parent.rowFocusListeners.iterator(); rowChangeListenersIter.hasNext();) {
- IRowFocusListener listener = (IRowFocusListener) rowChangeListenersIter.next();
- listener.depart(parent, topRow+currentRow, currentRow().getRowControl());
- }
- }
- /**
- * Request deletion of the current row from the underlying data structure.
- *
- * @return true if the deletion was successful; false otherwise.
- */
- private boolean fireDeleteEvent() {
- if (parent.deleteHandlers.size() < 1) {
- return false;
- }
- int absoluteRow = topRow + currentRow;
- for (Iterator deleteHandlersIter = parent.deleteHandlers.iterator(); deleteHandlersIter.hasNext();) {
- IDeleteHandler handler = (IDeleteHandler) deleteHandlersIter.next();
- if (!handler.canDelete(absoluteRow)) {
- return false;
- }
- }
- for (Iterator deleteHandlersIter = parent.deleteHandlers.iterator(); deleteHandlersIter.hasNext();) {
- IDeleteHandler handler = (IDeleteHandler) deleteHandlersIter.next();
- handler.deleteRow(absoluteRow);
- }
- return true;
- }
- /**
- * Request that the model insert a new row into itself.
- *
- * @return The 0-based offset of the new row from the start of the collection or -1 if a
- * new row could not be inserted.
- */
- private int fireInsertEvent() {
- if (parent.insertHandlers.size() < 1) {
- return -1;
- }
- for (Iterator insertHandlersIter = parent.insertHandlers.iterator(); insertHandlersIter.hasNext();) {
- IInsertHandler handler = (IInsertHandler) insertHandlersIter.next();
- int resultRow = handler.insert(topRow+currentRow);
- if (resultRow >= 0) {
- return resultRow;
- }
- }
- return -1;
- }
- // Event Handling, utility methods ------------------------------------------------------------
- /**
- * Set the widget's selection to an empty selection.
- *
- * @param widget The widget to deselect
- */
- private void deselect(Widget widget) {
- if (DuckType.instanceOf(ISelectableRegionControl.class, widget)) {
- ISelectableRegionControl control = (ISelectableRegionControl) DuckType.implement(ISelectableRegionControl.class, widget);
- control.setSelection(0, 0);
- }
- }
- /**
- * Try to go to the next row in the collection.
- */
- private void handleNextRowNavigation() {
- if (currentRow < numRowsVisible-1) {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(getControl(currentColumn, currentRow));
- deferredSetFocus(getControl(0, currentRow+1), false);
- } else {
- if (topRow + numRowsVisible >= numRowsInCollection) {
- // We're at the end; don't go anywhere
- return;
- }
- // We have to scroll forwards
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(getControl(currentColumn, currentRow));
- setTopRow(topRow+1);
- deferredSetFocus(getControl(0, currentRow), true);
- }
- }
- /**
- * Try to go to the previous row in the collection.
- *
- * @param row The current table row.
- */
- private void handlePreviousRowNavigation(TableRow row) {
- if (currentRow == 0) {
- if (topRow == 0) {
- // We're at the beginning of the table; don't go anywhere
- return;
- }
- // We have to scroll backwards
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(getControl(currentColumn, currentRow));
- setTopRow(topRow-1);
- deferredSetFocus(getControl(row.getNumColumns()-1, 0), true);
- } else {
- if (!fireRequestRowChangeEvent()) {
- return;
- }
- needToRequestRC = false;
- deselect(getControl(currentColumn, currentRow));
- deferredSetFocus(getControl(row.getNumColumns()-1, currentRow-1), false);
- }
- }
- /**
- * Gets the current TableRow.
- *
- * @return the current TableRow
- */
- private TableRow currentRow() {
- if (currentRow > rows.size()-1) {
- return null;
- }
- return (TableRow) rows.get(currentRow);
- }
- /**
- * Returns the SWT control corresponding to the current row.
- *
- * @return the current row control.
- */
- public Control getCurrentRowControl() {
- TableRow currentRow = currentRow();
- if (currentRow == null) {
- return null;
- }
- return currentRow().getRowControl();
- }
- /**
- * Returns the TableRow by the specified 0-based offset from the top visible row.
- *
- * @param rowNumber 0-based offset of the requested fow starting from the top visible row.
- * @return The corresponding TableRow or null if there is none.
- */
- private TableRow getRowByNumber(int rowNumber) {
- if (rowNumber > rows.size()-1) {
- return null;
- }
- return (TableRow) rows.get(rowNumber);
- }
- /**
- * Return the SWT control at (column, row), where row is a 0-based number starting
- * from the top visible row.
- *
- * @param column the 0-based column.
- * @param row the 0-based row starting from the top visible row.
- * @return the SWT control at (column, row)
- */
- private Control getControl(int column, int row) {
- TableRow rowObject = getRowByNumber(row);
- if (rowObject == null) {
- throw new IndexOutOfBoundsException("Request for a nonexistent row"); //$NON-NLS-1$
- }
- Control result = rowObject.getColumnControl(column);
- return result;
- }
- /**
- * Return the 0-based row number corresponding to a particular TableRow object.
- *
- * @param row The TableRow to translate to row coordinates.
- * @return the 0-based row number or -1 if the specified TableRow is not visible.
- */
- private int getRowNumber(TableRow row) {
- int rowNumber = 0;
- for (Iterator rowIter = rows.iterator(); rowIter.hasNext();) {
- TableRow candidate = (TableRow) rowIter.next();
- if (candidate == row) {
- return rowNumber;
- }
- ++rowNumber;
- }
- return -1;
- }
- /**
- * Set the focus to the specified (column, row). If rowChange is true, fire a
- * row change event, otherwise be silent.
- *
- * @param column The 0-based column to focus
- * @param row The 0-based row to focus
- * @param rowChange true if a row change event should be fired; false otherwise.
- */
- private void internalSetSelection(int column, int row, boolean rowChange) {
- Control toFocus = getControl(column, row);
- deferredSetFocus(toFocus, rowChange);
- }
- /**
- * Set the focus to the specified control after allowing all pending events to complete
- * first. If rowChange is true, fire a row arrive event after the focus has been set.
- *
- * @param toFocus The SWT Control to focus
- * @param rowChange true if the rowArrive event should be fired; false otherwise.
- */
- private void deferredSetFocus(final Control toFocus, final boolean rowChange) {
- Display.getCurrent().asyncExec(new Runnable() {
- public void run() {
- toFocus.setFocus();
- if (rowChange) {
- fireRowArriveEvent();
- }
- }
- });
- }
- }