#!/usr/bin/perl -w use Gnome; use Gnome::Applet; require Gtk::Gdk::Pixbuf; my $applet = undef; my $timer = undef; my $icon = undef; my %images = (); my %icons = (); my %masks = (); my %pixbuf = (); my %pixes = (); my $alarm = 0; my $vis = 'blue'; exit(main()); #---------------------------------------------------------------------------- sub main { my $applet_name = "innaminute_applet"; Gnome::AppletWidget->init($applet_name); Gtk::Gdk::ImlibImage->init(); Gtk::Gdk::Pixbuf->init(); $applet = new Gnome::AppletWidget($applet_name); setupImages(); setupWidgets(); Gnome::AppletWidget->gtk_main(); return 0; } #---------------------------------------------------------------------------- # Loads all of the original images, for state icons and their masks. # We request a small initial render size, which will get overridden whenever # we learn the actual size required. # sub setupImages { my $path = "/home/speare/bin/"; foreach (qw(blue gray green yellow red)) { my $filename = "hexagon.$_.104.png"; my $image = Gtk::Gdk::ImlibImage->load_image($path . $filename); warn "Could not load image $filename" if not $image; $images{$_} = $image; } renderIcons(2); } # Whenever the widget changes size, this function re-renders the required # scaled icons and masks for each available state. # sub renderIcons { my $size = shift; foreach (keys %images) { $images{$_}->render($size, $size); # free existing icon and mask if they exist $icons{$_} = $images{$_}->copy_image(); $masks{$_} = $images{$_}->copy_mask(); } } # Whenever desired, the widget's current icon and mask can be chosen by # the predefined state keywords, such as 'red' or 'disabled'. # sub switchIcon { my ($icon, $state) = @_; return if not defined $icons{$state} or not defined $masks{$state}; $icon->set($icons{$state}, $masks{$state}); } #---------------------------------------------------------------------------- # Assembles the applet's widgets. Also sets up the responses for all of # the signals received by these widgets. # sub setupWidgets { my $idletip = "InnaMinute"; $applet->set_tooltip($idletip); # Set up the registered applet menu items. # The canned choices are simple future alarms, such as # "remind me in a minute", which is where the applet gets its name. # my @choices = ( [ "minute", "Remind me in a minute", sub { setAlarm(time() + 1*60); } ], [ "5minute", "Remind me in 5 minutes", sub { setAlarm(time() + 5*60); } ], [ "10minute", "Remind me in 10 minutes", sub { setAlarm(time() + 10*60); } ], [ "15minute", "Remind me in 15 minutes", sub { setAlarm(time() + 15*60); } ], [ "30minute", "Remind me in half an hour", sub { setAlarm(time() + 30*60); } ], [ "45minute", "Remind me in 45 minutes", sub { setAlarm(time() + 45*60); } ], [ "60minute", "Remind me in one hour", sub { setAlarm(time() + 1*60*60); } ], [ "reset", "Cancel pending reminder", sub { resetAlarm(); } ], ); foreach (@choices) { $applet->register_callback($_->[0], $_->[1], $_->[2]); } if (0) { $applet->register_callback("du de dum", "hop hop", sub { print "yeppa\n"; } ); $applet->register_callback_dir("test", "dir"); $applet->register_stock_callback("test/du de dam", "About", "hip hip", sub { print "yeppa 2\n"; } ); } # Create a mouse-sensitive widget which displays an icon. # The icon to be displayed may be adjusted based on applet status. $icon = new Gtk::Pixmap($icons{blue}, $masks{blue}); my $ev = [ 'button_press_mask' ]; my $oev = $applet->get_events(); push(@{$ev}, $oev) if scalar keys %{$oev}; $applet->set_events($ev); $applet->signal_connect('button_press_event', sub { onAppletPress($applet); return 1; } ); $icon->show(); $applet->add($icon); # Respond to generic panel size changes. We collect all these bits # of information in one map, and respond to them all in the same way. my $panel = { }; $applet->signal_connect("change_orient", sub { my $widget = shift; $panel->{orient} = shift; onPanelResize($icon, $panel); } ); $applet->signal_connect("back_change", sub { my $widget = shift; $panel->{backtype} = shift; $panel->{backpixmap} = shift; $panel->{backcolor} = shift; onPanelResize($icon, $panel); } ); $applet->signal_connect("change_pixel_size", sub { my $widget = shift; $panel->{panelsize} = shift; onPanelResize($icon, $panel); } ); # Finally. $applet->show(); } #---------------------------------------------------------------------------- # When the panel size is first discovered or subsequently resized, we # decide the appropriate layout and any graphic scaling necessary. # # In this case, it's a simple square that matches the panel's size. # sub onPanelResize { my ($icon, $panel) = (@_); my $size = $panel->{panelsize}; renderIcons($size) if defined $size; onTimer(); } # When the user clicks on the applet area, we decide the appropriate # action. This could be to pop up a menu or a toplevel window for further # choices, or to merely toggle through a simple state. # # In this case, it's a pop-up menu. # sub onAppletPress { print "onAppletPress\n"; my ($applet) = @_; } #---------------------------------------------------------------------------- # Updates the icon to the appropriate state depending on pending schedule. # Used as the Gtk timeout callback, but can be called in other situations. # sub onTimer { if (not defined $timer) { switchIcon($icon, 'blue'); return 1; } # If the time has elapsed, pop up the reminder. The alarm and timer # isn't actually fully reset until the reminder is cleared, so our # applet icon will still flash. # my $now = time(); if ($alarm > 0 && $alarm <= $now) { $alarm = 0; onAlarm(); } # Decide the color depending on how much time remains on the timer. # if ($vis eq 'green') { $vis = 'yellow'; } elsif ($vis eq 'yellow') { $vis = 'red'; } else { $vis = 'green'; } switchIcon($icon, $vis); return 1; } # Set up an alarm at a specified perl-time. Overrides any existing alarm. # Setting an alarm for time zero is the same as resetAlarm. # Setting an alarm in the past will immediately trigger. # sub setAlarm { my $when = shift; if ($when <= 0) { resetAlarm(); return; } $alarm = $when; print "Alarm set for ", $when - time(), " seconds in the future.\n"; $timer = Gtk->timeout_add(250, \&onTimer) if not defined $timer; onTimer(); } # Remove any pending alarm. # sub resetAlarm { $alarm = 0; switchIcon($icon, 'blue'); if (defined $timer) { Gtk->timeout_remove($timer); $timer = undef; } } # Display a simple modal box to remind the user that their alarm has rung. # TODO: ask window manager to make this reminder 'sticky' if possible. # sub onAlarm { my $window = new Gtk::Window("toplevel"); $window->set_title("InnaMinute"); $window->signal_connect("delete_event", sub { onKillReminder($window); } ); $window->border_width(10); $window->set_modal(1); my $message = "Reminder! Reminder!"; my $button = new Gtk::Button($message); $button->signal_connect("clicked", sub { onKillReminder($window); } ); $button->can_default(1); $button->show(); $window->add($button); $window->set_default($button); $window->show(); } # Terminate the reminder window, the animations timer, # and get everything back to normal. # sub onKillReminder { my $window = shift; $window->destroy(); resetAlarm(); } #---------------------------------------------------------------------------- # Gtk::Perl Tips and Tricks sub getWindowScreenPosition { my $window = shift; my ($x, $y, $w, $h) = @{$window->allocation}; ($x, $y) = $window->window->get_root_origin(); return ($x, $y, $w, $h); } sub setWindowScreenPosition { my ($window, $x, $y, $w, $h) = @_; $window->set_default_size($w, $h); $window->set_uposition($x, $y); }