"Fossies" - the Fresh Open Source Software Archive 
Member "jpilot-2_0_1/todo_gui.c" (3 Apr 2021, 105064 Bytes) of package /linux/privat/jpilot-2_0_1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /*******************************************************************************
2 * todo_gui.c
3 * A module of J-Pilot http://jpilot.org
4 *
5 * Copyright (C) 1999-2014 by Judd Montgomery
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ******************************************************************************/
20
21 /********************************* Includes ***********************************/
22 #include "config.h"
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <pi-dlp.h>
31 #include <slcurses.h>
32
33 #include "todo.h"
34 #include "i18n.h"
35 #include "utils.h"
36 #include "log.h"
37 #include "prefs.h"
38 #include "password.h"
39 #include "print.h"
40 #include "export.h"
41 #include "stock_buttons.h"
42
43 /********************************* Constants **********************************/
44 #define TODO_MAX_COLUMN_LEN 80
45 #define MAX_RADIO_BUTTON_LEN 100
46
47 #define NUM_TODO_PRIORITIES 5
48 #define NUM_TODO_CAT_ITEMS 16
49 #define NUM_TODO_CSV_FIELDS 8
50 #define CONNECT_SIGNALS 400
51 #define DISCONNECT_SIGNALS 401
52
53 /* RFCs use CRLF for Internet newline */
54 #define CRLF "\x0D\x0A"
55
56 /******************************* Global vars **********************************/
57 /* Keeps track of whether code is using ToDo, or Tasks database
58 * 0 is ToDo, 1 is Tasks */
59 static long todo_version = 0;
60
61 extern GtkWidget *glob_date_label;
62 extern int glob_date_timer_tag;
63
64 static GtkWidget *treeView;
65 static GtkTreeSelection *treeSelection;
66 static GtkListStore *listStore;
67 static GtkWidget *todo_desc, *todo_note;
68 static GObject *todo_desc_buffer, *todo_note_buffer;
69 static GtkWidget *todo_completed_checkbox;
70 static GtkWidget *private_checkbox;
71 static struct tm due_date;
72 static GtkWidget *due_date_button;
73 static GtkWidget *todo_no_due_date_checkbox;
74 static GtkWidget *radio_button_todo[NUM_TODO_PRIORITIES];
75 static GtkWidget *new_record_button;
76 static GtkWidget *apply_record_button;
77 static GtkWidget *add_record_button;
78 static GtkWidget *delete_record_button;
79 static GtkWidget *undelete_record_button;
80 static GtkWidget *copy_record_button;
81 static GtkWidget *cancel_record_button;
82 static GtkWidget *category_menu1;
83 static GtkWidget *category_menu2;
84 static GtkWidget *pane;
85 static GtkWidget *note_pane;
86
87 static ToDoList *glob_todo_list = NULL;
88 static ToDoList *export_todo_list = NULL;
89
90 static struct sorted_cats sort_l[NUM_TODO_CAT_ITEMS];
91
92 static struct ToDoAppInfo todo_app_info;
93 static int todo_category = CATEGORY_ALL;
94 static int column_selected;
95 static int row_selected;
96 static int record_changed;
97
98 /****************************** Prototypes ************************************/
99 static int todo_clear_details(void);
100
101 static int todo_redraw(void);
102
103 static int todo_find(void);
104
105 static void cb_add_new_record(GtkWidget *widget, gpointer data);
106
107 static void connect_changed_signals(int con_or_dis);
108
109
110 void addNewRecordToDataStructure(MyToDo *mtodo, gpointer data);
111
112 void deleteTodo(MyToDo *mtodo, gpointer data);
113
114 void undeleteTodo(MyToDo *mtodo, gpointer data);
115
116 int printTodo(MyToDo *mtodo, gpointer data);
117
118
119
120 gboolean printRecord(GtkTreeModel *model,
121 GtkTreePath *path,
122 GtkTreeIter *iter,
123 gpointer data);
124
125
126
127
128
129 gboolean
130 findRecord(GtkTreeModel *model,
131 GtkTreePath *path,
132 GtkTreeIter *iter,
133 gpointer data);
134
135 gboolean
136 selectRecordByRow(GtkTreeModel *model,
137 GtkTreePath *path,
138 GtkTreeIter *iter,
139 gpointer data);
140
141 gint compareNoteColumn(GtkTreeModel *model, GtkTreeIter *left, GtkTreeIter *right);
142
143 gint compareCheckColumn(GtkTreeModel *model, GtkTreeIter *left, GtkTreeIter *right);
144
145
146 /****************************** Main Code *************************************/
147 /* Called once on initialization of GUI */
148 static void init(void) {
149 time_t ltime;
150 struct tm *now;
151 long ivalue;
152
153 time(<ime);
154 now = localtime(<ime);
155
156 memcpy(&due_date, now, sizeof(struct tm));
157
158 get_pref(PREF_TODO_DAYS_TILL_DUE, &ivalue, NULL);
159 add_days_to_date(&due_date, (int) ivalue);
160
161 row_selected = 0;
162 column_selected = 0;
163
164 record_changed = CLEAR_FLAG;
165 }
166
167 static void update_due_button(GtkWidget *button, struct tm *t) {
168 const char *short_date;
169 char str[255];
170
171 if (t) {
172 get_pref(PREF_SHORTDATE, NULL, &short_date);
173 strftime(str, sizeof(str), short_date, t);
174
175 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(button))), str);
176 } else {
177 gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(button))), _("No Date"));
178 }
179 }
180
181 static void cb_cal_dialog(GtkWidget *widget,
182 gpointer data) {
183 long fdow;
184 int r = 0;
185 struct tm t;
186 GtkWidget *Pcheck_button;
187 GtkWidget *Pbutton;
188
189 Pcheck_button = todo_no_due_date_checkbox;
190 memcpy(&t, &due_date, sizeof(t));
191 Pbutton = due_date_button;
192
193 get_pref(PREF_FDOW, &fdow, NULL);
194
195 r = cal_dialog(GTK_WINDOW(gtk_widget_get_toplevel(widget)), _("Due Date"), (int) fdow,
196 &(t.tm_mon),
197 &(t.tm_mday),
198 &(t.tm_year));
199
200 if (r == CAL_DONE) {
201 mktime(&t);
202 memcpy(&due_date, &t, sizeof(due_date));
203 if (Pcheck_button) {
204 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Pcheck_button), FALSE);
205 /* The above call sets due_date forward by n days, so we correct it */
206 memcpy(&due_date, &t, sizeof(due_date));
207 update_due_button(Pbutton, &t);
208 }
209 if (Pbutton) {
210 update_due_button(Pbutton, &t);
211 }
212 }
213 }
214
215 int printTodo(MyToDo *mtodo, gpointer data) {
216 long this_many;
217 ToDoList *todo_list;
218 ToDoList todo_list1;
219
220 get_pref(PREF_PRINT_THIS_MANY, &this_many, NULL);
221
222 todo_list = NULL;
223 if (this_many == 1) {
224 if (mtodo < (MyToDo *) LIST_MIN_DATA) {
225 return EXIT_FAILURE;
226 }
227 memcpy(&(todo_list1.mtodo), mtodo, sizeof(MyToDo));
228 todo_list1.next = NULL;
229 todo_list = &todo_list1;
230 }
231 if (this_many == 2) {
232 get_todos2(&todo_list, SORT_ASCENDING, 2, 2, 2, 2, todo_category);
233 }
234 if (this_many == 3) {
235 get_todos2(&todo_list, SORT_ASCENDING, 2, 2, 2, 2, CATEGORY_ALL);
236 }
237
238 print_todos(todo_list, PN);
239
240 if ((this_many == 2) || (this_many == 3)) {
241 free_ToDoList(&todo_list);
242 }
243
244 return EXIT_SUCCESS;
245 }
246
247 int todo_print(void) {
248 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), printRecord, NULL);
249 return EXIT_SUCCESS;
250
251 }
252
253 static void set_new_button_to(int new_state) {
254 jp_logf(JP_LOG_DEBUG, "set_new_button_to new %d old %d\n", new_state, record_changed);
255
256 if (record_changed == new_state) {
257 return;
258 }
259
260 switch (new_state) {
261 case MODIFY_FLAG:
262 gtk_widget_show(cancel_record_button);
263 gtk_widget_show(copy_record_button);
264 gtk_widget_show(apply_record_button);
265
266 gtk_widget_hide(add_record_button);
267 gtk_widget_hide(delete_record_button);
268 gtk_widget_hide(new_record_button);
269 gtk_widget_hide(undelete_record_button);
270
271 break;
272 case NEW_FLAG:
273 gtk_widget_show(cancel_record_button);
274 gtk_widget_show(add_record_button);
275
276 gtk_widget_hide(apply_record_button);
277 gtk_widget_hide(copy_record_button);
278 gtk_widget_hide(delete_record_button);
279 gtk_widget_hide(new_record_button);
280 gtk_widget_hide(undelete_record_button);
281
282 break;
283 case CLEAR_FLAG:
284 gtk_widget_show(delete_record_button);
285 gtk_widget_show(copy_record_button);
286 gtk_widget_show(new_record_button);
287
288 gtk_widget_hide(add_record_button);
289 gtk_widget_hide(apply_record_button);
290 gtk_widget_hide(cancel_record_button);
291 gtk_widget_hide(undelete_record_button);
292
293 break;
294 case UNDELETE_FLAG:
295 gtk_widget_show(undelete_record_button);
296 gtk_widget_show(copy_record_button);
297 gtk_widget_show(new_record_button);
298
299 gtk_widget_hide(add_record_button);
300 gtk_widget_hide(apply_record_button);
301 gtk_widget_hide(cancel_record_button);
302 gtk_widget_hide(delete_record_button);
303 break;
304
305 default:
306 return;
307 }
308
309 record_changed = new_state;
310 }
311
312 static void cb_record_changed(GtkWidget *widget,
313 gpointer data) {
314 jp_logf(JP_LOG_DEBUG, "cb_record_changed\n");
315 if (record_changed == CLEAR_FLAG) {
316 connect_changed_signals(DISCONNECT_SIGNALS);
317 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL) > 0) {
318 set_new_button_to(MODIFY_FLAG);
319 } else {
320 set_new_button_to(NEW_FLAG);
321 }
322 } else if (record_changed == UNDELETE_FLAG) {
323 jp_logf(JP_LOG_INFO | JP_LOG_GUI,
324 _("This record is deleted.\n"
325 "Undelete it or copy it to make changes.\n"));
326 }
327 }
328
329 static void connect_changed_signals(int con_or_dis) {
330 int i;
331 static int connected = 0;
332
333 /* CONNECT */
334 if ((con_or_dis == CONNECT_SIGNALS) && (!connected)) {
335 connected = 1;
336
337
338 if (category_menu2) {
339 g_signal_connect(G_OBJECT(category_menu2), "changed", G_CALLBACK(cb_record_changed), NULL);
340 }
341 for (i = 0; i < NUM_TODO_PRIORITIES; i++) {
342 if (radio_button_todo[i]) {
343 g_signal_connect(G_OBJECT(radio_button_todo[i]), "toggled",
344 G_CALLBACK(cb_record_changed), NULL);
345 }
346 }
347 g_signal_connect(todo_desc_buffer, "changed",
348 G_CALLBACK(cb_record_changed), NULL);
349 g_signal_connect(todo_note_buffer, "changed",
350 G_CALLBACK(cb_record_changed), NULL);
351
352 g_signal_connect(G_OBJECT(todo_completed_checkbox), "toggled",
353 G_CALLBACK(cb_record_changed), NULL);
354 g_signal_connect(G_OBJECT(private_checkbox), "toggled",
355 G_CALLBACK(cb_record_changed), NULL);
356 g_signal_connect(G_OBJECT(todo_no_due_date_checkbox), "toggled",
357 G_CALLBACK(cb_record_changed), NULL);
358 g_signal_connect(G_OBJECT(due_date_button), "pressed",
359 G_CALLBACK(cb_record_changed), NULL);
360 }
361
362 /* DISCONNECT */
363 if ((con_or_dis == DISCONNECT_SIGNALS) && (connected)) {
364 connected = 0;
365 if (category_menu2) {
366 g_signal_handlers_disconnect_by_func(G_OBJECT(category_menu2), G_CALLBACK(cb_record_changed), NULL);
367 }
368 for (i = 0; i < NUM_TODO_PRIORITIES; i++) {
369 g_signal_handlers_disconnect_by_func(G_OBJECT(radio_button_todo[i]),
370 G_CALLBACK(cb_record_changed), NULL);
371 }
372 g_signal_handlers_disconnect_by_func(todo_desc_buffer,
373 G_CALLBACK(cb_record_changed), NULL);
374 g_signal_handlers_disconnect_by_func(todo_note_buffer,
375 G_CALLBACK(cb_record_changed), NULL);
376
377 g_signal_handlers_disconnect_by_func(G_OBJECT(todo_completed_checkbox),
378 G_CALLBACK(cb_record_changed), NULL);
379 g_signal_handlers_disconnect_by_func(G_OBJECT(private_checkbox),
380 G_CALLBACK(cb_record_changed), NULL);
381 g_signal_handlers_disconnect_by_func(G_OBJECT(todo_no_due_date_checkbox),
382 G_CALLBACK(cb_record_changed), NULL);
383 g_signal_handlers_disconnect_by_func(G_OBJECT(due_date_button),
384 G_CALLBACK(cb_record_changed), NULL);
385 }
386 }
387
388
389 static int todo_to_text(struct ToDo *todo, char *text, int len) {
390 char yes[] = "Yes";
391 char no[] = "No";
392 char empty[] = "";
393 char *complete;
394 char *description;
395 char *note;
396 char due[20];
397 const char *short_date;
398
399 if (todo->indefinite) {
400 strcpy(due, "Never");
401 } else {
402 get_pref(PREF_SHORTDATE, NULL, &short_date);
403 strftime(due, sizeof(due), short_date, &(todo->due));
404 }
405 complete = todo->complete ? yes : no;
406 description = todo->description ? todo->description : empty;
407 note = todo->note ? todo->note : empty;
408 g_snprintf(text, (gulong) len, "Due: %s\nPriority: %d\nComplete: %s\n\
409 Description: %s\nNote: %s\n", due, todo->priority, complete,
410 description, note);
411 return EXIT_SUCCESS;
412 }
413
414 /*
415 * Start Import Code
416 */
417 static int cb_todo_import(GtkWidget *parent_window,
418 const char *file_path, int type) {
419 FILE *in;
420 char text[65536];
421 char description[65536];
422 char note[65536];
423 struct ToDo new_todo;
424 unsigned char attrib;
425 int i, ret, index;
426 int import_all;
427 ToDoList *todolist;
428 ToDoList *temp_todolist;
429 struct CategoryAppInfo cai;
430 char old_cat_name[32];
431 int suggested_cat_num;
432 int new_cat_num;
433 int priv, indefinite, priority, completed;
434 int year, month, day;
435
436 in = fopen(file_path, "r");
437 if (!in) {
438 jp_logf(JP_LOG_WARN, _("Unable to open file: %s\n"), file_path);
439 return EXIT_FAILURE;
440 }
441
442 /* CSV */
443 if (type == IMPORT_TYPE_CSV) {
444 jp_logf(JP_LOG_DEBUG, "Todo import CSV [%s]\n", file_path);
445 /* Get the first line containing the format and check for reasonableness */
446 if (fgets(text, sizeof(text), in) == NULL) {
447 jp_logf(JP_LOG_WARN, "fgets failed %s %d\n", __FILE__, __LINE__);
448 }
449 ret = verify_csv_header(text, NUM_TODO_CSV_FIELDS, file_path);
450 if (EXIT_FAILURE == ret) return EXIT_FAILURE;
451
452 import_all = FALSE;
453 while (1) {
454 /* Read the category field */
455 ret = read_csv_field(in, text, sizeof(text));
456 if (feof(in)) break;
457 #ifdef JPILOT_DEBUG
458 printf("category is [%s]\n", text);
459 #endif
460 g_strlcpy(old_cat_name, text, 16);
461 attrib = 0;
462 /* Figure out what the best category number is */
463 suggested_cat_num = 0;
464 for (i = 0; i < NUM_TODO_CAT_ITEMS; i++) {
465 if (todo_app_info.category.name[i][0] == '\0') continue;
466 if (!strcmp(todo_app_info.category.name[i], old_cat_name)) {
467 suggested_cat_num = i;
468 break;
469 }
470 }
471
472 /* Read the private field */
473 ret = read_csv_field(in, text, sizeof(text));
474 #ifdef JPILOT_DEBUG
475 printf("private is [%s]\n", text);
476 #endif
477 sscanf(text, "%d", &priv);
478
479 /* Read the indefinite field */
480 ret = read_csv_field(in, text, sizeof(text));
481 #ifdef JPILOT_DEBUG
482 printf("indefinite is [%s]\n", text);
483 #endif
484 sscanf(text, "%d", &indefinite);
485
486 /* Read the Due Date field */
487 ret = read_csv_field(in, text, sizeof(text));
488 #ifdef JPILOT_DEBUG
489 printf("due date is [%s]\n", text);
490 #endif
491 sscanf(text, "%d/%d/%d", &year, &month, &day);
492
493 /* Read the Priority field */
494 ret = read_csv_field(in, text, sizeof(text));
495 #ifdef JPILOT_DEBUG
496 printf("priority is [%s]\n", text);
497 #endif
498 sscanf(text, "%d", &priority);
499
500 /* Read the Completed field */
501 ret = read_csv_field(in, text, sizeof(text));
502 #ifdef JPILOT_DEBUG
503 printf("completed is [%s]\n", text);
504 #endif
505 sscanf(text, "%d", &completed);
506
507 /* Read the Description field */
508 ret = read_csv_field(in, description, sizeof(text));
509 #ifdef JPILOT_DEBUG
510 printf("todo description [%s]\n", description);
511 #endif
512
513 /* Read the Note field */
514 ret = read_csv_field(in, note, sizeof(text));
515 #ifdef JPILOT_DEBUG
516 printf("todo note [%s]\n", note);
517 #endif
518
519 new_todo.indefinite = indefinite;
520 memset(&(new_todo.due), 0, sizeof(new_todo.due));
521 new_todo.due.tm_year = year - 1900;
522 new_todo.due.tm_mon = month - 1;
523 new_todo.due.tm_mday = day;
524 new_todo.priority = priority;
525 new_todo.complete = completed;
526 new_todo.description = description;
527 new_todo.note = note;
528
529 todo_to_text(&new_todo, text, sizeof(text));
530 if (!import_all) {
531 ret = import_record_ask(parent_window, pane,
532 text,
533 &(todo_app_info.category),
534 old_cat_name,
535 priv,
536 suggested_cat_num,
537 &new_cat_num);
538 } else {
539 new_cat_num = suggested_cat_num;
540 }
541 if (ret == DIALOG_SAID_IMPORT_QUIT) break;
542 if (ret == DIALOG_SAID_IMPORT_SKIP) continue;
543 if (ret == DIALOG_SAID_IMPORT_ALL) import_all = TRUE;
544
545 attrib = (unsigned char) ((new_cat_num & 0x0F) | (priv ? dlpRecAttrSecret : 0));
546 if ((ret == DIALOG_SAID_IMPORT_YES) || (import_all)) {
547 pc_todo_write(&new_todo, NEW_PC_REC, attrib, NULL);
548 }
549 }
550 }
551
552 /* Palm Desktop DAT format */
553 if (type == IMPORT_TYPE_DAT) {
554 jp_logf(JP_LOG_DEBUG, "Todo import DAT [%s]\n", file_path);
555 if (dat_check_if_dat_file(in) != DAT_TODO_FILE) {
556 jp_logf(JP_LOG_WARN, _("File doesn't appear to be todo.dat format\n"));
557 fclose(in);
558 return EXIT_FAILURE;
559 }
560 todolist = NULL;
561 dat_get_todos(in, &todolist, &cai);
562 import_all = FALSE;
563 for (temp_todolist = todolist; temp_todolist; temp_todolist = temp_todolist->next) {
564 index = temp_todolist->mtodo.unique_id - 1;
565 if (index < 0) {
566 g_strlcpy(old_cat_name, _("Unfiled"), 16);
567 index = 0;
568 } else {
569 g_strlcpy(old_cat_name, cai.name[index], 16);
570 }
571 /* Figure out what category it was in the dat file */
572 index = temp_todolist->mtodo.unique_id - 1;
573 suggested_cat_num = 0;
574 if (index > -1) {
575 for (i = 0; i < NUM_TODO_CAT_ITEMS; i++) {
576 if (todo_app_info.category.name[i][0] == '\0') continue;
577 if (!strcmp(todo_app_info.category.name[i], old_cat_name)) {
578 suggested_cat_num = i;
579 break;
580 }
581 }
582 }
583
584 ret = 0;
585 todo_to_text(&(temp_todolist->mtodo.todo), text, sizeof(text));
586 if (!import_all) {
587 ret = import_record_ask(parent_window, pane,
588 text,
589 &(todo_app_info.category),
590 old_cat_name,
591 (temp_todolist->mtodo.attrib & 0x10),
592 suggested_cat_num,
593 &new_cat_num);
594 } else {
595 new_cat_num = suggested_cat_num;
596 }
597 if (ret == DIALOG_SAID_IMPORT_QUIT) break;
598 if (ret == DIALOG_SAID_IMPORT_SKIP) continue;
599 if (ret == DIALOG_SAID_IMPORT_ALL) import_all = TRUE;
600
601 attrib = (unsigned char) ((new_cat_num & 0x0F) |
602 ((temp_todolist->mtodo.attrib & 0x10) ? dlpRecAttrSecret : 0));
603 if ((ret == DIALOG_SAID_IMPORT_YES) || (import_all)) {
604 pc_todo_write(&(temp_todolist->mtodo.todo), NEW_PC_REC,
605 attrib, NULL);
606 }
607 }
608 free_ToDoList(&todolist);
609 }
610
611 todo_refresh();
612 fclose(in);
613 return EXIT_SUCCESS;
614 }
615
616 int todo_import(GtkWidget *window) {
617 char *type_desc[] = {
618 N_("CSV (Comma Separated Values)"),
619 N_("DAT/TDA (Palm Archive Formats)"),
620 NULL
621 };
622 int type_int[] = {
623 IMPORT_TYPE_CSV,
624 IMPORT_TYPE_DAT,
625 0
626 };
627
628 /* Hide ABA import of TaskDB until file format has been decoded */
629 /* FIXME: Uncomment when support for Tasks has been added
630 if (todo_version==1) {
631 type_desc[1] = NULL;
632 type_int[1] = 0;
633 }
634 */
635
636 import_gui(window, pane, type_desc, type_int, cb_todo_import);
637 return EXIT_SUCCESS;
638 }
639 /*
640 * End Import Code
641 */
642
643 /*
644 * Start Export code
645 */
646
647
648
649 static void cb_todo_export_ok(GtkWidget *export_window, GtkWidget *treeView,
650 int type, const char *filename) {
651 MyToDo *mtodo;
652 GList *list, *temp_list;
653 FILE *out;
654 struct stat statb;
655 int i, r;
656 const char *short_date = NULL;
657 time_t ltime;
658 struct tm *now = NULL;
659 char *button_text[] = {N_("OK")};
660 char *button_overwrite_text[] = {N_("No"), N_("Yes")};
661 char text[1024];
662 char date_string[1024];
663 char str1[256], str2[256];
664 char pref_time[40];
665 char csv_text[65550];
666 char *p;
667 gchar *end;
668 char username[256];
669 char hostname[256];
670 const char *svalue;
671 long userid = -1;
672 long char_set;
673 char *utf;
674
675 /* Open file for export, including corner cases where file exists or
676 * can't be opened */
677 if (!stat(filename, &statb)) {
678 if (S_ISDIR(statb.st_mode)) {
679 g_snprintf(text, sizeof(text), _("%s is a directory"), filename);
680 dialog_generic(GTK_WINDOW(export_window),
681 _("Error Opening File"),
682 DIALOG_ERROR, text, 1, button_text);
683 return;
684 }
685 g_snprintf(text, sizeof(text), _("Do you want to overwrite file %s?"), filename);
686 r = dialog_generic(GTK_WINDOW(export_window),
687 _("Overwrite File?"),
688 DIALOG_QUESTION, text, 2, button_overwrite_text);
689 if (r != DIALOG_SAID_2) {
690 return;
691 }
692 }
693
694 out = fopen(filename, "w");
695 if (!out) {
696 g_snprintf(text, sizeof(text), _("Error opening file: %s"), filename);
697 dialog_generic(GTK_WINDOW(export_window),
698 _("Error Opening File"),
699 DIALOG_ERROR, text, 1, button_text);
700 return;
701 }
702
703 /* Write a header for TEXT file */
704 if (type == EXPORT_TYPE_TEXT) {
705 get_pref(PREF_SHORTDATE, NULL, &short_date);
706 get_pref_time_no_secs(pref_time);
707 time(<ime);
708 now = localtime(<ime);
709 strftime(str1, sizeof(str1), short_date, now);
710 strftime(str2, sizeof(str2), pref_time, now);
711 g_snprintf(date_string, sizeof(date_string), "%s %s", str1, str2);
712 fprintf(out, _("ToDo exported from %s %s on %s\n\n"),
713 PN, VERSION, date_string);
714 }
715
716 /* Write a header to the CSV file */
717 if (type == EXPORT_TYPE_CSV) {
718 fprintf(out,
719 "CSV todo version "VERSION": Category, Private, Indefinite, Due Date, Priority, Completed, ToDo Text, Note\n");
720 }
721
722 /* Special setup for ICAL export */
723 if (type == EXPORT_TYPE_ICALENDAR) {
724 get_pref(PREF_CHAR_SET, &char_set, NULL);
725 if (char_set < CHAR_SET_UTF) {
726 jp_logf(JP_LOG_WARN, _("Host character encoding is not UTF-8 based.\n"
727 " Exported ical file may not be standards-compliant\n"));
728 }
729
730 get_pref(PREF_USER, NULL, &svalue);
731 /* Convert User Name stored in Palm character set */
732 g_strlcpy(text, svalue, 128);
733 text[127] = '\0';
734 charset_p2j(text, 128, (int) char_set);
735 str_to_ical_str(username, sizeof(username), text);
736 get_pref(PREF_USER_ID, &userid, NULL);
737 gethostname(text, sizeof(hostname));
738 text[sizeof(hostname) - 1] = '\0';
739 str_to_ical_str(hostname, sizeof(hostname), text);
740 time(<ime);
741 now = gmtime(<ime);
742 }
743
744 get_pref(PREF_CHAR_SET, &char_set, NULL);
745 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
746 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView));
747 list = gtk_tree_selection_get_selected_rows(selection, &model);
748
749
750 for (i = 0, temp_list = list; temp_list; temp_list = temp_list->next, i++) {
751 GtkTreePath *path = temp_list->data;
752 GtkTreeIter iter;
753 if (gtk_tree_model_get_iter(model, &iter, path)) {
754 gtk_tree_model_get(model, &iter, TODO_DATA_COLUMN_ENUM, &mtodo, -1);
755 if (!mtodo) {
756 continue;
757 jp_logf(JP_LOG_WARN, _("Can't export todo %d\n"), (long) temp_list->data + 1);
758 }
759 switch (type) {
760 case EXPORT_TYPE_CSV:
761 utf = charset_p2newj(todo_app_info.category.name[mtodo->attrib & 0x0F], 16, (int) char_set);
762 str_to_csv_str(csv_text, utf);
763 fprintf(out, "\"%s\",", csv_text);
764 g_free(utf);
765 fprintf(out, "\"%s\",", (mtodo->attrib & dlpRecAttrSecret) ? "1" : "0");
766 fprintf(out, "\"%s\",", mtodo->todo.indefinite ? "1" : "0");
767 if (mtodo->todo.indefinite) {
768 fprintf(out, "\"\",");
769 } else {
770 strftime(text, sizeof(text), "%Y/%02m/%02d", &(mtodo->todo.due));
771 fprintf(out, "\"%s\",", text);
772 }
773 fprintf(out, "\"%d\",", mtodo->todo.priority);
774 fprintf(out, "\"%s\",", mtodo->todo.complete ? "1" : "0");
775 if (mtodo->todo.description) {
776 str_to_csv_str(csv_text, mtodo->todo.description);
777 fprintf(out, "\"%s\",", csv_text);
778 } else {
779 fprintf(out, "\"\",");
780 }
781 if (mtodo->todo.note) {
782 str_to_csv_str(csv_text, mtodo->todo.note);
783 fprintf(out, "\"%s\"\n", csv_text);
784 } else {
785 fprintf(out, "\"\",");
786 }
787 break;
788
789 case EXPORT_TYPE_TEXT:
790 utf = charset_p2newj(todo_app_info.category.name[mtodo->attrib & 0x0F], 16, (int) char_set);
791 fprintf(out, _("Category: %s\n"), utf);
792 g_free(utf);
793
794 fprintf(out, _("Private: %s\n"),
795 (mtodo->attrib & dlpRecAttrSecret) ? _("Yes") : _("No"));
796 if (mtodo->todo.indefinite) {
797 fprintf(out, _("Due Date: None\n"));
798 } else {
799 strftime(text, sizeof(text), short_date, &(mtodo->todo.due));
800 fprintf(out, _("Due Date: %s\n"), text);
801 }
802 fprintf(out, _("Priority: %d\n"), mtodo->todo.priority);
803 fprintf(out, _("Completed: %s\n"), mtodo->todo.complete ? _("Yes") : _("No"));
804 if (mtodo->todo.description) {
805 fprintf(out, _("Description: %s\n"), mtodo->todo.description);
806 }
807 if (mtodo->todo.note) {
808 fprintf(out, _("Note: %s\n\n"), mtodo->todo.note);
809 }
810 break;
811
812 case EXPORT_TYPE_ICALENDAR:
813 /* RFC 2445: Internet Calendaring and Scheduling Core
814 * Object Specification */
815 if (i == 0) {
816 fprintf(out, "BEGIN:VCALENDAR"CRLF);
817 fprintf(out, "VERSION:2.0"CRLF);
818 fprintf(out, "PRODID:%s"CRLF, FPI_STRING);
819 }
820 fprintf(out, "BEGIN:VTODO"CRLF);
821 if (mtodo->attrib & dlpRecAttrSecret) {
822 fprintf(out, "CLASS:PRIVATE"CRLF);
823 }
824 fprintf(out, "UID:palm-todo-%08x-%08lx-%s@%s"CRLF,
825 mtodo->unique_id, userid, username, hostname);
826 fprintf(out, "DTSTAMP:%04d%02d%02dT%02d%02d%02dZ"CRLF,
827 now->tm_year + 1900,
828 now->tm_mon + 1,
829 now->tm_mday,
830 now->tm_hour,
831 now->tm_min,
832 now->tm_sec);
833 str_to_ical_str(text, sizeof(text),
834 todo_app_info.category.name[mtodo->attrib & 0x0F]);
835 fprintf(out, "CATEGORIES:%s"CRLF, text);
836 if (mtodo->todo.description) {
837 g_strlcpy(str1, mtodo->todo.description, 51);
838 /* truncate the string on a UTF-8 character boundary */
839 if (char_set > CHAR_SET_UTF) {
840 if (!g_utf8_validate(str1, -1, (const gchar **) &end))
841 *end = 0;
842 }
843 } else {
844 /* Handle pathological case with null description. */
845 str1[0] = '\0';
846 }
847 if ((p = strchr(str1, '\n'))) {
848 *p = '\0';
849 }
850 str_to_ical_str(text, sizeof(text), str1);
851 fprintf(out, "SUMMARY:%s%s"CRLF, text,
852 strlen(str1) > 49 ? "..." : "");
853 str_to_ical_str(text, sizeof(text), mtodo->todo.description);
854 fprintf(out, "DESCRIPTION:%s", text);
855 if (mtodo->todo.note && mtodo->todo.note[0]) {
856 str_to_ical_str(text, sizeof(text), mtodo->todo.note);
857 fprintf(out, "\\n"CRLF" %s"CRLF, text);
858 } else {
859 fprintf(out, ""CRLF);
860 }
861 fprintf(out, "STATUS:%s"CRLF, mtodo->todo.complete ? "COMPLETED" : "NEEDS-ACTION");
862 fprintf(out, "PRIORITY:%d"CRLF, mtodo->todo.priority);
863 if (!mtodo->todo.indefinite) {
864 fprintf(out, "DUE;VALUE=DATE:%04d%02d%02d"CRLF,
865 mtodo->todo.due.tm_year + 1900,
866 mtodo->todo.due.tm_mon + 1,
867 mtodo->todo.due.tm_mday);
868 }
869 fprintf(out, "END:VTODO"CRLF);
870 if (temp_list->next == NULL) {
871 fprintf(out, "END:VCALENDAR"CRLF);
872 }
873 break;
874 default:
875 jp_logf(JP_LOG_WARN, _("Unknown export type\n"));
876 }
877 }
878 }
879
880 if (out) {
881 fclose(out);
882 }
883 }
884
885 static void cb_todo_update_listStore(GtkWidget *exportTreeView, int category) {
886 if (exportTreeView == NULL || !GTK_IS_TREE_VIEW(exportTreeView)) {
887 return;
888 }
889 todo_update_liststore(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(exportTreeView))), NULL,
890 &export_todo_list, category, FALSE);
891 }
892
893 static GtkWidget *cb_todo_init_treeView() {
894 GtkListStore *listStore = gtk_list_store_new(TODO_NUM_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING, GDK_TYPE_PIXBUF,
895 G_TYPE_STRING,
896 G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_BOOLEAN,
897 G_TYPE_STRING, G_TYPE_BOOLEAN);
898 GtkTreeModel *model = GTK_TREE_MODEL(listStore);
899 GtkWidget *todo_treeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
900 GtkCellRenderer *taskRenderer = gtk_cell_renderer_text_new();
901 // gtk_cell_renderer_set_fixed_size(taskRenderer, -1, 1);
902
903 GtkTreeViewColumn *taskColumn = gtk_tree_view_column_new_with_attributes("Task",
904 taskRenderer,
905 "text", TODO_TEXT_COLUMN_ENUM,
906 "cell-background-rgba",
907 TODO_BACKGROUND_COLOR_ENUM,
908 "cell-background-set",
909 TODO_BACKGROUND_COLOR_ENABLED_ENUM,
910 NULL);
911 gtk_tree_view_column_set_sort_column_id(taskColumn, TODO_TEXT_COLUMN_ENUM);
912
913
914 GtkCellRenderer *dateRenderer = gtk_cell_renderer_text_new();
915 // gtk_cell_renderer_set_fixed_size(dateRenderer, -1, 1);
916
917 GtkTreeViewColumn *dateColumn = gtk_tree_view_column_new_with_attributes("Due",
918 dateRenderer,
919 "text", TODO_DATE_COLUMN_ENUM,
920 "cell-background-rgba",
921 TODO_BACKGROUND_COLOR_ENUM,
922 "cell-background-set",
923 TODO_BACKGROUND_COLOR_ENABLED_ENUM,
924 "foreground", TODO_FOREGROUND_COLOR_ENUM,
925 "foreground-set",
926 TODO_FORGROUND_COLOR_ENABLED_ENUM,
927 NULL);
928 gtk_tree_view_column_set_sort_column_id(dateColumn, TODO_DATE_COLUMN_ENUM);
929
930 GtkCellRenderer *priorityRenderer = gtk_cell_renderer_text_new();
931 // gtk_cell_renderer_set_fixed_size(priorityRenderer, -1, 1);
932 GtkTreeViewColumn *priorityColumn = gtk_tree_view_column_new_with_attributes("",
933 priorityRenderer,
934 "text", TODO_PRIORITY_COLUMN_ENUM,
935 "cell-background-rgba",
936 TODO_BACKGROUND_COLOR_ENUM,
937 "cell-background-set",
938 TODO_BACKGROUND_COLOR_ENABLED_ENUM,
939 NULL);
940 gtk_tree_view_column_set_sort_column_id(priorityColumn, TODO_PRIORITY_COLUMN_ENUM);
941
942 GtkCellRenderer *noteRenderer = gtk_cell_renderer_pixbuf_new();
943 GtkTreeViewColumn *noteColumn = gtk_tree_view_column_new_with_attributes("",
944 noteRenderer,
945 "pixbuf", TODO_NOTE_COLUMN_ENUM,
946 "cell-background-rgba",
947 TODO_BACKGROUND_COLOR_ENUM,
948 "cell-background-set",
949 TODO_BACKGROUND_COLOR_ENABLED_ENUM,
950 NULL);
951 gtk_tree_view_column_set_sort_column_id(noteColumn, TODO_NOTE_COLUMN_ENUM);
952
953
954 GtkCellRenderer *checkRenderer = gtk_cell_renderer_toggle_new();
955
956 GtkTreeViewColumn *checkColumn = gtk_tree_view_column_new_with_attributes("", checkRenderer, "active",
957 TODO_CHECK_COLUMN_ENUM,
958 "cell-background-rgba",
959 TODO_BACKGROUND_COLOR_ENUM,
960 "cell-background-set",
961 TODO_BACKGROUND_COLOR_ENABLED_ENUM, NULL);
962 gtk_tree_view_insert_column(GTK_TREE_VIEW (todo_treeView), checkColumn, TODO_CHECK_COLUMN_ENUM);
963 gtk_tree_view_insert_column(GTK_TREE_VIEW (todo_treeView), priorityColumn, TODO_PRIORITY_COLUMN_ENUM);
964 gtk_tree_view_insert_column(GTK_TREE_VIEW (todo_treeView), noteColumn, TODO_NOTE_COLUMN_ENUM);
965 gtk_tree_view_insert_column(GTK_TREE_VIEW (todo_treeView), dateColumn, TODO_DATE_COLUMN_ENUM);
966 gtk_tree_view_insert_column(GTK_TREE_VIEW (todo_treeView), taskColumn, TODO_TEXT_COLUMN_ENUM);
967 gtk_tree_view_column_set_clickable(checkColumn, gtk_false());
968 gtk_tree_view_column_set_clickable(priorityColumn, gtk_false());
969 gtk_tree_view_column_set_clickable(noteColumn, gtk_false());
970 gtk_tree_view_column_set_clickable(dateColumn, gtk_false());
971 gtk_tree_view_column_set_clickable(taskColumn, gtk_false());
972 gtk_tree_view_column_set_sizing(checkColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
973 gtk_tree_view_column_set_sizing(dateColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
974 gtk_tree_view_column_set_sizing(priorityColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
975 gtk_tree_view_column_set_sizing(noteColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
976 gtk_tree_view_column_set_sizing(taskColumn, GTK_TREE_VIEW_COLUMN_FIXED);
977 return GTK_WIDGET(todo_treeView);
978 }
979
980 static void cb_todo_export_done(GtkWidget *widget, const char *filename) {
981 free_ToDoList(&export_todo_list);
982 if (widget != NULL && GTK_IS_TREE_VIEW(widget)) {
983 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(widget))));
984 }
985
986 set_pref(PREF_TODO_EXPORT_FILENAME, 0, filename, TRUE);
987 }
988
989 int todo_export(GtkWidget *window) {
990 int w, h, x, y;
991 char *type_text[] = {N_("Text"),
992 N_("CSV"),
993 N_("iCalendar"),
994 NULL};
995 int type_int[] = {EXPORT_TYPE_TEXT, EXPORT_TYPE_CSV, EXPORT_TYPE_ICALENDAR};
996
997 w = gdk_window_get_width(gtk_widget_get_window(window));
998 h = gdk_window_get_height(gtk_widget_get_window(window));
999 gdk_window_get_root_origin(gtk_widget_get_window(window), &x, &y);
1000
1001 w = gtk_paned_get_position(GTK_PANED(pane));
1002 x += 40;
1003
1004 export_gui(window,
1005 w, h, x, y, 5, sort_l,
1006 PREF_TODO_EXPORT_FILENAME,
1007 type_text,
1008 type_int,
1009 cb_todo_init_treeView,
1010 cb_todo_update_listStore,
1011 cb_todo_export_done,
1012 cb_todo_export_ok
1013 );
1014
1015 return EXIT_SUCCESS;
1016 }
1017
1018 /*
1019 * End Export Code
1020 */
1021
1022
1023 /* Find position of category in sorted category array
1024 * via its assigned category number */
1025 static int find_sort_cat_pos(int cat) {
1026 int i;
1027
1028 for (i = 0; i < NUM_TODO_CAT_ITEMS; i++) {
1029 if (sort_l[i].cat_num == cat) {
1030 return i;
1031 }
1032 }
1033
1034 return -1;
1035 }
1036
1037 /* Find a category's position in the category menu.
1038 * This is equal to the category number except for the Unfiled category.
1039 * The Unfiled category is always in the last position which changes as
1040 * the number of categories changes */
1041 static int find_menu_cat_pos(int cat) {
1042 int i;
1043
1044 if (cat != NUM_TODO_CAT_ITEMS - 1) {
1045 return cat;
1046 } else { /* Unfiled category */
1047 /* Count how many category entries are filled */
1048 for (i = 0; i < NUM_TODO_CAT_ITEMS; i++) {
1049 if (!sort_l[i].Pcat[0]) {
1050 return i;
1051 }
1052 }
1053 return 0;
1054 }
1055 }
1056
1057 gboolean deleteRecord(GtkTreeModel *model,
1058 GtkTreePath *path,
1059 GtkTreeIter *iter,
1060 gpointer data) {
1061 int *i = gtk_tree_path_get_indices(path);
1062 if (i[0] == row_selected) {
1063 MyToDo *mytodo = NULL;
1064 gtk_tree_model_get(model, iter, TODO_DATA_COLUMN_ENUM, &mytodo, -1);
1065 deleteTodo(mytodo, data);
1066 return TRUE;
1067 }
1068
1069 return FALSE;
1070
1071
1072 }
1073
1074 void deleteTodo(MyToDo *mtodo, gpointer data) {
1075
1076 int flag;
1077 int show_priv;
1078 long char_set;
1079
1080 if (mtodo < (MyToDo *) LIST_MIN_DATA) {
1081 return;
1082 }
1083
1084 /* Convert to Palm character set */
1085 get_pref(PREF_CHAR_SET, &char_set, NULL);
1086 if (char_set != CHAR_SET_LATIN1) {
1087 if (mtodo->todo.description)
1088 charset_j2p(mtodo->todo.description, (int) strlen(mtodo->todo.description) + 1, char_set);
1089 if (mtodo->todo.note)
1090 charset_j2p(mtodo->todo.note, (int) strlen(mtodo->todo.note) + 1, char_set);
1091 }
1092
1093 /* Do masking like Palm OS 3.5 */
1094 show_priv = show_privates(GET_PRIVATES);
1095 if ((show_priv != SHOW_PRIVATES) &&
1096 (mtodo->attrib & dlpRecAttrSecret)) {
1097 return;
1098 }
1099 /* End Masking */
1100 flag = GPOINTER_TO_INT(data);
1101 if ((flag == MODIFY_FLAG) || (flag == DELETE_FLAG)) {
1102 jp_logf(JP_LOG_DEBUG, "calling delete_pc_record\n");
1103 delete_pc_record(TODO, mtodo, flag);
1104 if (flag == DELETE_FLAG) {
1105 /* when we redraw we want to go to the line above the deleted one */
1106 if (row_selected > 0) {
1107 row_selected--;
1108 }
1109 }
1110 }
1111
1112 if (flag == DELETE_FLAG) {
1113 todo_redraw();
1114 }
1115 }
1116
1117 static void cb_delete_todo(GtkWidget *widget,
1118 gpointer data) {
1119 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), deleteRecord, data);
1120 return;
1121
1122 }
1123
1124 gboolean undeleteRecord(GtkTreeModel *model,
1125 GtkTreePath *path,
1126 GtkTreeIter *iter,
1127 gpointer data) {
1128 int *i = gtk_tree_path_get_indices(path);
1129 if (i[0] == row_selected) {
1130 MyToDo *mytodo = NULL;
1131 gtk_tree_model_get(model, iter, TODO_DATA_COLUMN_ENUM, &mytodo, -1);
1132 undeleteTodo(mytodo, data);
1133 return TRUE;
1134 }
1135
1136 return FALSE;
1137
1138
1139 }
1140
1141 gboolean printRecord(GtkTreeModel *model,
1142 GtkTreePath *path,
1143 GtkTreeIter *iter,
1144 gpointer data) {
1145 int *i = gtk_tree_path_get_indices(path);
1146 if (i[0] == row_selected) {
1147 MyToDo *mytodo = NULL;
1148 gtk_tree_model_get(model, iter, TODO_DATA_COLUMN_ENUM, &mytodo, -1);
1149 printTodo(mytodo, data);
1150 return TRUE;
1151 }
1152
1153 return FALSE;
1154
1155
1156 }
1157
1158 void undeleteTodo(MyToDo *mtodo, gpointer data) {
1159 int flag;
1160 int show_priv;
1161 if (mtodo < (MyToDo *) LIST_MIN_DATA) {
1162 return;
1163 }
1164
1165 /* Do masking like Palm OS 3.5 */
1166 show_priv = show_privates(GET_PRIVATES);
1167 if ((show_priv != SHOW_PRIVATES) &&
1168 (mtodo->attrib & dlpRecAttrSecret)) {
1169 return;
1170 }
1171 /* End Masking */
1172
1173 jp_logf(JP_LOG_DEBUG, "mtodo->unique_id = %d\n", mtodo->unique_id);
1174 jp_logf(JP_LOG_DEBUG, "mtodo->rt = %d\n", mtodo->rt);
1175
1176 flag = GPOINTER_TO_INT(data);
1177 if (flag == UNDELETE_FLAG) {
1178 if (mtodo->rt == DELETED_PALM_REC ||
1179 mtodo->rt == DELETED_PC_REC) {
1180 undelete_pc_record(TODO, mtodo, flag);
1181 }
1182 /* Possible later addition of undelete for modified records
1183 else if (mtodo->rt == MODIFIED_PALM_REC)
1184 {
1185 cb_add_new_record(widget, GINT_TO_POINTER(COPY_FLAG));
1186 }
1187 */
1188 }
1189
1190 todo_redraw();
1191 }
1192
1193 static void cb_undelete_todo(GtkWidget *widget,
1194 gpointer data) {
1195
1196 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), undeleteRecord, data);
1197 return;
1198
1199 }
1200
1201 static void cb_cancel(GtkWidget *widget, gpointer data) {
1202 set_new_button_to(CLEAR_FLAG);
1203 todo_refresh();
1204 }
1205
1206 static void cb_edit_cats(GtkWidget *widget, gpointer data) {
1207 struct ToDoAppInfo ai;
1208 char db_name[FILENAME_MAX];
1209 char pdb_name[FILENAME_MAX];
1210 char full_name[FILENAME_MAX];
1211 unsigned char buffer[65536];
1212 int num;
1213 size_t size;
1214 void *buf;
1215 struct pi_file *pf;
1216 #ifdef ENABLE_MANANA
1217 long ivalue;
1218 #endif
1219
1220 jp_logf(JP_LOG_DEBUG, "cb_edit_cats\n");
1221
1222 #ifdef ENABLE_MANANA
1223 get_pref(PREF_MANANA_MODE, &ivalue, NULL);
1224 if (ivalue) {
1225 strcpy(pdb_name, "MananaDB.pdb");
1226 strcpy(db_name, "MananaDB");
1227 } else {
1228 strcpy(pdb_name, "ToDoDB.pdb");
1229 strcpy(db_name, "ToDoDB");
1230 }
1231 #else
1232 strcpy(pdb_name, "ToDoDB.pdb");
1233 strcpy(db_name, "ToDoDB");
1234 #endif
1235
1236 get_home_file_name(pdb_name, full_name, sizeof(full_name));
1237
1238 buf = NULL;
1239 memset(&ai, 0, sizeof(ai));
1240
1241 pf = pi_file_open(full_name);
1242 pi_file_get_app_info(pf, &buf, &size);
1243
1244 num = unpack_ToDoAppInfo(&ai, buf, size);
1245 if (num <= 0) {
1246 jp_logf(JP_LOG_WARN, _("Error reading file: %s\n"), pdb_name);
1247 return;
1248 }
1249
1250 pi_file_close(pf);
1251
1252 edit_cats(widget, db_name, &(ai.category));
1253
1254 size = (size_t) pack_ToDoAppInfo(&ai, buffer, sizeof(buffer));
1255
1256 pdb_file_write_app_block(db_name, buffer, (int) size);
1257
1258 cb_app_button(NULL, GINT_TO_POINTER(REDRAW));
1259 }
1260
1261 static void cb_category(GtkComboBox *item, int selection) {
1262 int b;
1263 if (!item) return;
1264 if (gtk_combo_box_get_active(GTK_COMBO_BOX(item)) < 0) {
1265 return;
1266 }
1267 int selectedItem = get_selected_category_from_combo_box(item);
1268 if (selectedItem == -1) {
1269 return;
1270 }
1271
1272 if (todo_category == selectedItem) { return; }
1273
1274 b = dialog_save_changed_record_with_cancel(pane, record_changed);
1275 if (b == DIALOG_SAID_1) { /* Cancel */
1276 int index, index2;
1277
1278 if (todo_category == CATEGORY_ALL) {
1279 index = 0;
1280 index2 = 0;
1281 } else {
1282 index = find_sort_cat_pos(todo_category);
1283 index2 = find_menu_cat_pos(index) + 1;
1284 index += 1;
1285 }
1286
1287 if (index < 0) {
1288 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1289 } else {
1290 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), index2);
1291 }
1292
1293 return;
1294 }
1295 if (b == DIALOG_SAID_3) { /* Save */
1296 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1297 }
1298
1299 if (selectedItem == CATEGORY_EDIT) {
1300 cb_edit_cats(GTK_WIDGET(item), NULL);
1301 } else {
1302 todo_category = selectedItem;
1303 }
1304 row_selected = 0;
1305 jp_logf(JP_LOG_DEBUG, "todo_category = %d\n", todo_category);
1306 todo_update_liststore(listStore, category_menu1, &glob_todo_list, todo_category, TRUE);
1307 }
1308
1309 static void cb_check_button_no_due_date(GtkWidget *widget, gpointer data) {
1310 long till_due;
1311 struct tm *now;
1312 time_t ltime;
1313
1314 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
1315 update_due_button(due_date_button, NULL);
1316 } else {
1317 time(<ime);
1318 now = localtime(<ime);
1319 memcpy(&due_date, now, sizeof(struct tm));
1320
1321 get_pref(PREF_TODO_DAYS_TILL_DUE, &till_due, NULL);
1322 add_days_to_date(&due_date, (int) till_due);
1323
1324 update_due_button(due_date_button, &due_date);
1325 }
1326 }
1327
1328 static int todo_clear_details(void) {
1329 time_t ltime;
1330 struct tm *now;
1331 int new_cat;
1332 int sorted_position;
1333 long default_due, till_due;
1334
1335 time(<ime);
1336 now = localtime(<ime);
1337
1338 /* Need to disconnect these signals first */
1339 connect_changed_signals(DISCONNECT_SIGNALS);
1340
1341 gtk_widget_freeze_child_notify(todo_desc);
1342 gtk_widget_freeze_child_notify(todo_note);
1343
1344 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(todo_desc_buffer), "", -1);
1345 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(todo_note_buffer), "", -1);
1346
1347 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button_todo[0]), TRUE);
1348
1349 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(todo_completed_checkbox), FALSE);
1350
1351 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private_checkbox), FALSE);
1352
1353 get_pref(PREF_TODO_DAYS_DUE, &default_due, NULL);
1354 get_pref(PREF_TODO_DAYS_TILL_DUE, &till_due, NULL);
1355 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(todo_no_due_date_checkbox),
1356 !default_due);
1357
1358 memcpy(&due_date, now, sizeof(struct tm));
1359
1360 if (default_due) {
1361 add_days_to_date(&due_date, (int) till_due);
1362 update_due_button(due_date_button, &due_date);
1363 } else {
1364 update_due_button(due_date_button, NULL);
1365 }
1366
1367 gtk_widget_thaw_child_notify(todo_desc);
1368 gtk_widget_thaw_child_notify(todo_note);
1369
1370 if (todo_category == CATEGORY_ALL) {
1371 new_cat = 0;
1372 } else {
1373 new_cat = todo_category;
1374 }
1375 sorted_position = find_sort_cat_pos(new_cat);
1376 if (sorted_position < 0) {
1377 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1378 } else {
1379 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu2), find_menu_cat_pos(sorted_position));
1380 }
1381
1382 set_new_button_to(CLEAR_FLAG);
1383 connect_changed_signals(CONNECT_SIGNALS);
1384
1385 return EXIT_SUCCESS;
1386 }
1387
1388 static int todo_get_details(struct ToDo *new_todo, unsigned char *attrib) {
1389 int i;
1390 GtkTextIter start_iter;
1391 GtkTextIter end_iter;
1392
1393 new_todo->indefinite = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(todo_no_due_date_checkbox)));
1394 if (!(new_todo->indefinite)) {
1395 new_todo->due.tm_mon = due_date.tm_mon;
1396 new_todo->due.tm_mday = due_date.tm_mday;
1397 new_todo->due.tm_year = due_date.tm_year;
1398 jp_logf(JP_LOG_DEBUG, "todo_get_details: setting due date=%d/%d/%d\n", new_todo->due.tm_mon,
1399 new_todo->due.tm_mday, new_todo->due.tm_year);
1400 } else {
1401 memset(&(new_todo->due), 0, sizeof(new_todo->due));
1402 }
1403 new_todo->priority = 1;
1404 for (i = 0; i < NUM_TODO_PRIORITIES; i++) {
1405 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio_button_todo[i]))) {
1406 new_todo->priority = i + 1;
1407 break;
1408 }
1409 }
1410 new_todo->complete = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(todo_completed_checkbox)));
1411 /* Can there be an entry with no description? */
1412 /* Yes, but the Palm Pilot gui doesn't allow it to be entered on the Palm, */
1413 /* it will show it though. I allow it. */
1414 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(todo_desc_buffer),
1415 &start_iter, &end_iter);
1416 new_todo->description = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(todo_desc_buffer),
1417 &start_iter, &end_iter, TRUE);
1418 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(todo_note_buffer),
1419 &start_iter, &end_iter);
1420 new_todo->note = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(todo_note_buffer),
1421 &start_iter, &end_iter, TRUE);
1422 if (new_todo->note[0] == '\0') {
1423 free(new_todo->note);
1424 new_todo->note = NULL;
1425 }
1426
1427 if (GTK_IS_WIDGET(category_menu2)) {
1428 *attrib = get_selected_category_from_combo_box(GTK_COMBO_BOX(category_menu2));
1429 }
1430 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(private_checkbox))) {
1431 *attrib |= dlpRecAttrSecret;
1432 }
1433
1434 #ifdef JPILOT_DEBUG
1435 jp_logf(JP_LOG_DEBUG, "attrib = %d\n", *attrib);
1436 jp_logf(JP_LOG_DEBUG, "indefinite=%d\n",new_todo->indefinite);
1437 if (!new_todo->indefinite) {
1438 jp_logf(JP_LOG_DEBUG, "due: %d/%d/%d\n",new_todo->due.tm_mon,
1439 new_todo->due.tm_mday,
1440 new_todo->due.tm_year);
1441 }
1442 jp_logf(JP_LOG_DEBUG, "priority=%d\n",new_todo->priority);
1443 jp_logf(JP_LOG_DEBUG, "complete=%d\n",new_todo->complete);
1444 jp_logf(JP_LOG_DEBUG, "description=[%s]\n",new_todo->description);
1445 jp_logf(JP_LOG_DEBUG, "note=[%s]\n",new_todo->note);
1446 #endif
1447
1448 return EXIT_SUCCESS;
1449 }
1450
1451 gboolean
1452 addNewRecord(GtkTreeModel *model,
1453 GtkTreePath *path,
1454 GtkTreeIter *iter,
1455 gpointer data) {
1456
1457 int *i = gtk_tree_path_get_indices(path);
1458 if (i[0] == row_selected) {
1459 MyToDo *mytodo = NULL;
1460 gtk_tree_model_get(model, iter, TODO_DATA_COLUMN_ENUM, &mytodo, -1);
1461 addNewRecordToDataStructure(mytodo, data);
1462 return TRUE;
1463 }
1464 return FALSE;
1465
1466
1467 }
1468
1469 void addNewRecordToDataStructure(MyToDo *mtodo, gpointer data) {
1470
1471 struct ToDo new_todo;
1472 unsigned char attrib = 0;
1473 int flag;
1474 int show_priv;
1475 unsigned int unique_id;
1476 flag = GPOINTER_TO_INT(data);
1477 unique_id = 0;
1478
1479
1480 //while(gtk_tree_path_)
1481 /* Do masking like Palm OS 3.5 */
1482 if ((flag == COPY_FLAG) || (flag == MODIFY_FLAG)) {
1483 show_priv = show_privates(GET_PRIVATES);
1484 if (mtodo < (MyToDo *) LIST_MIN_DATA) {
1485 return;
1486 }
1487 if ((show_priv != SHOW_PRIVATES) &&
1488 (mtodo->attrib & dlpRecAttrSecret)) {
1489 return;
1490 }
1491 }
1492 /* End Masking */
1493 if (flag == CLEAR_FLAG) {
1494 /* Clear button was hit */
1495 todo_clear_details();
1496 connect_changed_signals(DISCONNECT_SIGNALS);
1497 set_new_button_to(NEW_FLAG);
1498 gtk_widget_grab_focus(GTK_WIDGET(todo_desc));
1499 return;
1500 }
1501 if ((flag != NEW_FLAG) && (flag != MODIFY_FLAG) && (flag != COPY_FLAG)) {
1502 return;
1503 }
1504 if (flag == MODIFY_FLAG) {
1505
1506 unique_id = mtodo->unique_id;
1507 if (mtodo < (MyToDo *) LIST_MIN_DATA) {
1508 return;
1509 }
1510 if ((mtodo->rt == DELETED_PALM_REC) ||
1511 (mtodo->rt == DELETED_PC_REC) ||
1512 (mtodo->rt == MODIFIED_PALM_REC)) {
1513 jp_logf(JP_LOG_INFO, _("You can't modify a record that is deleted\n"));
1514 return;
1515 }
1516 }
1517 todo_get_details(&new_todo, &attrib);
1518
1519 set_new_button_to(CLEAR_FLAG);
1520
1521 if (flag == MODIFY_FLAG) {
1522 cb_delete_todo(NULL, data);
1523 if ((mtodo->rt == PALM_REC) || (mtodo->rt == REPLACEMENT_PALM_REC)) {
1524 pc_todo_write(&new_todo, REPLACEMENT_PALM_REC, attrib, &unique_id);
1525 } else {
1526 unique_id = 0;
1527 pc_todo_write(&new_todo, NEW_PC_REC, attrib, &unique_id);
1528 }
1529 } else {
1530 unique_id = 0;
1531 pc_todo_write(&new_todo, NEW_PC_REC, attrib, &unique_id);
1532 }
1533 free_ToDo(&new_todo);
1534
1535 /* Don't return to modified record if search gui active */
1536 if (!glob_find_id) {
1537 glob_find_id = unique_id;
1538 }
1539 todo_redraw();
1540
1541
1542 return;
1543 }
1544
1545 static void cb_add_new_record(GtkWidget *widget, gpointer data) {
1546 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL) != 0) {
1547 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), addNewRecord, data);
1548 } else {
1549 //no records exist in category yet.
1550 addNewRecordToDataStructure(NULL, data);
1551 }
1552 }
1553
1554 /* Do masking like Palm OS 3.5 */
1555 static void clear_mytodos(MyToDo *mtodo) {
1556 mtodo->unique_id = 0;
1557 mtodo->attrib = (unsigned char) (mtodo->attrib & 0xF8);
1558 mtodo->todo.complete = 0;
1559 mtodo->todo.priority = 1;
1560 mtodo->todo.indefinite = 1;
1561 if (mtodo->todo.description) {
1562 free(mtodo->todo.description);
1563 mtodo->todo.description = strdup("");
1564 }
1565 if (mtodo->todo.note) {
1566 free(mtodo->todo.note);
1567 mtodo->todo.note = strdup("");
1568 }
1569
1570 return;
1571 }
1572
1573 /* End Masking */
1574
1575
1576 static gint sortNoteColumn(GtkTreeModel *model,
1577 GtkTreeIter *left,
1578 GtkTreeIter *right,
1579 gpointer columnId) {
1580 gint sortcol = GPOINTER_TO_INT(columnId);
1581 gint ret = 0;
1582 switch (sortcol) {
1583 case TODO_NOTE_COLUMN_ENUM: {
1584 ret = compareNoteColumn(model, left, right);
1585 }
1586 break;
1587 default:
1588 break;
1589 }
1590 return ret;
1591
1592 }
1593
1594 gint compareCheckColumn(GtkTreeModel *model, GtkTreeIter *left, GtkTreeIter *right) {
1595 gint ret;
1596 gboolean *name1, *name2;
1597
1598 gtk_tree_model_get(GTK_TREE_MODEL(model), left, TODO_CHECK_COLUMN_ENUM, &name1, -1);
1599 gtk_tree_model_get(GTK_TREE_MODEL(model), right, TODO_CHECK_COLUMN_ENUM, &name2, -1);
1600 if (!name1 && name2) {
1601 ret = 1;
1602 } else if (name1 && !name2) {
1603 ret = -1;
1604 } else {
1605 ret = 0;
1606 }
1607 return ret;
1608 }
1609
1610 gint compareNoteColumn(GtkTreeModel *model, GtkTreeIter *left, GtkTreeIter *right) {
1611 gint ret;
1612 GdkPixbuf *note1, *note2;
1613
1614 gtk_tree_model_get(GTK_TREE_MODEL(model), left, TODO_NOTE_COLUMN_ENUM, ¬e1, -1);
1615 gtk_tree_model_get(GTK_TREE_MODEL(model), right, TODO_NOTE_COLUMN_ENUM, ¬e2, -1);
1616
1617 if (note1 == NULL && note2 == NULL) {
1618 ret = 0;
1619 } else if (note1 == NULL && note2 != NULL) {
1620 ret = -1;
1621 } else if (note1 != NULL && note2 == NULL) {
1622 ret = 1;
1623 } else {
1624 ret = 0;
1625 }
1626 return ret;
1627 }
1628
1629
1630 static void column_clicked_cb(GtkTreeViewColumn *column) {
1631 column_selected = gtk_tree_view_column_get_sort_column_id(column);
1632
1633 }
1634
1635 static void checkedCallBack(GtkCellRendererToggle *renderer, gchar *path, GtkListStore *model) {
1636 GtkTreeIter iter;
1637 gboolean active;
1638 MyToDo *mtodo;
1639 active = gtk_cell_renderer_toggle_get_active(renderer);
1640
1641 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL (model), &iter, path);
1642 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, TODO_DATA_COLUMN_ENUM, &mtodo, -1);
1643 if (active) {
1644 // gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), 0, 0);
1645 gtk_list_store_set(GTK_LIST_STORE (model), &iter, TODO_CHECK_COLUMN_ENUM, FALSE, -1);
1646 mtodo->todo.complete = 0;
1647 } else {
1648 // gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), 0.5, 0.5);
1649 gtk_list_store_set(GTK_LIST_STORE (model), &iter, TODO_CHECK_COLUMN_ENUM, TRUE, -1);
1650 mtodo->todo.complete = 1;
1651 }
1652 }
1653
1654
1655 static gboolean handleRowSelection(GtkTreeSelection *selection,
1656 GtkTreeModel *model,
1657 GtkTreePath *path,
1658 gboolean path_currently_selected,
1659 gpointer userdata) {
1660 GtkTreeIter iter;
1661
1662 struct ToDo *todo;
1663 MyToDo *mtodo;
1664 int b;
1665 int index, sorted_position;
1666 //unsigned int unique_id = 0;
1667 time_t ltime;
1668 struct tm *now;
1669
1670 if ((gtk_tree_model_get_iter(model, &iter, path)) && (!path_currently_selected)) {
1671
1672 int *i = gtk_tree_path_get_indices(path);
1673 row_selected = i[0];
1674 gtk_tree_model_get(model, &iter, TODO_DATA_COLUMN_ENUM, &mtodo, -1);
1675 if ((record_changed == MODIFY_FLAG) || (record_changed == NEW_FLAG)) {
1676 //if (mtodo != NULL) {
1677 // unique_id = mtodo->unique_id;
1678 //}
1679 // We need to turn this "scroll with mouse held down" thing off
1680 button_set_for_motion(0);
1681 b = dialog_save_changed_record_with_cancel(pane, record_changed);
1682 if (b == DIALOG_SAID_1) { /* Cancel */
1683 return TRUE;
1684 }
1685 if (b == DIALOG_SAID_3) { /* Save */
1686 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1687 }
1688 set_new_button_to(CLEAR_FLAG);
1689
1690 return TRUE;
1691 }
1692 time(<ime);
1693 now = localtime(<ime);
1694 if (mtodo == NULL) {
1695 return TRUE;
1696 }
1697 if (mtodo->rt == DELETED_PALM_REC ||
1698 (mtodo->rt == DELETED_PC_REC))
1699 /* Possible later addition of undelete code for modified deleted records
1700 || mtodo->rt == MODIFIED_PALM_REC
1701 */
1702 {
1703 set_new_button_to(UNDELETE_FLAG);
1704 } else {
1705 set_new_button_to(CLEAR_FLAG);
1706 }
1707 connect_changed_signals(DISCONNECT_SIGNALS);
1708 //Not sure this is really needed as about 10 lines up does the same check.
1709 if (mtodo == NULL) {
1710 return TRUE;
1711 }
1712 todo = &(mtodo->todo);
1713 gtk_widget_freeze_child_notify(todo_desc);
1714 gtk_widget_freeze_child_notify(todo_note);
1715 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(todo_desc_buffer), "", -1);
1716 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(todo_note_buffer), "", -1);
1717 index = mtodo->attrib & 0x0F;
1718 sorted_position = find_sort_cat_pos(index);
1719 int pos = findSortedPostion(sorted_position, GTK_COMBO_BOX(category_menu2));
1720 if (pos != sorted_position && index != 0) {
1721 /* Illegal category */
1722 jp_logf(JP_LOG_DEBUG, "Category is not legal\n");
1723 index = sorted_position = 0;
1724 sorted_position = find_sort_cat_pos(index);
1725 }
1726 if (sorted_position < 0) {
1727 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1728 }
1729 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu2), find_menu_cat_pos(sorted_position));
1730
1731 if (todo->description) {
1732 if (todo->description[0]) {
1733 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(todo_desc_buffer), todo->description, -1);
1734 }
1735 }
1736
1737 if (todo->note) {
1738 if (todo->note[0]) {
1739 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(todo_note_buffer), todo->note, -1);
1740 }
1741 }
1742
1743 if ((todo->priority < 1) || (todo->priority > 5)) {
1744 jp_logf(JP_LOG_WARN, _("Priority out of range\n"));
1745 } else {
1746 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button_todo[todo->priority - 1]), TRUE);
1747 }
1748
1749 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(todo_completed_checkbox), todo->complete);
1750
1751 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private_checkbox),
1752 mtodo->attrib & dlpRecAttrSecret);
1753
1754 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(todo_no_due_date_checkbox),
1755 todo->indefinite);
1756 if (!todo->indefinite) {
1757 update_due_button(due_date_button, &(todo->due));
1758 due_date.tm_mon = todo->due.tm_mon;
1759 due_date.tm_mday = todo->due.tm_mday;
1760 due_date.tm_year = todo->due.tm_year;
1761 } else {
1762 update_due_button(due_date_button, NULL);
1763 due_date.tm_mon = now->tm_mon;
1764 due_date.tm_mday = now->tm_mday;
1765 due_date.tm_year = now->tm_year;
1766 }
1767
1768 gtk_widget_thaw_child_notify(todo_desc);
1769 gtk_widget_thaw_child_notify(todo_note);
1770 /* If they have clicked on the checkmark box then do a modify */
1771 /* if (column == 0) {
1772 gtk_signal_emit_by_name(G_OBJECT(todo_completed_checkbox), "clicked");
1773 gtk_signal_emit_by_name(G_OBJECT(apply_record_button), "clicked");
1774 }*/
1775 connect_changed_signals(CONNECT_SIGNALS);
1776 return TRUE;
1777 }
1778 // gtk_tree_model_get(model, &iter, TODO_TEXT_COLUMN_ENUM, &name, -1);
1779
1780
1781 return TRUE; /* allow selection state to change */
1782 }
1783
1784 static gboolean cb_key_pressed_left_side(GtkWidget *widget,
1785 GdkEventKey *event,
1786 gpointer next_widget) {
1787 GtkTextBuffer *text_buffer;
1788 GtkTextIter iter;
1789
1790 if (event->keyval == GDK_KEY_Return) {
1791 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1792 gtk_widget_grab_focus(GTK_WIDGET(next_widget));
1793 /* Position cursor at start of text */
1794 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(next_widget));
1795 gtk_text_buffer_get_start_iter(text_buffer, &iter);
1796 gtk_text_buffer_place_cursor(text_buffer, &iter);
1797 return TRUE;
1798 }
1799
1800 return FALSE;
1801 }
1802
1803 static gboolean cb_key_pressed_right_side(GtkWidget *widget,
1804 GdkEventKey *event,
1805 gpointer data) {
1806 if ((event->keyval == GDK_KEY_Return) && (event->state & GDK_SHIFT_MASK)) {
1807 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1808 gtk_widget_grab_focus(GTK_WIDGET(treeView));
1809 return TRUE;
1810 }
1811 /* Call external editor for note text */
1812 if (data != NULL &&
1813 (event->keyval == GDK_KEY_e) && (event->state & GDK_CONTROL_MASK)) {
1814 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1815
1816 /* Get current text and place in temporary file */
1817 GtkTextIter start_iter;
1818 GtkTextIter end_iter;
1819 char *text_out;
1820
1821 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(todo_note_buffer),
1822 &start_iter, &end_iter);
1823 text_out = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(todo_note_buffer),
1824 &start_iter, &end_iter, TRUE);
1825
1826
1827 char tmp_fname[] = "jpilot.XXXXXX";
1828 int tmpfd = mkstemp(tmp_fname);
1829 if (tmpfd < 0) {
1830 jp_logf(JP_LOG_WARN, _("Could not get temporary file name\n"));
1831 if (text_out)
1832 free(text_out);
1833 return TRUE;
1834 }
1835
1836 FILE *fptr = fdopen(tmpfd, "w");
1837 if (!fptr) {
1838 jp_logf(JP_LOG_WARN, _("Could not open temporary file for external editor\n"));
1839 if (text_out)
1840 free(text_out);
1841 return TRUE;
1842 }
1843 fwrite(text_out, strlen(text_out), 1, fptr);
1844 fwrite("\n", 1, 1, fptr);
1845 fclose(fptr);
1846
1847 /* Call external editor */
1848 char command[1024];
1849 const char *ext_editor;
1850
1851 get_pref(PREF_EXTERNAL_EDITOR, NULL, &ext_editor);
1852 if (!ext_editor) {
1853 jp_logf(JP_LOG_INFO, "External Editor command empty\n");
1854 if (text_out)
1855 free(text_out);
1856 return TRUE;
1857 }
1858
1859 if ((strlen(ext_editor) + strlen(tmp_fname) + 1) > sizeof(command)) {
1860 jp_logf(JP_LOG_WARN, _("External editor command too long to execute\n"));
1861 if (text_out)
1862 free(text_out);
1863 return TRUE;
1864 }
1865 g_snprintf(command, sizeof(command), "%s %s", ext_editor, tmp_fname);
1866
1867 /* jp_logf(JP_LOG_STDOUT|JP_LOG_FILE, _("executing command = [%s]\n"), command); */
1868 if (system(command) == -1) {
1869 /* Read data back from temporary file into memo */
1870 char text_in[0xFFFF];
1871 size_t bytes_read;
1872
1873 fptr = fopen(tmp_fname, "rb");
1874 if (!fptr) {
1875 jp_logf(JP_LOG_WARN, _("Could not open temporary file from external editor\n"));
1876 return TRUE;
1877 }
1878 bytes_read = fread(text_in, 1, 0xFFFF, fptr);
1879 fclose(fptr);
1880 unlink(tmp_fname);
1881
1882 text_in[--bytes_read] = '\0'; /* Strip final newline */
1883 /* Only update text if it has changed */
1884 if (strcmp(text_out, text_in)) {
1885 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(todo_note_buffer),
1886 text_in, -1);
1887 }
1888 }
1889
1890 if (text_out)
1891 free(text_out);
1892
1893 return TRUE;
1894 } /* End of external editor if */
1895
1896 return FALSE;
1897 }
1898
1899 void todo_liststore_clear(GtkListStore *pListStore) {
1900 gtk_list_store_clear(pListStore);
1901
1902 }
1903
1904 static gboolean cb_key_pressed_tab(GtkWidget *widget,
1905 GdkEventKey *event,
1906 gpointer next_widget) {
1907 GtkTextIter cursor_pos_iter;
1908 GtkTextBuffer *text_buffer;
1909
1910 if (event->keyval == GDK_KEY_Tab) {
1911 /* See if they are at the end of the text */
1912 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
1913 gtk_text_buffer_get_iter_at_mark(text_buffer, &cursor_pos_iter, gtk_text_buffer_get_insert(text_buffer));
1914 if (gtk_text_iter_is_end(&cursor_pos_iter)) {
1915 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1916 gtk_widget_grab_focus(GTK_WIDGET(next_widget));
1917 return TRUE;
1918 }
1919 }
1920 return FALSE;
1921 }
1922
1923 static gboolean cb_key_pressed_shift_tab(GtkWidget *widget,
1924 GdkEventKey *event,
1925 gpointer next_widget) {
1926 if (event->keyval == GDK_KEY_ISO_Left_Tab) {
1927 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1928 gtk_widget_grab_focus(GTK_WIDGET(next_widget));
1929 return TRUE;
1930 }
1931 return FALSE;
1932 }
1933
1934 /* This redraws the treeView and goes back to the same line number */
1935 static int todo_redraw(void) {
1936 todo_update_liststore(listStore, category_menu1, &glob_todo_list, todo_category, TRUE);
1937 return EXIT_SUCCESS;
1938 }
1939
1940 int todo_cycle_cat(void) {
1941 int b;
1942 int i, new_cat;
1943
1944 b = dialog_save_changed_record(pane, record_changed);
1945 if (b == DIALOG_SAID_2) {
1946 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1947 }
1948
1949 if (todo_category == CATEGORY_ALL) {
1950 new_cat = -1;
1951 } else {
1952 new_cat = find_sort_cat_pos(todo_category);
1953 }
1954
1955 for (i = 0; i < NUM_TODO_CAT_ITEMS; i++) {
1956 new_cat++;
1957 if (new_cat >= NUM_TODO_CAT_ITEMS) {
1958 todo_category = CATEGORY_ALL;
1959 break;
1960 }
1961 if ((sort_l[new_cat].Pcat) && (sort_l[new_cat].Pcat[0])) {
1962 todo_category = sort_l[new_cat].cat_num;
1963 break;
1964 }
1965 }
1966
1967 row_selected = 0;
1968
1969 return EXIT_SUCCESS;
1970 }
1971
1972 int todo_refresh(void) {
1973 int index, index2;
1974
1975 if (glob_find_id) {
1976 todo_category = CATEGORY_ALL;
1977 }
1978 if (todo_category == CATEGORY_ALL) {
1979 index = 0;
1980 index2 = 0;
1981 } else {
1982 index = find_sort_cat_pos(todo_category);
1983 index2 = find_menu_cat_pos(index) + 1;
1984 index += 1;
1985 }
1986 todo_update_liststore(listStore, category_menu1, &glob_todo_list, todo_category, TRUE);
1987 if (index < 0) {
1988 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1989 } else {
1990 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), index2);
1991 }
1992
1993 return EXIT_SUCCESS;
1994 }
1995
1996 void todo_update_liststore(GtkListStore *pListStore, GtkWidget *tooltip_widget,
1997 ToDoList **todo_list, int category, int main) {
1998 GtkTreeIter iter;
1999 int num_entries, entries_shown;
2000 char str[50];
2001 GdkPixbuf *pixbuf_note;
2002 GdkPixbuf *pixbuf_check;
2003 GdkPixbuf *pixbuf_checked;
2004 gboolean checkColumnDisplay = gtk_true();
2005 GdkPixbuf *noteColumnDisplay;
2006 ToDoList *temp_todo;
2007 char dateDisplay[50];
2008 char priorityDisplay[50];
2009 char descriptionDisplay[TODO_MAX_COLUMN_LEN + 2];
2010 const char *svalue;
2011 long hide_completed, hide_not_due;
2012 long show_tooltips;
2013 int show_priv;
2014 time_t ltime;
2015 struct tm *now, *due;
2016 int comp_now, comp_due;
2017
2018 free_ToDoList(todo_list);
2019
2020 /* Need to get all records including private ones for the tooltips calculation */
2021 num_entries = get_todos2(todo_list, SORT_ASCENDING, 2, 2, 1, 1, CATEGORY_ALL);
2022
2023 /* Start by clearing existing entry if in main window */
2024 if (main) {
2025 todo_clear_details();
2026 GtkTreeSelection *treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
2027 gtk_tree_selection_set_select_function(treeSelection, NULL, NULL, NULL);
2028 }
2029 todo_liststore_clear(pListStore);
2030
2031
2032 /* Collect preferences and constant pixmaps for loop */
2033 get_pref(PREF_TODO_HIDE_COMPLETED, &hide_completed, NULL);
2034 get_pref(PREF_TODO_HIDE_NOT_DUE, &hide_not_due, NULL);
2035 show_priv = show_privates(GET_PRIVATES);
2036 get_pixbufs(PIXMAP_NOTE, &pixbuf_note);
2037 get_pixbufs(PIXMAP_BOX_CHECK, &pixbuf_check);
2038 get_pixbufs(PIXMAP_BOX_CHECKED, &pixbuf_checked);
2039 /*#ifdef __APPLE__
2040 mask_note = NULL;
2041 mask_check = NULL;
2042 mask_checked = NULL;
2043 #endif
2044 */
2045 /* Current time used for calculating overdue items */
2046 time(<ime);
2047 now = localtime(<ime);
2048 comp_now = now->tm_year * 380 + now->tm_mon * 31 + now->tm_mday - 1;
2049
2050 entries_shown = 0;
2051 for (temp_todo = *todo_list; temp_todo; temp_todo = temp_todo->next) {
2052
2053 if (((temp_todo->mtodo.attrib & 0x0F) != category) &&
2054 category != CATEGORY_ALL) {
2055 continue;
2056 }
2057
2058 /* Do masking like Palm OS 3.5 */
2059 if ((show_priv == MASK_PRIVATES) &&
2060 (temp_todo->mtodo.attrib & dlpRecAttrSecret)) {
2061 gtk_list_store_append(pListStore, &iter);
2062 gtk_list_store_set(pListStore, &iter,
2063 TODO_CHECK_COLUMN_ENUM, "---",
2064 TODO_PRIORITY_COLUMN_ENUM, "---",
2065 TODO_TEXT_COLUMN_ENUM, "--------------------",
2066 TODO_DATE_COLUMN_ENUM, "----------",
2067 TODO_DATA_COLUMN_ENUM, &(temp_todo->mtodo),
2068 -1);
2069 clear_mytodos(&temp_todo->mtodo);
2070 entries_shown++;
2071 continue;
2072 }
2073 /* End Masking */
2074
2075 /* Allow a record found through search window to temporarily be
2076 displayed even if it would normally be hidden by option settings */
2077 if (!glob_find_id || (glob_find_id != temp_todo->mtodo.unique_id)) {
2078 /* Hide the completed records if need be */
2079 if (hide_completed && temp_todo->mtodo.todo.complete) {
2080 continue;
2081 }
2082
2083 /* Hide the not due yet records if need be */
2084 if ((hide_not_due) && (!(temp_todo->mtodo.todo.indefinite))) {
2085 due = &(temp_todo->mtodo.todo.due);
2086 comp_due = due->tm_year * 380 + due->tm_mon * 31 + due->tm_mday - 1;
2087 if (comp_due > comp_now) {
2088 continue;
2089 }
2090 }
2091 }
2092
2093 /* Hide the private records if need be */
2094 if ((show_priv != SHOW_PRIVATES) &&
2095 (temp_todo->mtodo.attrib & dlpRecAttrSecret)) {
2096 continue;
2097 }
2098
2099
2100
2101 /* Put a checkbox or checked checkbox pixmap up */
2102 if (temp_todo->mtodo.todo.complete > 0) {
2103 checkColumnDisplay = TRUE;
2104 } else {
2105 checkColumnDisplay = FALSE;
2106 }
2107
2108 /* Print the priority number */
2109 sprintf(priorityDisplay, "%d", temp_todo->mtodo.todo.priority);
2110
2111 /* Put a note pixmap up */
2112 if (temp_todo->mtodo.todo.note[0]) {
2113 noteColumnDisplay = pixbuf_note;
2114 } else {
2115 noteColumnDisplay = NULL;
2116 }
2117
2118 /* Print the due date */
2119 if (!temp_todo->mtodo.todo.indefinite) {
2120 get_pref(PREF_SHORTDATE, NULL, &svalue);
2121 strftime(dateDisplay, sizeof(dateDisplay), svalue, &(temp_todo->mtodo.todo.due));
2122 } else {
2123 sprintf(dateDisplay, _("No date"));
2124 }
2125
2126 /* Print the todo text */
2127 lstrncpy_remove_cr_lfs(descriptionDisplay, temp_todo->mtodo.todo.description, TODO_MAX_COLUMN_LEN);
2128 gtk_list_store_append(pListStore, &iter);
2129
2130 GdkRGBA bgColor;
2131 gboolean showBgColor = FALSE;
2132 GdkRGBA fgColor;
2133 gboolean showFgColor = FALSE;
2134 switch (temp_todo->mtodo.rt) {
2135 case NEW_PC_REC:
2136 case REPLACEMENT_PALM_REC:
2137
2138 bgColor = get_color(LIST_NEW_RED, LIST_NEW_GREEN, LIST_NEW_BLUE);
2139 showBgColor = TRUE;
2140 break;
2141 case DELETED_PALM_REC:
2142 case DELETED_PC_REC:
2143
2144 bgColor = get_color(LIST_DEL_RED, LIST_DEL_GREEN, LIST_DEL_BLUE);
2145 showBgColor = TRUE;
2146 break;
2147 case MODIFIED_PALM_REC:
2148
2149 bgColor = get_color(LIST_MOD_RED, LIST_MOD_GREEN, LIST_MOD_BLUE);
2150 showBgColor = TRUE;
2151 break;
2152 default:
2153 if (temp_todo->mtodo.attrib & dlpRecAttrSecret) {
2154
2155 bgColor = get_color(LIST_PRIVATE_RED, LIST_PRIVATE_GREEN, LIST_PRIVATE_BLUE);
2156 showBgColor = TRUE;
2157 } else {
2158 showBgColor = FALSE;
2159 }
2160 }
2161
2162 if (!(temp_todo->mtodo.todo.indefinite)) {
2163 due = &(temp_todo->mtodo.todo.due);
2164 comp_due = due->tm_year * 380 + due->tm_mon * 31 + due->tm_mday - 1;
2165
2166 if (comp_due < comp_now) {
2167 fgColor = get_color(LIST_OVERDUE_RED, LIST_OVERDUE_GREEN, LIST_OVERDUE_BLUE);
2168
2169 } else if (comp_due == comp_now) {
2170 fgColor = get_color(LIST_DUENOW_RED, LIST_DUENOW_GREEN, LIST_DUENOW_BLUE);
2171 }
2172 showFgColor = TRUE;
2173 }
2174 gtk_list_store_set(pListStore, &iter,
2175 TODO_CHECK_COLUMN_ENUM, checkColumnDisplay,
2176 TODO_PRIORITY_COLUMN_ENUM, priorityDisplay,
2177 TODO_NOTE_COLUMN_ENUM, noteColumnDisplay,
2178 TODO_TEXT_COLUMN_ENUM, descriptionDisplay,
2179 TODO_DATE_COLUMN_ENUM, dateDisplay,
2180 TODO_DATA_COLUMN_ENUM, &(temp_todo->mtodo),
2181 TODO_BACKGROUND_COLOR_ENUM, showBgColor ? &bgColor : NULL,
2182 TODO_BACKGROUND_COLOR_ENABLED_ENUM, showBgColor,
2183 TODO_FOREGROUND_COLOR_ENUM, showFgColor ? gdk_rgba_to_string(&fgColor) : NULL,
2184 TODO_FORGROUND_COLOR_ENABLED_ENUM, showFgColor,
2185 -1);
2186
2187 entries_shown++;
2188 }
2189 if ((main) && (entries_shown > 0)) {
2190 // Set callback for a row selected
2191 gtk_tree_selection_set_select_function(treeSelection, handleRowSelection, NULL, NULL);
2192 /* First, select any record being searched for */
2193 if (glob_find_id) {
2194 todo_find();
2195 }
2196 /* Second, try the currently selected row */
2197 else if (row_selected < entries_shown) {
2198 gtk_tree_model_foreach(GTK_TREE_MODEL(pListStore), selectRecordByRow, NULL);
2199 }
2200 /* Third, select row 0 if nothing else is possible */
2201 else {
2202 row_selected = 0;
2203 gtk_tree_model_foreach(GTK_TREE_MODEL(pListStore), selectRecordByRow, NULL);
2204 }
2205
2206 }
2207 if (tooltip_widget) {
2208 get_pref(PREF_SHOW_TOOLTIPS, &show_tooltips, NULL);
2209 if (todo_list == NULL) {
2210 set_tooltip((int) show_tooltips, tooltip_widget, _("0 records"));
2211 } else {
2212 sprintf(str, _("%d of %d records"), entries_shown, num_entries);
2213 set_tooltip((int) show_tooltips, tooltip_widget, str);
2214 }
2215 }
2216 }
2217
2218 gboolean
2219 findRecord(GtkTreeModel *model,
2220 GtkTreePath *path,
2221 GtkTreeIter *iter,
2222 gpointer data) {
2223
2224 if (glob_find_id) {
2225 MyToDo *mytodo = NULL;
2226
2227 gtk_tree_model_get(model, iter, TODO_DATA_COLUMN_ENUM, &mytodo, -1);
2228 if (mytodo->unique_id == glob_find_id) {
2229 GtkTreeSelection *selection = NULL;
2230 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
2231 gtk_tree_selection_set_select_function(selection, handleRowSelection, NULL, NULL);
2232 gtk_tree_selection_select_path(selection, path);
2233 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeView), path,
2234 gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), TODO_TEXT_COLUMN_ENUM),
2235 FALSE, 1.0, 0.0);
2236 glob_find_id = 0;
2237 return TRUE;
2238 }
2239 }
2240 return FALSE;
2241 }
2242
2243
2244 gboolean
2245 selectRecordByRow(GtkTreeModel *model,
2246 GtkTreePath *path,
2247 GtkTreeIter *iter,
2248 gpointer data) {
2249 int *i = gtk_tree_path_get_indices(path);
2250 if (i[0] == row_selected) {
2251 GtkTreeSelection *selection = NULL;
2252 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
2253 gtk_tree_selection_select_path(selection, path);
2254 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeView), path,
2255 gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), TODO_TEXT_COLUMN_ENUM),
2256 FALSE, 1.0, 0.0);
2257 return TRUE;
2258 }
2259
2260 return FALSE;
2261 }
2262
2263 static int todo_find(void) {
2264 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), findRecord, NULL);
2265 return EXIT_SUCCESS;
2266
2267 }
2268
2269 int todo_gui_cleanup(void) {
2270 int b;
2271
2272 b = dialog_save_changed_record(pane, record_changed);
2273 if (b == DIALOG_SAID_2) {
2274 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
2275 }
2276 free_ToDoList(&glob_todo_list);
2277 connect_changed_signals(DISCONNECT_SIGNALS);
2278 set_pref(PREF_TODO_PANE, gtk_paned_get_position(GTK_PANED(pane)), NULL, TRUE);
2279 set_pref(PREF_TODO_NOTE_PANE, gtk_paned_get_position(GTK_PANED(note_pane)), NULL, TRUE);
2280 set_pref(PREF_LAST_TODO_CATEGORY, todo_category, NULL, TRUE);
2281 set_pref(PREF_TODO_SORT_COLUMN, column_selected, NULL, TRUE);
2282
2283 set_pref(PREF_TODO_SORT_ORDER,
2284 gtk_tree_view_column_get_sort_order(gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), column_selected)),
2285 NULL, TRUE);
2286 GtkTreeSelection *treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
2287 gtk_tree_selection_set_select_function(treeSelection, NULL, NULL, NULL);
2288 todo_liststore_clear(listStore);
2289 return EXIT_SUCCESS;
2290 }
2291
2292
2293 /* Main function */
2294 int todo_gui(GtkWidget *vbox, GtkWidget *hbox) {
2295 GtkWidget *scrolled_window;
2296 GtkWidget *pixbufwid;
2297 GdkPixbuf *pixbuf;
2298 GtkWidget *vbox1, *vbox2;
2299 GtkWidget *hbox_temp, *hbox_temp2;
2300 GtkWidget *vbox_temp;
2301 GtkWidget *separator;
2302 GtkWidget *label;
2303 char str[MAX_RADIO_BUTTON_LEN];
2304 int i;
2305 GSList *group;
2306 long ivalue;
2307 GtkAccelGroup *accel_group;
2308 long char_set;
2309 long show_tooltips;
2310 char *cat_name;
2311 get_pref(PREF_TODO_VERSION, &todo_version, NULL);
2312 init();
2313 get_todo_app_info(&todo_app_info);
2314 /* Initialize categories */
2315 get_pref(PREF_CHAR_SET, &char_set, NULL);
2316 for (i = 1; i < NUM_TODO_CAT_ITEMS; i++) {
2317 cat_name = charset_p2newj(todo_app_info.category.name[i], 31, (int) char_set);
2318 strcpy(sort_l[i - 1].Pcat, cat_name);
2319 free(cat_name);
2320 sort_l[i - 1].cat_num = i;
2321 }
2322 /* put reserved 'Unfiled' category at end of list */
2323 cat_name = charset_p2newj(todo_app_info.category.name[0], 31, (int) char_set);
2324 strcpy(sort_l[NUM_TODO_CAT_ITEMS - 1].Pcat, cat_name);
2325 free(cat_name);
2326 sort_l[NUM_TODO_CAT_ITEMS - 1].cat_num = 0;
2327
2328 qsort(sort_l, NUM_TODO_CAT_ITEMS - 1, sizeof(struct sorted_cats), cat_compare);
2329
2330 #ifdef JPILOT_DEBUG
2331 for (i=0; i<NUM_TODO_CAT_ITEMS; i++) {
2332 printf("cat %d [%s]\n", sort_l[i].cat_num, sort_l[i].Pcat);
2333 }
2334 #endif
2335
2336 get_pref(PREF_LAST_TODO_CATEGORY, &ivalue, NULL);
2337 todo_category = (int) ivalue;
2338
2339 if ((todo_category != CATEGORY_ALL)
2340 && (todo_app_info.category.name[todo_category][0] == '\0')) {
2341 todo_category = CATEGORY_ALL;
2342 }
2343
2344 /* Create basic GUI with left and right boxes and sliding pane */
2345 accel_group = gtk_accel_group_new();
2346 gtk_window_add_accel_group(GTK_WINDOW(gtk_widget_get_toplevel(vbox)),
2347 accel_group);
2348 get_pref(PREF_SHOW_TOOLTIPS, &show_tooltips, NULL);
2349
2350 pane = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
2351 get_pref(PREF_TODO_PANE, &ivalue, NULL);
2352 gtk_paned_set_position(GTK_PANED(pane), (gint) ivalue);
2353
2354 gtk_box_pack_start(GTK_BOX(hbox), pane, TRUE, TRUE, 5);
2355
2356 vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2357 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2358
2359 gtk_paned_pack1(GTK_PANED(pane), vbox1, TRUE, FALSE);
2360 gtk_paned_pack2(GTK_PANED(pane), vbox2, TRUE, FALSE);
2361
2362 /* Left side of GUI */
2363
2364 /* Separator */
2365 separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2366 gtk_box_pack_start(GTK_BOX(vbox1), separator, FALSE, FALSE, 5);
2367
2368 //time(<ime);
2369 //now = localtime(<ime);
2370
2371 /* Make the 'Today is:' label */
2372 glob_date_label = gtk_label_new(" ");
2373 gtk_box_pack_start(GTK_BOX(vbox1), glob_date_label, FALSE, FALSE, 0);
2374 timeout_date(NULL);
2375 glob_date_timer_tag = g_timeout_add(CLOCK_TICK, timeout_sync_up, NULL);
2376
2377 /* Separator */
2378 separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2379 gtk_box_pack_start(GTK_BOX(vbox1), separator, FALSE, FALSE, 5);
2380
2381 /* Left-side Category box */
2382 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2383 gtk_box_pack_start(GTK_BOX(vbox1), hbox_temp, FALSE, FALSE, 0);
2384
2385 /* Left-side Category menu */
2386 make_category_menu(&category_menu1,
2387 sort_l, cb_category, TRUE, TRUE);
2388 gtk_box_pack_start(GTK_BOX(hbox_temp), category_menu1, TRUE, TRUE, 0);
2389
2390 /* Todo list scrolled window */
2391 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2392 gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 0);
2393 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2394 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2395 //gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), (GtkShadowType) GTK_TYPE_SHADOW_TYPE);
2396 gtk_box_pack_start(GTK_BOX(vbox1), scrolled_window, TRUE, TRUE, 0);
2397 //
2398 listStore = gtk_list_store_new(TODO_NUM_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING,
2399 G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_BOOLEAN, G_TYPE_STRING,
2400 G_TYPE_BOOLEAN);
2401 GtkTreeSortable *sortable;
2402 sortable = GTK_TREE_SORTABLE(listStore);
2403 gtk_tree_sortable_set_sort_func(sortable, TODO_NOTE_COLUMN_ENUM, sortNoteColumn,
2404 GINT_TO_POINTER(TODO_NOTE_COLUMN_ENUM), NULL);
2405 GtkTreeModel *model = GTK_TREE_MODEL(listStore);
2406 treeView = gtk_tree_view_new_with_model(model);
2407
2408 GtkCellRenderer *taskRenderer = gtk_cell_renderer_text_new();
2409 // gtk_cell_renderer_set_fixed_size(taskRenderer, -1, 1);
2410
2411 GtkTreeViewColumn *taskColumn = gtk_tree_view_column_new_with_attributes("Task",
2412 taskRenderer,
2413 "text", TODO_TEXT_COLUMN_ENUM,
2414 "cell-background-rgba",
2415 TODO_BACKGROUND_COLOR_ENUM,
2416 "cell-background-set",
2417 TODO_BACKGROUND_COLOR_ENABLED_ENUM,
2418 NULL);
2419 gtk_tree_view_column_set_sort_column_id(taskColumn, TODO_TEXT_COLUMN_ENUM);
2420
2421
2422 GtkCellRenderer *dateRenderer = gtk_cell_renderer_text_new();
2423 // gtk_cell_renderer_set_fixed_size(dateRenderer, -1, 1);
2424
2425 GtkTreeViewColumn *dateColumn = gtk_tree_view_column_new_with_attributes("Due",
2426 dateRenderer,
2427 "text", TODO_DATE_COLUMN_ENUM,
2428 "cell-background-rgba",
2429 TODO_BACKGROUND_COLOR_ENUM,
2430 "cell-background-set",
2431 TODO_BACKGROUND_COLOR_ENABLED_ENUM,
2432 "foreground", TODO_FOREGROUND_COLOR_ENUM,
2433 "foreground-set",
2434 TODO_FORGROUND_COLOR_ENABLED_ENUM,
2435 NULL);
2436 gtk_tree_view_column_set_sort_column_id(dateColumn, TODO_DATE_COLUMN_ENUM);
2437
2438 GtkCellRenderer *priorityRenderer = gtk_cell_renderer_text_new();
2439 // gtk_cell_renderer_set_fixed_size(priorityRenderer, -1, 1);
2440 GtkTreeViewColumn *priorityColumn = gtk_tree_view_column_new_with_attributes("",
2441 priorityRenderer,
2442 "text", TODO_PRIORITY_COLUMN_ENUM,
2443 "cell-background-rgba",
2444 TODO_BACKGROUND_COLOR_ENUM,
2445 "cell-background-set",
2446 TODO_BACKGROUND_COLOR_ENABLED_ENUM,
2447 NULL);
2448 gtk_tree_view_column_set_sort_column_id(priorityColumn, TODO_PRIORITY_COLUMN_ENUM);
2449
2450 GtkCellRenderer *noteRenderer = gtk_cell_renderer_pixbuf_new();
2451 gtk_cell_renderer_set_alignment(noteRenderer,0,0);
2452 gtk_cell_renderer_set_padding(noteRenderer,4,0);
2453
2454 GtkTreeViewColumn *noteColumn = gtk_tree_view_column_new_with_attributes("",
2455 noteRenderer,
2456 "pixbuf", TODO_NOTE_COLUMN_ENUM,
2457 "cell-background-rgba",
2458 TODO_BACKGROUND_COLOR_ENUM,
2459 "cell-background-set",
2460 TODO_BACKGROUND_COLOR_ENABLED_ENUM,
2461 NULL);
2462 gtk_tree_view_column_set_sort_column_id(noteColumn, TODO_NOTE_COLUMN_ENUM);
2463
2464
2465 GtkCellRenderer *checkRenderer = gtk_cell_renderer_toggle_new();
2466 gtk_cell_renderer_set_alignment(checkRenderer,0,0);
2467 gtk_cell_renderer_set_padding(checkRenderer,4,0);
2468 GtkTreeViewColumn *checkColumn = gtk_tree_view_column_new_with_attributes("", checkRenderer, "active",
2469 TODO_CHECK_COLUMN_ENUM,
2470 "cell-background-rgba",
2471 TODO_BACKGROUND_COLOR_ENUM,
2472 "cell-background-set",
2473 TODO_BACKGROUND_COLOR_ENABLED_ENUM, NULL);
2474 // FIXME, this doesn't mark the record changed and toggle the right side toggle button
2475 g_signal_connect (checkRenderer, "toggled",
2476 G_CALLBACK(checkedCallBack),
2477 listStore);
2478 gtk_tree_view_column_set_sort_column_id(checkColumn, TODO_CHECK_COLUMN_ENUM);
2479 gtk_tree_view_insert_column(GTK_TREE_VIEW (treeView), checkColumn, TODO_CHECK_COLUMN_ENUM);
2480 gtk_tree_view_insert_column(GTK_TREE_VIEW (treeView), priorityColumn, TODO_PRIORITY_COLUMN_ENUM);
2481 gtk_tree_view_insert_column(GTK_TREE_VIEW (treeView), noteColumn, TODO_NOTE_COLUMN_ENUM);
2482 gtk_tree_view_insert_column(GTK_TREE_VIEW (treeView), dateColumn, TODO_DATE_COLUMN_ENUM);
2483 gtk_tree_view_insert_column(GTK_TREE_VIEW (treeView), taskColumn, TODO_TEXT_COLUMN_ENUM);
2484 gtk_tree_view_column_set_clickable(checkColumn, gtk_true());
2485 gtk_tree_view_column_set_clickable(priorityColumn, gtk_true());
2486 gtk_tree_view_column_set_clickable(noteColumn, gtk_true());
2487 gtk_tree_view_column_set_clickable(dateColumn, gtk_true());
2488 gtk_tree_view_column_set_clickable(taskColumn, gtk_true());
2489 gtk_tree_view_column_set_sizing(checkColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2490 gtk_tree_view_column_set_sizing(dateColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2491 gtk_tree_view_column_set_sizing(priorityColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2492 gtk_tree_view_column_set_sizing(noteColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2493 gtk_tree_view_column_set_sizing(taskColumn, GTK_TREE_VIEW_COLUMN_FIXED);
2494 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView)),
2495 GTK_SELECTION_BROWSE);
2496
2497
2498 /* Put pretty pictures in the treeView column headings */
2499 get_pixbufs(PIXMAP_NOTE, &pixbuf);
2500 pixbufwid = gtk_image_new_from_pixbuf(pixbuf);
2501 gtk_widget_show(GTK_WIDGET(pixbufwid));
2502 gtk_tree_view_column_set_widget(noteColumn, pixbufwid);
2503 gtk_tree_view_column_set_alignment(noteColumn, GTK_JUSTIFY_LEFT);
2504
2505
2506 get_pixbufs(PIXMAP_BOX_CHECKED, &pixbuf);
2507 pixbufwid = gtk_image_new_from_pixbuf(pixbuf);
2508 gtk_widget_show(GTK_WIDGET(pixbufwid));
2509 gtk_tree_view_column_set_widget(checkColumn, pixbufwid);
2510
2511 gtk_tree_view_column_set_alignment(checkColumn, GTK_JUSTIFY_LEFT);
2512
2513 // register function to handle column header clicks..
2514 g_signal_connect (taskColumn, "clicked", G_CALLBACK(column_clicked_cb), NULL);
2515 g_signal_connect (noteColumn, "clicked", G_CALLBACK(column_clicked_cb), NULL);
2516 g_signal_connect (checkColumn, "clicked", G_CALLBACK(column_clicked_cb), NULL);
2517 g_signal_connect (priorityColumn, "clicked", G_CALLBACK(column_clicked_cb), NULL);
2518 g_signal_connect (dateColumn, "clicked", G_CALLBACK(column_clicked_cb), NULL);
2519 treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
2520
2521 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolled_window),
2522 GTK_POLICY_NEVER,
2523 GTK_POLICY_AUTOMATIC);
2524
2525 gtk_tree_selection_set_select_function(treeSelection, handleRowSelection, NULL, NULL);
2526 gtk_widget_set_events(treeView, GDK_BUTTON1_MOTION_MASK);
2527 g_signal_connect (G_OBJECT(treeView), "motion_notify_event",
2528 G_CALLBACK(motion_notify_event), NULL);
2529 g_signal_connect (G_OBJECT(treeView), "button-press-event",
2530 G_CALLBACK(button_pressed_for_motion), NULL);
2531 g_signal_connect (G_OBJECT(treeView), "button-release-event",
2532 G_CALLBACK(button_released_for_motion), NULL);
2533 /* Restore previous sorting configuration */
2534 get_pref(PREF_TODO_SORT_COLUMN, &ivalue, NULL);
2535 column_selected = (int) ivalue;
2536
2537 for (int x = 0; x < TODO_NUM_COLS - 5; x++) {
2538 gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), x), gtk_false());
2539 }
2540 gtk_tree_view_column_set_sort_indicator(gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), column_selected),
2541 gtk_true());
2542 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(treeView));
2543
2544 get_pref(PREF_TODO_SORT_ORDER, &ivalue, NULL);
2545 gtk_tree_sortable_set_sort_column_id(sortable, column_selected, (GtkSortType) ivalue);
2546
2547
2548 g_object_unref(model);
2549 gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(treeView));
2550 /* Right side of GUI */
2551
2552 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
2553 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
2554
2555 /* Cancel button */
2556 CREATE_BUTTON(cancel_record_button, _("Cancel"), CANCEL, _("Cancel the modifications"), GDK_KEY_Escape, 0, "ESC")
2557 g_signal_connect(G_OBJECT(cancel_record_button), "clicked",
2558 G_CALLBACK(cb_cancel), NULL);
2559
2560 /* Delete button */
2561 CREATE_BUTTON(delete_record_button, _("Delete"), DELETE, _("Delete the selected record"), GDK_d, GDK_CONTROL_MASK,
2562 "Ctrl+D")
2563 g_signal_connect(G_OBJECT(delete_record_button), "clicked",
2564 G_CALLBACK(cb_delete_todo),
2565 GINT_TO_POINTER(DELETE_FLAG));
2566
2567 /* Undelete Button */
2568 CREATE_BUTTON(undelete_record_button, _("Undelete"), UNDELETE, _("Undelete the selected record"), 0, 0, "")
2569 g_signal_connect(G_OBJECT(undelete_record_button), "clicked",
2570 G_CALLBACK(cb_undelete_todo),
2571 GINT_TO_POINTER(UNDELETE_FLAG));
2572
2573 /* Copy button */
2574 CREATE_BUTTON(copy_record_button, _("Copy"), COPY, _("Copy the selected record"), GDK_c,
2575 GDK_CONTROL_MASK | GDK_SHIFT_MASK, "Ctrl+Shift+C")
2576 g_signal_connect(G_OBJECT(copy_record_button), "clicked",
2577 G_CALLBACK(cb_add_new_record),
2578 GINT_TO_POINTER(COPY_FLAG));
2579
2580 /* New button */
2581 CREATE_BUTTON(new_record_button, _("New Record"), NEW, _("Add a new record"), GDK_n, GDK_CONTROL_MASK, "Ctrl+N")
2582 g_signal_connect(G_OBJECT(new_record_button), "clicked",
2583 G_CALLBACK(cb_add_new_record),
2584 GINT_TO_POINTER(CLEAR_FLAG));
2585
2586 /* "Add Record" button */
2587 CREATE_BUTTON(add_record_button, _("Add Record"), ADD, _("Add the new record"), GDK_KEY_Return, GDK_CONTROL_MASK,
2588 "Ctrl+Enter")
2589 g_signal_connect(G_OBJECT(add_record_button), "clicked",
2590 G_CALLBACK(cb_add_new_record),
2591 GINT_TO_POINTER(NEW_FLAG));
2592 #ifndef ENABLE_STOCK_BUTTONS
2593 gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(gtk_bin_get_child(GTK_BIN(add_record_button)))),
2594 "label_high");
2595 #endif
2596
2597 /* "Apply Changes" button */
2598 CREATE_BUTTON(apply_record_button, _("Apply Changes"), APPLY, _("Commit the modifications"), GDK_KEY_Return,
2599 GDK_CONTROL_MASK, "Ctrl+Enter")
2600 g_signal_connect(G_OBJECT(apply_record_button), "clicked",
2601 G_CALLBACK(cb_add_new_record),
2602 GINT_TO_POINTER(MODIFY_FLAG));
2603 #ifndef ENABLE_STOCK_BUTTONS
2604 gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(gtk_bin_get_child(GTK_BIN(apply_record_button)))),
2605 "label_high");
2606 #endif
2607
2608 /* Separator */
2609 separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2610 gtk_box_pack_start(GTK_BOX(vbox2), separator, FALSE, FALSE, 5);
2611
2612 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2613 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
2614
2615 /* Right-side Category menu */
2616
2617 make_category_menu(&category_menu2,
2618 sort_l, NULL, FALSE, FALSE);
2619 gtk_box_pack_start(GTK_BOX(hbox_temp), category_menu2, TRUE, TRUE, 0);
2620
2621 /* Private checkbox */
2622 private_checkbox = gtk_check_button_new_with_label(_("Private"));
2623 gtk_box_pack_end(GTK_BOX(hbox_temp), private_checkbox, FALSE, FALSE, 0);
2624
2625 /* Completed checkbox */
2626 todo_completed_checkbox = gtk_check_button_new_with_label(_("Completed"));
2627 gtk_box_pack_start(GTK_BOX(vbox2), todo_completed_checkbox, FALSE, FALSE, 0);
2628
2629 /* Priority radio buttons */
2630 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2631 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
2632
2633 label = gtk_label_new(_("Priority:"));
2634 gtk_box_pack_start(GTK_BOX(hbox_temp), label, FALSE, FALSE, 2);
2635 set_tooltip((int) show_tooltips, label, _("Set priority Alt+#"));
2636
2637 group = NULL;
2638 for (i = 0; i < NUM_TODO_PRIORITIES; i++) {
2639 sprintf(str, "%d", i + 1);
2640 radio_button_todo[i] = gtk_radio_button_new_with_label(group, str);
2641 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_button_todo[i]));
2642 gtk_widget_add_accelerator(radio_button_todo[i], "clicked", accel_group,
2643 (guint) GDK_KEY_1 + i, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
2644 gtk_box_pack_start(GTK_BOX(hbox_temp),
2645 radio_button_todo[i], FALSE, FALSE, 0);
2646 }
2647
2648 /* "Date Due:" label */
2649 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2650 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
2651
2652 /* Spacer to line up */
2653 hbox_temp2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2654 gtk_box_pack_start(GTK_BOX(hbox_temp), hbox_temp2, FALSE, FALSE, 1);
2655
2656 label = gtk_label_new(_("Date Due:"));
2657 gtk_box_pack_start(GTK_BOX(hbox_temp), label, FALSE, FALSE, 0);
2658
2659 /* "Date Due" button */
2660 due_date_button = gtk_button_new_with_label("");
2661 gtk_box_pack_start(GTK_BOX(hbox_temp), due_date_button, FALSE, FALSE, 5);
2662 g_signal_connect(G_OBJECT(due_date_button), "clicked",
2663 G_CALLBACK(cb_cal_dialog), NULL);
2664
2665 /* "No Date" check box */
2666 todo_no_due_date_checkbox = gtk_check_button_new_with_label(_("No Date"));
2667 g_signal_connect(G_OBJECT(todo_no_due_date_checkbox), "clicked",
2668 G_CALLBACK(cb_check_button_no_due_date), NULL);
2669 gtk_box_pack_start(GTK_BOX(hbox_temp), todo_no_due_date_checkbox, FALSE, FALSE, 0);
2670
2671 note_pane = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
2672 get_pref(PREF_TODO_NOTE_PANE, &ivalue, NULL);
2673 gtk_paned_set_position(GTK_PANED(note_pane), (gint) ivalue);
2674 gtk_box_pack_start(GTK_BOX(vbox2), note_pane, TRUE, TRUE, 0);
2675
2676 /* Description text box */
2677 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2678 gtk_paned_pack1(GTK_PANED(note_pane), hbox_temp, TRUE, FALSE);
2679
2680 todo_desc = gtk_text_view_new();
2681 todo_desc_buffer = G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(todo_desc)));
2682 gtk_text_view_set_editable(GTK_TEXT_VIEW(todo_desc), TRUE);
2683 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(todo_desc), GTK_WRAP_WORD);
2684
2685 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2686 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2687 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2688 gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 1);
2689 gtk_container_add(GTK_CONTAINER(scrolled_window), todo_desc);
2690 gtk_box_pack_start(GTK_BOX(hbox_temp), scrolled_window, TRUE, TRUE, 0);
2691 /* Note text box */
2692 vbox_temp = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2693 gtk_paned_pack2(GTK_PANED(note_pane), vbox_temp, TRUE, FALSE);
2694
2695 label = gtk_label_new(_("Note"));
2696 gtk_box_pack_start(GTK_BOX(vbox_temp), label, FALSE, FALSE, 0);
2697
2698 todo_note = gtk_text_view_new();
2699 todo_note_buffer = G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(todo_note)));
2700 gtk_text_view_set_editable(GTK_TEXT_VIEW(todo_note), TRUE);
2701 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(todo_note), GTK_WRAP_WORD);
2702
2703 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2704 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2705 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2706 gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 1);
2707 gtk_container_add(GTK_CONTAINER(scrolled_window), todo_note);
2708 gtk_box_pack_start(GTK_BOX(vbox_temp), scrolled_window, TRUE, TRUE, 0);
2709
2710 /* Capture the TAB key to change focus with it */
2711 g_signal_connect(G_OBJECT(todo_desc), "key_press_event",
2712 G_CALLBACK(cb_key_pressed_tab), todo_note);
2713
2714 g_signal_connect(G_OBJECT(todo_note), "key_press_event",
2715 G_CALLBACK(cb_key_pressed_shift_tab), todo_desc);
2716
2717 /* Capture the Enter & Shift-Enter key combinations to move back and
2718 * forth between the left- and right-hand sides of the display. */
2719 g_signal_connect(G_OBJECT(treeView), "key_press_event",
2720 G_CALLBACK(cb_key_pressed_left_side), todo_desc);
2721
2722 g_signal_connect(G_OBJECT(todo_desc), "key_press_event",
2723 G_CALLBACK(cb_key_pressed_right_side), NULL);
2724
2725 g_signal_connect(G_OBJECT(todo_note), "key_press_event",
2726 G_CALLBACK(cb_key_pressed_right_side),
2727 GINT_TO_POINTER(1));
2728
2729 /**********************************************************************/
2730
2731 gtk_widget_show_all(vbox);
2732 gtk_widget_show_all(hbox);
2733
2734 gtk_widget_hide(add_record_button);
2735 gtk_widget_hide(apply_record_button);
2736 gtk_widget_hide(undelete_record_button);
2737 gtk_widget_hide(cancel_record_button);
2738
2739 todo_refresh();
2740
2741 return EXIT_SUCCESS;
2742 }
2743