0068: MVC XI - Multi-level TreeStore

The last time we looked at an MVC example, we dug into the basics of the TreeStore, but we didn’t look at how it deals with multi-level, multi-column data. Today we pick up where we left off and do just that.

Note: The example used herein is based heavily on the D-language TreeStore example found on a Google Sites page built back in 2013. I could find no credits, so to the anonymous writer of that tutorial, I thank you.

A Multi-level TreeStore

Results of this example:
Current example output
Current example output
Current example terminal output
Current example terminal output (click for enlarged view)

The Model

The heart of this example is the LocationTreeStore where we set up a three-tier hierarchy of population numbers for some of the continents, countries, and cities on our planet. The procedure is more or less the same as what we’ve done before except that some of the work is farmed out to the addLocation() function… which we’ll look at momentarily.

In the original 2013 example, there were two functions for adding locations:

  • addLocation, and
  • addChildLocation.

But I wanted to bring a little something to the table, so I reworked them as a single, overloaded function called simply addLocation(). And while I was at it, I also added pretty-ish formatting to the numbers. Here’s how it all goes down:

First, we declare the variables:

private:
TreeIter parentIterAsia, childIterChina, childIterJapan;
TreeIter parentIterEurope, childIterUK, childIterFrance;
TreeIter parentIterNorthAmerica, childIterCanada, childIterUSA;

Then in the constructor, we lay out the Model:

super([GType.STRING, GType.STRING]);

Following which, we populate each hierarchy of continent, country, and city like this:

parentIterNorthAmerica = addLocation("North America", cast(uint)366_496_802);
childIterCanada = addLocation(parentIterNorthAmerica, "Canada", cast(uint)37_307_925);
addLocation(childIterCanada, "Ottawa", cast(uint)1_378_151);
childIterUSA = addLocation(parentIterNorthAmerica, "United States", cast(uint)329_293_776);
addLocation(childIterUSA, "Washington, DC", cast(uint)713_244);

Note that the numbers are all cast as uint types at this stage.

The Overloaded Function

Here are the two overloads:

	// Adds a location and returns the TreeIter of the item added.
	public TreeIter addLocation(in string name, in uint population)
	{
		TreeIter iter = createIter();
		setValue(iter, 0, name);
		setValue(iter, 1, format("%,?d", ',', population));
		
		return(iter);
		
	} // addLocation()
	

	// Adds a child location to the specified parent TreeIter.
	public TreeIter addLocation(TreeIter parent, in string name, in uint population)
	{
		TreeIter childIter = createIter(parent);
		setValue(childIter, 0, name);
		setValue(childIter, 1, format("%,?d", ',', population));
		
		return(childIter);
		
	} // addLocation()

The first overload deals with top-tier rows. It needs two arguments, a string for the name of the continent and a uint for the population. And it returns a TreeIter.

The second overload has three arguments, a TreeIter for the parent that’ll “be the boss of” the child we’re creating, a string for the child’s name, and, of course, a uint.

Note: When doing overloaded functions, make sure each overload’s first argument is of a unique type (uint, string, etc.) so the D compiler can easily differentiate between them.

The View

Structurally, the view is no different than what we used in the earlier TreeStore example:

class LocationTreeView : TreeView
{
	private:
	TreeViewColumn countryColumn, populationColumn;
	LocationTreeStore locationTreeStore;
	
	public:
	this()
	{
		super();
		locationTreeStore = new LocationTreeStore();
		setModel(locationTreeStore);

		// Add Country Column
		countryColumn = new TreeViewColumn("Location", new CellRendererText(), "text", 0);
		appendColumn(countryColumn);
		
		// Add Capital Column
		populationColumn = new TreeViewColumn("Population", new CellRendererText(), "text", 1);
		appendColumn(populationColumn);

	} // this()
	
} // class LocationTreeView

We simply:

  • instantiate the TreeView,
  • associate the model (LocationTreeStore), and
  • pop in the TreeViewColumns.

Imports

The only other thing we need to do—and this, only because I wanted to prettify the numbers—is to do one extra import statement:

import std.string;

Conclusion

All this TreeView/TreeStore stuff is rather easy once we’ve broken it all down. Yes, there’s a lot involved, but none of it’s really a brain bender when we approach it one concept at a time.

Hope you liked the MVC series. Until next time, have fun coding.

Comments? Questions? Observations?

Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.

You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.

© Copyright 2023 Ron Tarrant