/* * ExampleTreeViewListStore.java * * Copyright (c) 2007-2008 Operational Dynamics Consulting Pty Ltd, and Others * * The code in this file, and the library it is a part of, are made available * to you by the authors under the terms of the "GNU General Public Licence, * version 2" See the LICENCE file for the terms governing usage and * redistribution. */ package treeview; import org.gnome.gdk.Event; import org.gnome.gtk.CellRendererText; import org.gnome.gtk.DataColumn; import org.gnome.gtk.DataColumnBoolean; import org.gnome.gtk.DataColumnInteger; import org.gnome.gtk.DataColumnString; import org.gnome.gtk.Gtk; import org.gnome.gtk.ListStore; import org.gnome.gtk.TreeIter; import org.gnome.gtk.TreePath; import org.gnome.gtk.TreeView; import org.gnome.gtk.TreeViewColumn; import org.gnome.gtk.Widget; import org.gnome.gtk.Window; /** * A tutorial example of using a TreeView backed by a ListStore. In this * little demonstration, we list a number of famous hiking trails and how to * get to them. * * @author Andrew Cowie */ public class ExampleTrailHeads { public ExampleTrailHeads() { final Window w; final TreeView view; final ListStore model; TreeIter row; CellRendererText renderer; TreeViewColumn vertical; w = new Window(); // ------------------------------------------------ final DataColumnString placeName; final DataColumnString trailHead; final DataColumnString elevationFormatted; final DataColumnInteger elevationSort; final DataColumnBoolean accessibleByTrain; /* * So we begin our explorations of the TreeView API here. First thing * is to create a model. You'll notice we declared the variables * above. You don't have to do that, of course, but it makes calling * the ListStore constructor more palatable. More generally, you will * frequently have the DataColumns as instance field variables (so * they can be accessed throughout the file), rather than just local * variables. So you'll end up pre-declaring them regardless, and that * makes the constructor call nice and compact: */ model = new ListStore(new DataColumn[] { placeName = new DataColumnString(), trailHead = new DataColumnString(), elevationFormatted = new DataColumnString(), elevationSort = new DataColumnInteger(), accessibleByTrain = new DataColumnBoolean() }); /* * You almost never populate your TreeModels with data statically from * your source code. We have done so here to demonstrate the use of * appendRow() to get a new horizontal data row and then the various * forms of setValue() to store data in that row. * * In practise, however, you will find the setting of data to be * fairly involved; after all, it takes some trouble to decide what * you want to display, extract if from your domain object layer and * format it prior to insertion into the TreeModel for display by a * TreeView. * * Notice the use of Pango markup in the placeName column. This can be * a powerful way of improving your information density, but be aware * that if some rows have multiple lines and some don't, the row * height will be inconsistent and will look really ugly. So make sure * they all have the same font information and number of newlines. You * would use utility methods to help keep things organized, of course, * but we've kept it straight forward here. */ row = model.appendRow(); model.setValue(row, placeName, "Blue Mountains national park\n<small>(Six Foot Track)</small>"); model.setValue(row, trailHead, "Katoomba, NSW, Australia"); model.setValue(row, elevationFormatted, "1015 m"); model.setValue(row, elevationSort, 1005); model.setValue(row, accessibleByTrain, true); row = model.appendRow(); model.setValue(row, placeName, "Kilimanjaro\n<small>(Machame route)</small>"); model.setValue(row, trailHead, "Nairobi, Kenya"); model.setValue(row, elevationFormatted, "5894 m"); model.setValue(row, elevationSort, 5894); model.setValue(row, accessibleByTrain, false); row = model.appendRow(); model.setValue(row, placeName, "Appalachian Trail\n<small>(roller coaster section)</small>"); model.setValue(row, trailHead, "Harpers Ferry, West Virginia, USA"); model.setValue(row, elevationFormatted, "147 m"); model.setValue(row, elevationSort, 147); model.setValue(row, accessibleByTrain, true); /* * Now onto the view side. First we need the top level TreeView which * is the master Widget into which everything else is mixed. */ view = new TreeView(model); /* * Now the vertical display columns. The sequence is to get a * TreeViewColumn, then create the CellRenderer to be put into it, and * then set whatever data mappings are appropriate along with any * settings for the vertical TreeViewColumn. This is the heart of the * TreeView/TreeModel API * * Note that we reuse the renderer and vertical variables; unlike the * DataColumns, there's no need to keep coming up with unique column * names as you rarely need to reference them later. */ vertical = view.appendColumn(); vertical.setTitle("Place"); renderer = new CellRendererText(vertical); renderer.setMarkup(placeName); vertical = view.appendColumn(); vertical.setTitle("Nearest town"); renderer = new CellRendererText(vertical); renderer.setText(trailHead); renderer.setAlignment(0.0f, 0.0f); vertical.setExpand(true); vertical = view.appendColumn(); vertical.setTitle("Elevation"); renderer = new CellRendererText(vertical); renderer.setText(elevationFormatted); renderer.setAlignment(0.0f, 0.0f); /* * Because the elevation is a formatted String, it won't sort * properly. So we use a DataColumnInteger populated with the raw * altitude number to indicate the sort order. (If you don't believe * me, try changing the sort column to elevationFormatted instead of * elevationSort, and you'll see it order as 1015, 147, 5894). As * you'll see from the documentation, setSortColumn() also makes the * headers clickable, which saves you having to mess with TreeView's * setHeadersClickable() or TreeViewColumn's setClickable(), although * there are occasional use cases for them. * * Then we call clicked() to force the header we want things to be * sorted on to actually be active. This is especially necessary if * you've defined sorting for more than one vertical column, but if * you want sorting on from the start, you need to call it. */ vertical.setSortColumn(elevationSort); vertical.clicked(); /* * And that's it! You've now done everything you need to have a * working TreeView. * * ... except that if it's for more than information, you probably * want something to happen when someone clicks on a row. So, we hook * up a handler to the ROW_ACTIVATED signal. The TreePath it gives you * is the useful bit. */ view.connect(new TreeView.ROW_ACTIVATED() { public void onRowActivated(TreeView source, TreePath path, TreeViewColumn vertical) { final TreeIter row; final String place, height; row = model.getIter(path); place = model.getValue(row, trailHead); height = model.getValue(row, elevationFormatted); System.out.println("You want to go to " + place + " in order to climb to " + height); } }); /* * The rest of this file is the usual minimum wrapper to make this * presentable as a Window on the display. */ // ------------------------------------------------ w.add(view); w.setTitle("Trail Heads"); w.showAll(); w.connect(new Window.DELETE_EVENT() { public boolean onDeleteEvent(Widget source, Event event) { Gtk.mainQuit(); return false; } }); } public static void main(String[] args) { Gtk.init(args); new ExampleTrailHeads(); Gtk.main(); } }