📄 ktable.java
字号:
/*
* Copyright (C) 2004 by Friederich Kupzog Elektronik & Software
* 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
Authors:
Friederich Kupzog, fkmk@kupzog.de, www.kupzog.de/fkmk
Lorenz Maierhofer, lorenz.maierhofer@logicmindguide.com
*/
package de.kupzog.ktable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Shell;
import de.kupzog.ktable.renderers.TextCellRenderer;
/**
* Custom drawn tabel widget for SWT GUIs.
* <p>
* The idea of KTable is to have a flexible grid of cells to display data in it.
* The class focuses on displaying data and not on collecting the data to
* display. The latter is done by the <code>KTableModel</code> which has to be implemented
* for each specific case. Some default tasks are done by a base implementation
* called <code>KTableDefaultModel</code>. Look also into <code>KTableSortedModel</code> that provides
* a transparent sorting of cells.<br>
* The table asks the table model for the amount of
* columns and rows, the sizes of columns and rows and for the content of the
* cells which are currently drawn. Even if the table has a million rows, it
* won�t get slower because it only requests those cells it currently draws.
* Only a bad table model can influence the drawing speed negatively.
* <p>
* When drawing a cell, the table calls a <code>KTableCellRenderer</code> to do this work.
* The table model determines which cell renderer is used for which cell. A
* default renderer is available (<code>KTableCellRenderer.defaultRenderer</code>), but the
* creation of self-written renderers for specific purposes is assumed.
* Some default renderers are available in the package <code>de.kupzog.ktable.cellrenderers.*</code>.
* <p>
* KTable allows to F columns and rows. Each column can have an individual
* size while the rows are all of the same height except the first row. Multiple
* column and row headers are possible. These "fixed" cells will not be scrolled
* out of sight. The column and row count always starts in the upper left corner
* with 0, independent of the number of column headers or row headers.
* <p>
* It is also possible to span cells over several rows and/or columns. The KTable
* asks the model do provide this information via <code>belongsToCell(col, row)</code>.
* This method must return the cell the given cell should be merged with.
* <p>
* Changing of model values is possible by implementations of <code>KTableCellEditor</code>.
* Again the KTable asks the model to provide an implementation. Note that there
* are multiple celleditors available in the package <code>de.kupzog.ktable.editors</code>!
*
* @author Friederich Kupzog
* @see de.kupzog.ktable.KTableModel
* @see de.kupzog.ktable.KTableDefaultModel
* @see de.kupzog.ktable.KTableSortedModel
* @see de.kupzog.ktable.KTableCellRenderer
* @see de.kupzog.ktable.KTableCellEditor
* @see de.kupzog.ktable.KTableCellSelectionListener
*
*/
public class KTable extends Canvas {
// Data and data editing facilities:
protected KTableModel m_Model;
protected KTableCellEditor m_CellEditor;
// current visible:
protected int m_TopRow;
protected int m_LeftColumn;
// Selection
protected HashMap m_Selection;
protected int m_FocusRow;
protected int m_FocusCol;
protected int m_MainFocusRow;
protected int m_MainFocusCol;
protected int m_ClickColumnIndex;
protected int m_ClickRowIndex;
private int m_Style=SWT.NONE;
// important measures
protected int m_RowsVisible;
protected int m_RowsFullyVisible;
protected int m_ColumnsVisible;
protected int m_ColumnsFullyVisible;
// column sizes
protected int m_ResizeColumnIndex;
protected int m_ResizeColumnLeft;
protected int m_ResizeRowIndex;
protected int m_ResizeRowTop;
protected int m_NewRowSize;
protected int m_NewColumnSize;
protected boolean m_Capture = false;
protected Image m_LineRestore;
protected int m_LineX;
protected int m_LineY;
// resize area
protected int m_ResizeAreaSize = 10;
// diverse
protected Display m_Display;
protected ArrayList cellSelectionListeners;
protected ArrayList cellDoubleClickListeners;
protected ArrayList cellResizeListeners;
protected Cursor m_defaultCursor;
protected Point m_defaultCursorSize;
protected Cursor m_defaultRowResizeCursor;
protected Cursor m_defaultColumnResizeCursor;
protected String m_nativTooltip;
//////////////////////////////////////////////////////////////////////////////
// CONSTRUCTOR & DISPOSE
//////////////////////////////////////////////////////////////////////////////
/**
* Creates a new KTable.
*
* possible styles:
* <ul>
* <li><b>SWT.V_SCROLL</b> - show vertical scrollbar and allow vertical scrolling by arrow keys</li>
* <li><b>SWT.H_SCROLL</b> - show horizontal scrollbar and allow horizontal scrolling by arrow keys</li>
* <li><b>SWTX.AUTO_SCROLL</b> - Dynamically shows vertical and horizontal scrollbars when they are necessary.</li>
* <li><b>SWTX.FILL_WITH_LASTCOL</b> - Makes the table enlarge the last column to always fill all space.</li>
* <li><b>SWTX.FILL_WITH_DUMMYCOL</b> - Makes the table fill any remaining space with dummy columns to fill all space.</li>
* <li><b>SWT.FLAT</b> - Does not paint a dark outer border line.</li>
* <li><b>SWT.MULTI</b> - Sets the "Multi Selection Mode".
* In this mode, more than one cell or row can be selected.
* The user can achieve this by shift-click and ctrl-click.
* The selected cells/rows can be scattored ofer the complete table.
* If you pass false, only a single cell or row can be selected.
* This mode can be combined with the "Row Selection Mode".</li>
* <li><b>SWT.FULL_SELECTION</b> - Sets the "Full Selection Mode".
* In the "Full Selection Mode", the table always selects a complete row.
* Otherwise, each individual cell can be selected.
* This mode can be combined with the "Multi Selection Mode".</li>
* <li><b>SWTX.EDIT_ON_KEY</b> - Activates a possibly present cell editor
* on every keystroke. (Default: only ENTER). However, note that editors
* can specify which events they accept.</li>
* <li><b>SWTX.MARK_FOCUS_HEADERS</b> - Makes KTable draw left and top header cells
* in a different style when the focused cell is in their row/column.
* This mimics the MS Excel behavior that helps find the currently
* selected cell(s).</li>
* <li><b>SWT.HIDE_SELECTION</b> - Hides the selected cells when the KTable
* looses focus.</li>
* After creation a table model should be added using setModel().
*/
public KTable(Composite parent, int style) {
// Initialize canvas to draw on.
super(parent, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | style);
m_Style = style;
//inits
m_Display = Display.getCurrent();
m_Selection = new HashMap();
m_CellEditor = null;
m_TopRow = 0;
m_LeftColumn = 0;
m_FocusRow = 0;
m_FocusCol = 0;
m_RowsVisible = 0;
m_RowsFullyVisible = 0;
m_ColumnsVisible = 0;
m_ColumnsFullyVisible = 0;
m_ResizeColumnIndex = -1;
m_ResizeRowIndex = -1;
m_ResizeRowTop = -1;
m_NewRowSize = -1;
m_NewColumnSize = -1;
m_ResizeColumnLeft = -1;
m_ClickColumnIndex = -1;
m_ClickRowIndex = -1;
m_LineRestore = null;
m_LineX = 0;
m_LineY = 0;
cellSelectionListeners = new ArrayList(10);
cellDoubleClickListeners = new ArrayList(10);
cellResizeListeners = new ArrayList(10);
// Listener creation
createListeners();
// handle tooltip initialization:
m_nativTooltip = super.getToolTipText();
super.setToolTipText("");
// apply various style bits:
if ((style & SWTX.AUTO_SCROLL) == SWTX.AUTO_SCROLL) {
addListener(SWT.Resize, new Listener() {
public void handleEvent(Event event) {
updateScrollbarVisibility();
}
});
addCellResizeListener(new KTableCellResizeListener() {
public void rowResized(int row, int newHeight) {
updateScrollbarVisibility();
}
public void columnResized(int col, int newWidth) {
updateScrollbarVisibility();
}
});
}
}
public void dispose() {
if (m_defaultCursor != null)
m_defaultCursor.dispose();
if (m_defaultRowResizeCursor != null)
m_defaultRowResizeCursor.dispose();
if (m_defaultColumnResizeCursor != null)
m_defaultColumnResizeCursor.dispose();
super.dispose();
}
protected void createListeners() {
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
onPaint(event);
}
});
addControlListener(new ControlAdapter() {
public void controlResized(ControlEvent e) {
redraw();
}
});
addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
onMouseDown(e);
}
public void mouseUp(MouseEvent e) {
onMouseUp(e);
}
public void mouseDoubleClick(MouseEvent e) {
onMouseDoubleClick(e);
}
});
addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
onMouseMove(e);
}
});
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
onKeyDown(e);
}
});
addCellSelectionListener(new KTableCellSelectionAdapter() {
private Point[] oldSelections;
public void cellSelected(int col, int row, int statemask) {
if (isHighlightSelectionInHeader() && (statemask & SWT.SHIFT) == 0) {
Point[] selections = getCellSelection();
GC gc = new GC(KTable.this);
repaintRelevantCells(gc, oldSelections);
repaintRelevantCells(gc, selections);
gc.dispose();
oldSelections = selections;
}
}
private void repaintRelevantCells(GC gc, Point[] selections) {
if (selections==null) return;
Rectangle bounds = getClientArea();
Rectangle oldClipping = gc.getClipping();
int fixedWidth = 0;
int fixedHeight = 0;
for (int k=0; k<m_Model.getFixedHeaderColumnCount(); k++)
fixedWidth += getCellRectIgnoreSpan(k, 0).width+1;
for (int k=0; k<m_Model.getFixedHeaderRowCount(); k++)
fixedHeight += getCellRectIgnoreSpan(0, k).height+1;
for (int i=0; i<selections.length; i++) {
int col = selections[i].x;
int row = selections[i].y;
for (int j=0; j<getModel().getFixedHeaderColumnCount(); j++) {
Point valid = getValidCell(j, row);
// allow painting of GC only on columns, not on rows:
Rectangle rowClip = new Rectangle(1,1+fixedHeight,fixedWidth,bounds.height-1-fixedHeight);
rowClip.intersect(oldClipping);
gc.setClipping(rowClip);
drawCell(gc, valid.x, valid.y);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -