The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Tree View/Tree Store
#
# The GtkTreeStore is used to store data in tree form, to be
# used later on by a GtkTreeView to display it. This demo builds
# a simple GtkTreeStore and displays it. If you're new to the
# GtkTreeView widgets and associates, look into the GtkListStore
# example first.
#

package tree_store;

use Gtk2;
use Glib qw(TRUE FALSE);

my $window = undef;

# columns
use constant HOLIDAY_NAME_COLUMN => 0;
use constant ALEX_COLUMN         => 1;
use constant HAVOC_COLUMN        => 2;
use constant TIM_COLUMN          => 3;
use constant OWEN_COLUMN         => 4;
use constant DAVE_COLUMN         => 5;

use constant VISIBLE_COLUMN      => 6;
use constant WORLD_COLUMN        => 7;
use constant NUM_COLUMNS         => 8;


#
# tree data 
#

my @january = (
  { label => "New Years Day",              alex => TRUE,  havoc => TRUE, tim => TRUE,  owen => TRUE, dave => FALSE, world_holiday => TRUE  },
  { label => "Presidential Inauguration",  alex => FALSE, havoc => TRUE, tim => FALSE, owen => TRUE, dave => FALSE, world_holiday => FALSE },
  { label => "Martin Luther King Jr. day", alex => FALSE, havoc => TRUE, tim => FALSE, owen => TRUE, dave => FALSE, world_holiday => FALSE },
);

my @february = (
  { label => "Presidents' Day", alex => FALSE, havoc => TRUE,  tim => FALSE, owen => TRUE,  dave => FALSE, world_holiday => FALSE },
  { label => "Groundhog Day",   alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Valentine's Day", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => TRUE,  world_holiday => TRUE  },
);

my @march = (
  { label => "National Tree Planting Day", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "St Patrick's Day",           alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => TRUE  },
);

my @april = (
  { label => "April Fools' Day",                  alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => TRUE  },
  { label => "Army Day",                          alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Earth Day",                         alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => TRUE  },
  { label => "Administrative Professionals' Day", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
);

my @may = (
  { label => "Nurses' Day",            alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "National Day of Prayer", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Mothers' Day",           alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => TRUE  },
  { label => "Armed Forces Day",       alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Memorial Day",           alex => TRUE,  havoc => TRUE,  tim => TRUE,  owen => TRUE,  dave => FALSE, world_holiday => TRUE  },
);

my @june = (
  { label => "June Fathers' Day",                 alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => TRUE  },
  { label => "Juneteenth (Liberation of Slaves)", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Flag Day",                          alex => FALSE, havoc => TRUE,  tim => FALSE, owen => TRUE,  dave => FALSE, world_holiday => FALSE },
);

my @july = (
  { label => "Parents' Day",     alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => TRUE  },
  { label => "Independence Day", alex => FALSE, havoc => TRUE,  tim => FALSE, owen => TRUE,  dave => FALSE, world_holiday => FALSE },
);

my @august = (
  { label => "Air Force Day",   alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Coast Guard Day", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Friendship Day",  alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
);

my @september = (
  { label => "Grandparents' Day",                   alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => TRUE  },
  { label => "Citizenship Day or Constitution Day", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Labor Day",                           alex => TRUE,  havoc => TRUE,  tim => TRUE,  owen => TRUE,  dave => FALSE, world_holiday => TRUE  },
);

my @october = (
  { label => "National Children's Day", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Bosses' Day",             alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Sweetest Day",            alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Mother-in-Law's Day",     alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Navy Day",                alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Columbus Day",            alex => FALSE, havoc => TRUE,  tim => FALSE, owen => TRUE,  dave => FALSE, world_holiday => FALSE },
  { label => "Halloween",               alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => TRUE  },
);

my @november = (
  { label => "Marine Corps Day", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Veterans' Day",    alex => TRUE,  havoc => TRUE,  tim => TRUE,  owen => TRUE,  dave => FALSE, world_holiday => TRUE  },
  { label => "Thanksgiving",     alex => FALSE, havoc => TRUE,  tim => FALSE, owen => TRUE,  dave => FALSE, world_holiday => FALSE },
);

my @december = (
  { label => "Pearl Harbor Remembrance Day", alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
  { label => "Christmas",                    alex => TRUE,  havoc => TRUE,  tim => TRUE,  owen => TRUE,  dave => FALSE, world_holiday => TRUE  },
  { label => "Kwanzaa",                      alex => FALSE, havoc => FALSE, tim => FALSE, owen => FALSE, dave => FALSE, world_holiday => FALSE },
);


my @toplevel = (
  {label => "January",     children => \@january},
  {label => "February",    children => \@february},
  {label => "March",       children => \@march},
  {label => "April",       children => \@april},
  {label => "May",         children => \@may},
  {label => "June",        children => \@june},
  {label => "July",        children => \@july},
  {label => "August",      children => \@august},
  {label => "September",   children => \@september},
  {label => "October",     children => \@october},
  {label => "November",    children => \@november},
  {label => "December",    children => \@december},
);


#static GtkTreeModel *
#create_model (void)
sub create_model {
   # create tree store
#  model = gtk_tree_store_new (NUM_COLUMNS,
#			      G_TYPE_STRING,
#			      G_TYPE_BOOLEAN,
#			      G_TYPE_BOOLEAN,
#			      G_TYPE_BOOLEAN,
#			      G_TYPE_BOOLEAN,
#			      G_TYPE_BOOLEAN,
#			      G_TYPE_BOOLEAN,
#			      G_TYPE_BOOLEAN);
   my $model = Gtk2::TreeStore->new (qw/ Glib::String 
                                         Glib::Boolean 
                                         Glib::Boolean 
                                         Glib::Boolean 
                                         Glib::Boolean 
                                         Glib::Boolean 
                                         Glib::Boolean 
                                         Glib::Boolean /);

   # add data to the tree store/
   foreach my $month (@toplevel) {
       my $iter = $model->append (undef);
       $model->set ($iter,
                    HOLIDAY_NAME_COLUMN, $month->{label},
                    ALEX_COLUMN,    FALSE,
                    HAVOC_COLUMN,   FALSE,
                    TIM_COLUMN,     FALSE,
                    OWEN_COLUMN,    FALSE,
                    DAVE_COLUMN,    FALSE,
                    VISIBLE_COLUMN, FALSE,
                    WORLD_COLUMN,   FALSE);

       # add children
       foreach my $holiday (@{ $month->{children} }) {
          my $child_iter = $model->append ($iter);
          $model->set ($child_iter,
                       HOLIDAY_NAME_COLUMN, $holiday->{label},
                       ALEX_COLUMN, $holiday->{alex},
                       HAVOC_COLUMN, $holiday->{havoc},
                       TIM_COLUMN, $holiday->{tim},
                       OWEN_COLUMN, $holiday->{owen},
                       DAVE_COLUMN, $holiday->{dave},
                       VISIBLE_COLUMN, TRUE,
                       WORLD_COLUMN, $holiday->{world_holiday});
       }
    }

    return $model;
}

#static void
#item_toggled (GtkCellRendererToggle *cell,
#	      gchar                 *path_str,
#	      gpointer               data)
sub item_toggled {
   my ($cell, $path_str, $model) = @_;
   my $path = Gtk2::TreePath->new_from_string ($path_str);

   my $column = $cell->get_data ("column");
   warn ("column is $column\n");

   # get toggled iter
   $iter = $model->get_iter ($path);
   my ($toggle_item) = $model->get ($iter, $column);

   # do something with the value
   $toggle_item ^= 1;

   # set new value
   $model->set ($iter, $column, $toggle_item);

#  /* clean up */
#  gtk_tree_path_free (path);
}

#static void
#add_columns (GtkTreeView *treeview)
sub add_columns {
   my $treeview = shift;
   my $model = $treeview->get_model;

   # column for holiday names
   my $renderer = Gtk2::CellRendererText->new;
   $renderer->set (xalign => 0.0);

   my $col_offset = $treeview->insert_column_with_attributes 
   					(-1, "Holiday", $renderer,
					 text => HOLIDAY_NAME_COLUMN);
   my $column = $treeview->get_column ($col_offset - 1);
   $column->set_clickable (TRUE);

   # alex column
   $renderer = Gtk2::CellRendererToggle->new;
   $renderer->set (xalign => 0.0);
   $renderer->set_data (column => ALEX_COLUMN);

   $renderer->signal_connect (toggled => \&item_toggled, $model);

   $col_offset = $treeview->insert_column_with_attributes 
 					(-1, "Alex", $renderer,
					 active => ALEX_COLUMN, 
					 visible => VISIBLE_COLUMN,
					 activatable => WORLD_COLUMN);

   $column = $treeview->get_column ($col_offset - 1);
   $column->set_sizing ('fixed');
   $column->set_fixed_width (50);
   $column->set_clickable (TRUE);

   # havoc column
   $renderer = Gtk2::CellRendererToggle->new;
   $renderer->set (xalign => 0.0);
   $renderer->set_data (column => HAVOC_COLUMN);

   $renderer->signal_connect (toggled => \&item_toggled, $model);

   $col_offset = $treeview->insert_column_with_attributes 
   					(-1, "Havoc", $renderer,
					 active => HAVOC_COLUMN, 
					 visible => VISIBLE_COLUMN);

   $column = $treeview->get_column ($col_offset - 1);
   $column->set_sizing ('fixed');
   $column->set_fixed_width (50);
   $column->set_clickable (TRUE);

   # tim column
   $renderer = Gtk2::CellRendererToggle->new;
   $renderer->set (xalign => 0.0);
   $renderer->set_data (column => TIM_COLUMN);

   $renderer->signal_connect (toggled => \&item_toggled, $model);

   $col_offset = $treeview->insert_column_with_attributes 
					(-1, "Tim", $renderer,
					 active => TIM_COLUMN,
					 visible => VISIBLE_COLUMN,
					 activatable => WORLD_COLUMN);

   $column = $treeview->get_column ($col_offset - 1);
   $column->set_sizing ('fixed');
   $column->set_fixed_width (50);
   $column->set_clickable (TRUE);

   # owen column
   $renderer = Gtk2::CellRendererToggle->new;
   $renderer->set (xalign => 0.0);
   $renderer->set_data (column => OWEN_COLUMN);

   $renderer->signal_connect (toggled => \&item_toggled, $model);

   $col_offset = $treeview->insert_column_with_attributes 
   					(-1, "Owen", $renderer,
					 active => OWEN_COLUMN,
					 visible => VISIBLE_COLUMN);

   $column = $treeview->get_column ($col_offset - 1);
   $column->set_sizing ('fixed');
   $column->set_fixed_width (50);
   $column->set_clickable (TRUE);

   # dave column
   $renderer = Gtk2::CellRendererToggle->new;
   $renderer->set (xalign => 0.0);
   $renderer->set_data (column => DAVE_COLUMN);

   $renderer->signal_connect (toggled => \&item_toggled, $model);

   $col_offset = $treeview->insert_column_with_attributes 
   				   (-1, "Dave", $renderer,
				    active => DAVE_COLUMN,
				    visible => VISIBLE_COLUMN);

   $column = $treeview->get_column ($col_offset - 1);
   $column->set_sizing ('fixed');
   $column->set_fixed_width (50);
   $column->set_clickable (TRUE);
}

sub do {
  if (!$window) {
       # create window, etc
       $window = Gtk2::Window->new ('toplevel');
       $window->set_title ("Card planning sheet");
       $window->signal_connect (destroy => sub { $window = undef });

       my $vbox = Gtk2::VBox->new (FALSE, 8);
       $vbox->set_border_width (8);
       $window->add ($vbox);

       $vbox->pack_start (Gtk2::Label->new ("Jonathan's Holiday Card Planning Sheet"),
 			  FALSE, FALSE, 0);

       my $sw = Gtk2::ScrolledWindow->new (undef, undef);
       $sw->set_shadow_type ('etched-in');
       $sw->set_policy ('automatic', 'automatic');
       $vbox->pack_start ($sw, TRUE, TRUE, 0);

       # create model
       my $model = create_model ();

       # create tree view
       ##my $treeview = Gtk2::TreeView->new_with_model ($model);
       my $treeview = Gtk2::TreeView->new ($model);
###      g_object_unref (model);
       $treeview->set_rules_hint (TRUE);
       $treeview->get_selection->set_mode ('multiple');

       add_columns ($treeview);

       $sw->add ($treeview);

       # expand all rows after the treeview widget has been realized
       $treeview->signal_connect (realize => sub { $_[0]->expand_all; 1 });
       $window->set_default_size (650, 400);
   }

   if (!$window->visible) {
       $window->show_all;
   } else {
       $window->destroy;
       $window = undef;
   }
 
   return $window;
}

1;
__END__
Copyright (C) 2003 by the gtk2-perl team (see the file AUTHORS for the
full list)

This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Library General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU Library General Public License for more
details.

You should have received a copy of the GNU Library General Public License along
with this library; if not, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA  02111-1307  USA.