From 478b2762e61cee2aa2b073e37cfcdaf220692acf Mon Sep 17 00:00:00 2001
From: Frederic Culot <calcurse@culot.org>
Date: Sun, 30 Dec 2007 16:27:58 +0000
Subject: Ability to attach notes to todo items added

---
 src/apoint.c   |  54 ++++++++++---------------
 src/apoint.h   |   7 ++--
 src/calcurse.c |  20 +++++++++-
 src/day.c      |  74 +++++++++++++++++++++++++++++++----
 src/event.h    |   3 +-
 src/io.c       | 121 ++++++++++++++++++++++++++++++++++++---------------------
 src/notify.c   |   8 ++--
 src/todo.c     |  81 +++++++++++++++++++++++++++++++++-----
 src/todo.h     |  13 ++++---
 src/utils.c    | 112 ++++++++++++++++++++++++++++++++++------------------
 src/utils.h    |  12 ++++--
 src/vars.c     |  21 +++++++++-
 src/vars.h     |   7 +++-
 src/wins.c     |  33 +++++++++++++++-
 src/wins.h     |   3 +-
 15 files changed, 413 insertions(+), 156 deletions(-)

(limited to 'src')

diff --git a/src/apoint.c b/src/apoint.c
index 7a87e6e..d963cd8 100755
--- a/src/apoint.c
+++ b/src/apoint.c
@@ -1,4 +1,4 @@
-/*	$calcurse: apoint.c,v 1.18 2007/10/21 13:42:34 culot Exp $	*/
+/*	$calcurse: apoint.c,v 1.19 2007/12/30 16:27:58 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -250,7 +250,7 @@ apoint_delete(conf_t *conf, unsigned *nb_events, unsigned *nb_apoints)
 			} else if (deleted_item_type == 0) {
 				to_be_removed = 0;		
 			} else
-				ierror(errmsg);
+				ierror(errmsg, IERROR_FATAL);
 				/* NOTREACHED */
 
 			if (hilt > 1) 
@@ -265,8 +265,6 @@ apoint_delete(conf_t *conf, unsigned *nb_events, unsigned *nb_apoints)
 	}
 }
 
-
-
 unsigned 
 apoint_inday(apoint_llist_node_t *i, long start)
 {
@@ -389,35 +387,8 @@ void apoint_delete_bynum(long start, unsigned num)
 	pthread_mutex_unlock(&(alist_p->mutex));
 
 	/* NOTREACHED */
-	ierror(_("FATAL ERROR in apoint_delete_bynum: no such appointment"));
-}
-
-/* 
- * Print an item date in the appointment panel.
- */
-void display_item_date(WINDOW *win, int incolor, apoint_llist_node_t *i,
-			int type, long date, int y, int x)
-{
-	char a_st[100], a_end[100];
-	int recur = 0;
-
-	apoint_sec2str(i, type, date, a_st, a_end);
-	if (type == RECUR_EVNT || type == RECUR_APPT)
-		recur = 1;
-	if (incolor == 0) 
-		custom_apply_attr(win, ATTR_HIGHEST);
-	if (recur)
-		if (i->state & APOINT_NOTIFY)
-			mvwprintw(win, y, x, " *!%s -> %s", a_st, a_end);
-		else
-			mvwprintw(win, y, x, " * %s -> %s", a_st, a_end);
-	else
-		if (i->state & APOINT_NOTIFY)
-			mvwprintw(win, y, x, " -!%s -> %s", a_st, a_end);
-		else
-			mvwprintw(win, y, x, " - %s -> %s", a_st, a_end);
-	if (incolor == 0) 
-		custom_remove_attr(win, ATTR_HIGHEST);
+	ierror(_("FATAL ERROR in apoint_delete_bynum: no such appointment"),
+	    IERROR_FATAL);
 }
 
 /*
@@ -566,7 +537,8 @@ apoint_switch_notify(void)
 
 	/* NOTREACHED */
 	ierror(
-	    _("FATAL ERROR in apoint_switch_notify: no such appointment"));
+	    _("FATAL ERROR in apoint_switch_notify: no such appointment"),
+	    IERROR_FATAL);
 }
 
 /* Updates the Appointment panel */
@@ -617,3 +589,17 @@ apoint_update_panel(window_t *winapp, int which_pan)
 	    winapp->y + title_lines + 1, winapp->x + bordr, 
     	    winapp->y + winapp->h - 2*bordr, winapp->x + winapp->w - 3*bordr);
 }
+
+/* Attach a note to an appointment or event */
+void 
+apoint_edit_note(void)
+{
+
+}
+
+/* View a note previously attached to an appointment or event */
+void 
+apoint_view_note(void)
+{
+
+}
diff --git a/src/apoint.h b/src/apoint.h
index c77ca70..182f03d 100755
--- a/src/apoint.h
+++ b/src/apoint.h
@@ -1,4 +1,4 @@
-/*	$calcurse: apoint.h,v 1.9 2007/08/15 15:33:54 culot Exp $	*/
+/*	$calcurse: apoint.h,v 1.10 2007/12/30 16:27:58 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -44,6 +44,7 @@ typedef struct apoint_llist_node {
 	long dur;		/* duration of the appointment in seconds */
 	char state;		/* 8 bits to store item state */
 	char *mesg;
+	char *note;
 } apoint_llist_node_t;
 
 typedef struct apoint_llist {
@@ -67,13 +68,13 @@ void 			apoint_sec2str(apoint_llist_node_t *, int, long,
 void 			apoint_write(apoint_llist_node_t *, FILE *);
 apoint_llist_node_t    *apoint_scan(FILE *, struct tm, struct tm, char);
 void 			apoint_delete_bynum(long, unsigned);
-void 			display_item_date(WINDOW *, int, apoint_llist_node_t *,
-			    int, long, int, int);
 void 			apoint_scroll_pad_down(int, int);
 void 			apoint_scroll_pad_up(int);
 struct notify_app_s    *apoint_check_next(struct notify_app_s *, long);
 apoint_llist_node_t    *apoint_recur_s2apoint_s(recur_apoint_llist_node_t *);
 void 			apoint_switch_notify(void);
 void			apoint_update_panel(window_t *, int);
+void			apoint_edit_note(void);
+void			apoint_view_note(void);
 
 #endif /* CALCURSE_APOINT_H */
diff --git a/src/calcurse.c b/src/calcurse.c
index b62f9b9..ab25298 100755
--- a/src/calcurse.c
+++ b/src/calcurse.c
@@ -1,4 +1,4 @@
-/*	$calcurse: calcurse.c,v 1.58 2007/12/10 18:56:08 culot Exp $	*/
+/*	$calcurse: calcurse.c,v 1.59 2007/12/30 16:27:58 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -346,6 +346,24 @@ main(int argc, char **argv)
 			}
 			break;
 
+		case 'N':
+		case 'n':	
+			/* Attach a note to an item, create it if necessary */
+			if (wins_slctd() == APP && apoint_hilt() != 0)
+				apoint_edit_note();
+			else if (wins_slctd() == TOD && todo_hilt() != 0)
+				todo_edit_note(conf.editor);
+			do_storage = true;
+			break;
+
+		case '>':	
+			/* View a note previously attached to an item */
+			if (wins_slctd() == APP && apoint_hilt() != 0)
+				apoint_view_note();
+			else if (wins_slctd() == TOD && todo_hilt() != 0)
+				todo_view_note(conf.pager);
+			break;
+
 		case '?':	/* Online help system */
 			status_bar();
 			help_screen();
diff --git a/src/day.c b/src/day.c
index 95635f9..0d27bee 100755
--- a/src/day.c
+++ b/src/day.c
@@ -1,4 +1,4 @@
-/*	$calcurse: day.c,v 1.30 2007/10/21 13:42:34 culot Exp $	*/
+/*	$calcurse: day.c,v 1.31 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -34,6 +34,7 @@
 #include "utils.h"
 #include "apoint.h"
 #include "event.h"
+#include "custom.h"
 #include "day.h"
 
 static struct day_item_s 	*day_items_ptr;
@@ -301,6 +302,63 @@ day_item_s2apoint_s(apoint_llist_node_t *a, struct day_item_s *p)
 	a->mesg = p->mesg;
 }
 
+/* 
+ * Print an item date in the appointment panel.
+ */
+static void 
+display_item_date(int incolor, apoint_llist_node_t *i, int type, long date, 
+    int y, int x)
+{
+	WINDOW *win;
+	char a_st[100], a_end[100];
+	int recur = 0;
+
+	win = apad->ptrwin;
+	apoint_sec2str(i, type, date, a_st, a_end);
+	if (type == RECUR_EVNT || type == RECUR_APPT)
+		recur = 1;
+	if (incolor == 0) 
+		custom_apply_attr(win, ATTR_HIGHEST);
+	if (recur)
+		if (i->state & APOINT_NOTIFY)
+			mvwprintw(win, y, x, " *!%s -> %s", a_st, a_end);
+		else
+			mvwprintw(win, y, x, " * %s -> %s", a_st, a_end);
+	else
+		if (i->state & APOINT_NOTIFY)
+			mvwprintw(win, y, x, " -!%s -> %s", a_st, a_end);
+		else
+			mvwprintw(win, y, x, " - %s -> %s", a_st, a_end);
+	if (incolor == 0) 
+		custom_remove_attr(win, ATTR_HIGHEST);
+}
+
+/* 
+ * Print an item description in the corresponding panel window.
+ */
+static void 
+display_item(int incolor, char *msg, int recur, int note, int len, int y, int x)
+{
+	WINDOW *win;
+	int ch_recur, ch_note;
+	char buf[len];
+
+	win = apad->ptrwin;
+	ch_recur = (recur) ? '*' : ' ';
+	ch_note = (note) ? '>' : ' ';
+	if (incolor == 0) 
+		custom_apply_attr(win, ATTR_HIGHEST);
+	if (strlen(msg) < len)
+		mvwprintw(win, y, x, " %c%c%s", ch_recur, ch_note, msg);
+	else {
+		strncpy(buf, msg, len - 1);
+		buf[len - 1] = '\0';
+		mvwprintw(win, y, x, " %c%c%s...", ch_recur, ch_note, buf);
+	}
+	if (incolor == 0) 
+		custom_remove_attr(win, ATTR_HIGHEST);
+}
+
 /* 
  * Write the appointments and events for the selected day in a pad.
  * An horizontal line is drawn between events and appointments, and the
@@ -339,8 +397,8 @@ day_write_pad(long date, int width, int length, int incolor)
 				day_saved_item->type = p->type;
 				day_saved_item->mesg = p->mesg;
 			}
-			display_item(apad->ptrwin, item_number - incolor, p->mesg, 
-				recur, width - 5, line, x_pos);
+			display_item(item_number - incolor, p->mesg, recur, 0, 
+			    width - 7, line, x_pos);
 			line++;
 			draw_line = true;
 		} else {
@@ -360,10 +418,10 @@ day_write_pad(long date, int width, int length, int incolor)
 				apoint_sec2str(&a, p->type, date,
 				    day_saved_item->start, day_saved_item->end);
 			}
-			display_item_date(apad->ptrwin, item_number - incolor, 
-				&a, p->type, date, line + 1, x_pos);	
-			display_item(apad->ptrwin, item_number - incolor, p->mesg,
-				0, width - 7, line + 2, x_pos + 2);
+			display_item_date(item_number - incolor, &a, p->type, 
+			    date, line + 1, x_pos);	
+			display_item(item_number - incolor, p->mesg, 0, 0, 
+			    width - 7, line + 2, x_pos);
 			line = line + 3;
 		}
 	}
@@ -382,7 +440,7 @@ void day_popup_item(void)
 		item_in_popup(day_saved_item->start, day_saved_item->end,
 			day_saved_item->mesg, _("Appointment :"));
 	else
-		ierror(error);
+		ierror(error, IERROR_FATAL);
 		/* NOTREACHED */
 }
 
diff --git a/src/event.h b/src/event.h
index 3b2e794..48e2ac5 100755
--- a/src/event.h
+++ b/src/event.h
@@ -1,4 +1,4 @@
-/*	$calcurse: event.h,v 1.2 2007/07/28 13:11:42 culot Exp $	*/
+/*	$calcurse: event.h,v 1.3 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -35,6 +35,7 @@ struct event_s {
 	int id;	        /* event identifier */
         long day;	/* seconds since 1 jan 1970 */
 	char *mesg;
+	char *note;
 };
 
 extern struct event_s *eventlist;
diff --git a/src/io.c b/src/io.c
index 704b007..00625a3 100755
--- a/src/io.c
+++ b/src/io.c
@@ -1,4 +1,4 @@
-/*	$calcurse: io.c,v 1.23 2007/10/21 13:42:34 culot Exp $	*/
+/*	$calcurse: io.c,v 1.24 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -30,6 +30,7 @@
 #include <time.h>
 #include <math.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include "i18n.h"
 #include "utils.h"
@@ -327,6 +328,7 @@ io_init(char *cfile)
 	snprintf(path_dir, BUFSIZ, "%s/" DIR_NAME, home);
 	snprintf(path_todo, BUFSIZ, "%s/" TODO_PATH, home);
 	snprintf(path_conf, BUFSIZ, "%s/" CONF_PATH, home);
+	snprintf(path_notes, BUFSIZ, "%s/" NOTES_DIR, home);
 	if (cfile == NULL) {
 		snprintf(path_apts, BUFSIZ, "%s/" APTS_PATH, home);
 	} else {
@@ -485,13 +487,19 @@ io_save_cal(conf_t *conf)
 	}
 
 	/* Save the todo data file. */
-	if (show_bar) progress_bar(PROGRESS_BAR_SAVE, 1);
+	if (show_bar) 
+		progress_bar(PROGRESS_BAR_SAVE, 1);
 	data_file = fopen(path_todo, "w");
 	if (data_file == (FILE *) 0)
 	        status_mesg(access_pb, ""); 
 	else {
-		for (i = todolist; i != 0; i = i->next)
-			fprintf(data_file, "[%d] %s\n", i->id, i->mesg);
+		for (i = todolist; i != 0; i = i->next) {
+			if (i->note != NULL)
+				fprintf(data_file, "[%d]>%s %s\n", i->id, 
+				    i->note, i->mesg);
+			else
+				fprintf(data_file, "[%d] %s\n", i->id, i->mesg);
+		}
 		fclose(data_file);
 	}
 
@@ -680,7 +688,7 @@ io_load_todo(void)
 	char *nl;
 	int nb_tod = 0;
 	int c, id;
-	char buf[BUFSIZ], e_todo[BUFSIZ];
+	char buf[BUFSIZ], e_todo[BUFSIZ], note[NOTESIZ + 1];
 
 	data_file = fopen(path_todo, "r");
 	if (data_file == NULL) {
@@ -692,68 +700,91 @@ io_load_todo(void)
 		if (c == EOF) {
 			break;
 		} else if (c == '[') { /* new style with id */
-			fscanf(data_file, "%d] ", &id);
+			fscanf(data_file, "%d]", &id);
 		} else {
 			id = 9;
 			ungetc(c, data_file);
 		}
+		/* Now read the attached note, if any. */
+		c = getc(data_file);
+		if (c == '>') {
+			fgets(note, NOTESIZ + 1, data_file);
+			note[NOTESIZ] = '\0';
+			fprintf(stderr, "note: [%s]\n", note);
+			getc(data_file);
+		} else
+			note[0] = '\0';
+		/* Then read todo description. */
 		fgets(buf, BUFSIZ, data_file);
 		nl = strchr(buf, '\n');
 		if (nl) {
 			*nl = '\0';
 		}
 		io_extract_data(e_todo, buf, strlen(buf));
-		todo_add(e_todo, id);
+		todo_add(e_todo, id, note);
 		++nb_tod;
 	}
 	fclose(data_file);
 	todo_set_nb(nb_tod);
 }
 
-/* Checks if data files exist. If not, create them */
-int 
-io_check_data_files(void)
+static void
+check_directory(char *dir, int *missing)
 {
-	FILE *data_file;
-	int no_data_file;
-
-	no_data_file = 0;
-	/* Create the calcurse repertory if not present. */
-	mkdir(path_dir, 0700);
-
-	data_file = fopen(path_todo, "r");
-	if (data_file == NULL) {
-		no_data_file++;
-		data_file = fopen(path_todo, "w");
-		if (data_file == NULL) {
-			perror(path_todo);
-			return no_data_file;
+	errno = 0;
+	if (mkdir(dir, 0700) != 0) {
+		if (errno != EEXIST) {
+			fprintf(stderr, 
+			    _("FATAL ERROR: could not create %s: %s\n"),
+			    dir, strerror(errno));
+			exit(EXIT_FAILURE);
 		}
-	}
-	fclose(data_file);
+	} else
+		(*missing)++;
+}
 
-	data_file = fopen(path_apts, "r");
-	if (data_file == NULL) {
-		no_data_file++;
-		data_file = fopen(path_apts, "w");
-		if (data_file == NULL) {
-			perror(path_apts);
-			return no_data_file;
+static void
+check_file(char *file, int *missing)
+{
+	FILE *fd;
+
+	errno = 0;
+	if ((fd = fopen(file, "r")) == NULL) {
+		(*missing)++;
+		if ((fd = fopen(file, "w")) == NULL) {
+			fprintf(stderr, 
+			    _("FATAL ERROR: could not create %s: %s\n"),
+			    file, strerror(errno));
+			exit(EXIT_FAILURE);
 		}
 	}
-	fclose(data_file);
+	fclose(fd);
+}
 
-	data_file = fopen(path_conf, "r");
-	if (data_file == NULL) {
-		no_data_file++;
-		data_file = fopen(path_conf, "w");
-		if (data_file == NULL) {
-			perror(path_conf);
-			return no_data_file;
-		}
-	}
-	fclose(data_file);
-        return no_data_file;
+/* 
+ * Checks if data files exist. If not, create them. 
+ * The following structure has to be created:
+ *
+ *	$HOME/.calcurse/
+ *                 |
+ *                 +--- notes/
+ *                 |___ conf
+ *                 |___ apts
+ *                 |___ todo
+ */
+int 
+io_check_data_files(void)
+{
+	int missing;
+
+	missing = 0;
+	errno = 0;
+	check_directory(path_dir, &missing);
+	check_directory(path_notes, &missing);
+	check_file(path_todo, &missing);
+	check_file(path_apts, &missing);
+	check_file(path_conf, &missing);
+        return (missing);
 }
 
 /* Draw the startup screen */
diff --git a/src/notify.c b/src/notify.c
index b4b60b7..3531a02 100755
--- a/src/notify.c
+++ b/src/notify.c
@@ -1,4 +1,4 @@
-/*	$calcurse: notify.c,v 1.22 2007/10/21 13:41:51 culot Exp $	*/
+/*	$calcurse: notify.c,v 1.23 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -132,11 +132,13 @@ launch_cmd(char *cmd, char *shell)
 	pid = fork();
 	
 	if (pid < 0)
-		ierror(_("FATAL ERROR in launch_cmd: could not fork"));
+		ierror(_("FATAL ERROR in launch_cmd: could not fork"),
+		    IERROR_WARN);
 	else if (pid == 0) /* Child: launch user defined command */
 		if (execlp(shell, shell, "-c", cmd, (char *)NULL) < 0)
 			ierror(_("FATAL ERROR in launch_cmd: could not "
-			    "launch user command"));
+			    "launch user command"),
+			    IERROR_WARN);
 }
 
 /* 
diff --git a/src/todo.c b/src/todo.c
index d56819a..5f1160d 100755
--- a/src/todo.c
+++ b/src/todo.c
@@ -1,4 +1,4 @@
-/*	$calcurse: todo.c,v 1.15 2007/10/21 13:42:34 culot Exp $	*/
+/*	$calcurse: todo.c,v 1.16 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -28,6 +28,7 @@
 #include <string.h>
 
 #include "utils.h"
+#include "custom.h"
 #include "i18n.h"
 #include "todo.h"
 
@@ -130,20 +131,21 @@ todo_new_item(void)
 			status_mesg(mesg_id, "");
 			ch = wgetch(win[STA].p);
 		}
-		todo_add(todo_input, ch - '0');
+		todo_add(todo_input, ch - '0', NULL);
 		todos++;
 	}
 }
 
 /* Add an item in the todo linked list. */
 struct todo_s *
-todo_add(char *mesg, int id)
+todo_add(char *mesg, int id, char *note)
 {
 	struct todo_s *o, **i;
 	o = (struct todo_s *) malloc(sizeof(struct todo_s));
 	o->mesg = (char *) malloc(strlen(mesg) + 1);
 	strncpy(o->mesg, mesg, strlen(mesg) + 1);
 	o->id = id;
+	o->note = (note != NULL && note[0] != '\0') ? strdup(note) : NULL;
 	i = &todolist;
 	for (;;) {
 		if (*i == 0 || (*i)->id > id) {
@@ -169,6 +171,8 @@ todo_delete_bynum(unsigned num)
 		if (n == num) {
 			*iptr = i->next;
 			free(i->mesg);
+			if (i->note != NULL)
+				free(i->note);
 			free(i);
 			return;
 		}
@@ -261,11 +265,13 @@ todo_chg_priority(int action)
 	struct todo_s *backup;
 	char backup_mesg[BUFSIZ];
 	int backup_id;
+	char backup_note[NOTESIZ + 1];
 	int do_chg = 1;
 
 	backup = todo_get_item(hilt);
 	strncpy(backup_mesg, backup->mesg, strlen(backup->mesg) + 1);
 	backup_id = backup->id;
+	strncpy(backup_note, backup->note, NOTESIZ + 1);
 	if (action == '+') {
 		(backup_id > 1) ? backup_id-- : do_chg--;
 	} else if (action == '-') {
@@ -276,7 +282,7 @@ todo_chg_priority(int action)
 	}	
 	if (do_chg) {
 		todo_delete_bynum(hilt - 1);
-		backup = todo_add(backup_mesg, backup_id);
+		backup = todo_add(backup_mesg, backup_id, backup_note);
 		hilt = todo_get_position(backup);	
 	} 
 }
@@ -293,12 +299,36 @@ todo_edit_item(void)
 	updatestring(win[STA].p, &i->mesg, 0, 1);
 }
 
+/* Display todo items in the corresponding panel. */
+static void
+display_todo_item(int incolor, char *msg, int prio, int note, int len, int y,
+    int x)
+{
+	WINDOW *w;
+	int ch_note;
+	char buf[len];
+
+	w = win[TOD].p;
+	ch_note = (note) ? '>' : '.';
+	if (incolor == 0) 
+		custom_apply_attr(w, ATTR_HIGHEST);
+	if (strlen(msg) < len)
+		mvwprintw(w, y, x, "%d%c %s", prio, ch_note, msg);
+	else {
+		strncpy(buf, msg, len - 1);
+		buf[len - 1] = '\0';
+		mvwprintw(w, y, x, "%d%c %s...", prio, ch_note, buf);
+	}
+	if (incolor == 0) 
+		custom_remove_attr(w, ATTR_HIGHEST);
+}
+
 /* Updates the ToDo panel. */
 void 
 todo_update_panel(window_t *wintod, int which_pan)
 {
 	struct todo_s *i;
-	int len = wintod->w - 6;
+	int len = wintod->w - 8;
 	int num_todo = 0;
 	int y_offset = 3, x_offset = 1;
 	int t_realpos = -1;
@@ -306,7 +336,6 @@ todo_update_panel(window_t *wintod, int which_pan)
 	int todo_lines = 1;
 	int max_items = wintod->h - 4;
 	int incolor = -1;
-	char mesg[BUFSIZ] = "";
 
 	/* Print todo item in the panel. */
 	erase_window_part(win[TOD].p, 1, title_lines, wintod->w - 2, 
@@ -318,10 +347,8 @@ todo_update_panel(window_t *wintod, int which_pan)
 		if (incolor == 0) 
 			msgsav = i->mesg; 
 		if (t_realpos >= 0 && t_realpos < max_items) {
-			snprintf(mesg, BUFSIZ, "%d. ", i->id);	
-			strncat(mesg, i->mesg, strlen(i->mesg));
-			display_item(win[TOD].p, incolor, mesg, 0, 
-					len, y_offset, x_offset);
+			display_todo_item(incolor, i->mesg, i->id,
+			    (i->note != NULL) ? 1 : 0, len, y_offset, x_offset);
 			y_offset = y_offset + todo_lines;	
 		}
 	}
@@ -342,3 +369,37 @@ todo_update_panel(window_t *wintod, int which_pan)
 	
 	wnoutrefresh(win[TOD].p);
 }
+
+/* Attach a note to a todo */
+void 
+todo_edit_note(char *editor)
+{
+	struct todo_s *i;
+	char fullname[BUFSIZ];
+	char *filename;
+
+	i = todo_get_item(hilt);
+	if (i->note == NULL) {
+		if ((filename = new_tempfile(path_notes, NOTESIZ))
+		    != NULL)
+			i->note = filename;
+		else
+			return;
+	}
+	snprintf(fullname, BUFSIZ, "%s%s", path_notes, i->note);
+	wins_launch_external(fullname, editor);
+}
+
+/* View a note previously attached to a todo */
+void 
+todo_view_note(char *pager)
+{
+	struct todo_s *i;
+	char fullname[BUFSIZ];
+
+	i = todo_get_item(hilt);
+	if (i->note == NULL)
+		return;
+	snprintf(fullname, BUFSIZ, "%s%s", path_notes, i->note);
+	wins_launch_external(fullname, pager);
+}
diff --git a/src/todo.h b/src/todo.h
index e8d128b..2620ef7 100755
--- a/src/todo.h
+++ b/src/todo.h
@@ -1,4 +1,4 @@
-/*	$calcurse: todo.h,v 1.9 2007/08/15 15:35:25 culot Exp $	*/
+/*	$calcurse: todo.h,v 1.10 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -30,9 +30,10 @@
 #include "wins.h"
 
 struct todo_s {
-	struct todo_s *next;
-	char *mesg;
-	int id;
+	struct todo_s  *next;
+	char 	       *mesg;
+	int 		id;
+	char 	       *note;
 };
 
 extern struct todo_s *todolist;
@@ -49,10 +50,12 @@ void		todo_first_decrease(void);
 int		todo_hilt_pos(void);
 char	       *todo_saved_mesg(void);
 void 		todo_new_item(void); 
-struct todo_s  *todo_add(char *, int);
+struct todo_s  *todo_add(char *, int, char *);
 void		todo_delete(conf_t *);
 void 		todo_chg_priority(int);
 void 		todo_edit_item(void);
 void		todo_update_panel(window_t *, int);
+void		todo_edit_note(char *);
+void		todo_view_note(char *);
 
 #endif /* CALCURSE_TODO_H */
diff --git a/src/utils.c b/src/utils.c
index a571fae..d2963a5 100755
--- a/src/utils.c
+++ b/src/utils.c
@@ -1,4 +1,4 @@
-/*	$calcurse: utils.c,v 1.37 2007/10/21 13:42:34 culot Exp $	*/
+/*	$calcurse: utils.c,v 1.38 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -27,9 +27,11 @@
 #include <time.h>	
 #include <string.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <ctype.h>
 #include <sys/types.h>
 #include <math.h>
+#include <errno.h>
 
 #include "i18n.h"
 #include "wins.h"
@@ -50,7 +52,7 @@ exit_calcurse(int status)
 
 /* Function to exit on internal error. */
 void
-ierror(const char *errmsg)
+ierror(const char *errmsg, ierror_sev_e sev)
 {
 	WINDOW *errwin;
 	char *label = _("INTERNAL ERROR");
@@ -66,14 +68,17 @@ ierror(const char *errmsg)
 	custom_apply_attr(errwin, ATTR_HIGHEST);
 	box(errwin, 0, 0);
 	wins_show(errwin, label);
-	mvwprintw(errwin, 3, 1, reportmsg);
+	if (sev == IERROR_FATAL)
+		mvwprintw(errwin, 3, 1, reportmsg);
 	mvwprintw(errwin, 5, (wincol - strlen(msg)) / 2, "%s", msg);
-	mvwprintw(errwin, winrow - 2, wincol - strlen(exitmsg) - 1, "%s", 
-	    exitmsg);
+	if (sev == IERROR_FATAL)
+		mvwprintw(errwin, winrow - 2, wincol - strlen(exitmsg) - 1, "%s", 
+		    exitmsg);
 	custom_remove_attr(errwin, ATTR_HIGHEST);
 	wrefresh(errwin);
 	wgetch(errwin);
-	exit_calcurse(EXIT_FAILURE);
+	if (sev == IERROR_FATAL)
+		exit_calcurse(EXIT_FAILURE);
 }
 
 /* Function to handle an assertion failure. */
@@ -84,7 +89,7 @@ aerror(const char *file, int line, const char *assertion)
 
 	snprintf(errmsg, BUFSIZ,
 	    "assert \"%s\" failed: file \"%s\", line %d", assertion, file, line);
-	ierror(errmsg);
+	ierror(errmsg, IERROR_FATAL);
 }
 
 /* 
@@ -94,7 +99,7 @@ aerror(const char *file, int line, const char *assertion)
 void 
 status_mesg(char *mesg_line1, char *mesg_line2)
 {
-	erase_window_part(win[STA].p, 0, 0, col, 2);
+	erase_status_bar();
 	custom_apply_attr(win[STA].p, ATTR_HIGHEST);
 	mvwprintw(win[STA].p, 0, 0, mesg_line1);
 	mvwprintw(win[STA].p, 1, 0, mesg_line2);
@@ -350,7 +355,8 @@ updatestring(WINDOW *win, char **str, int x, int y)
 		len = strlen(newstr) + 1;
 		if ((*str = (char *) realloc(*str, len)) == NULL) {
 			/* NOTREACHED */
-			ierror(_("FATAL ERROR in updatestring: out of memory"));
+			ierror(_("FATAL ERROR in updatestring: out of memory"),
+			    IERROR_FATAL);
 		} else 
 			(void)memcpy(*str, newstr, len);
 	} 	
@@ -742,33 +748,6 @@ void item_in_popup(char *saved_a_start, char *saved_a_end, char *msg,
 	delwin(popup_win);
 }
 
-/* 
- * Print an item description in the corresponding panel window.
- */
-void display_item(WINDOW *win, int incolor, char *msg, int recur, 
-			int len, int y, int x)
-{
-	char buf[len];
-
-	if (incolor == 0) 
-		custom_apply_attr(win, ATTR_HIGHEST);
-	if (strlen(msg) < len) {
-		if (recur)
-			mvwprintw(win, y, x, "*%s", msg);
-		else
-			mvwprintw(win, y, x, " %s", msg);
-	} else {
-		strncpy(buf, msg, len - 1);
-		buf[len - 1] = '\0';
-		if (recur)
-			mvwprintw(win, y, x, "*%s...", buf);
-		else
-			mvwprintw(win, y, x, " %s...", buf);
-	}
-	if (incolor == 0) 
-		custom_remove_attr(win, ATTR_HIGHEST);
-}
-
 /* Reset the status bar page. */
 void reset_status_page(void)
 {
@@ -792,7 +771,7 @@ void other_status_page(int panel)
 		nb_item = NB_TOD_CMDS;
 		break;
 	default:
-		ierror(error);
+		ierror(error, IERROR_FATAL);
 	}
 	max_page = ceil( nb_item / (2*CMDS_PER_LINE) ) + 1;
 	if (status_page < max_page) {
@@ -841,6 +820,26 @@ char *mycpy(const char *src)
 		return NULL;
 }
 
+long
+mystrtol(const char *str)
+{
+	char *ep;
+	long lval;
+	const char *not_a_number = 
+	    _("FATAL ERROR in mystrtol: could not convert string");
+	const char *out_of_range =
+	    _("FATAL ERROR in mystrtol: number is out of range");
+
+	errno = 0;
+	lval = strtol(str, &ep, 10);
+	if (str[0] == '\0' || *ep != '\0')
+		ierror(not_a_number, IERROR_FATAL);
+	if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
+		ierror(out_of_range, IERROR_FATAL);
+
+	return (lval);
+}
+
 /* Print the given option value with appropriate color. */
 void 
 print_option_incolor(WINDOW *win, bool option, int pos_y, int pos_x)
@@ -856,7 +855,8 @@ print_option_incolor(WINDOW *win, bool option, int pos_y, int pos_x)
 		strncpy(option_value, _("no"), BUFSIZ);
 	} else {
 		ierror(
-		    _("option not defined - Problem in print_option_incolor()"));
+		    _("option not defined - Problem in print_option_incolor()"),
+		    IERROR_FATAL);
 	}
 	custom_apply_attr(win, color);
 	mvwprintw(win, pos_y, pos_x, "%s", option_value);
@@ -864,3 +864,39 @@ print_option_incolor(WINDOW *win, bool option, int pos_y, int pos_x)
 	wnoutrefresh(win);
 	doupdate();
 }
+
+/* 
+ * Create a new unique file, and return a newly allocated string which contains
+ * the random part of the file name. 
+ */
+char *
+new_tempfile(const char *prefix, int trailing_len)
+{
+	char fullname[BUFSIZ];
+	int prefix_len, fd;
+	FILE *file;
+
+	if (prefix == NULL)
+		return (NULL);
+	
+	prefix_len = strlen(prefix);
+	if (prefix_len + trailing_len >= BUFSIZ)
+		return (NULL);
+	memcpy(fullname, prefix, prefix_len);
+	memset(fullname + prefix_len, 'X', trailing_len);
+	fullname[prefix_len + trailing_len] = '\0';
+	if ((fd = mkstemp(fullname)) == -1 ||
+	    (file = fdopen(fd, "w+")) == NULL) {
+		if (fd != -1) {
+			unlink(fullname);
+			close(fd);
+		}
+		ierror(
+		    _("FATAL ERROR: temporary file could not be created!"),
+		    IERROR_WARN);
+		return (NULL);
+	}
+	fclose(file);
+
+	return (strdup(fullname + prefix_len));
+}
diff --git a/src/utils.h b/src/utils.h
index 396fb9c..4d57a66 100755
--- a/src/utils.h
+++ b/src/utils.h
@@ -1,4 +1,4 @@
-/*	$calcurse: utils.h,v 1.23 2007/08/04 14:32:31 culot Exp $	*/
+/*	$calcurse: utils.h,v 1.24 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -56,8 +56,13 @@ typedef struct { /* structure defining a keybinding */
 	char *label;
 } binding_t;
 
+typedef enum {
+	IERROR_FATAL,
+	IERROR_WARN
+} ierror_sev_e;
+
 void	exit_calcurse(int);
-void	ierror(const char *);
+void	ierror(const char *, ierror_sev_e);
 void	aerror(const char *, int, const char *);
 void 	status_mesg(char *, char *);
 void	erase_status_bar(void);
@@ -80,12 +85,13 @@ long 	min2sec(unsigned);
 int 	check_time(char *);
 void 	draw_scrollbar(WINDOW *, int, int, int, int, int, bool);
 void 	item_in_popup(char *, char *, char *, char *);
-void 	display_item(WINDOW *, int, char *, int, int, int, int);
 void 	reset_status_page(void);
 void 	other_status_page(int);
 long 	get_today(void);
 long 	now(void);
 char   *mycpy(const char *);
+long	mystrtol(const char *);
 void 	print_option_incolor(WINDOW *, bool, int, int);
+char   *new_tempfile(const char *, int);
 
 #endif /* CALCURSE_UTILS_H */
diff --git a/src/vars.c b/src/vars.c
index fbbb666..6dab20b 100755
--- a/src/vars.c
+++ b/src/vars.c
@@ -1,4 +1,4 @@
-/*	$calcurse: vars.c,v 1.5 2007/10/21 13:40:13 culot Exp $	*/
+/*	$calcurse: vars.c,v 1.6 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -76,6 +76,7 @@ char path_dir[] = "";
 char path_todo[] = "";
 char path_apts[] = "";
 char path_conf[] = "";
+char path_notes[] = "";
 
 /* Variable to handle pads. */
 struct pad_s *apad;
@@ -90,12 +91,30 @@ struct nbar_s *nbar;
 void 
 vars_init(conf_t *conf)
 {
+	char *PATH_VI = "/usr/bin/vi";
+	char *PATH_LESS = "/usr/bin/less";
+	char *ed, *pg;
+
 	/* Variables for user configuration */
 	conf->confirm_quit = true; 
 	conf->confirm_delete = true; 
 	conf->auto_save = true;
 	conf->skip_system_dialogs = false;
 	conf->skip_progress_bar = false;
+
+	/* Default external editor and pager */
+	ed = getenv("VISUAL");
+	if (ed == NULL || ed[0] == '\0')
+		ed = getenv("EDITOR");
+	if (ed == NULL || ed[0] == '\0')
+		ed = PATH_VI;
+	conf->editor = ed;
+
+	pg = getenv("PAGER");
+	if (pg == NULL || pg[0] == '\0')
+		pg = PATH_LESS;
+	conf->pager = pg;
+
 	wins_set_layout(1);
 
 	calendar_set_first_day_of_week(MONDAY);
diff --git a/src/vars.h b/src/vars.h
index 2f7064a..3cedf83 100755
--- a/src/vars.h
+++ b/src/vars.h
@@ -1,4 +1,4 @@
-/*	$calcurse: vars.h,v 1.18 2007/12/10 19:04:08 culot Exp $	*/
+/*	$calcurse: vars.h,v 1.19 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -35,6 +35,7 @@
 #define TODO_PATH 	".calcurse/todo"
 #define APTS_PATH	".calcurse/apts"
 #define CONF_PATH	".calcurse/conf"
+#define NOTES_DIR	".calcurse/notes/"
 
 #define CTRL(x)         ((x) & 0x1f)
 #define ESCAPE		27
@@ -52,6 +53,7 @@
 #define WEEKINDAYS	7
 
 #define STATUSHEIGHT	2
+#define	NOTESIZ		10
 
 struct pad_s {
 	int width;
@@ -78,6 +80,8 @@ typedef struct {
 	bool confirm_delete;
 	bool skip_system_dialogs;
 	bool skip_progress_bar;
+	char *editor;
+	char *pager;
 } conf_t;
 
 extern int 		col, row;
@@ -89,6 +93,7 @@ extern char 		path_dir[BUFSIZ];
 extern char 		path_todo[BUFSIZ];
 extern char 		path_apts[BUFSIZ];
 extern char 		path_conf[BUFSIZ];
+extern char		path_notes[BUFSIZ];
 extern struct pad_s    *apad;
 extern struct nbar_s   *nbar;
 
diff --git a/src/wins.c b/src/wins.c
index b238996..fe79588 100755
--- a/src/wins.c
+++ b/src/wins.c
@@ -1,4 +1,4 @@
-/*	$Id: wins.c,v 1.8 2007/10/21 13:39:49 culot Exp $	*/
+/*	$Id: wins.c,v 1.9 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -337,7 +337,8 @@ wins_update(void)
 		break;
 
 	default:
-		ierror(_("FATAL ERROR in wins_update: no window selected\n"));
+		ierror(_("FATAL ERROR in wins_update: no window selected\n"),
+		    IERROR_FATAL);
 		/* NOTREACHED */
 	}
 
@@ -361,3 +362,31 @@ wins_reset(void)
 	wins_reinit();
 	wins_update();
 }
+
+/*
+ * While inside interactive mode, launch the external command cmd on the given
+ * file.
+ */
+void
+wins_launch_external(const char *file, const char *cmd)
+{
+	char *p;
+	
+	if (asprintf(&p, "%s %s", cmd, file) == -1)
+		return;
+
+	if (notify_bar())
+		notify_stop_main_thread();
+	def_prog_mode();
+	endwin();
+	clear();
+	refresh();
+	system(p);
+	reset_prog_mode();
+	clearok(curscr, TRUE);
+	curs_set(0);
+	refresh();
+	if (notify_bar())
+		notify_start_main_thread();
+	free(p);
+}
diff --git a/src/wins.h b/src/wins.h
index 629a6e4..6bc04ed 100755
--- a/src/wins.h
+++ b/src/wins.h
@@ -1,4 +1,4 @@
-/*	$Id: wins.h,v 1.4 2007/10/21 13:39:07 culot Exp $	*/
+/*	$Id: wins.h,v 1.5 2007/12/30 16:27:59 culot Exp $	*/
 
 /*
  * Calcurse - text-based organizer
@@ -61,5 +61,6 @@ void 		wins_show(WINDOW *, char *);
 void 		wins_get_config(void);
 void 		wins_update(void);
 void		wins_reset(void);
+void		wins_launch_external(const char *, const char *);
 
 #endif /* CALCURSE_WINS_H */
-- 
cgit v1.2.3-70-g09d2