|
|
< Day Day Up > |
|
9.1 Classes Tree and TreeItem9.1.1 Example
9.1.2 Tree and TreeItem Hierarchies
9.1.3 Tree Styles
9.1.4 Tree Events
9.1.5 TreeItem Styles (none)
9.1.6 TreeItem Events (none)
Trees are selectable controls that are capable of displaying hierarchies of nodes. Each node in a tree is represented by an instance of the class TreeItem. A tree item can be expanded or collapsed, showing or hiding other tree items. When an item is collapsed, a small "plus" indicator is displayed. An expanded item shows a "minus" indicator. These indicators are platform-specific and may not resemble plus or minus characters.[1] Tree items can draw a string, an icon, or both at the same time.
Users are familiar with tree controls because they are often used to implement the desktop file system browser. File systems are hierarchical. This makes Tree the ideal choice for a visual representation of a file system. However, it is prohibitively time-consuming to construct a tree item for every directory in a file system. When there are potentially too many items in a tree, programs often create the tree items on demand. The best time to do this is when items are expanded. If the user does not expand an item, child items are not created (see the section Using SWT.Expand to Fill a Tree Lazily). Like lists and tables, when a tree is created with the SWT.MULTI style, users can select more than one tree item at the same time. Unlike List and Table, Tree is not indexed-based. For example, you cannot query the fifth row in a tree because the fifth row can change when the fourth row is expanded. Tree items by themselves do not support any styles or listeners. Instead, listeners and styles are associated with the tree. Sometimes a tree is used to represent a hierarchy of boolean options. These kinds of trees, called check box trees, are created with the style SWT.CHECK.[2] Check box trees draw check boxes next to each item. These check boxes often resemble check buttons.
9.1.7 Creating a HierarchyCreating a hierarchy of tree items is straightforward in SWT. The constructors in the class TreeItem allow the parent of an item to be the tree or another tree item. When the parent of an item is a tree, the tree item is a root. Otherwise, it is an interior node in the hierarchy. For greater flexibility, the position parameter of the TreeItem constructor allows a tree item to be inserted at an index, relative to the other tree items in the items list of the parent. The following program creates a tree that is three levels deep with four nodes on each level. The result is shown in Figure 9.1.
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
Tree tree = new Tree(shell, SWT.BORDER);
for (int i = 0; i < 4; i++) {
TreeItem itemI = new TreeItem(tree, SWT.NULL);
itemI.setText("Item " + i);
for (int j = 0; j < 4; j++) {
TreeItem itemJ = new TreeItem(itemI, SWT.NULL);
itemJ.setText("Item " + i + " " + j);
for (int k = 0; k < 4; k++) {
TreeItem itemK =
new TreeItem(itemJ, SWT.NULL);
itemK.setText(
"Item " + i + " " + j + " " + k);
}
}
}
tree.setSize(200, 200);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
Figure 9.1.
9.1.8 Text and ImagesTreeItem supports the standard API to assign text and images to a widget. Like Button, TreeItem does not support mnemonics, wrapping, the SWT.WRAP style, or more than one line of text in an item.
9.1.9 Expanding and CollapsingAt any particular time, a tree item that has children is in either one of two states: expanded or collapsed. A tree item that is expanded shows its children. When a tree item is collapsed, it does not show its children. Tree items that do not have any children are called leaf items and do not have an expanded or collapsed state.[4]
To expand or collapse TreeItem, use setExpanded(). Calling setExpanded() on a leaf item does nothing.
When a tree item is either expanded or collapsed by the user, a corresponding SWT.Expand or SWT.Collapse event is issued. If the setExpanded() method is used to expand or collapse the item, an event is not issued. On some platforms, such as Windows, when the tree item is collapsed, the selection can change as a result of the collapse operation. For example, if the selection is in an item belonging to the subtree that is about to be collapsed, the selection is assigned elsewhere in the tree (usually to the item that is being collapsed). In this case, an SWT.Selection event is issued, regardless of whether the user collapsed the tree item or the program collapsed it using setExpanded(false).
9.1.10 Hierarchical OperationsTree and TreeItem provide a number of hierarchical operations that allow you to determine the parent朿hild relationship between items. The methods to do this are found in both the Tree and TreeItem classes. Because Tree is not an index-based control, these methods are not index-based.
The following code fragment prints a text representation of a tree by visiting every node and printing it indented by its depth in the tree.
static void traverse(TreeItem[] items) {
for (int i = 0; i < items.length; i++) {
TreeItem item = items[i];
String string = item.toString();
while (item.getParentItem() != null) {
string = "\t" + string;
item = item.getParentItem();
}
System.out.println(string);
traverse(items[i].getItems());
}
}
This code is simple and was written to show the use of getParentItem() and getItems(). A better implementation would avoid the string concatenation and include a level parameter that was incremented with every recursive call. This would allow us to get rid of the loop that computes depth. At first, this implementation of traverse() might seem a little strange. Good programming practice dictates that recursive traversal code should perform the operation on the item, then make the recursive call on each child of the item. However, our traverse() method takes an array of items. This implementation of traverse(), although unorthodox, is correct. Its implementation was forced by the fact that although Tree.getItems() and TreeItem.getItems() have the same method signature, there is no interface to capture the commonality. Without resorting to casting, it is not possible to write a version of the traverse() method that takes a single object. Here is an alternate implementation of traverse() that uses two methods: one that takes a tree item and another that takes a tree. It does not cast but contains two identical loops.
static void traverse(TreeItem item) {
String string = item.toString();
TreeItem temp = item.getParentItem();
while (temp != null) {
string = "\t" + string;
temp = temp.getParentItem();
}
System.out.println(string);
TreeItem[] items = item.getItems();
for (int i = 0; i < items.length; i++) {
traverse(items[i]);
}
}
static void traverse(Tree tree) {
TreeItem[] items = tree.getItems();
for (int i = 0; i < items.length; i++) {
traverse(items[i]);
}
}
As of R3.0, interfaces are not defined in SWT to capture commonality between classes. The rationale for this design decision is beyond the scope of this book. 9.1.11 Checked and Grayed Tree ItemsTrees support a checked and grayed state for tree items. As was previously described, in order to check or gray the check box for a tree item, the tree must have been created with the SWT.CHECK style. Figure 9.2 shows a check box tree running on Windows. Figure 9.2.
The checked state of a tree item is independent of the checked state of its parent or child items. This means that when an item is checked or unchecked, other items are unaffected. However, many users expect that when an item is checked or unchecked, its child items are also automatically checked or unchecked. Grayed tree items are often used to indicate that some of the items below a checked item are not checked. Because neither of these features is implemented by default, you will need to write the code to make this happen. The following program maintains the checked state and grayed state of the tree items in a tree, as described above. An SWT.Selection listener is used to compute the two states when the user selects a check box. The result, with some items checked, is shown in Figure 9.3.
static void checkPath(
TreeItem item,
boolean checked,
boolean grayed) {
if (item == null) return;
if (grayed) {
checked = true;
} else {
int index = 0;
TreeItem[] items = item.getItems();
while (index < items.length) {
TreeItem child = items[index];
if (child.getGrayed()
|| checked != child.getChecked()) {
checked = grayed = true;
break;
}
index++;
}
}
item.setChecked(checked);
item.setGrayed(grayed);
checkPath(item.getParentItem(), checked, grayed);
}
static void checkItems(TreeItem item, boolean checked) {
item.setGrayed(false);
item.setChecked(checked);
TreeItem[] items = item.getItems();
for (int i = 0; i < items.length; i++) {
checkItems(items[i], checked);
}
}
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
Tree tree = new Tree(shell, SWT.BORDER | SWT.CHECK);
tree.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
if (event.detail == SWT.CHECK) {
TreeItem item = (TreeItem) event.item;
boolean checked = item.getChecked();
checkItems(item, checked);
checkPath(
item.getParentItem(),
checked,
false);
}
}
});
for (int i = 0; i < 4; i++) {
TreeItem itemI = new TreeItem(tree, SWT.NULL);
itemI.setText("Item " + i);
for (int j = 0; j < 4; j++) {
TreeItem itemJ = new TreeItem(itemI, SWT.NULL);
itemJ.setText("Item " + i + " " + j);
for (int k = 0; k < 4; k++) {
TreeItem itemK =
new TreeItem(itemJ, SWT.NULL);
itemK.setText(
"Item " + i + " " + j + " " + k);
}
}
}
tree.setSize(200, 200);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
Figure 9.3.
The checkItems() method recursively checks or unchecks the item and all child items. As you would expect, regardless of the new checked state, all items are ungrayed. This is done because items in the subtree are all either checked or unchecked. The checkPath() method is used to ensure that when the checked state of an item changes, its ancestors in the tree are either checked, grayed, or unchecked to match the new state of their children. This method is initially called with the parent of the item whose state has changed, the new checked state, and the desired grayed state. While processing the ancestors of an item, if any child is either grayed or checked, the item must become both grayed and checked. 9.1.12 TreeItem Foreground, Background, and FontTreeItem supports the ability to set the foreground color, background color, and font used by an individual item.
9.1.13 The SelectionApplication programs often need to work with the selection in a tree. Once again, because trees are not index-based controls, selection is specified using tree items. The following methods are used to manipulate the selection in a tree:
9.1.14 ScrollingTree controls provide the ability to scroll a tree by getting and setting the top item. The top item of a tree is the tree item that is displayed at the top of the control. The top item is set using the setTopItem() method.
Generally speaking, application programs rarely need to scroll a tree to show the selection. This is because setSelection() automatically makes the selection visible to the user. However, there are circumstances where you might need to show the selection. In these cases, the showSelection() method can be used.
9.1.15 Removing ItemsTree items are removed using the TreeItem method dispose(). When an item is disposed, child items are also disposed. One way to remove all the tree items in a tree is to dispose of all the roots. This can be time-consuming and cause too many redraws, especially when there are many items in the tree. The recommended way to dispose all items in a tree is to use the Tree method removeAll().
9.1.16 Tree EventsSWT.Selection (SelectionEvent)The SWT.Selection event (typed event SelectionEvent) is sent whenever the user selects an item with the mouse or the keyboard or when the selection changes because of the tree being expanded or collapsed (see the Why Can setExpanded() Cause SWT.Selection? box). The relevant event fields during a selection event for Tree are as follows.
SWT.DefaultSelection (SelectionEvent)The SWT.DefaultSelection event (typed event SelectionEvent) is sent whenever the user performs the platform-specific operation that is treated as a 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 Tree are as follows.
SWT.Expand (TreeEvent)Table 9.1 shows the expand event for tree controls.
The SWT.Expand event (typed event TreeEvent) is sent whenever the user expands a tree item with the mouse or the keyboard or when the item is expanded as the result of any method other than setExpanded(). The relevant event fields during an expand event for Tree are as follows.
Using SWT.Expand to Fill a Tree LazilyBuilding a tree that contains thousands of items is time-consuming. For example, a tree used to represent a large hierarchical file system would need not only to create thousands of tree items but also to visit every directory on the file system. Fortunately, the SWT.Expand event can be used to create tree items lazily, as the user expands the tree. The following example uses an SWT.Expand listener to fill a tree with directories and files as the user expands the tree. This involves creating a dummy item to force the tree to show the plus indicator. This dummy item does not have a file or directory associated with it. When a real item is created, the setData() method is used to store the file or directory. The roots of the tree, which are always directories, are initialized to contain dummy items. When a tree item is expanded for the first time, the dummy item is deleted, and the real items are created. If any of the real items are directories, a dummy child item is created for each one, allowing the lazy filling process to continue the next time the user selects one of these items. Figure 9.4 shows the result open to an arbitrary place in a file system.
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Lazy Tree");
Tree tree = new Tree(shell, SWT.BORDER);
/* Initialize the roots of the tree */
File[] roots = File.listRoots();
for (int i = 0; i < roots.length; i++) {
TreeItem root = new TreeItem(tree, SWT.NULL);
root.setText(roots[i].toString());
root.setData(roots[i]);
/* Use a dummy item to force the '+' */
new TreeItem(root, SWT.NULL);
}
/* Use SWT.Expand to lazily fill the tree */
tree.addListener(SWT.Expand, new Listener() {
public void handleEvent(Event event) {
/*
* If the item does not contain a
* dummy node, return. A dummy item
* is a single child of the root that
* does not have any application data.
*/
TreeItem root = (TreeItem) event.item;
TreeItem[] items = root.getItems();
if (items.length != 1) return;
if (items[0].getData() != null) return;
items[0].dispose();
/* Create the item children */
File file = (File) root.getData();
File[] files = file.listFiles();
if (files == null) return;
for (int i = 0; i < files.length; i++) {
TreeItem item =new TreeItem(root,SWT.NULL);
item.setText(files[i].getName());
item.setData(files[i]);
/* Use a dummy item to force the '+' */
if (files[i].isDirectory()) {
new TreeItem(item, SWT.NULL);
}
}
}
});
/* Set size of the tree and open the shell */
tree.setSize(300, 300);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
Figure 9.4.
Note that a better implementation would sort the items, show icons, and include only directories as nodes in the tree. SWT.Collapse (TreeEvent)Table 9.2 shows the collapse event for tree controls.
The SWT.Collapse event (typed event TreeEvent) is sent whenever the user collapses an item with the mouse or the keyboard or when the item is collapsed as the result of any method other than setExpanded(). The relevant event fields during a collapse event for Tree are as follows.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
< Day Day Up > |
|