Standard Widget Toolkit trees: Creating, sorting, and searching

This article explains how to develop and implement trees in the Standard Widget Toolkit (SWT). Learn how an SWT tree is created and populated with data, how columns can be used to categorize data, how a tree can be extended to support row sorting, and how the tree's content can be searched.

Share:

George Kyriacou, Reliability Test Engineer , IBM

George Kyriacou joined the IBM Software Lab in Dublin, Ireland, in 2008 after completing an MSc in Computer Science from Trinity College. He currently works as a Reliability Test Engineer for the IBM Lotus Domino System Test team, where he develops tools to improve the testing of Lotus Notes and Lotus Domino. You can reach him at George_Kyriacou@ie.ibm.com.



29 June 2009 (First published 12 May 2009)

Also available in Chinese Spanish

Editor's note: Know a lot about this topic? Want to share your expertise? Participate in the IBM Lotus software wiki program today.

Introduction

The Standard Widget Toolkit (SWT) is a multi-operating system graphical user interface (GUI) toolkit that gives developers access to the native widgets and graphics components of the different underlying platforms, using Java™ as its single programming language.

SWT was originally developed for use by the Eclipse project; however, over the years it has evolved into a stand-alone toolkit that can be used for any application requiring an advanced GUI implementation with the native look and feel of the platform on which it is running.

Because the IBM® Lotus® Expeditor toolkit is based on the Eclipse platform, you need a good working knowledge of the SWT toolkit so that you can develop composite applications and plug-ins that are built using IBM Lotus and IBM WebSphere® products.

This article focuses on trees, which are one aspect of SWT, an example structure of which is shown in figure 1.

Figure 1. SWT tree example
SWT tree example

One difficult aspect of SWT trees is to implement sorting of the tree’s items, a topic on which information is much sought after in public support forums. This article provides a detailed treatment of SWT's useful tree feature. It is intended to supplement existing content and enable you to quickly master and use it in your own composite applications.


Approach and prerequisites

This article is in the format of a step-by-step walk-through with code snippets. Screen captures are used to help better depict the direction that the implementation takes. The end solution is complete and can be easily integrated into any existing SWT application.

To get the most from the article, you should have an intermediate knowledge of the Java programming language and basic knowledge of the SWT toolkit. It is assumed that you are already familiar with Java development in Eclipse and that you know how to link a project with the SWT toolkit. For more information, refer to Developing SWT applications using Eclipse.


Understanding trees

Trees in GUI applications provide an intuitive means of data navigation by organizing information in a hierarchical structure. You can work on a specific set of data while any unnecessary information stays hidden and does not obstruct the process. An example of this is the Preferences window in Eclipse (see figure 2).

Figure 2. Eclipse Preferences window
Eclipse Preferences window

Basic SWT tree

Let’s start with a basic SWT tree. The code in listing 1 is an example of a basic tree in SWT.

Listing 1. Basic tree implementation
1:import org.eclipse.swt.SWT;
2:import org.eclipse.swt.layout.FillLayout;
3:import org.eclipse.swt.widgets.Display;
4:import org.eclipse.swt.widgets.Shell;
5:import org.eclipse.swt.widgets.Tree;
6:import org.eclipse.swt.widgets.TreeItem;
7:
8:public class First {
9:    public static void main(String[] args) {
10:        Display display = new Display();
11:
12:        Shell shell = new Shell(display);
13:        shell.setText("SWT Trees");
14:        shell.setLayout(new FillLayout());
15:        shell.setSize(400, 300);
16:
17:        Tree tree = new Tree(shell, SWT.BORDER);
18:
19:        for (int i = 0; i < 5; i++) {
20:            TreeItem treeItem = new TreeItem(tree, 0);
21:            treeItem.setText("TreeItem" + i);
22:            for (int j = 0; j < 5; j++) {
23:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
24:                subTreeItem.setText("SubTreeItem" + j);
25:                for (int k = 0; k < 5; k++) {
26:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
27:                            SWT.NONE);
28:                    subSubTreeItem.setText("SubSubTreeItem" + k);
29:                }
30:            }
31:        }
32:
33:        shell.open();
34:        while (!shell.isDisposed()) {
35:            if (!display.readAndDispatch())
36:                display.sleep();
37:        }
38:        display.dispose();
39:    }
40:}

We create a tree by instantiating the Tree class (line 17 of listing 1) and create the branches of the tree by instantiating the TreeItem class (lines 20, 23, and 26). Figure 3 shows how the basic tree displays when the example is run on Microsoft® Windows®.

Figure 3. Basic tree
Basic tree

Basic trees with columns

Columns can be used to categorize data. The code example in listing 2 shows how columns can be added to a tree.

Listing 2. Basic tree with columns
1:import java.util.Random;
2:
3:import org.eclipse.swt.SWT;
4:import org.eclipse.swt.layout.FillLayout;
5:import org.eclipse.swt.widgets.Display;
6:import org.eclipse.swt.widgets.Shell;
7:import org.eclipse.swt.widgets.Tree;
8:import org.eclipse.swt.widgets.TreeColumn;
9:import org.eclipse.swt.widgets.TreeItem;
10:
11:public class Columns {
12:    public static void main(String[] args) {
13:        Display display = new Display();
14:
15:        Shell shell = new Shell(display);
16:        shell.setText("Basic Tree with Columns");
17:        shell.setLayout(new FillLayout());
18:        shell.setSize(400, 300);
19:
20:        Tree tree = new Tree(shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
21:        tree.setHeaderVisible(true);
22:
23:        TreeColumn column1 = new TreeColumn(tree, SWT.NONE);
24:        column1.setText("TreeColumn0");
25:        column1.setWidth(200);
26:        column1.setAlignment(SWT.LEFT);
27:
28:        TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
29:        column2.setText("TreeColumn1");
30:        column2.setWidth(200);
31:        column2.setAlignment(SWT.CENTER);
32:
33:        Random generator = new Random();
34:        
35:        for (int i = 0; i < 5; i++) {
36:            TreeItem treeItem = new TreeItem(tree, 0);
37:            treeItem.setText(new String[] { "TreeItem" + i,
38:                    Integer.toString(generator.nextInt()) });
39:            for (int j = 0; j < 5; j++) {
40:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
41:                subTreeItem.setText(new String[] { "SubTreeItem" + j,
42:                        Integer.toString(generator.nextInt()) });
43:                for (int k = 0; k < 5; k++) {
44:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
45:                            SWT.NONE);
46:                    subSubTreeItem.setText(new String[] { "SubSubTreeItem" + k,
47:                            Integer.toString(generator.nextInt()) });
48:                }
49:            }
50:        }
51:
52:        shell.open();
53:        while (!shell.isDisposed()) {
54:            if (!display.readAndDispatch())
55:                display.sleep();
56:        }
57:        display.dispose();
58:    }
59:}

We add columns to the tree by instantiating the TreeColumn class (lines 23 and 28). Note that it’s important to set the width of the columns (lines 25 and 30); if the width is not set, the columns do not display properly. Also, the tree’s header must be set as visible (line 21) for the column’s headers to display.

To set the data of each column, an array of type String must be passed to the setText method of the tree items (lines 37, 41, and 46). Here we use a random integer for the value of the second column (see figure 4).

Figure 4. Basic tree with columns
Basic tree with columns

Sorting trees

To add sorting to SWT trees, we must implement a selection listener that is called whenever one of the tree’s columns is selected. Listing 3 shows example code of a selection listener that can be used for sorting the columns of the sample application.

Listing 3. Sort tree selection listener implementation
1:import java.text.Collator;
2:import java.util.Locale;
3:import java.util.regex.Pattern;
4:
5:import org.eclipse.swt.SWT;
6:import org.eclipse.swt.events.SelectionEvent;
7:import org.eclipse.swt.events.SelectionListener;
8:import org.eclipse.swt.widgets.Tree;
9:import org.eclipse.swt.widgets.TreeColumn;
10:import org.eclipse.swt.widgets.TreeItem;
11:
12:public class SortTreeListener implements SelectionListener {
13:    @Override
14:    public void widgetDefaultSelected(SelectionEvent e) {
15:        
16:    }
17:
18:    @Override
19:    public void widgetSelected(SelectionEvent e) {
20:        sortTree(e);
21:    }
22:
23:    private void sortTree(SelectionEvent e) {
24:        TreeColumn column = (TreeColumn) e.widget;
25:        Tree tree = column.getParent();
26:        TreeItem[] treeItems = tree.getItems();
27:        TreeColumn sortColumn = tree.getSortColumn();
28:        TreeColumn columns[] = tree.getColumns();
29:        tree.setSortColumn(column);
30:        int numOfColumns = columns.length;
31:        int columnIndex = this.findColumnIndex(columns, column, numOfColumns);
32:        Collator collator = Collator.getInstance(Locale.getDefault());
33:        Boolean sort = false;
34:        Pattern pattern = Pattern.compile("([\\+]*|[\\-]*)\\d+");
35:        if ((column.equals(sortColumn)) && 
			(tree.getSortDirection() == SWT.UP)) {
36:            tree.setSortDirection(SWT.DOWN);
37:            for (int i = 1; i < treeItems.length; i++) {
38:                String value1 = treeItems[i].getText(columnIndex).trim();
39:                for (int j = 0; j < i; j++) {
40:                    String value2 = treeItems[j].getText(columnIndex).trim();
41:                    if (pattern.matcher(value1).matches()
42:                            && pattern.matcher(value2).matches()) {
43:                        double d1 = this.getDouble(value1);
44:                        double d2 = this.getDouble(value2);
45:                        if (d1 > d2) {
46:                            sort = true;
47:                        }
48:                    } else if (collator.compare(value1, value2) > 0) {
49:                        sort = true;
50:                    }
51:                    if (sort) {
52:                        String[] values = this.getColumnValues(treeItems[i],
53:                                numOfColumns);
54:                        TreeItem[] subItems = treeItems[i].getItems();
55:                        TreeItem item = new TreeItem(tree, SWT.NONE, j);
56:                        item.setText(values);
57:                        for (TreeItem si : subItems) {
58:                            TreeItem[] subSubItems = si.getItems();
59:                            TreeItem subItem = new TreeItem(item, SWT.NONE);
60:                            subItem.setText(this.getColumnValues(si, numOfColumns));
61:                            for (TreeItem ssi : subSubItems) {
62:                                TreeItem subSubItem = new TreeItem(subItem,
63:                                        SWT.NONE);
64:                                subSubItem.setText(this.getColumnValues(ssi,
65:                                        numOfColumns));
66:                            }
67:                        }
68:                        treeItems[i].dispose();
69:                        treeItems = tree.getItems();
70:                        sort = false;
71:                        break;
72:                    }
73:                }
74:            }
75:        } else {
76:            tree.setSortDirection(SWT.UP);
77:            for (int i = 1; i < treeItems.length; i++) {
78:                String value1 = treeItems[i].getText(columnIndex).trim();
79:                for (int j = 0; j < i; j++) {
80:                    String value2 = treeItems[j].getText(columnIndex).trim();
81:                    if (pattern.matcher(value1).matches()
82:                            && pattern.matcher(value2).matches()) {
83:                        double d1 = this.getDouble(value1);
84:                        double d2 = this.getDouble(value2);
85:                        if (d1 < d2) {
86:                            sort = true;
87:                        }
88:                    } else if (collator.compare(value1, value2) < 0) {
89:                        sort = true;
90:                    }
91:                    if (sort) {
92:                        String[] values = this.getColumnValues(treeItems[i],
93:                                numOfColumns);
94:                        TreeItem[] subItems = treeItems[i].getItems();
95:                        TreeItem item = new TreeItem(tree, SWT.NONE, j);
96:                        item.setText(values);
97:                        for (TreeItem si : subItems) {
98:                            TreeItem[] subSubItems = si.getItems();
99:                            TreeItem subItem = new TreeItem(item, SWT.NONE);
100:                            subItem.setText(this.getColumnValues(si, numOfColumns));
101:                            for (TreeItem ssi : subSubItems) {
102:                                TreeItem subSubItem = new TreeItem(subItem,
103:                                        SWT.NONE);
104:                                subSubItem.setText(this.getColumnValues(ssi,
105:                                        numOfColumns));
106:                            }
107:                        }
108:                        treeItems[i].dispose();
109:                        treeItems = tree.getItems();
110:                        sort = false;
111:                        break;
112:                    }
113:                }
114:            }
115:        }
116:    }
117:
118:    /**
119:     * Find the index of a column
120:     * 
121:     * @param columns
122:     * @param numOfColumns
123:     * @return int
124:     */
125:    private int findColumnIndex(TreeColumn[] columns, TreeColumn column,
126:            int numOfColumns) {
127:        int index = 0;
128:        for (int i = 0; i < numOfColumns; i++) {
129:            if (column.equals(columns[i])) {
130:                index = i;
131:                break;
132:            }
133:        }
134:        return index;
135:    }
136:
137:    /**
138:     * Get the double value from a string
139:     * 
140:     * @param str
141:     * @return double
142:     */
143:    private double getDouble(String str) {
144:        double d;
145:        if (str.startsWith("+")) {
146:            d = Double.parseDouble(str.split("\\+")[1]);
147:        } else {
148:            d = Double.parseDouble(str);
149:        }
150:        return d;
151:    }
152:
153:    /**
154:     * Get the array of string value from the provided TreeItem
155:     * 
156:     * @param treeItem
157:     * @param numOfColumns
158:     * @return String[]
159:     */
160:    private String[] getColumnValues(TreeItem treeItem, int numOfColumns) {
161:        String[] values = new String[numOfColumns];
162:        for (int i = 0; i < numOfColumns; i++) {
163:            values[i] = treeItem.getText(i);
164:        }
165:        return values;
166:    }
167:}

The first thing that we do is determine which of the columns is selected by calling the widget() function of the SelectionEvent (line 24 in listing 3).

You must then gather the tree items and column information of the tree (lines 25 to 31). Here you must find the index of the column in the position as it appears in the tree. Because there is no method in the TreeColumn or Tree class that can give you this piece of information, you must determine the column ID by comparing all the columns of the tree with the selected column and finding the correct index number.

The method findColumnIndex (line 125) is designed to perform this step. The column index must be found to extract the textual contents of the column. That content is then used to compare each column, to determine its correct position in the sorted tree.

You must verify whether the column selected to be sorted is the immediately preceding column to have been sorted and, if it is, in which direction (ascending or descending) it was previously sorted. This condition is shown in line 35, where if the column is being sorted for a consecutive time and the previous direction was upward, it must be switched to downward sorting. For any other case, the tree must be sorted upward (lines 75 to 116).

In addition, it must be determined whether the values stored in the column being sorted are numerical. Treating numerical values as text yields incorrect results, especially if those values are signed; therefore, the column text must be converted to a numerical type.

To find out if a column contains a number, a regular expression can be used (line 34). If the pattern matches (lines 41 and 81), then store the column’s value in a variable of type double (lines 43 to 44 and 83 to 84), using the getDouble(String str) method (line 143).

The sorting is performed by iterating through the tree’s items (lines 37 to 39). All the values stored in the columns are compared in pairs (lines 45, 48, 85, and 88). If two items need to switch places, a Boolean variable sort is set to true (lines 46, 49, 86, and 89). When the sort variable is set to true, the two affected items are recreated as new tree items in the correct order, and the old items are removed from the tree (lines 51 to 67 and 91 to 107).

To set the arrow that shows the direction in which the column is sorted, the setSortDirection(int direction) method of the Tree class is called (lines 36 to 37).

To enable sorting of the columns, you must add the following code to the main method before calling the Shell.open() method:

column1.addSelectionListener(new SortTreeListener());
column2.addSelectionListener(new SortTreeListener());

Listing 4 shows the full implementation of the Basic tree with columns, in which the column-sorting capability is added in lines 27 and 33.

Listing 4. Basic tree with column sorting added
1:import java.util.Random;
2:
3:import org.eclipse.swt.SWT;
4:import org.eclipse.swt.layout.FillLayout;
5:import org.eclipse.swt.widgets.Display;
6:import org.eclipse.swt.widgets.Shell;
7:import org.eclipse.swt.widgets.Tree;
8:import org.eclipse.swt.widgets.TreeColumn;
9:import org.eclipse.swt.widgets.TreeItem;
10:
11:public class Sorting {
12:    public static void main(String[] args) {
13:        Display display = new Display();
14:
15:        Shell shell = new Shell(display);
16:        shell.setText("Sorting Trees");
17:        shell.setLayout(new FillLayout());
18:        shell.setSize(400, 300);
19:
20:        Tree tree = new Tree(shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
21:        tree.setHeaderVisible(true);
22:
23:        TreeColumn column1 = new TreeColumn(tree, SWT.NONE);
24:        column1.setText("TreeColumn0");
25:        column1.setWidth(200);
26:        column1.setAlignment(SWT.LEFT);
27:        column1.addSelectionListener(new SortTreeListener());
28:
29:        TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
30:        column2.setText("TreeColumn1");
31:        column2.setWidth(200);
32:        column2.setAlignment(SWT.CENTER);
33:        column2.addSelectionListener(new SortTreeListener());
34:
35:        Random generator = new Random();
36:
37:        for (int i = 0; i < 5; i++) {
38:            TreeItem treeItem = new TreeItem(tree, 0);
39:            treeItem.setText(new String[] { "TreeItem" + i,
40:                    Integer.toString(generator.nextInt()) });
41:            for (int j = 0; j < 5; j++) {
42:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
43:                subTreeItem.setText(new String[] { "SubTreeItem" + j,
44:                        Integer.toString(generator.nextInt()) });
45:                for (int k = 0; k < 5; k++) {
46:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
47:                            SWT.NONE);
48:                    subSubTreeItem.setText(new String[] { "SubSubTreeItem" + k,
49:                            Integer.toString(generator.nextInt()) });
50:                }
51:            }
52:        }
53:
54:        shell.open();
55:        while (!shell.isDisposed()) {
56:            if (!display.readAndDispatch())
57:                display.sleep();
58:        }
59:        display.dispose();
60:    }
61:}

Figure 5 shows a basic tree with column sorting.

Figure 5. Basic tree with column sorting
Basic tree with column sorting

Expanding and collapsing all tree items

A useful feature to add to your applications is the capability to expand or collapse all top-level tree items. You can do this by adding two buttons: one that expands and one that collapses all the top-level tree items. The code in listing 5 is an example of how this feature can be implemented.

Listing 5. Implementation of expanding and collapsing tree items
1:import java.util.Random;
2:
3:import org.eclipse.swt.SWT;
4:import org.eclipse.swt.events.SelectionEvent;
5:import org.eclipse.swt.events.SelectionListener;
6:import org.eclipse.swt.layout.GridData;
7:import org.eclipse.swt.layout.GridLayout;
8:import org.eclipse.swt.widgets.Button;
9:import org.eclipse.swt.widgets.Display;
10:import org.eclipse.swt.widgets.Shell;
11:import org.eclipse.swt.widgets.Tree;
12:import org.eclipse.swt.widgets.TreeColumn;
13:import org.eclipse.swt.widgets.TreeItem;
14:
15:public class ExpandAll {
16:    private static Tree tree = null;
17:    
18:    public static void main(String[] args) {
19:        Display display = new Display();
20:
21:        Shell shell = new Shell(display);
22:        shell.setText("Expand All Items");
23:        GridLayout gridLayout = new GridLayout();
24:        gridLayout.numColumns = 2;
25:        shell.setLayout(gridLayout);
26:        shell.setSize(400, 300);
27:
28:        Button expButton = new Button(shell, SWT.PUSH);
29:        expButton.setText("+");
30:        GridData gridData = new GridData(23, 23);
31:        expButton.setLayoutData(gridData);
32:        expButton.addSelectionListener(new ExpandAllItemsListener(true));
33:       
34:        Button colButton = new Button(shell, SWT.PUSH);
35:        colButton.setText("-");
36:        gridData = new GridData(23, 23);
37:        colButton.setLayoutData(gridData);
38:        colButton.addSelectionListener(new ExpandAllItemsListener(false));
39:
40:        tree = new Tree(shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
41:        tree.setHeaderVisible(true);
42:
43:        gridData = new GridData(GridData.FILL_BOTH);
44:        gridData.horizontalSpan = 2;
45:        tree.setLayoutData(gridData);
46:        
47:        TreeColumn column1 = new TreeColumn(tree, SWT.NONE);
48:        column1.setText("TreeColumn0");
49:        column1.setWidth(200);
50:        column1.setAlignment(SWT.LEFT);
51:        column1.addSelectionListener(new SortTreeListener());
52:
53:        TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
54:        column2.setText("TreeColumn1");
55:        column2.setWidth(200);
56:        column2.setAlignment(SWT.CENTER);
57:        column2.addSelectionListener(new SortTreeListener());
58:
59:        Random generator = new Random();
60:
61:        for (int i = 0; i < 5; i++) {
62:            TreeItem treeItem = new TreeItem(tree, 0);
63:            treeItem.setText(new String[] { "TreeItem" + i,
64:                    Integer.toString(generator.nextInt()) });
65:            for (int j = 0; j < 5; j++) {
66:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
67:                subTreeItem.setText(new String[] { "SubTreeItem" + j,
68:                        Integer.toString(generator.nextInt()) });
69:                for (int k = 0; k < 5; k++) {
70:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
71:                            SWT.NONE);
72:                    subSubTreeItem.setText(new String[] { "SubSubTreeItem" + k,
73:                            Integer.toString(generator.nextInt()) });
74:                }
75:            }
76:        }
77:
78:        shell.open();
79:        while (!shell.isDisposed()) {
80:            if (!display.readAndDispatch())
81:                display.sleep();
82:        }
83:        display.dispose();
84:    }
85:    
86:    static class ExpandAllItemsListener implements SelectionListener {
87:        private boolean expand = false;
88:
89:        public ExpandAllItemsListener(Boolean expand) {
90:            this.expand = expand;
91:        }
92:
93:        @Override
94:        public void widgetDefaultSelected(SelectionEvent e) {
95:            expandTreeItems();
96:        }
97:
98:        @Override
99:        public void widgetSelected(SelectionEvent e) {
100:            expandTreeItems();
101:        }
102:
103:        public void expandTreeItems() {
104:            TreeItem[] treeItems = tree.getItems();
105:            if (treeItems != null) {
106:                for (TreeItem treeItem : treeItems) {
107:                    treeItem.setExpanded(expand);
108:                }
109:            }
110:        }
111:    }
112:}

The first step is to add the two buttons (lines 28 to 38 in listing 5) that expand or collapse all the tree’s items. The next step is to implement a selection listener (lines 86 to 111) that executes when one of the two buttons is selected.

The constructor for the SelectionListener implementation accepts a Boolean variable that defines whether the tree items are to be expanded or collapsed, depending on which button is selected. The expandTreeItems() method (line 103) iterates through the TreeItems array and calls the setExpanded(boolean expanded) method of the TreeItem class (line 107), passing the Boolean variable that was set by the class’s constructor.

To get the array of the top-level tree items, you can call the getItems() method of the Tree class (line 104).

Figure 6 shows a tree with all the TreeItems expanded.

Figure 6. Tree with TreeItems expanded
Tree with TreeItems expanded

Searching trees

Another feature you can add to an application is a search feature, which is especially useful if users must deal with large sets of data.

The code in listing 6 shows how you can implement a listener to search the top-level elements of the tree.

Listing 6. Search tree items listener
1:import org.eclipse.jface.dialogs.InputDialog;
2:import org.eclipse.swt.SWT;
3:import org.eclipse.swt.events.KeyEvent;
4:import org.eclipse.swt.events.KeyListener;
5:import org.eclipse.swt.widgets.MessageBox;
6:import org.eclipse.swt.widgets.Tree;
7:import org.eclipse.swt.widgets.TreeItem;
8:
9:public class SearchListener implements KeyListener {
10:    private Tree tree = null;
11:
12:    @Override
13:    public void keyPressed(KeyEvent e) {
14:        tree = (Tree) e.widget;
15:
16:        String searchString = showSearchPopup();
17:        if (searchString == null) {
18:            return;
19:        }
20:
21:        if (!findString(searchString, tree.getItems())) {
22:            MessageBox messageBox = new MessageBox(tree.getShell(), SWT.OK
23:                    | SWT.ICON_ERROR);
24:            messageBox.setMessage("Could not find: '" + searchString + "'");
25:            messageBox.setText("Search Error");
26:            messageBox.open();
27:        }
28:
29:    }
30:
31:    @Override
32:    public void keyReleased(KeyEvent e) {
33:
34:    }
35:
36:    private String showSearchPopup() {
37:        InputDialog d = new InputDialog(this.tree.getParent().getShell(),
38:                "Search", "Search text", "", null);
39:        d.open();
40:        return d.getValue();
41:    }
42:
43:    private boolean findString(String searchString, TreeItem[] treeItems) {
44:        for (TreeItem treeItem : treeItems) {
45:            for (int i = 0; i < tree.getColumnCount(); i++) {
46:                String text = treeItem.getText(i);
47:                if ((text.toUpperCase().contains(searchString.toUpperCase()))) {
48:                    tree.setSelection(treeItem);
49:                    return true;
50:                }
51:            }
52:        }
53:
54:        return false;
55:    }
56:}

This listener can be added to the tree as a KeyListener, so that when a user types something while one of the tree’s items is selected, an input window displays as shown in figure 7.

Figure 7. Search window
Search window

The algorithm is simple; it iterates through the top-level items of the tree and compares their text contents with the search string (lines 43 to 52 in listing 6). When the search string is found in one of the tree items, that tree item is selected (line 48). If that string is not found, the Search Error message, Could not find: 'TreeItemX', displays (lines 21 to 27).

Now, the only modification to be done is to add the SearchListener to the tree’s collection of key listeners, as shown in line 23 of listing 7.

Listing 7. Basic tree search
1:import java.util.Random;
2:
3:import org.eclipse.swt.SWT;
4:import org.eclipse.swt.layout.FillLayout;
5:import org.eclipse.swt.widgets.Display;
6:import org.eclipse.swt.widgets.Shell;
7:import org.eclipse.swt.widgets.Tree;
8:import org.eclipse.swt.widgets.TreeColumn;
9:import org.eclipse.swt.widgets.TreeItem;
10:
11:public class Searching {
12:    public static void main(String[] args) {
13:        Display display = new Display();
14:
15:        Shell shell = new Shell(display);
16:        shell.setText("Searching");
17:        shell.setLayout(new FillLayout());
18:        shell.setSize(400, 300);
19:
20:        Tree tree = new Tree(shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL
21:                | SWT.FULL_SELECTION);
22:        tree.setHeaderVisible(true);
23:        tree.addKeyListener(new SearchListener());
24:
25:        TreeColumn column1 = new TreeColumn(tree, SWT.NONE);
26:        column1.setText("TreeColumn0");
27:        column1.setWidth(200);
28:        column1.setAlignment(SWT.LEFT);
29:        column1.addSelectionListener(new SortTreeListener());
30:
31:        TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
32:        column2.setText("TreeColumn1");
33:        column2.setWidth(200);
34:        column2.setAlignment(SWT.CENTER);
35:        column2.addSelectionListener(new SortTreeListener());
36:
37:        Random generator = new Random();
38:
39:        for (int i = 0; i < 5; i++) {
40:            TreeItem treeItem = new TreeItem(tree, 0);
41:            treeItem.setText(new String[] { "TreeItem" + i,
42:                    Integer.toString(generator.nextInt()) });
43:            for (int j = 0; j < 5; j++) {
44:                TreeItem subTreeItem = new TreeItem(treeItem, SWT.NONE);
45:                subTreeItem.setText(new String[] { "SubTreeItem" + j,
46:                        Integer.toString(generator.nextInt()) });
47:                for (int k = 0; k < 5; k++) {
48:                    TreeItem subSubTreeItem = new TreeItem(subTreeItem,
49:                            SWT.NONE);
50:                    subSubTreeItem.setText(new String[] { "SubSubTreeItem" + k,
51:                            Integer.toString(generator.nextInt()) });
52:                }
53:            }
54:        }
55:
56:        shell.open();
57:        while (!shell.isDisposed()) {
58:            if (!display.readAndDispatch())
59:                display.sleep();
60:        }
61:        display.dispose();
62:    }
63:}

Finally, the search algorithm can be easily modified to do a recursive search of all the tree’s items.


Conclusion

SWT trees are an extremely effective way of organizing and presenting data to the user. Using the information and the code examples from this article, you can enhance and make the most of your composite or SWT-based applications.


Acknowledgement

The author extends special thanks to Gary Denner for his continuous support and for his help with this article.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into IBM collaboration and social software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Lotus, WebSphere
ArticleID=387299
ArticleTitle=Standard Widget Toolkit trees: Creating, sorting, and searching
publish-date=06292009