|
|
< Day Day Up > |
|
9.2 Classes Table, TableItem, and TableColumn9.2.1 Example
9.2.2 Table, TableItem, and TableColumn Hierarchies
9.2.3 Table Styles
9.2.4 Table Events
9.2.5 TableItem Styles (none)
9.2.6 TableItem Events (none)
9.2.7 TableColumn Styles
9.2.8 TableColumn Events
Tables are selectable controls that are capable of displaying vertical rows of items. Each row is represented by an instance of TableItem. Table items are automatically added when created and are positioned in the parent using the location constructor parameter. Tables are analogous to lists but are more flexible. Although lists can contain only strings, tables can contain strings and images. Tables are also capable of displaying multiple columns. A table that is displaying columns uses instances of TableColumn to represent each column. Tables can optionally include headers and grid lines. The header portion of a table resembles a row of push buttons displayed at the top of the control. Each button in the header is the width of the corresponding table column. Grid lines refer to a horizontal and/or vertical pattern that is drawn to accentuate the rows and columns. This pattern is often composed of a matrix or grid of lines drawn between the items or by alternating the background color of the rows.[5]
Table shares many of the same concepts, limitations, and behaviors of List and Tree.
Like List and Tree, Table can allow either a single item or multiple items to be selected at a time and supports scroll bars and borders. If you are familiar with the sections Classes Tree and TreeItem and Class List, you will find the material in this section easy to follow. The API of class Table is very similar to those classes. Users are familiar with tables because they are used everywhere on the desktop. For example, the Windows XP Task Manager uses a table with multiple columns to show tasks and their status.
9.2.9 Full Row SelectionWhen the user selects a table item, some platforms require the user to select either the text or icon in order to select the item.[6] On other platforms, the user can select anywhere within the same row. This behavior is platform-specific but can be configured by creating a table with the style SWT.FULL _SELECTION. When this style is provided, selecting any column on the same row as a table item selects the item. Tables often indicate SWT.FULL _SELECTION by drawing the selection highlight over the entire row. It is important to note that SWT.FULL_SELECTION is a hint and may not be honored on some platforms.
9.2.10 Hiding the SelectionWhen a table loses focus, the selection is either unchanged or draws in some manner to indicate that focus is no longer in the control. The SWT.HIDE _SELECTION style causes a table to hide its selection when focus is lost, drawing as though it were not selected. The SWT.HIDE_SELECTION style is a hint that may not be honored on some platforms. 9.2.11 Tables in List ModeWhen you create table items but do not create any table columns, Table acts like List. This kind of table is said to be operating in "list mode." However, even though you did not create a table column, the user still sees a column of items, just like the column of strings that is displayed by a list. This can be a bit confusing at first because there is clearly a column of items on the screen but a table column was not created. It helps in this case to think of the table as a fancy kind of list, one that is capable of displaying both icons and strings. List does not require you to create a list column in order to display strings, so neither does Table. The following program creates a table with 1,000 items but does not create a table column.
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
Table table = new Table(shell, SWT.BORDER);
for (int i = 0; i < 1000; i++) {
TableItem item = new TableItem(table, SWT.NULL);
item.setText("Item " + i);
}
table.setSize(200, 200);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
9.2.12 Getting the ItemsUsing getItems(), you can retrieve the items that you created in a table.
9.2.13 Headers and Grid LinesHeaders and grid lines are properties of tables. Although it is possible to show both when a table is in list mode, it generally is not very useful. You will use both when we describe Tables with Multiple Columns later in this chapter.
9.2.14 Item HeightYou can find out the height of an item within a table control using getItemHeight().
The getItemHeight() method is often used to compute the initial size of a control, in this case, a table. Refer to the section Lines and the Line Delimiter in Class Text for an example of this. The code to compute the initial size for a table is equivalent but uses getItemHeight() and getHeaderHeight() instead of getLineHeight().
int height =
rows * table.getItemHeight() + table.getHeaderHeight();
int width = columns * fm.getAverageCharWidth();
table.setSize(table.computeSize(width, height));
9.2.15 Tables with Multiple ColumnsTo have multiple columns in a table, you must create instances of TableColumn to represent each column. As soon as you create the first table column, the table is no longer operating in list mode. This means that you are responsible for setting the widths of the columns so that the user can see the data that they will contain. The following program creates a table with 1,000 items and creates a single table column. The table column is set to be 100 pixels wide so the user can see the strings. Otherwise, the code is identical to the example code in Tables in List Mode.
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
Table table = new Table(shell, SWT.BORDER);
TableColumn column = new TableColumn(table, SWT.NONE);
column.setWidth(100);
for (int i = 0; i < 1000; i++) {
TableItem item = new TableItem(table, SWT.NULL);
item.setText("Item " + i);
}
table.setSize(200, 200);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
A table that has one table column is not particularly useful (although were the header to be visible, a program could use it to invoke a sort operation). On some platforms, tables use the column width to determine when scroll bars are needed. This means that in the above example, if a table item were to contain a string that was wider than 100 pixels, it would be clipped. This does not happen to a table that is operating in list mode. Because there are no table columns, there is nothing to clip. When you use a table that has multiple columns, often you will decide to show the table header. By default, the header is not visible. Showing the header allows the user to resize the columns. Here is a more realistic example that creates a table with a header and multiple columns. To ensure that the table and table columns are a reasonable size, the table columns are packed after all of the items have been created. Figure 9.5 shows the result running on Macintosh OS X.
static final int COLUMNS = 8, ROWS = 8;
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
Table table = new Table(shell, SWT.BORDER);
table.setHeaderVisible(true);
table.setLinesVisible(true);
for (int i = 0; i < COLUMNS; i++) {
TableColumn column=new TableColumn(table,SWT.NONE);
column.setText("Column " + i);
}
for (int i = 0; i < ROWS; i++) {
TableItem item = new TableItem(table, SWT.NULL);
for (int j = 0; j < COLUMNS; j++) {
item.setText(j, "Item " + i + "-" + j);
}
}
for (int i = 0; i < COLUMNS; i++) {
TableColumn column = table.getColumn(i);
column.pack();
}
table.pack();
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
Figure 9.5.![]() 9.2.16 Text and Images in a Table ColumnTableColumn, like many other widgets in SWT, supports the standard API to set and get text and images. TableColumn supports mnemonic characters in strings but does not wrap or support the SWT.WRAP style.
9.2.17 The Width of a Table ColumnWhen a table column is created, like the other widgets in SWT, its width is zero. You can use setWidth() or pack() to resize a table column.
9.2.18 Alignment in a Table ColumnWhen a table column is created, the alignment of the column can be configured using one of the styles SWT.LEFT, SWT.CENTER, or SWT.RIGHT, just like Label and Button. Alignment in TableColumn differs from alignment in the other widgets because it applies to the entire column, not just the text or image in the header. After the column has been created, the setAlignment() method can be used to change alignment.
The default alignment style for a table column is SWT.LEFT. Unlike Button alignment, TableColumn alignment is not part of the platform look and feel, so you should feel free to change it to suit your needs.
9.2.19 Text and Images in a Table ItemTableItem, like TreeItem and other widgets, supports the standard API to set text and images. Unlike TreeItem, when the table has multiple columns, you can set the text and image for each individual cell. TableItem does not support mnemonics, wrapping, or the SWT.WRAP style.
TableItem provides two convenience methods that allow you to set multiple strings and images at the same time. They are currently no faster than setting the individual properties but may be optimized in the future.
9.2.20 Checked and Grayed Table ItemsLike Tree, Table supports checked and grayed check boxes. In order to check or gray the check box for an item, Table must have been created with the SWT.CHECK style.
9.2.21 TableItem Foreground, Background, and FontTable supports the ability to set the foreground color, background color, and font of a table item. Unlike TreeItem (which does not support columns), TableItem allows you to set the color and font of an individual cell.
9.2.22 Removing Table ItemsTable items are removed using the remove() and removeAll() methods.[8] Removing a table item has the same affect as disposing of it. However, it is much faster to remove a range of table items rather than dispose of them one at a time. For this reason, and because tables are index-based controls, it makes sense for Table to define remove() operations.
9.2.23 The SelectionTable supports the same kind of selection API as List, with the significant difference that parameters that are Strings in class List are TableItems in class Table. Tables cannot, by definition, contain duplicate instances of TableItem.[9] As you would expect, the strings and icons within different instances of TableItem can be the same, causing the two objects to look the same, despite the fact that they are not identical.
9.2.24 Deselecting and Selecting ItemsTo manipulate the selection of individual items without affecting the current selection, Table defines the equivalent List API. The methods in this section differ from the setSelection() methods in Table in the same significant ways.
9.2.25 ScrollingBecause Table and List are index-based, scrolling in a table is equivalent to scrolling in a list. Both use the "top index" concept.
Tables, lists, trees, and text controls all define showSelection(). Tables define the method showItem() that has an equivalent method in class Tree.
9.2.26 Search and Hit Test OperationsTables provide a number of search operations that allow you to find the index of an item and determine the item that is underneath a point. The term hit testing is used to describe this kind of operation. Hit testing for controls was discussed in Getting the Cursor Control in the Display chapter.
9.2.27 Implementing Your Own Hit Test OperationsSometimes you might need to find out the row and column of the item where the mouse was pressed in a table with multiple columns. Unfortunately, the getItem(Point point) method does not do what you want. For tables that are not created with SWT.FULL_SELECTION, only the first column is searched. Fortunately, table items allow you to determine their bounds. Using this, you can write your own hit test operation.
The following fragment adds an SWT.MouseDown listener to a table. This listener uses getBounds() and Rectangle operations to determine the row and column in the table where the mouse was pressed.
table.addListener(SWT.MouseDown, new Listener() {
public void handleEvent(Event event) {
Rectangle rect = table.getClientArea();
int itemCount = table.getItemCount();
int columnCount = table.getColumnCount();
int i = table.getTopIndex();
while (i < itemCount) {
TableItem item = table.getItem(i);
for (int j = 0; j < columnCount; j++) {
Rectangle bounds = item.getBounds(j);
if (bounds.y > rect.height) return;
if (bounds.contains(event.x, event.y)) {
System.out.println(item.getText(j));
return;
}
}
i++;
}
}
});
9.2.28 Working with Large TablesTables that are virtual allow you to configure table items on demand. When the data for each table item is required, the table requests it from your program. The advantage of virtual tables is that they request only the data that they need. Performance is improved by putting the cost of configuring items where it is unnoticed and not bothering to configure items that are unreferenced.
The following program creates a large table and fills it with strings, one page of data at a time. While Table is being filled, a progress bar shows the number of table items that have been created. The function that fills the table is called fillTable(). Several alternate implementations of the fillTable() method follow the main body of the code.
static final int COLUMNS = 3, ROWS = 100000, PAGE = 100;
static final String[][] DATA = new String[ROWS][COLUMNS];
static {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLUMNS; j++) {
DATA[i][j] = "Item " + i + "-" + j;
}
}
}
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
RowLayout layout = new RowLayout(SWT.VERTICAL);
layout.fill = true;
shell.setLayout(layout);
final Table table = new Table(shell, SWT.BORDER);
table.setLayoutData(new RowData(400, 400));
table.setHeaderVisible(true);
for (int i = 0; i < COLUMNS; i++) {
TableColumn column=new TableColumn(table,SWT.NONE);
column.setText("Column " + i);
column.setWidth(128);
}
final ProgressBar progress =
new ProgressBar(shell, SWT.NONE);
progress.setMaximum(ROWS - 1);
shell.pack();
shell.open();
fillTable(table, progress, PAGE);
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
Using setRedraw() When Creating Table ItemsThe simplest way to improve performance when filling a table is to use setRedraw(). Many operating systems use setRedraw() to optimize drawing and defer expensive calculations when adding (the native equivalent of) table items. For a complete description of setRedraw(), see Turning off Redraw in the Control Fundamentals chapter. Here is an implementation of fillTable() that uses setRedraw() to turn off drawing while the table items are created.
static void fillTable(
final Table table,
final ProgressBar progress,
final int page) {
table.setRedraw(false);
for (int i = 0; i < ROWS; i++) {
TableItem item = new TableItem(table, SWT.NULL);
for (int j = 0; j < COLUMNS; j++) {
item.setText(j, DATA[i][j]);
}
if (i % page == 0) progress.setSelection(i);
}
table.setRedraw(true);
progress.setSelection(0);
}
Creating TableItems in an Idle HandlerOne technique that avoids making the user wait for a large table is to create table items in an "idle handler" (see Using asyncExec() from the User Interface Thread in the chapter Display). Because asyncExec() runnables are executed when the event loop is idle, priority is given to the user interface thread. Creating table items this way is somewhat slower than the equivalent code that fills the table in the user interface thread. This is due to the overhead of the runnables and the fact that setRedraw() cannot be used without causing the table to flash. However, this approach has the advantage that the user can browse the items that have been created so far while the table is still being filled. A smarter version of fillTable() could also allow the user to cancel the operation. This implementation of fillTable() uses asyncExec() to create a table item when the event loop is idle.
static void fillTable(
final Table table,
final ProgressBar progress,
final int page) {
final Display display = table.getDisplay();
Runnable runnable = new Runnable() {
int index = 0;
public void run() {
if (table.isDisposed()) return;
int end = Math.min(index + page, ROWS);
while (index < end) {
TableItem item =
new TableItem(table, SWT.NULL);
for (int j = 0; j < COLUMNS; j++) {
item.setText(j, DATA[index][j]);
}
index++;
}
if (end == ROWS) end = 0;
progress.setSelection(end);
if (index < ROWS) display.asyncExec(this);
}
};
display.asyncExec(runnable);
}
Creating Table Items in Another ThreadThis approach is similar to using asyncExec() from the user interface thread. It has similar behavior and performance characteristics. However, the approach differs because it uses syncExec() and does not favor the user interface thread. If asyncExec() were to be used instead, the table-filling thread would never be blocked waiting on the user interface thread because asyncExec() does not wait. Threading, syncExec(), and asyncExec() are discussed in great detail in the Multithreaded Programming section of the chapter Display. The following fillTable() implementation uses syncExec() to create items, a page at a time, in the user interface thread.
static void fillTable(
final Table table,
final ProgressBar progress,
final int page) {
final Display display = table.getDisplay();
Thread thread = new Thread() {
public void run() {
int index = 0;
while (index < ROWS) {
if (table.isDisposed()) return;
final int start = index;
final int end=Math.min(index + page, ROWS);
display.syncExec(new Runnable() {
public void run() {
if (table.isDisposed()) return;
for (int i = start; i < end; i++) {
TableItem item =
new TableItem(table,SWT.NULL);
for (int j=0; j<COLUMNS; j++) {
item.setText(j,DATA[i][j]);
}
}
int value = end == ROWS ? 0 : end;
progress.setSelection(value);
}
});
index = end;
}
}
};
thread.start();
}
Each of the above strategies will be applicable in some situation. Even when the virtual table support becomes available in SWT, these solutions are still applicable for data that is streamed. 9.2.29 Table EventsSWT.Selection (SelectionEvent)The SWT.Selection event (typed event SelectionEvent) is sent whenever the user selects an item with the mouse or the keyboard. The relevant event fields during a selection event for Table are as follows.
SWT.DefaultSelection (SelectionEvent)The SWT.DefaultSelection event (typed event SelectionEvent) is sent whenever the user performs the platform-specific operation that indicates default selection. On most platforms, default selection occurs when the user double-clicks on an item or presses the <Enter> key. The relevant event fields during a default selection event for Table are as follows.
9.2.30 Using SWT.Selection with a Check Box TableThe following program uses the SWT.Selection event along with the item and detail fields to detect when a table item has been either checked or unchecked.
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
Table table = new Table(shell, SWT.CHECK | SWT.BORDER);
for (int i = 0; i < 32; i++) {
TableItem item = new TableItem(table, SWT.NULL);
item.setText("Item " + i);
}
table.setSize(200, 200);
table.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
if (event.detail == SWT.CHECK) {
TableItem item = (TableItem) event.item;
System.out.println(
item + " checked " + item.getChecked());
}
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
9.2.31 TableColumn EventsSWT.Selection (SelectionEvent)The SWT.Selection event (typed event SelectionEvent) is sent whenever the user selects a column header with the mouse or the keyboard. Selection events contain meaningful values in only the display, widget, and type fields. Using SWT.Selection to Sort Items in a TableSorting items in a table using a table column selection to initiate the sort is a very common user interface metaphor. The following program creates a table with two columns of data. Each item of data is stored in the class MyData.
static class MyData {
String string1, string2;
public MyData(String string1, String string2) {
this.string1 = string1;
this.string2 = string2;
}
}
When the table is created, the setData() method is used to associate instances of MyData with TableItem. The sort() method sorts data by column in either ascending or descending order. The first part of the method creates an array of MyData using getData() to retrieve instances of MyData from the items in the table. The column parameter indicates the column in the table that should be sorted. After the data has been sorted, it is assigned back to each table item, and the string for the item is updated.
static void sort(
Table table,
final int column,
final boolean descend) {
int count = table.getItemCount();
MyData[] list = new MyData[count];
for (int i = 0; i < count; i++) {
Object data = table.getItem(i).getData();
list[i] = (MyData) data;
}
Arrays.sort(list, new Comparator() {
public int compare(Object a, Object b) {
MyData d1 = (MyData) (descend ? b : a);
MyData d2 = (MyData) (descend ? a : b);
switch (column) {
case 0 :
return d1.string1.compareTo(d2.string1);
case 1 :
return d1.string2.compareTo(d2.string2);
}
return 0;
}
});
for (int i = 0; i < list.length; i++) {
TableItem item = table.getItem(i);
item.setText(0, list[i].string1);
item.setText(1, list[i].string2);
item.setData(list[i]);
}
}
The main program creates the table and two table columns. The current sorting order (ascending or descending) is stored as a boolean in each column using setData(). When a column is selected, the index of the column that was selected is computed and the sorting retrieved. Finally, the column is sorted by inverting the current sorting order.
static int ROWS = 10000;
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
final Table table = new Table(shell, SWT.BORDER);
table.setHeaderVisible(true);
for (int i = 0; i < 2; i++) {
TableColumn column =
new TableColumn(table, SWT.NONE);
column.setText("Column " + i);
column.setData(new Boolean(false));
}
Random r = new Random();
table.setRedraw(false);
for (int i = 0; i < ROWS; i++) {
TableItem item = new TableItem(table, SWT.NULL);
MyData data =
new MyData(
"A" + r.nextInt(1000),
"B" + r.nextInt(1000));
item.setText(0, data.string1);
item.setText(1, data.string2);
item.setData(data);
}
sort(table, 0, false);
table.setRedraw(true);
for (int i = 0; i < table.getColumnCount(); i++) {
final TableColumn column = table.getColumn(i);
column.pack();
column.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
int index = table.indexOf(column);
if (index != -1) {
Boolean b = (Boolean) column.getData();
boolean value = b.booleanValue();
sort(table, index, !value);
column.setData(new Boolean(!value));
}
}
});
}
table.setSize(200, 200);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
SWT.DefaultSelection (SelectionEvent)The SWT.DefaultSelection event (typed event SelectionEvent) is sent whenever the user performs the platform-specific operation that indicates default selection. On most platforms, default selection occurs when the user double-clicks on a column header. Default selection events contain meaningful values in only the display, widget, and type fields. SWT.Move (ControlEvent)The SWT.Move event (typed event MoveEvent) is sent whenever a column is moved. Move events contain meaningful values in only the display, widget, and type fields. SWT.Resize (ControlEvent)The SWT.Resize event (typed event ControlEvent) is sent whenever a column is resized. Resize events contain meaningful values in only the display, widget, and type fields. |
|
|
< Day Day Up > |
|