A.33. Trees

TRUE = 1;
FALSE = 0;

/* The GTK_TREE_ITEM_SUBTREE() macro isn't supported in Gamma/GTK.
 * This resouce takes its place, and must be assigned whenever the
 * GtkTreeItem.set_subtree() method is used. */
class_add_ivar (GtkTreeItem, #subtree);

/* Information from SetTree Parameters window for creating the tree. */
class TreeInfo
{
  select_mode;
  draw_line;
  view_line;
  no_root_item;
  num_items;
  depth_items;
}

/* Status of Tree Sample buttons: sensitive or not. */
class TreeButtons
{
  add_button;
  remove_button;
  subtree_button;
}

/* A list of all items in a Tree Sample, and a total number of
   items added to the sample. */
class SampleInfo
{
  item_list = list();
  added = 0;  
}

/* Returns a list of selected items in the tree sample.
 * This takes the place of GTK_TREE_SELECTION(), and allows
 * for multiple selections.*/
method SampleInfo.selected()
{
  local select_list;
  
  with item in self.item_list select_list=tcollect
    if ((instance_p(item)) && (item.state == GTK_STATE_SELECTED))
      item;
    else
      nil;
  
  select_list;
}     

/* creates an adjustment */
function make_adj (low, up, step, pg_inc, pg_size)
{
  adj = new(GtkAdjustment);
  adj.lower = low;
  adj.upper = up;
  adj.step_increment = step;
  adj.page_increment = pg_inc;
  adj.page_size = pg_size;
  adj;
}

function cb_tree_changed(buttons, s_info)
{
  local change_list;
  
  if (change_list = s_info.selected())
    {
      buttons.add_button.set_sensitive(TRUE);
      buttons.remove_button.set_sensitive(TRUE);
      buttons.subtree_button.set_sensitive(TRUE);
    }
  else
    {
      buttons.add_button.set_sensitive(FALSE);
      buttons.remove_button.set_sensitive(FALSE);
      buttons.subtree_button.set_sensitive(FALSE);
    }
}

function cb_add_new_item(tree, s_info)
{
  local item_subtree, add_list, item_new;
  
  if (add_list = s_info.selected())
    {
      with selected_item in add_list do
	{
	  item_new = gtk_tree_item_new_with_label (string("added-item-",
							  s_info.added));
	  if (!selected_item.subtree)
	    {
	      item_subtree = gtk_tree_new();	  
	      selected_item.set_subtree(item_subtree);
	      selected_item.subtree = item_subtree;
	    }
	  s_info.added++;
	  s_info.item_list = cons(item_new, s_info.item_list);
	  selected_item.subtree.append(item_new);
	  item_new.show();
	}
    }
}

function cb_remove_item(tree, s_info)
{
  local clear_list;

  if (clear_list = s_info.selected())
    tree.remove_items (clear_list);
}

function cb_remove_subtree(tree, s_info)
{
  local clear_list;

  if (clear_list = s_info.selected())
    with selected_item in clear_list do
      if ((selected_item.subtree) &&
	  (!destroyed_p(selected_item.subtree)))
	selected_item.remove_subtree();
}

function tree_info_cb(wgt)
{
  if (class_name(wgt) == #GtkSpinButton)
    wgt.get_value_as_int();
  else
    if (wgt.get_active() == TRUE)
      1;
    else
      0;
}

function create_subtree(item, level, item_max, recurse_max)
{
  local item_subtree, item_new, nb_item, no_root_item;

  if (level != recurse_max)
    {
      if (level == -1)
	{
	  /* query with no root item */
	  level = 0;
	  item_subtree = item;
	  princ(class_name(item), "\n");
	  no_root_item = 1;
	}
      else
	{
	  /* create subtree and associate it with current item */
	  item_subtree = gtk_tree_new();	  
	  no_root_item = 0;
	}

      /* create new item(s) */
      for (nb_item = 0; nb_item < item_max; nb_item++)
	{
	  item_new = gtk_tree_item_new_with_label(string("item-", level,
							 "-", nb_item));
	  s_info.item_list = cons(item_new, s_info.item_list);
	  item_subtree.append(item_new);
	  create_subtree(item_new, level+1, item_max, recurse_max);
	  item_new.show();
	}

      if (no_root_item == 0)
	{
	  item.set_subtree(item_subtree);
	  item.subtree = item_subtree;
	}
    }
}

function create_tree_sample(t_info)
{
  local window, box1, box2, separator, button, scrolled_win,
  root_tree, root_item, tree_buttons, max;

  if ((max = pow (t_info.num_items, t_info.depth_items)) > 10000)
    princ(format(string("%1.3g total items? That will take a very",
		 " long time. Try less.\n"), max));
  else
    {      
      /* create top level window */
      window = new(GtkWindow);
      window.signal ("destroy", #window = nil);
      window.title = "Tree Sample";

      /* instantiate a TreeButtons */
      tree_buttons = new(TreeButtons);
      
      /* instantiate a SampleInfo */
      s_info = new(SampleInfo);
      
      box1 = new(GtkVBox);
      window.add(box1);
      box1.show();
      
      /* create tree box */
      box2 = new(GtkVBox);
      box1.pack_start(box2, TRUE, TRUE, 0);
      box2.border_width = 5;
      box2.show();
      
      /* create scrolled window */
      scrolled_win = new(GtkScrolledWindow);
      scrolled_win.set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
      box2.pack_start(scrolled_win, TRUE, TRUE, 0);
      scrolled_win.set_usize(200, 200);
      scrolled_win.show();
      
      tree_item = new(GtkTreeItem);
      
      /* create root tree widget */
      root_tree = new(GtkTree);
      root_tree.signal("selection_changed",
		       `cb_tree_changed(@tree_buttons, @s_info));
      scrolled_win.add_with_viewport(root_tree);
      root_tree.set_selection_mode(t_info.select_mode);
      root_tree.set_view_lines(t_info.draw_line);
      root_tree.set_view_mode(t_info.view_line);
      root_tree.show();
      
      if (t_info.no_root_item == 1)
	{
	  /* set root tree to subtree function with root item variable */
	  root_item = root_tree;
	}
      else
	{
	  /* create root tree item widget */
	  root_item = gtk_tree_item_new_with_label("root item");
	  root_tree.append(root_item);
	  root_item.show();
	  s_info.item_list = cons(root_item, s_info.item_list);
	}
      
      create_subtree(root_item, -(t_info.no_root_item), t_info.num_items,
		     t_info.depth_items);
      
      box2 = new(GtkVBox);
      box1.pack_start(box2, FALSE, FALSE, 0);
      box2.border_width = 5;
      box2.show();
      
      button = new(GtkButton);
      button.label = "add Item";
      button.set_sensitive(FALSE);
      button.signal("clicked", `cb_add_new_item(@root_tree, @s_info));
      box2.pack_start(button, TRUE, TRUE, 0);
      button.show();
      tree_buttons.add_button = button;
      
      button = new(GtkButton);
      button.label = "Remove Item(s)";
      button.set_sensitive(FALSE);
      button.signal("clicked", `cb_remove_item(@root_tree, @s_info));
      box2.pack_start(button, TRUE, TRUE, 0);
      button.show();
      tree_buttons.remove_button = button;
      
      button = new(GtkButton);
      button.label = "Remove Subtree";
      button.set_sensitive(FALSE);
      button.signal("clicked", `cb_remove_subtree(@root_tree, @s_info));
      box2.pack_start(button, TRUE, TRUE, 0);
      button.show();
      tree_buttons.subtree_button = button;
      
      /* create separator */
      separator = new(GtkHSeparator);
      box2.pack_start(separator, FALSE, FALSE, 0);
      separator.show();
      
      /* create button box */
      box2 = new(GtkVBox);
      box2.border_width = 5;
      box1.pack_start(box2, FALSE, FALSE, 0);
      box2.show();
      
      button = new(GtkButton);
      button.label = "Close";
      box2.pack_start(button, TRUE, TRUE, 0);
      button.signal("clicked", `(@window).destroy());
      button.show();
      
      window.show();
    }
}

function create_tree_mode_window ()
{
  local box1, box2, box3, box4, box5, button, frame, separator, label,
  spinner, adj;
  
  win_tree_mode_window = new (GtkWindow);
  win_tree_mode_window.signal ("destroy", #win_tree_mode_window = nil);
  win_tree_mode_window.title = "SetTree Parameters";
  win_tree_mode_window.border_width = 0;
  
  box1 = new(GtkVBox);
  win_tree_mode_window.add(box1);
  
  /* create upper box - selection box */
  box2 = new(GtkVBox);
  box2.spacing = 5;
  box2.border_width = 5;
  box1.pack_start(box2, TRUE, TRUE, 0);
  
  box3 = new(GtkHBox);
  box3.spacing = 5;
  box2.pack_start(box3, TRUE, TRUE, 0);
  
  /* create selection mode frame */
  frame = new(GtkFrame);
  frame.label = "Selection Mode";
  box3.pack_start(frame, TRUE, TRUE, 0);
  
  box4 = new(GtkVBox);
  frame.add(box4);
  box4.border_width = 5;

  /* instantiate a TreeInfo */
  tree_info = new(TreeInfo);
  
  /* create radio buttons.*/
  button1 = gtk_radio_button_new_with_label(nil, "SINGLE");
  button1.signal("clicked", `(@tree_info).select_mode = GTK_SELECTION_SINGLE);
  button1.set_active(TRUE);
  box4.pack_start(button1, TRUE, TRUE, 0);
  
  button2 = gtk_radio_button_new_with_label(list(button1), "BROWSE");
  button2.signal("clicked", `(@tree_info).select_mode = GTK_SELECTION_BROWSE);
  button2.set_active(TRUE);
  box4.pack_start(button2, TRUE, TRUE, 0);
  
  button3 = gtk_radio_button_new_with_label(list(button1, button2),
					    "MULTIPLE");
  button3.signal("clicked", `(@tree_info).select_mode = GTK_SELECTION_MULTIPLE);
  button1.set_active(TRUE);
  box4.pack_start(button3, TRUE, TRUE, 0);
  
  /* create option mode frame */
  frame = new(GtkFrame);
  frame.label = "Options";
  box3.pack_start(frame, TRUE, TRUE, 0);
  
  box4 = new(GtkVBox);
  frame.add(box4);
  box4.border_width = 5;
  
  /* create check buttons */
  button = gtk_check_button_new_with_label("Draw line");
  box4.pack_start(button, TRUE, TRUE, 0);
  button.signal("toggled", `(@tree_info).draw_line = tree_info_cb(@button));
  button.set_active(TRUE);
  
  button = gtk_check_button_new_with_label("View Line mode");
  box4.pack_start(button, TRUE, TRUE, 0);
  button.signal("toggled", `(@tree_info).view_line = tree_info_cb(@button));
  button.set_active(TRUE);
  
  button = gtk_check_button_new_with_label("Without Root item");
  box4.pack_start(button, TRUE, TRUE, 0);
  button.signal("toggled", `(@tree_info).no_root_item = tree_info_cb(@button));
  button.set_active(FALSE);
  tree_info.no_root_item = 0;
  
  /* create size parameters frame */
  frame = new(GtkFrame);
  frame.label = "Size Parameters";
  box2.pack_start(frame, TRUE, TRUE, 0);
  
  box4 = new(GtkHBox);
  box4.spacing = 5;
  frame.add(box4);
  box4.border_width = 5;
  
  /* create number of item spin button */
  box5 = new(GtkHBox);
  box5.spacing = 5;
  box4.pack_start(box5, FALSE, FALSE, 0);
  
  label = new(GtkLabel);
  label.set_text("Number of items : ");
  label.set_alignment(0, 0.5);
  box5.pack_start(label, FALSE, TRUE, 0);
  
  adj = make_adj(1.0, 255.0, 1.0, 5.0, 0.0);
  
  spinner = new(GtkSpinButton);
  spinner.configure(adj, 0, 0);
  spinner.signal("changed", `(@tree_info).num_items = tree_info_cb(@spinner));
  spinner.set_value(3);
  box5.pack_start(spinner, FALSE, TRUE, 0);
  
  /* create recursion level spin button */
  box5 = new(GtkHBox);
  box5.spacing = 5;
  box4.pack_start(box5, FALSE, FALSE, 0);
  
  label = new(GtkLabel);
  label.set_text("Depth : ");
  label.set_alignment(0, 0.5);
  box5.pack_start(label, FALSE, TRUE, 0);
  
  adj = make_adj(0.0, 255.0, 1.0, 5.0, 0.0);
  
  spinner = new(GtkSpinButton);
  spinner.configure(adj, 0, 0);
  spinner.signal("changed", `(@tree_info).depth_items =  tree_info_cb(@spinner));
  spinner.set_value(3);
  box5.pack_start(spinner, FALSE, TRUE, 0);
  
  /* create horizontal separator */
  separator = new(GtkHSeparator);
  box1.pack_start(separator, FALSE, FALSE, 0);
  
  /* create bottom button box */
  box2 = new(GtkHBox);
  box2.homogeneous = (TRUE);
  box2.spacing = 10;
  box1.pack_start(box2, FALSE, FALSE, 0);
  box2.border_width = 5;
  
  button = new(GtkButton);
  button.label = "Create Tree";
  button.signal("clicked",`create_tree_sample(@tree_info));
  box2.pack_start(button, TRUE, TRUE, 0);
  
  button = new(GtkButton);
  button.label = "Close";
  button.signal("clicked", `(@win_tree_mode_window).destroy());
  box2.pack_start(button, TRUE, TRUE, 0);

  win_tree_mode_window.show_all();
  win_tree_mode_window;
}

function main ()
{
  local window, win_tree_mode_window;
  TRUE=1;
  FALSE=0;
  window = create_tree_mode_window ();
  window.signal ("destroy", #exit_program(0));
  init_ipc("tree_mode_win", "tree_mode_winq");
  gtk_main ();
}