2.6. Sample Code: TreeViewDemo.g

This tutorial demonstrates how to create a tree to view data.

Output

Code

/* All user scripts should derive from the base "Application" class */

require ("Application");
require ("WindowsSupport");

class TreeViewDemo Application
{
    window;
    tree;
    data = [["top_level_1",
                ["level_1_1",
                    ["level_2_1",
                        "level_3_1", 
                        ["level_3_2", "level_4_1"],
                        "level_3_3"
                    ], 
                    ["level_2_2",
                        "level_3_4", "level_3_5", "level_3_6"
                    ]
                ],
                ["level_1_2",
                    "level_2_3", "level_2_4"
                ]
            ]
           ];
}

/* Use methods to create functions outside the 'main line'. */
method TreeViewDemo.FillTree (data)
{
    local   root = .tree.GetRootItem();
    with descendants in data do
    {
        .FillTreeRecursive(root, descendants);
    }
}

// Adding an item uses the InsertItem method.
method TreeViewDemo.FillTreeRecursive (parent, data)
{
    local   nodename = array_p(data) ? data[0] : data;
    local   htreeitem = .tree.InsertItem(nodename, 0, 0, parent, TVI_LAST);
    local   i;
    
    if (array_p(data))
    {
        for (i=1; i<length(data); i++)
        {
            .FillTreeRecursive(htreeitem, data[i]);
        }
    }
}

// Find the full path name of the selected item in the tree
method TreeViewDemo.GetTreeSelection ()
{
    local       hitem = .tree.GetSelectedItem();
    .GetTreeItemFullname (hitem);
}

// Get just the text label of the given item
method TreeViewDemo.GetTreeItemText (hitem)
{
    local       item = new TVITEM();
    local       buf;
    
    item.hItem = hitem;
    item.mask = TVIF_TEXT;
    item.cchTextMax = 128;
    item.pszText = make_buffer(128);
    .tree.GetItem (item);
    buf = item.pszText;
    buffer_to_string (buf);
}

// Find the full path name of the given item.  The path
// separator can be changed by modifying the / below
method TreeViewDemo.GetTreeItemFullname (hitem)
{
    local       itemtext, pname, hparent, hgrandparent, sep = "/";
    if (hitem != 0)
    {
        itemtext = .GetTreeItemText (hitem);
        hparent = .tree.GetParentItem(hitem);
        if (hparent != 0)
        {
            hgrandparent = .tree.GetParentItem(hparent);
            // If you want to make the first separator different from the
            // rest, change it here.
            if (hgrandparent == 0)
                sep = "/";
        }
            
        pname = .GetTreeItemFullname (hparent);
        if (pname)
            string (pname, sep, itemtext);
        else
            itemtext;
    }
    else
        nil;
}

// Callback when an item is selected
method TreeViewDemo.cbItemSelected (itemname)
{
    princ ("You selected item: ", itemname, "\n");
}

// Callback before a branch in the tree is expanded or collapsed
method TreeViewDemo.cbPointExpanding ()
{
    local   item = map_volatile_pointer (pnmh, NMTREEVIEW);
    local   hitem = item.itemNew.hItem;
    local   pointname = .GetTreeItemFullname (hitem);

    if (item.action == TVE_COLLAPSE)
    {
        princ ("Item: ", pointname, " is about to collapse\n");
    }
    else
    {
        princ ("Item: ", pointname, " is about to expand\n");
    }

    // Allow the tree to expand or collapse
    0;
}

// Callback after a branch is expanded or collapsed
method TreeViewDemo.cbPointExpanded ()
{
    local   item = map_volatile_pointer (pnmh, NMTREEVIEW);
    local   hitem = item.itemNew.hItem;
    local   pointname = .GetTreeItemFullname (hitem);

    if (item.action == TVE_COLLAPSE)
    {
        princ ("Item: ", pointname, " has collapsed\n");
    }
    else
    {
        princ ("Item: ", pointname, " has expanded\n");
    }

    // Allow the tree to expand or collapse
    0;
}

/* Write the 'main line' of the program here. */
method TreeViewDemo.constructor ()
{
    local   win = new GWindow();
    local   rect = CreateRect(100, 100, 220, 220);
    local   clientrect = CreateRect(0,0,0,0);
    
    win.Create (0, rect, "TreeView Example", WS_OVERLAPPEDWINDOW, 0);
    win.MessageHandler (WM_DESTROY, `(!destroyed_p(@self) ? destroy(@self) : nil));
    .window = win;
    .window.SetBackground (0, GetSysColor (COLOR_3DFACE), 0);
    
    // Find the interior size of the window so we can size the TreeView
    win.GetClientRect(clientrect);
    
    // ------- TreeView example starts here
    
    // Create a TreeView.  Use TVS_* to set the options.
    local   tv = win.CreateControl (GTreeViewCtrl, 5, 5,
				    clientrect.right - 10, clientrect.bottom - 10,
        "ExampleTreeView", TVS_CHECKBOXES | TVS_HASLINES |
			   TVS_LINESATROOT | TVS_HASBUTTONS |
        TVS_TRACKSELECT | TVS_NOHSCROLL);

    .tree = tv;
    
    // If the window has a different background, make sure the combo box
    // stays white.
    tv.SetChildBackground (0, GetSysColor (COLOR_WINDOW), 0);
    
    // Fill the tree once at the start
    .FillTree(.data);
    
    // Trigger an event when the user selects an item
    win.NotifyHandler (TVN_SELCHANGED, tv,
		       `(@self).cbItemSelected((@self).GetTreeSelection()));
    win.NotifyHandler (TVN_ITEMEXPANDING, tv, `(@self).cbPointExpanding());
    win.NotifyHandler (TVN_ITEMEXPANDED, tv, `(@self).cbPointExpanded());
    
    // There is no event in Windows for changing a check-box state in a TreeView !?
    // Checkbox events are absurdly difficult to deal with in C++, and impossible
    //in Gamma.
    
    // You can add events to handle other use cases as well...
    //win.NotifyHandler (NM_DBLCLK, tv, `(@self).cbDoubleClick((@self).GetTreeSelection()));
    //tv.MessageHandler (WM_RBUTTONDOWN, `((@self).cbRightClick()));
    
    // Reize the TreeView when the window resizes
    win.AddControlResizeFlags (tv, DLSZ_SIZE_X | DLSZ_SIZE_Y);
    
    // ------- TreeView example ends here
    
    win.ShowWindow (SW_SHOW);
}

/* Any code to be run when the program gets shut down. */
method TreeViewDemo.destructor ()
{
    if (instance_p(.window))
        destroy (.window);
}

/* Start the program by instantiating the class.  If your
 * constructor code does not create a persistent reference to
 * the instance (self), then it will be destroyed by the
 * garbage collector soon after creation.  If you do not want
 * this to happen, assign the instance to a global variable, or
 * create a static data member in your class to which you assign
 * 'self' during the construction process.  ApplicationSingleton()
 * does this for you automatically. */
ApplicationSingleton (TreeViewDemo);