Chapter 33. The Calendar Widget

Inheritance Hierarchy

Object
   +--- Widget
         +--- Calendar
         

The Calendar widget is an effective way to display and retrieve date related information. It is a very simple widget to create and work with.

Creating a Calendar widget is a simple as:

$calendar = new Gtk::Calendar();

There might be times where you need to change a lot of information within this widget and the following functions allow you to make multiple change to a Calendar widget without the user seeing multiple on-screen updates.

$calendar->freeze();

$calendar->thaw();

They work just like the freeze/thaw functions of other widgets.

The Calendar widget has a few options that allow you to change the way the widget both looks and operates by using the following function:

$calendar->display_options( $flags );

The $flags argument can be formed by combining any of the following five options:

The following functions are used to set the the currently displayed date:

$calendar->select_month( $month, $year );

$calendar->select_day( $day );

The return value from select_month() is a true or false value indicating whether the selection was successful.

With select_day() the specified day number is selected within the current month, if that is possible. A $day value of 0 will deselect any current selection.

In addition to having a day selected, any number of days in the month may be "marked". A marked day is highlighted within the calendar display. The following functions are provided to manipulate marked days:

$calendar->mark_day( $day );

$calendar->unmark_day( $day );

$calendar->clear_marks();

The currently marked days are stored within an array. This array is 31 elements long so to test whether a particular day is currently marked, you need to access the corresponding element of the array (don't forget that the array elements are numbered 0 to 30). For example:

if ( $calendar->marked_date[ $day - 1 ] )
  {
    print( "Day $day is marked\n" );
  }

Note that marks are persistent across month and year changes.

The final Calendar widget function is used to retrieve the currently selected date, month and/or year.

( $year, $month, $day ) = $calendar->get_date();

The Calendar widget can generate a number of signals indicating date selection and change. The names of these signals are self explanatory, and are:

33.1. Calendar Widget Example

That just leaves us with the need to put all of this together into example code.

Calendar Widget Example Source

      
#!/usr/bin/perl -w

# Be advised, this program is more complicated than the others in the
# tutorial.  However, I wanted to document as many features as
# possible.  I am also taking advantage of Perl data structures more
# heavily than normal.


use 
Gtk
;
use 
strict
;

set_locale Gtk;
init Gtk;


my
 $false = 0;

my
 $true = 1;

my
 $year_base = 1900;

my
 $i;


my
 @flags = ( "Show Heading",
	      "Show Day Names",
	      "No Month Change",
	      "Show Week Numbers",
	      "Week Start Monday" );


my
 %calendar_data = ( "
", "
" );


my
 $window;

my
 $vbox;

my
 $vbox2;

my
 $vbox3;

my
 $hbox;

my
 $hbbox;

my
 $calendar;

my
 $toggle;

my
 $button;

my
 $frame;

my
 $separator;

my
 $label;

my
 $bbox;


# Set up calendar data
$calendar_data{ "window" } = undef;
$calendar_data{ "font" } = undef;
$calendar_data{ "font_dialog" } = undef;
$calendar_data{ "settings" } = [ 0, 0, 0, 0, 0 ];

# Create the window
$window = new Gtk::Window( 'toplevel' );
$window->set_title( "Calendar Example" );
$window->border_width( 5 );
$window->signal_connect( 'destroy', 
sub
 { Gtk->
exit
( 0 ); } );
$window->signal_connect( 'delete-event', \&gtk_false );
$window->set_policy( $false, $false, $true );

$vbox = new Gtk::VBox( $false, 10 );
$window->add( $vbox );

# The top part of the window, Calendar, flags and fontsel.
$hbox = new Gtk::HBox( $false, 10 );
$vbox->pack_start( $hbox, $true, $true, 10 );

$hbbox = new Gtk::HButtonBox();
$hbox->pack_start( $hbbox, $false, $false, 10 );
$hbbox->set_layout( 'spread' );
$hbbox->set_spacing( 5 );

# Create the Calendar widget
$frame = new Gtk::Frame( "Calendar" );
$hbbox->pack_start( $frame, $false, $true, 10 );

$calendar = new Gtk::Calendar();
$calendar_data{ "window" } = $calendar;
calendar_set_flags();
$calendar->mark_day( 19 );
$frame->add( $calendar );


# Set up calendar callbacks
$calendar->signal_connect( 'month_changed',
			   \&calendar_month_changed );
$calendar->signal_connect( 'day_selected',
			   \&calendar_day_selected );
$calendar->signal_connect( 'day_selected_double_click',
			   \&calendar_day_selected_double_click );
$calendar->signal_connect( 'prev_month',
			   \&calendar_prev_month );
$calendar->signal_connect( 'next_month',
			   \&calendar_next_month );
$calendar->signal_connect( 'prev_year',
			   \&calendar_prev_year );
$calendar->signal_connect( 'next_year',
			   \&calendar_next_year );

$separator = new Gtk::VSeparator();
$hbox->pack_start( $separator, $false, $true, 0 );

$vbox2 = new Gtk::VBox( $false, 10 );
$hbox->pack_start( $vbox2, $false, $false, 10 );

# Build the Right frame with the flags in
$frame = new Gtk::Frame( "Flags" );
$vbox2->pack_start( $frame, $true, $true, 10 );

$vbox3 = new Gtk::VBox( $true, 5 );
$frame->add( $vbox3 );


for
 $i ( 0..4 )
{
   $toggle = new Gtk::CheckButton( $flags[ $i ] );
   $toggle->signal_connect( 'toggled', \&calendar_toggle_flag );
   $vbox3->pack_start( $toggle, $true, $true, 0 );
   $calendar_data{ 'flag_checkboxes' }[ $i ] = $toggle;
}

# Build the right font-button
$button = new Gtk::Button( "Font..." );
$button->signal_connect( 'clicked', \&calendar_select_font );
$vbox2->pack_start( $button, $false, $false, 0 );

#  Build the Signal-event part.
$frame = new Gtk::Frame( "Signal Events" );
$vbox->pack_start( $frame, $true, $true, 10 );

$vbox2 = new Gtk::VBox( $true, 5 );
$frame->add( $vbox2 );

# Frame to report signals
$hbox = new Gtk::HBox( $false, 3 );
$vbox2->pack_start( $hbox, $false, $true, 0 );
$label = new Gtk::Label( "Signal:" );
$hbox->pack_start( $label, $false, $true, 0 );
$calendar_data{ "last_sig" } = new Gtk::Label( "
" );
$hbox->pack_start( $calendar_data{ "last_sig" }, $false, $true, 0 );

$hbox = new Gtk::HBox( $false, 3 );
$vbox2->pack_start( $hbox, $false, $true, 0 );
$label = new Gtk::Label( "Previous Signal:" );
$hbox->pack_start( $label, $false, $true, 0 );
$calendar_data{ "prev_sig" } = new Gtk::Label( "
" );
$hbox->pack_start( $calendar_data{ "prev_sig" }, $false, $true, 0 );

$hbox = new Gtk::HBox( $false, 3 );
$vbox2->pack_start( $hbox, $false, $true, 0 );
$label = new Gtk::Label( "Second Previous Signal:" );
$hbox->pack_start( $label, $false, $true, 0 );
$calendar_data{ "prev2_sig" } = new Gtk::Label( "
" );
$hbox->pack_start( $calendar_data{ "prev2_sig" }, $false, $true, 0 );

$bbox = new Gtk::HButtonBox();
$vbox->pack_start( $bbox, $false, $false, 0 );
$bbox->set_layout( 'end' );

# Close button
$button = new Gtk::Button( "Close" );
$button->signal_connect( 'clicked', 
sub
 { Gtk->
exit
( 0 ); } );
$bbox->add( $button );
$button->can_default( $true );
$button->grab_default();

$window->show_all();

main Gtk;

exit
( 0 );



### Subroutines


# Function that takes the selected calendar date and converts it to a
# American style string.


sub
 
calendar_date_to_string

{
   
my
 $string;
   
my
 $year;
   
my
 $month;
   
my
 $day;

   
my
 @months = qw( January February March     April   May      June
		    July    August   September October November December );

   ( $year, $month, $day ) = $calendar_data{ 'window' }->get_date();

   $string = $months[ $month ] . " " . $day . ", " . $year;

   
return
 ( $string );
}


# Add a new string to the signal strings, moving previous strings to
# their appropriate place.


sub
 
calendar_set_signal_strings

{
   
my
 ( $sig_str ) = @_;

   
my
 $prev_sig;

   $prev_sig = $calendar_data{ 'prev_sig' }->get();
   $calendar_data{ 'prev2_sig' }->set( $prev_sig );

   $prev_sig = $calendar_data{ 'last_sig' }->get();
   $calendar_data{ 'prev_sig' }->set( $prev_sig );

   $calendar_data{ 'last_sig' }->set( $sig_str );
}


# Callback to report that the month has been changed.


sub
 
calendar_month_changed

{
   
my
 $buffer = "month_changed: " . calendar_date_to_string();

   calendar_set_signal_strings( $buffer );
}


# Callback to report that the day has been changed.


sub
 
calendar_day_selected

{
   
my
 $buffer = "day_selected: " . calendar_date_to_string();

   calendar_set_signal_strings( $buffer );
}


# Callback to report that the previous month button has been pressed.


sub
 
calendar_prev_month

{
   
my
 $buffer = "prev_month: " . calendar_date_to_string();

   calendar_set_signal_strings( $buffer );
}


# Callback to report that the next month button has been pressed.


sub
 
calendar_next_month

{
   
my
 $buffer = "next_month: " . calendar_date_to_string();

   calendar_set_signal_strings( $buffer );
}


# Callback to report that the previous year button has been pressed.


sub
 
calendar_prev_year

{
   
my
 $buffer = "prev_year: " . calendar_date_to_string();

   calendar_set_signal_strings( $buffer );
}


# Callback to report that the next year button has been pressed.


sub
 
calendar_next_year

{
   
my
 $buffer = "next_year: " . calendar_date_to_string();

   calendar_set_signal_strings( $buffer );
}


# A function that set the calendar settings based on the toggle
# buttons.


sub
 
calendar_set_flags

{
   
my
 $i;
   
my
 $options = 0;

   
for
 ( $i = 0; $i < 5; $i++ )
   {
      
if
 ( $calendar_data{ 'settings' }[ $i ] )
      {
	 $options = $options + ( 1 << $i );
      }
   }
   
if
 ( $calendar_data{ 'window' } )
   {
      $calendar_data{ 'window' }->display_options( $options );
   }
}


# Callback for radio buttons.  When toggled, they change the
# appropriate calendar setting.


sub
 
calendar_toggle_flag

{
   
my
 ( $toggle ) = @_;

   
my
 $j = 0;

   
for
 
my
 $i ( 0..4 )
   {
      
if
 ( $calendar_data{ 'flag_checkboxes' }[ $i ] == $toggle )
      {
	 $j = $i;
      }
   }

   $calendar_data{ 'settings' }[$j] = not $calendar_data{ 'settings' }[$j];
   calendar_set_flags();
}


# Callback called when the OK button of the font dialog is clicked on.


sub
 
calendar_font_selection_ok

{
   
my
 ( $button ) = @_;

   
my
 $style;
   
my
 $font;

   $calendar_data{'font'} = $calendar_data{ 'font_dialog' }->get_font_name();

   
if
 ( $calendar_data{ 'window' } )
   {
      $font = $calendar_data{ 'font_dialog' }->get_font();

      
if
 ( $font )
      {
	 $style=new Gtk::Style();
	 $style->font($font);
	 $calendar_data{ 'window' }->set_style( $style );
      }
   }
}


# Callback attached to the "Change Font" button.  It starts up a font
# dialog, allowing you to set a new font.


sub
 
calendar_select_font

{
   
my
 ( $button ) = @_;

   
my
 $window;

   
if
 ( $calendar_data{ 'font_dialog' } )
   {
      $window = $calendar_data{ 'font_dialog' };
   }
   
else

   {
      $window = new Gtk::FontSelectionDialog( "Font Selection Dialog" );
      $window->position( 'mouse' );
      $window->signal_connect( 'destroy', 
sub
 { $window->destroyed(); } );
      $window->ok_button->signal_connect( 'clicked',
					  \&calendar_font_selection_ok );
      $window->cancel_button->signal_connect( 'clicked',
					      
sub
 { $window->hide(); } );

      $calendar_data{ 'font_dialog' } = $window;
   }

   
if
 ( $window->visible )
   {
      $window->destroy();
   }
   
else

   {
      $window->show();
   }
}



# END EXAMPLE PROGRAM
      
   

Calendar Example Screenshot