#!/usr/bin/perl use strict; use Tk; use Tk::ROText; use Getopt::Long; use DBI; my (%title, %description, %priority, %taskid, %offset, $app, $menubar, $filemenu, $itemmenu, $helpmenu, $listbox, $textfield); my $dotfile = sprintf('%s/.muchtodo', $ENV{HOME}); my %opts = get_prefs($dotfile) if (-f $dotfile); list_options(\%opts); my $userid = get_or_insert_user(); GetOptions(\%opts, 'delay=i', 'geometry=s', 'verbose'); apply_defaults(\%opts); list_options(\%opts) if ($opts{verbose}); main(); select(undef,undef,undef,$opts{delay}) if defined($opts{delay}); MainLoop(); quit(); sub main { my ($grid_width, $tot_width, $scroll_width); if (defined $opts{scrollwidth} && $opts{scrollwidth} =~ /(\d+)\/(\d+)/) { $grid_width = $2; $tot_width = sprintf('%%%d', $2); $scroll_width = sprintf('%%%d', $1); } else { ($grid_width, $tot_width, $scroll_width) = (40, '%40', '%10'); } $app = MainWindow->new(); $app -> geometry($opts{geometry}); $app -> configure(-background => 'white'); $app -> title("Much To Do About Nothing"); $app -> formGrid($grid_width,30); ($menubar = $app->Frame()) -> form(-in => $app, -fill => 'both', -left => '%0', -right => $tot_width, -top => '%0'); $menubar -> configure(-background => 'white'); ($filemenu = $menubar->Menubutton(-text => 'File', -activebackground => 'yellow', -relief => 'raised', -tearoff => 0, -borderwidth => 2)) -> pack (-side => 'left', -padx => 2, -pady => 2); $filemenu->command(-label => 'Quit', -accelerator => 'Alt+Q', -underline => 0, -command => \&quit); ($itemmenu = $menubar->Menubutton(-text => 'Reminders', -activebackground => 'yellow', -relief => 'raised', -tearoff => 0, -borderwidth => 2)) -> pack (-side => 'left', -padx => 2); $itemmenu->command(-label => 'New', -accelerator => 'Alt+N', -underline => 0, -command => \&add_reminder); $itemmenu->command(-label => 'Edit', -accelerator => 'Alt+E', -underline => 0, -command => \&listbox_dclick_event); $itemmenu->command(-label => 'Delete', -accelerator => 'Alt+D', -underline => 0, -command => \&listbox_delete_event); $itemmenu->command(-label => 'Reload All', -accelerator => 'Alt+R', -underline => 0, -command => \&reload_tasks); ($helpmenu = $menubar->Menubutton(-text => 'Help', -activebackground => 'yellow', -relief => 'raised', -tearoff => 0, -borderwidth => 2)) -> pack (-side => 'right', -padx => 2); $helpmenu->command(-label => 'About', -accelerator => 'Alt+A', -underline => 0, -command => \&about); ($listbox = $app->Scrolled('Listbox', -background => 'white', -selectbackground => 'yellow', -scrollbars => 'ow', -font => $opts{font}, -width => 25, -height => 12)) -> form(-in => $app, -fill => 'both', -left => ['%0',5], -right => $scroll_width, -top => ['%0',32], -bottom => ['%30',-5]); $listbox->bind('<1>', \&listbox_click_event); $listbox->bind('', \&listbox_dclick_event); ($textfield = $app->Scrolled('ROText', -background => 'white', -selectbackground => 'white', -scrollbars => 'ow', -font => $opts{boldfont}, -width => 50, -height => 15, -padx => 5, -pady => 5, -wrap => 'word')) -> form(-in => $app, -fill => 'both', -left => $scroll_width, -right => [$tot_width,-5], -top => ['%0',32], -bottom => ['%30',-5]); $app->bind('' => \&about); $app->bind('' => \&listbox_delete_event); $app->bind('' => \&listbox_dclick_event); $app->bind('' => \&add_reminder); $app->bind('' => \&quit); $app->bind('' => \&quit); $app->bind('' => \&reload_tasks); reload_tasks(); $listbox->selectionSet(0,0); } sub delete_reminder { my ($taskid) = @_; my ($dialog, $title, $sure, $okbutton, $cancelbutton, $timer); $dialog = MainWindow->new(); $dialog -> title("Delete Reminder"); $dialog -> configure(-background => 'white'); $dialog -> formGrid(10,3); ($title = $dialog->Label(-text => sprintf('Delete %s', $title{$taskid}), -font => $opts{boldfont}, -foreground => '#666699', -background => 'white', -anchor => 'center', -relief => 'flat', -width => 30, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%10',-5], -top => ['%0',5], -bottom => ['%1',-5]); ($sure = $dialog->Label(-text => 'Are You Sure?', -font => $opts{boldfont}, -foreground => 'red', -background => 'white', -anchor => 'center', -relief => 'flat', -width => 30, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%10',-5], -top => ['%1',5], -bottom => ['%2',-5]); ($okbutton = $dialog->Button(-text => 'Kill It!', -command => sub { $textfield->delete('1.0', 'end'); $listbox->delete('active'); delete_task($userid, $taskid); reload_tasks(); $listbox->selectionSet(0,0); $timer = $okbutton->after(10, sub { $dialog->destroy(); } ); }, -activebackground => 'red', -anchor => 'center', -relief => 'raised', -width => 4, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%2',-25], -right => ['%5',-5], -top => ['%2',5], -bottom => ['%3',-5]); ($cancelbutton = $dialog->Button(-text => 'Never Mind', -command => sub { $timer = $okbutton->after(10, sub { $dialog->destroy(); } ); }, -activebackground => 'yellow', -anchor => 'center', -relief => 'raised', -width => 4, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%5',5], -right => ['%8',25], -top => ['%2',5], -bottom => ['%3',-5]); $dialog->bind('' => sub { $dialog->destroy(); }); } sub add_reminder { my ($dialog, $label, $label2, $title, $textbox, $radio1, $radio2, $radio3, $radio4, $radio5, $priority, $okbutton, $cancelbutton, $reminder, $text, $timer); $dialog = MainWindow->new(); $dialog -> title("New Reminder"); $dialog -> configure(-background => 'white'); $dialog -> geometry("500x500"); $dialog -> formGrid(10,10); ($label = $dialog->Label(-text => 'Title and Priority:', -font => $opts{boldfont}, -foreground => 'blue', -background => 'white', -anchor => 'w', -relief => 'flat', -width => 24, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%4',-5], -top => ['%0',5], -bottom => ['%0',35]); ($title = $dialog->Text(-width => 20, -height => 1, -background => 'white', -selectbackground => 'yellow', -font => $opts{boldfont}, -wrap => 'none')) -> form(-in => $dialog, -fill => 'both', -left => ['%4'], -right => ['%10',-5], -top => ['%0',5], -bottom => ['%0',35]); ($radio1 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'Highest', -value => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%2'], -top => ['%0',40], -bottom => ['%0',70]); ($radio2 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'High', -value => 2)) -> form(-in => $dialog, -fill => 'both', -left => ['%2'], -right => ['%4'], -top => ['%0',40], -bottom => ['%0',70]); ($radio3 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'Medium', -value => 3)) -> form(-in => $dialog, -fill => 'both', -left => ['%4'], -right => ['%6'], -top => ['%0',40], -bottom => ['%0',70]); ($radio4 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'Low', -value => 4)) -> form(-in => $dialog, -fill => 'both', -left => ['%6'], -right => ['%8'], -top => ['%0',40], -bottom => ['%0',70]); ($radio5 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'Lowest', -value => 5)) -> form(-in => $dialog, -fill => 'both', -left => ['%8'], -right => ['%10',-5], -top => ['%0',40], -bottom => ['%0',70]); $radio3->select; ($textbox = $dialog->Scrolled('Text', -width => 50, -padx => 5, -pady => 5, -height => 1, -background => 'white', -selectbackground => 'yellow', -scrollbars => 'ow', -font => $opts{boldfont}, -wrap => 'word')) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%10',-5], -top => ['%0',75], -bottom => ['%10',-35]); ($okbutton = $dialog->Button(-text => 'OK', -command => sub { chomp($reminder = $title->get('1.0', 'end')); chomp($text = $textbox->get('1.0', 'end')); insert_task($userid, $reminder, $priority, $text); reload_tasks(); load_reminder($taskid{$reminder}); select_reminder($reminder); $timer = $okbutton->after(10, sub { $dialog->destroy(); } ); }, -activebackground => 'green', -anchor => 'center', -relief => 'raised', -width => 4, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%3',5], -right => ['%5',-5], -top => ['%10',-30], -bottom => ['%10',-5]); ($cancelbutton = $dialog->Button(-text => 'Cancel', -command => sub { $timer = $okbutton->after(10, sub { $dialog->destroy(); } ); }, -activebackground => 'yellow', -anchor => 'center', -relief => 'raised', -width => 4, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%5',5], -right => ['%7',-5], -top => ['%10',-30], -bottom => ['%10',-5]); $dialog->bind('' => sub { $dialog->destroy(); }); } sub edit_reminder { my ($taskid) = @_; my ($dialog, $title, $titlebox, $textbox, $okbutton, $cancelbutton, $savebutton, $text, $priority, $label1, $label2, $radio1, $radio2, $radio3, $radio4, $radio5, $timer); $title = $title{$taskid}; $priority = $priority{$taskid}; $text = $description{$taskid}; $dialog = MainWindow->new(); $dialog -> title(sprintf("Edit '%s'", $title)); $dialog -> configure(-background => 'white'); $dialog -> geometry("500x500"); $dialog -> formGrid(10,10); ($label1 = $dialog->Label(-text => 'Title and Priority:', -font => $opts{boldfont}, -foreground => 'blue', -background => 'white', -anchor => 'w', -relief => 'flat', -width => 8, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%4',-5], -top => ['%0',5], -bottom => ['%0',35]); ($titlebox = $dialog->Text(-width => 20, -height => 1, -background => 'white', -selectbackground => 'yellow', -font => $opts{boldfont}, -wrap => 'none')) -> form(-in => $dialog, -fill => 'both', -left => ['%4'], -right => ['%10',-5], -top => ['%0',5], -bottom => ['%0',35]); $titlebox->Contents($title{$taskid}); ($radio1 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'Highest', -value => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%2'], -top => ['%0',40], -bottom => ['%0',70]); ($radio2 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'High', -value => 2)) -> form(-in => $dialog, -fill => 'both', -left => ['%2'], -right => ['%4'], -top => ['%0',40], -bottom => ['%0',70]); ($radio3 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'Medium', -value => 3)) -> form(-in => $dialog, -fill => 'both', -left => ['%4'], -right => ['%6'], -top => ['%0',40], -bottom => ['%0',70]); ($radio4 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'Low', -value => 4)) -> form(-in => $dialog, -fill => 'both', -left => ['%6'], -right => ['%8'], -top => ['%0',40], -bottom => ['%0',70]); ($radio5 = $dialog->Radiobutton(-variable => \$priority, -foreground => 'black', -background => 'white', -activebackground => 'yellow', -activeforeground => 'blue', -selectcolor => 'red', -relief => 'flat', -text => 'Lowest', -value => 5)) -> form(-in => $dialog, -fill => 'both', -left => ['%8'], -right => ['%10',-5], -top => ['%0',40], -bottom => ['%0',70]); ($textbox = $dialog->Scrolled('Text', -width => 50, -height => 12, -padx => 5, -pady => 5, -background => 'white', -selectbackground => 'yellow', -scrollbars => 'ow', -font => $opts{boldfont}, -wrap => 'word')) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%10',-5], -top => ['%0',75], -bottom => ['%10',-35]); $textbox->insert('1.0', $text); ($okbutton = $dialog->Button(-text => 'OK', -command => sub { chomp($title = $titlebox->Contents()); chomp($text = $textbox->Contents()); update_task($userid, $taskid, $title, $priority, $text); reload_tasks(); load_reminder($taskid{$title}); select_reminder($title); $timer = $okbutton->after(10, sub { $dialog->destroy(); } ); }, -activebackground => 'green', -anchor => 'center', -relief => 'raised', -width => 4, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%2',10], -right => ['%4',-10], -top => ['%10',-30], -bottom => ['%10',-5]); ($savebutton = $dialog->Button(-text => 'Update', -command => sub { chomp($title = $titlebox->Contents()); chomp($text = $textbox->Contents()); update_task($userid, $taskid, $title, $priority, $text); reload_tasks(); load_reminder($taskid{$title}); select_reminder($title); }, -activebackground => 'cyan', -anchor => 'center', -relief => 'raised', -width => 4, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%4',10], -right => ['%6',-10], -top => ['%10',-30], -bottom => ['%10',-5]); ($cancelbutton = $dialog->Button(-text => 'Cancel', -command => sub { $timer = $okbutton->after(10, sub { $dialog->destroy(); } ); }, -activebackground => 'yellow', -anchor => 'center', -relief => 'raised', -width => 4, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%6',10], -right => ['%8',-10], -top => ['%10',-30], -bottom => ['%10',-5]); $dialog->bind('' => sub { $dialog->destroy(); }); } sub about { my ($dialog, $title, $text, $okbutton, $credit, $timer); $dialog = MainWindow->new(); $dialog -> title("Nothing Much About..."); $dialog -> configure(-background => 'white'); $dialog -> geometry("600x750"); $dialog -> formGrid(10,10); ($title = $dialog->Label(-text => 'Nothing Much About "Much To Do"', -font => $opts{boldfont}, -foreground => 'blue', -background => 'white', -anchor => 'center', -relief => 'flat', -width => 50, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%10',-5], -top => ['%0',5], -bottom => ['%0',35]); ($text = $dialog->ROText(-width => 30, -height => 2, -padx => 5, -pady => 5, -relief => 'flat', -spacing3 => 10, -foreground => 'blue', -background => 'white', -selectforeground => 'blue', -selectbackground => 'white', -font => $opts{font}, -wrap => 'word')) -> form(-in => $dialog, -fill => 'both', -left => ['%0',20], -right => ['%10',-20], -top => ['%0',30], -bottom => ['%10',-70]); $text->insert('1.0', "This is version 2.2 of Much To Do About Nothing, a simple to-do list application written using Perl/Tk and Perl DBI. It is free software; you may do whatever you want with it, so long as you credit the author. Don't expect too much from it; it was written to learn PerlTk, and it's very basic. This release uses a MySQL database to store reminders, but it can be easily modified for any database with a DBD module available. The database must be created by hand at present. Every attempt is made to make database transactions atomic and not hold the database open. There is no set limit upon the number or length of reminders. Reminders are displayed in order of priority from highest to lowest, then alphabetically by title. Click on a reminder's title in the left pane to view it on the right pane. You can add, delete or edit reminders, or re-sort the list, using the Reminders menu. (You must have a reminder title selected in the listbox to edit that reminder.) You can also edit a reminder by double-clicking its title in the left pane. You can re-prioritize a reminder when you edit it. The Update button in the Edit window saves your changes, and updates the left-pane list, without closing the Edit window. The font and geometry can be set in a ~/.muchtoto file using the following simple syntax: geometry: 800x800 font: -*-CG Omega-medium-r-normal--*-140-*-*-p-*-iso8859 boldfont: -*-CG Omega-bold-r-normal--*-140-*-*-p-*-iso8859 If not specified, the geometry defaults to 800x600, and the normal and bold fonts default to CG Omega. If normal font is specified but bold isn't, it will be assumed you want the bold version of your specified normal font as your bold font. If you don't have CG Omega, you'll probably want to change it to, for example, Helvetica, but I'm touting CG Omega because it's a marvellously clear font for both titles and body text (though its true beauty doesn't become apparent until you print it), yet almost unknown and grossly underappreciated. BUGS: Widget geometries are precalculated for the default font, and may require minor tweaking if you use a significantly different sized font."); ($okbutton = $dialog->Button(-text => 'OK', -command => sub { $timer = $okbutton->after(10, sub { $dialog->destroy(); } ); }, -activebackground => 'yellow', -anchor => 'center', -relief => 'raised', -width => 4, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%4',5], -right => ['%6',-5], -top => ['%10',-60], -bottom => ['%10',-30]); ($credit = $dialog->Label(-text => 'Much To Do About Nothing v2.2, August 5 2007, Phil V. Stracchino ', -font => $opts{font}, -foreground => 'blue', -background => 'white', -anchor => 'center', -relief => 'flat', -width => 50, -height => 1)) -> form(-in => $dialog, -fill => 'both', -left => ['%0',5], -right => ['%10',-5], -top => ['%10',-25], -bottom => ['%10',-5]); $dialog->bind('' => sub { $dialog->destroy(); }); } sub insert_task { my ($userid, $title, $priority, $reminder) = @_; my ($dbh, $sth, $query); $title =~ s/(['"])/\\$1/g; $reminder =~ s/(['"])/\\$1/g; $query = sprintf('INSERT INTO task (userid, title, priority, reminder) VALUES (%d,\'%s\',%d, \'%s\')', $userid, $title, $priority, $reminder); $dbh = open_db(); $dbh->do($query) || die "Error:" . $dbh->errstr . "\n"; $dbh->commit || die "Error:" . $dbh->errstr . "\n"; $dbh->disconnect(); } sub update_task { my ($userid, $taskid, $title, $priority, $reminder) = @_; my ($dbh, $sth, $query); $title =~ s/(['"])/\\$1/g; $reminder =~ s/(['"])/\\$1/g; $query = sprintf('UPDATE task SET title=\'%s\', priority=%d, reminder=\'%s\' WHERE userid=%d AND taskid=%d', $title, $priority, $reminder, $userid, $taskid); $dbh = open_db(); $dbh->do($query) || die "Error:" . $dbh->errstr . "\n"; $dbh->commit || die "Error:" . $dbh->errstr . "\n"; $dbh->disconnect(); } sub delete_task { my ($userid, $taskid) = @_; my ($dbh, $sth, $query, $title); $title = $title{$taskid}; $dbh = open_db(); $sth = $dbh->prepare("DELETE FROM task WHERE userid=$userid AND taskid=$taskid") || die "Error:" . $dbh->errstr . "\n"; $sth->execute || die "Error:" . $dbh->errstr . "\n"; $sth->finish(); $dbh->commit || die "Error:" . $dbh->errstr . "\n"; $dbh->disconnect(); delete($taskid{$title}); delete($title{$taskid}); delete($priority{$taskid}); delete($description{$taskid}); } sub load_tasks { my ($userid) = @_; my ($sth, $dbh); my $row = 0; $dbh = open_db(); $sth = $dbh->prepare("SELECT taskid,title,priority,reminder FROM task WHERE userid=$userid order by priority,title,taskid") || die "Error:" . $dbh->errstr . "\n"; $sth->execute || die "Error:" . $dbh->errstr . "\n"; while (my $ref = $sth->fetchrow_arrayref) { $title{$$ref[0]} = $$ref[1]; $taskid{$$ref[1]} = $$ref[0]; $priority{$$ref[0]} = $$ref[2]; $description{$$ref[0]} = $$ref[3]; $offset{$$ref[1]} = $row++; } $sth->finish(); $dbh->disconnect(); return (scalar keys %title); } sub get_or_insert_user { my $user = $ENV{'USER'}; my ($dbh, $sth, $id, $ref); $dbh = open_db(); $sth = $dbh->prepare("SELECT userid FROM user where user='$user'") || die "Error:" . $dbh->errstr . "\n"; $sth->execute || die "Error:" . $dbh->errstr . "\n"; until ($ref = $sth->fetchrow_arrayref) { $sth = $dbh->prepare("INSERT INTO user (user) VALUES ('$user')") || die "Error:" . $dbh->errstr . "\n"; $sth->execute || die "Error:" . $dbh->errstr . "\n"; $sth = $dbh->prepare("SELECT userid FROM user where user='$user'") || die "Error:" . $dbh->errstr . "\n"; $sth->execute || die "Error:" . $dbh->errstr . "\n"; } $id = $$ref[0]; $sth->finish(); $dbh->commit || die "Error:" . $dbh->errstr . "\n"; $dbh->disconnect(); return ($id); } sub open_db { my $home = $ENV{'HOME'}; my ($dsn, $dbh, $user, $password); $dsn = "DBI:mysql:database=todo;" . "%s" . "mysql_read_default_group=todo;" . "mysql_read_default_file=$home/.my.cnf;" . "mysql_mysql_client_found_rows=TRUE;" . "mysql_mysql_ssl=TRUE"; $dsn = sprintf($dsn, $opts{sqlhost} ? sprintf('host=%s;port=%s;', $opts{sqlhost}, $opts{sqlport} ? $opts{sqlport} : '3306') : 'host=localhost;'); $dbh = DBI->connect($dsn, undef, undef, {RaiseError => 1, AutoCommit => 0}); return ($dbh); } sub reload_tasks { %title = (); %taskid = (); %description = (); %priority = (); load_tasks($userid); if (scalar keys %title) { while($listbox->index('end')) { $listbox->delete('end'); } foreach my $id (sort myorder keys %title) { my $title = sprintf ("[%d] %s", $priority{$id}, $title{$id}); $listbox->insert('end', $title); } load_reminder((sort myorder keys %title)[0]); } } sub myorder { $priority{$a} <=> $priority{$b} or $title{$a} cmp $title{$b} } sub listbox_click_event { my $text = $listbox->getSelected; if ($text) { $text =~ s/^\[\d+\]\s+//; load_reminder($taskid{$text}); } } sub listbox_dclick_event { my $text = $listbox->getSelected; if ($text) { $text =~ s/^\[\d+\]\s+//; load_reminder($taskid{$text}); edit_reminder($taskid{$text}); } } sub load_reminder { my ($taskid) = @_; $textfield->delete('1.0', 'end'); $textfield->insert('end', $description{$taskid}); } sub select_reminder { my $title = $_[0]; my $id = $offset{$title}; $listbox->selectionSet($id,$id); } sub listbox_delete_event { my $text = $listbox->getSelected; if ($text) { $text =~ s/^\[\d+\]\s+//; delete_reminder($taskid{$text}); } } sub quit { exit(0); } sub get_prefs { my %o; open(PREFS, $_[0]); while () { next if (/^#/); if (/^(\S.*\S)\s*:\s+(\S.*\S)\s*$/) { $o{$1} = $2; } } return(%o); } sub apply_defaults { my $o = $_[0]; $o->{geometry} = '800x600' unless ($o->{geometry}); $o->{font} = '-*-CG Omega-medium-r-normal--*-140-*-*-p-*-iso8859' unless ($o->{font}); (($o->{boldfont} = $o->{font}) =~ s/medium/bold/) unless ($o->{boldfont}); } sub list_options { my $o = $_[0]; print "Options:\n"; foreach my $key (sort keys %$o) { printf("\t%-12s : %s\n", $key, $o->{$key}); } }