Add SMTP SSL support using Net::SMTP::SSL
[email-reminder.git] / email-reminder-editor
blobf209e0496d9d93c1dbcd32baa37a4952e2cc00ad
1 #!/usr/bin/perl
3 =head1 NAME
5 Email-Reminder-Editor - edit special occasion reminders
7 =head1 SYNOPSIS
9 Simple editor for modifying special occasion email reminders.
11 =head1 DESCRIPTION
13 Email-reminder allows users to define events that they want to be
14 reminded of by email. Possible events include birthdays,
15 anniversaries and yearly events. Reminders can be sent on the day of
16 the event and a few days beforehand.
18 This is a simple editor that allows users to add/modify their
19 reminders. It saves changes automatically when the program is closed.
21 =head1 OPTIONS
23 =over 6
25 =item B<--help>
27 Displays basic usage message.
29 =item B<--simulate>
31 Does not actually save any changes.
33 =item B<--verbose>
35 Prints out information about what the program is doing.
37 =item B<--version>
39 Displays the version number.
41 =back
43 =head1 FILES
45 F<~/.email-reminders>
47 =head1 AUTHOR
49 Francois Marier <francois@debian.org>
51 =head1 SEE ALSO
53 collect-reminders, send-reminders
55 =head1 COPYRIGHT
57 Copyright (C) 2004-2008 by Francois Marier
59 Email-Reminder is free software; you can redistribute it and/or
60 modify it under the terms of the GNU General Public License as
61 published by the Free Software Foundation; either version 3 of the
62 License, or (at your option) any later version.
64 Email-Reminder is distributed in the hope that it will be useful,
65 but WITHOUT ANY WARRANTY; without even the implied warranty of
66 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
67 General Public License for more details.
69 You should have received a copy of the GNU General Public License
70 along with Email-Reminder; if not, write to the Free Software
71 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
72 02110-1301, USA.
74 =cut
76 use strict;
77 use warnings;
79 use Getopt::Long;
80 use Pod::Usage;
82 use Gtk2 '-init';
83 use Gtk2::SimpleList;
84 use Gtk2::SimpleMenu;
86 use constant TRUE => 1;
87 use constant FALSE => 0;
89 use EmailReminder::Event;
90 use EmailReminder::EventList;
92 # Command-line parameters
93 my $verbose = 0;
94 my $debug = 0;
95 my $simulate = 0;
96 my $version = 0;
97 my $help = 0;
98 GetOptions("verbose" => \$verbose,
99 "debug" => \$debug,
100 "simulate" => \$simulate,
101 "version" => \$version,
102 "help" => \$help,
105 # Constants
106 my $ANNIVERSARY_TAB = 'Anniversaries';
107 my $BIRTHDAY_TAB = 'Birthdays';
108 my $MONTHLY_TAB = 'Monthly Events';
109 my $WEEKLY_TAB = 'Weekly Events';
110 my $YEARLY_TAB = 'Yearly Events';
112 my $EMAIL_PREF_LABEL = 'Email:';
113 my $NAME_PREF_LABEL = 'Name:';
115 my $NEW_EVENT_NAME = '<new event>';
116 my $WINDOW_TITLE = 'Email-Reminder Editor';
118 my $DEFAULT_WIDTH = 600;
119 my $DEFAULT_HEIGHT = 400;
121 my $SEND_REMINDERS_EXECUTABLE = '/usr/bin/send-reminders';
123 # Global variables
124 my $window;
125 my $notebook;
126 my $events;
127 my %data;
129 sub load_data
131 $events = EmailReminder::EventList->new(glob("~/" . $EmailReminder::Utils::USER_CONFIG_FILE), TRUE);
132 print "Events loaded.\n" if $verbose;
135 sub save_data
137 $events->save($verbose) unless $simulate;
138 print "Events saved.\n" if $verbose;
141 sub text_cell_edited
143 my ($cell_renderer, $text_path, $new_text, $model) = @_;
144 my $path = Gtk2::TreePath->new_from_string($text_path);
145 $model->set_value($path, $cell_renderer->{column}, $new_text);
148 sub sort_by_column
150 return 0;
153 sub create_listbox
155 my $type = shift;
156 my @columns = ('ID', @_);
158 # TODO: make listbox sortable by clicking on column headers
159 my $model = $events->get_model($type);
160 #my $sort_model = Gtk2::TreeModelSort->new_with_model($model);
161 #$sort_model->set_default_sort_func(\&_sort_by_column);
162 #$sort_model->set_sort_column_id(1, 'ascending');
163 my $list = Gtk2::TreeView->new($model);
164 $list->{type} = $type;
166 my $col_num = 0;
167 foreach my $title (@columns) {
168 my $renderer = Gtk2::CellRendererText->new();
169 $renderer->set(editable => TRUE);
170 $renderer->{column} = $col_num;
171 $renderer->signal_connect (edited => \&text_cell_edited, $model);
173 my $col = Gtk2::TreeViewColumn->new_with_attributes($title, $renderer, 'text' => $col_num);
174 $col->set_resizable(TRUE);
175 #$col->set_clickable(FALSE);
176 #$col->set_sort_column_id($col_num);
177 $list->append_column($col);
179 $col_num++;
182 $list->get_column(0)->set_visible(FALSE) unless $debug; # hide the ID column
184 my $scrolled = Gtk2::ScrolledWindow->new;
185 $scrolled->set_policy('automatic', 'automatic');
186 $scrolled->add($list);
188 return $scrolled;
191 sub create_menu
193 my $menu_tree = [
194 _File => {
195 item_type => '<Branch>',
196 children => [
197 '_Test configuration' => {
198 callback => \&test_callback,
199 callback_action => 0,
201 Separator => {
202 item_type => '<Separator>',
204 _Quit => {
205 item_type => '<StockItem>',
206 callback => \&window_close,
207 extra_data => 'gtk-quit',
211 _Edit => {
212 item_type => '<Branch>',
213 children => [
214 '_New event' => {
215 item_type => '<StockItem>',
216 extra_data => 'gtk-new',
217 callback => \&new_callback,
219 '_Delete event' => {
220 item_type => '<StockItem>',
221 extra_data => 'gtk-delete',
222 accelerator => '<ctrl>D',
223 callback => \&delete_callback,
225 '_Edit reminders' => {
226 callback => \&edit_callback,
228 Separator => {
229 item_type => '<Separator>',
231 '_Preferences...' => {
232 item_type => '<StockItem>',
233 extra_data => 'gtk-preferences',
234 callback => \&prefs_callback,
240 return Gtk2::SimpleMenu->new(menu_tree => $menu_tree);
243 sub test_callback
245 unless (-x $SEND_REMINDERS_EXECUTABLE) {
246 my $error = Gtk2::MessageDialog->new($window, ['modal'], 'error', 'ok', "Cannot run '$SEND_REMINDERS_EXECUTABLE'. Check your installation.");
247 $error->run();
248 $error->destroy();
249 return;
252 save_data();
253 my $errorOutput = `$SEND_REMINDERS_EXECUTABLE`;
254 if ($errorOutput) {
255 my $error = Gtk2::MessageDialog->new($window, ['modal'], 'error', 'ok', $errorOutput);
256 $error->run();
257 $error->destroy();
261 sub new_callback
263 my (undef, $listbox) = get_selected_index();
265 my $event_type = $listbox->{type};
266 $events->add_event($event_type);
268 my $event_index = $listbox->get_model()->get_nb_events() - 1;
269 $listbox->get_selection()->select_path(Gtk2::TreePath->new_from_string($event_index));
272 sub delete_callback
274 my ($path, $listbox) = get_selected_index();
275 return undef unless defined($path);
277 $listbox->get_model()->delete_event($path);
280 sub edit_callback
282 my $selected = get_selected_event();
283 return unless defined($selected);
285 my $dialog = create_reminder_dialog($selected);
286 $dialog->show_all;
289 sub prefs_callback
291 my $dialog = create_prefs_dialog();
292 $dialog->run();
293 $dialog->destroy();
296 sub create_reminder_dialog
298 my ($event) = @_;
300 my $name = $event->get_name();
301 my $reminders = $event->get_reminders();
303 my $dialog = Gtk2::Dialog->new_with_buttons('Edit reminders', $window,
304 'destroy-with-parent',
305 'gtk-close' => 'close' );
307 my $reminder_label = Gtk2::Label->new("Current reminders for '$name':");
308 my $cb_sameday = Gtk2::CheckButton->new("Same day");
309 my $cb_advance = Gtk2::CheckButton->new("Days in advance:");
310 my $adj = Gtk2::Adjustment->new(1.0, 1.0, 364, 1.0, 10.0, 0.0);
311 my $spin_days = Gtk2::SpinButton->new($adj, 0, 0);
313 # Disable spin button unless the option is checked
314 $spin_days->set_sensitive(FALSE);
315 $cb_advance->signal_connect(toggled => sub {
316 $spin_days->set_sensitive($cb_advance->get_active());
319 my $hbox = Gtk2::HBox->new();
320 $hbox->add($cb_advance);
321 $hbox->add($spin_days);
323 my $checkboxes = Gtk2::VBox->new(FALSE, 6);
324 $checkboxes->set_border_width(6);
325 $checkboxes->add($reminder_label);
326 $checkboxes->add($cb_sameday);
327 $checkboxes->add($hbox);
329 foreach my $reminder (@$reminders)
331 if ($reminder == 0)
333 $cb_sameday->set_active(TRUE);
335 elsif ($reminder > 0)
337 $cb_advance->set_active(TRUE);
338 $spin_days->set_value($reminder);
342 $checkboxes->show_all;
343 $dialog->vbox->add($checkboxes);
345 $dialog->signal_connect(response => sub {
346 # Update values inside EventList
347 my @new_reminders = ();
348 push(@new_reminders, 0) if $cb_sameday->get_active();
349 push(@new_reminders, $spin_days->get_value())
350 if $cb_advance->get_active();
351 $event->set_reminders(\@new_reminders);
353 $_[0]->destroy;
354 });
356 return $dialog;
359 sub create_prefs_dialog
361 my $dialog = Gtk2::Dialog->new_with_buttons('Preferences', $window,
362 'modal',
363 'gtk-close' => 'close' );
365 my $info_label = Gtk2::Label->new("Set the default recipient for the reminder emails:");
366 my $name_label = Gtk2::Label->new($NAME_PREF_LABEL);
367 my $email_label = Gtk2::Label->new($EMAIL_PREF_LABEL);
369 my $fname = Gtk2::Entry->new();
370 my $lname = Gtk2::Entry->new();
371 my $email = Gtk2::Entry->new();
372 my @fullname = $events->get_user_name();
373 $fname->set_text($fullname[0]);
374 $lname->set_text($fullname[1]);
375 $email->set_text($events->get_user_email());
377 my $name = Gtk2::HBox->new();
378 $name->pack_start($fname, TRUE, TRUE, 0);
379 $name->pack_start($lname, TRUE, TRUE, 0);
381 my $options = Gtk2::Table->new(2, 2, FALSE);
382 $options->set_row_spacings(3);
383 $options->attach_defaults($name_label, 0, 1, 0, 1);
384 $options->attach_defaults($name, 1, 2, 0, 1);
385 $options->attach_defaults($email_label, 0, 1, 1, 2);
386 $options->attach_defaults($email, 1, 2, 1, 2);
388 $options->show_all();
389 $info_label->show();
390 $dialog->vbox->set_spacing(6);
391 $dialog->vbox->add($info_label);
392 $dialog->vbox->add($options);
394 $dialog->signal_connect(response => sub {
395 # Update values
396 $events->set_user_fname($fname->get_text());
397 $events->set_user_lname($lname->get_text());
398 unless ($events->set_user_email($email->get_text())) {
399 my $warning = Gtk2::MessageDialog->new($dialog, ['modal'], 'warning', 'ok', "The email address you entered is invalid; it has not been changed.");
400 $warning->run();
401 $warning->destroy();
405 return $dialog;
408 sub window_close
410 unless ($events->get_user_email())
412 my $warning = Gtk2::MessageDialog->new($window, ['modal'], 'warning', 'none', "You will not receive any reminders since you have not set your email address. \n\nWould you like to set your email address in the preferences now or quit?");
413 $warning->add_buttons('gtk-preferences' => 'no', 'gtk-quit' => 'yes');
415 my $response = $warning->run();
416 $warning->destroy();
418 if ('no' eq $response)
420 prefs_callback();
421 return TRUE;
425 $window->destroy();
428 sub init_ui
430 $window = Gtk2::Window->new;
431 $window->set_title($WINDOW_TITLE);
432 $window->set_default_size($DEFAULT_WIDTH, $DEFAULT_HEIGHT);
433 $window->set_resizable(TRUE);
435 $window->signal_connect(destroy => sub { Gtk2->main_quit; });
436 $window->signal_connect(delete_event => \&window_close);
438 my $vbox = Gtk2::VBox->new(FALSE, 0);
439 $window->add($vbox);
441 # Menu
442 my $menu = create_menu();
443 $window->add_accel_group($menu->{accel_group});
444 $vbox->pack_start($menu->{widget}, FALSE, FALSE, 0);
446 # Toolbar
447 my $toolbar = Gtk2::Toolbar->new() ;
448 $toolbar->set_style('both-horiz');
449 $toolbar->insert_stock('gtk-new', "Add a new event", undef, \&new_callback, undef, -1);
450 $toolbar->insert_stock('gtk-delete', "Delete the selected event", undef, \&delete_callback, undef, -1);
451 $toolbar->append_space();
452 $toolbar->insert_item("Edit reminders", "Edit reminders for the selected event", undef, undef, \&edit_callback, undef, -1);
453 $vbox->pack_start($toolbar, FALSE, FALSE, 0);
455 # Tabs
456 # TODO: make the accel Ctrl+PageDown/Up work everywhere
457 # (not just when focus is on the tabs)
458 $notebook = Gtk2::Notebook->new();
459 $notebook->set_tab_pos('top');
460 $vbox->pack_start($notebook, TRUE, TRUE, 0);
462 # Lists
463 my $listbox1 = create_listbox("birthday", 'Name', 'Birth date', 'Email');
464 my $listbox2 = create_listbox("anniversary", 'Person 1', 'Wedding date', 'Email 1', 'Person 2', 'Email 2');
465 my $listbox3 = create_listbox("yearly", 'Event name', 'Event date');
466 my $listbox4 = create_listbox("monthly", 'Event name', 'Event day');
467 my $listbox5 = create_listbox("weekly", 'Event name', 'Event day');
468 $notebook->append_page($listbox1, $BIRTHDAY_TAB);
469 $notebook->append_page($listbox2, $ANNIVERSARY_TAB);
470 $notebook->append_page($listbox3, $YEARLY_TAB);
471 $notebook->append_page($listbox4, $MONTHLY_TAB);
472 $notebook->append_page($listbox5, $WEEKLY_TAB);
475 sub get_selected_index
477 my $scrolled = $notebook->get_nth_page($notebook->get_current_page());
478 my $listbox = $scrolled->get_child();
480 my $path = $listbox->get_selection()->get_selected_rows();
481 return ($path, $listbox);
484 sub get_selected_event
486 my ($path, $listbox) = get_selected_index();
487 return undef unless defined($path);
489 my $event = $listbox->get_model()->get_event($path);
490 return $event;
493 sub run_gui
495 $window->show_all();
496 Gtk2->main;
499 sub main
501 print "Version: $EmailReminder::Utils::VERSION\n" if $verbose;
502 load_data();
503 init_ui();
504 run_gui();
505 save_data();
508 if ($help || $version) {
509 print "$WINDOW_TITLE $EmailReminder::Utils::VERSION\n";
510 if ($help) {
511 print "\n";
512 pod2usage(1);
513 } else {
514 exit(1);
516 } else {
517 main();