"Fossies" - the Fresh Open Source Software Archive 
Member "jpilot-2_0_1/memo_gui.c" (3 Apr 2021, 71091 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 * memo_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
32 #include "memo.h"
33 #include "i18n.h"
34 #include "utils.h"
35 #include "log.h"
36 #include "prefs.h"
37 #include "password.h"
38 #include "print.h"
39 #include "export.h"
40 #include "stock_buttons.h"
41
42 /********************************* Constants **********************************/
43 #define NUM_MEMO_CAT_ITEMS 16
44
45 #define MEMO_MAX_COLUMN_LEN 80
46 #define MEMO_LIST_CHAR_WIDTH 50
47
48 #define NUM_MEMO_CSV_FIELDS 3
49
50 #define CONNECT_SIGNALS 400
51 #define DISCONNECT_SIGNALS 401
52
53 /******************************* Global vars **********************************/
54 /* Keeps track of whether code is using Memo, or Memos database
55 * 0 is Memo, 1 is Memos */
56 static long memo_version = 0;
57
58 extern GtkWidget *glob_date_label;
59 extern int glob_date_timer_tag;
60
61 static struct MemoAppInfo memo_app_info;
62 static int memo_category = CATEGORY_ALL;
63 static int row_selected;
64
65 static GtkWidget *treeView;
66 static GtkListStore *listStore;
67 static GtkWidget *memo_text;
68 static GObject *memo_text_buffer;
69 static GtkWidget *private_checkbox;
70 static GtkWidget *category_menu1;
71 static GtkWidget *category_menu2;
72 static GtkWidget *pane;
73 static struct sorted_cats sort_l[NUM_MEMO_CAT_ITEMS];
74 static GtkWidget *new_record_button;
75 static GtkWidget *apply_record_button;
76 static GtkWidget *add_record_button;
77 static GtkWidget *delete_record_button;
78 static GtkWidget *undelete_record_button;
79 static GtkWidget *copy_record_button;
80 static GtkWidget *cancel_record_button;
81 static int record_changed;
82
83 static MemoList *glob_memo_list = NULL;
84 static MemoList *export_memo_list = NULL;
85
86 /****************************** Prototypes ************************************/
87 static int memo_clear_details(void);
88
89 static int memo_redraw(void);
90
91 static void connect_changed_signals(int con_or_dis);
92
93 static int memo_find(void);
94
95 static int memo_get_details(struct Memo *new_memo, unsigned char *attrib);
96
97 static void memo_update_liststore(GtkListStore *listStore, GtkWidget *tooltip_widget,
98 MemoList **memo_list, int category, int main);
99
100 static void cb_add_new_record(GtkWidget *widget, gpointer data);
101
102 static void cb_edit_cats(GtkWidget *widget, gpointer data);
103
104 void initializeTreeView();
105
106 gboolean handleRowSelectionForMemo(GtkTreeSelection *selection,
107 GtkTreeModel *model,
108 GtkTreePath *path,
109 gboolean path_currently_selected,
110 gpointer userdata);
111
112 gboolean
113 addNewRecordMemo(GtkTreeModel *model,
114 GtkTreePath *path,
115 GtkTreeIter *iter,
116 gpointer data);
117
118 gboolean deleteRecordMemo(GtkTreeModel *model,
119 GtkTreePath *path,
120 GtkTreeIter *iter,
121 gpointer data);
122
123 void delete_memo(MyMemo *mmemo, gpointer data);
124
125 void undelete_memo(MyMemo *mmemo, gpointer data);
126
127 gboolean undeleteRecordMemo(GtkTreeModel *model,
128 GtkTreePath *path,
129 GtkTreeIter *iter,
130 gpointer data);
131
132 gboolean printRecordMemo(GtkTreeModel *model,
133 GtkTreePath *path,
134 GtkTreeIter *iter,
135 gpointer data);
136
137 gboolean
138 findRecordMemo(GtkTreeModel *model,
139 GtkTreePath *path,
140 GtkTreeIter *iter,
141 gpointer data);
142
143 enum {
144 MEMO_COLUMN_ENUM = 0,
145 MEMO_DATA_COLUMN_ENUM,
146 MEMO_BACKGROUND_COLOR_ENUM,
147 MEMO_BACKGROUND_COLOR_ENABLED_ENUM,
148 MEMO_NUM_COLS
149 };
150
151 gboolean
152 selectRecordByRowMemo(GtkTreeModel *model,
153 GtkTreePath *path,
154 GtkTreeIter *iter,
155 gpointer data);
156
157 int print_memo(MyMemo *mmemo);
158
159 gboolean addNewMemo(MyMemo *mmemo, const void *data);
160
161 /****************************** Main Code *************************************/
162 static void set_new_button_to(int new_state) {
163 jp_logf(JP_LOG_DEBUG, "set_new_button_to new %d old %d\n", new_state, record_changed);
164
165 if (record_changed == new_state) {
166 return;
167 }
168
169 switch (new_state) {
170 case MODIFY_FLAG:
171 gtk_widget_show(cancel_record_button);
172 gtk_widget_show(copy_record_button);
173 gtk_widget_show(apply_record_button);
174
175 gtk_widget_hide(add_record_button);
176 gtk_widget_hide(delete_record_button);
177 gtk_widget_hide(new_record_button);
178 gtk_widget_hide(undelete_record_button);
179
180 break;
181 case NEW_FLAG:
182 gtk_widget_show(cancel_record_button);
183 gtk_widget_show(add_record_button);
184
185 gtk_widget_hide(apply_record_button);
186 gtk_widget_hide(copy_record_button);
187 gtk_widget_hide(delete_record_button);
188 gtk_widget_hide(new_record_button);
189 gtk_widget_hide(undelete_record_button);
190
191 break;
192 case CLEAR_FLAG:
193 gtk_widget_show(delete_record_button);
194 gtk_widget_show(copy_record_button);
195 gtk_widget_show(new_record_button);
196
197 gtk_widget_hide(add_record_button);
198 gtk_widget_hide(apply_record_button);
199 gtk_widget_hide(cancel_record_button);
200 gtk_widget_hide(undelete_record_button);
201
202 break;
203 case UNDELETE_FLAG:
204 gtk_widget_show(undelete_record_button);
205 gtk_widget_show(copy_record_button);
206 gtk_widget_show(new_record_button);
207
208 gtk_widget_hide(add_record_button);
209 gtk_widget_hide(apply_record_button);
210 gtk_widget_hide(cancel_record_button);
211 gtk_widget_hide(delete_record_button);
212 break;
213
214 default:
215 return;
216 }
217
218 record_changed = new_state;
219 }
220
221 static void cb_record_changed(GtkWidget *widget, gpointer data) {
222 jp_logf(JP_LOG_DEBUG, "cb_record_changed\n");
223 if (record_changed == CLEAR_FLAG) {
224 connect_changed_signals(DISCONNECT_SIGNALS);
225 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL) > 0) {
226 set_new_button_to(MODIFY_FLAG);
227 } else {
228 set_new_button_to(NEW_FLAG);
229 }
230 } else if (record_changed == UNDELETE_FLAG) {
231 jp_logf(JP_LOG_INFO | JP_LOG_GUI,
232 _("This record is deleted.\n"
233 "Undelete it or copy it to make changes.\n"));
234 }
235 }
236
237 static void connect_changed_signals(int con_or_dis) {
238 static int connected = 0;
239
240 /* CONNECT */
241 if ((con_or_dis == CONNECT_SIGNALS) && (!connected)) {
242 connected = 1;
243
244 if(category_menu2){
245 g_signal_connect(G_OBJECT(category_menu2),"changed",G_CALLBACK(cb_record_changed),NULL);
246 }
247 g_signal_connect(memo_text_buffer, "changed",
248 G_CALLBACK(cb_record_changed), NULL);
249
250 g_signal_connect(G_OBJECT(private_checkbox), "toggled",
251 G_CALLBACK(cb_record_changed), NULL);
252 }
253
254 /* DISCONNECT */
255 if ((con_or_dis == DISCONNECT_SIGNALS) && (connected)) {
256 connected = 0;
257 if(category_menu2) {
258 g_signal_handlers_disconnect_by_func(G_OBJECT(category_menu2), G_CALLBACK(cb_record_changed), NULL);
259 }
260 g_signal_handlers_disconnect_by_func(memo_text_buffer,
261 G_CALLBACK(cb_record_changed), NULL);
262 g_signal_handlers_disconnect_by_func(G_OBJECT(private_checkbox),
263 G_CALLBACK(cb_record_changed), NULL);
264 }
265 }
266
267 int print_memo(MyMemo *mmemo) {
268 long this_many;
269 MemoList *memo_list;
270 MemoList memo_list1;
271
272 get_pref(PREF_PRINT_THIS_MANY, &this_many, NULL);
273
274 memo_list = NULL;
275 if (this_many == 1) {
276 if (mmemo < (MyMemo *) LIST_MIN_DATA) {
277 return EXIT_FAILURE;
278 }
279 memcpy(&(memo_list1.mmemo), mmemo, sizeof(MyMemo));
280 memo_list1.next = NULL;
281 memo_list = &memo_list1;
282 }
283 if (this_many == 2) {
284 get_memos2(&memo_list, SORT_ASCENDING, 2, 2, 2, memo_category);
285 }
286 if (this_many == 3) {
287 get_memos2(&memo_list, SORT_ASCENDING, 2, 2, 2, CATEGORY_ALL);
288 }
289
290 print_memos(memo_list);
291
292 if ((this_many == 2) || (this_many == 3)) {
293 free_MemoList(&memo_list);
294 }
295
296 return EXIT_SUCCESS;
297 }
298
299 gboolean printRecordMemo(GtkTreeModel *model,
300 GtkTreePath *path,
301 GtkTreeIter *iter,
302 gpointer data) {
303 int *i = gtk_tree_path_get_indices(path);
304 if (i[0] == row_selected) {
305 MyMemo *mmemo = NULL;
306 gtk_tree_model_get(model, iter, MEMO_DATA_COLUMN_ENUM, &mmemo, -1);
307 print_memo(mmemo);
308 return TRUE;
309 }
310
311 return FALSE;
312
313
314 }
315
316 int memo_print(void) {
317 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), printRecordMemo, NULL);
318 return EXIT_SUCCESS;
319 }
320
321 /* Start Import Code */
322
323 static int cb_memo_import(GtkWidget *parent_window,
324 const char *file_path,
325 int type) {
326 FILE *in;
327 char line[256];
328 char text[65536];
329 struct Memo new_memo;
330 unsigned char attrib;
331 unsigned int len;
332 unsigned int text_len;
333 int i, ret, index;
334 int import_all;
335 MemoList *memolist;
336 MemoList *temp_memolist;
337 struct CategoryAppInfo cai;
338 char old_cat_name[32];
339 int suggested_cat_num;
340 int new_cat_num;
341 int priv;
342
343 in = fopen(file_path, "r");
344 if (!in) {
345 jp_logf(JP_LOG_WARN, _("Unable to open file: %s\n"), file_path);
346 return EXIT_FAILURE;
347 }
348
349 /* TEXT */
350 if (type == IMPORT_TYPE_TEXT) {
351 jp_logf(JP_LOG_DEBUG, "Memo import text [%s]\n", file_path);
352
353 /* Trick to get currently selected category into attrib */
354 memo_get_details(&new_memo, &attrib);
355 free_Memo(&new_memo);
356
357 text_len = 0;
358 text[0] = '\0';
359 while (!feof(in)) {
360 line[0] = '\0';
361 if (fgets(line, 255, in) == NULL) {
362 jp_logf(JP_LOG_WARN, "write failed %s %d\n", __FILE__, __LINE__);
363 }
364 line[255] = '\0';
365 len = (unsigned int) strlen(line);
366 if (text_len + len > 65534) {
367 len = 65534 - text_len;
368 line[len] = '\0';
369 jp_logf(JP_LOG_WARN, _("Memo text > 65535, truncating\n"));
370 strcat(text, line);
371 break;
372 }
373 text_len += len;
374 strcat(text, line);
375 }
376
377 /* convert to valid UTF-8 and recalculate the length */
378 jp_charset_p2j(text, sizeof(text));
379 text_len = (unsigned int) strlen(text);
380 #ifdef JPILOT_DEBUG
381 printf("text=[%s]\n", text);
382 printf("text_len=%d\n", text_len);
383 printf("strlen(text)=%d\n", strlen(text));
384 #endif
385
386 new_memo.text = text;
387
388 ret = import_record_ask(parent_window, pane,
389 new_memo.text,
390 &(memo_app_info.category),
391 _("Unfiled"),
392 0,
393 attrib & 0x0F,
394 &new_cat_num);
395 if ((ret == DIALOG_SAID_IMPORT_ALL) || (ret == DIALOG_SAID_IMPORT_YES)) {
396 pc_memo_write(&new_memo, NEW_PC_REC, attrib, NULL);
397 jp_logf(JP_LOG_WARN, _("Imported Memo %s\n"), file_path);
398 }
399 }
400
401 /* CSV */
402 if (type == IMPORT_TYPE_CSV) {
403 jp_logf(JP_LOG_DEBUG, "Memo import CSV [%s]\n", file_path);
404 /* Get the first line containing the format and check for reasonableness */
405 if (fgets(text, sizeof(text), in) == NULL) {
406 jp_logf(JP_LOG_WARN, "fgets failed %s %d\n", __FILE__, __LINE__);
407 }
408 ret = verify_csv_header(text, NUM_MEMO_CSV_FIELDS, file_path);
409 if (EXIT_FAILURE == ret) return EXIT_FAILURE;
410
411 import_all = FALSE;
412 while (1) {
413 /* Read the category field */
414 ret = read_csv_field(in, text, sizeof(text));
415 if (feof(in)) break;
416 #ifdef JPILOT_DEBUG
417 printf("category is [%s]\n", text);
418 #endif
419 g_strlcpy(old_cat_name, text, 16);
420 /* Figure out what the best category number is */
421 suggested_cat_num = 0;
422 for (i = 0; i < NUM_MEMO_CAT_ITEMS; i++) {
423 if (!memo_app_info.category.name[i][0]) continue;
424 if (!strcmp(memo_app_info.category.name[i], old_cat_name)) {
425 suggested_cat_num = i;
426 break;
427 }
428 }
429
430 /* Read the private field */
431 ret = read_csv_field(in, text, sizeof(text));
432 #ifdef JPILOT_DEBUG
433 printf("private is [%s]\n", text);
434 #endif
435 sscanf(text, "%d", &priv);
436
437 ret = read_csv_field(in, text, sizeof(text));
438 #ifdef JPILOT_DEBUG
439 printf("memo text is [%s]\n", text);
440 #endif
441 new_memo.text = text;
442 if (!import_all) {
443 ret = import_record_ask(parent_window, pane,
444 new_memo.text,
445 &(memo_app_info.category),
446 old_cat_name,
447 priv,
448 suggested_cat_num,
449 &new_cat_num);
450 } else {
451 new_cat_num = suggested_cat_num;
452 }
453 if (ret == DIALOG_SAID_IMPORT_QUIT) break;
454 if (ret == DIALOG_SAID_IMPORT_SKIP) continue;
455 if (ret == DIALOG_SAID_IMPORT_ALL) import_all = TRUE;
456
457 attrib = (unsigned char) ((new_cat_num & 0x0F) |
458 (priv ? dlpRecAttrSecret : 0));
459 if ((ret == DIALOG_SAID_IMPORT_YES) || (import_all)) {
460 pc_memo_write(&new_memo, NEW_PC_REC, attrib, NULL);
461 }
462 }
463 }
464
465 /* Palm Desktop DAT format */
466 if (type == IMPORT_TYPE_DAT) {
467 jp_logf(JP_LOG_DEBUG, "Memo import DAT [%s]\n", file_path);
468 if (dat_check_if_dat_file(in) != DAT_MEMO_FILE) {
469 jp_logf(JP_LOG_WARN, _("File doesn't appear to be memopad.dat format\n"));
470 fclose(in);
471 return EXIT_FAILURE;
472 }
473 memolist = NULL;
474 dat_get_memos(in, &memolist, &cai);
475 import_all = FALSE;
476 for (temp_memolist = memolist; temp_memolist; temp_memolist = temp_memolist->next) {
477 #ifdef JPILOT_DEBUG
478 printf("category=%d\n", temp_memolist->mmemo.unique_id);
479 printf("attrib=%d\n", temp_memolist->mmemo.attrib);
480 printf("private=%d\n", temp_memolist->mmemo.attrib & 0x10);
481 printf("memo=[%s]\n", temp_memolist->mmemo.memo.text);
482 #endif
483 new_memo.text = temp_memolist->mmemo.memo.text;
484 index = temp_memolist->mmemo.unique_id - 1;
485 if (index < 0) {
486 g_strlcpy(old_cat_name, _("Unfiled"), 16);
487 index = 0;
488 } else {
489 g_strlcpy(old_cat_name, cai.name[index], 16);
490 }
491 attrib = 0;
492 /* Figure out what category it was in the dat file */
493 index = temp_memolist->mmemo.unique_id - 1;
494 suggested_cat_num = 0;
495 if (index > -1) {
496 for (i = 0; i < NUM_MEMO_CAT_ITEMS; i++) {
497 if (memo_app_info.category.name[i][0] == '\0') continue;
498 if (!strcmp(memo_app_info.category.name[i], old_cat_name)) {
499 suggested_cat_num = i;
500 break;
501 }
502 }
503 }
504
505 ret = 0;
506 if (!import_all) {
507 ret = import_record_ask(parent_window, pane,
508 new_memo.text,
509 &(memo_app_info.category),
510 old_cat_name,
511 (temp_memolist->mmemo.attrib & 0x10),
512 suggested_cat_num,
513 &new_cat_num);
514 } else {
515 new_cat_num = suggested_cat_num;
516 }
517 if (ret == DIALOG_SAID_IMPORT_QUIT) break;
518 if (ret == DIALOG_SAID_IMPORT_SKIP) continue;
519 if (ret == DIALOG_SAID_IMPORT_ALL) import_all = TRUE;
520
521 attrib = (unsigned char) ((new_cat_num & 0x0F) |
522 ((temp_memolist->mmemo.attrib & 0x10) ? dlpRecAttrSecret : 0));
523 if ((ret == DIALOG_SAID_IMPORT_YES) || (import_all)) {
524 pc_memo_write(&new_memo, NEW_PC_REC, attrib, NULL);
525 }
526 }
527 free_MemoList(&memolist);
528 }
529
530 memo_refresh();
531 fclose(in);
532 return EXIT_SUCCESS;
533 }
534
535 int memo_import(GtkWidget *window) {
536 char *type_desc[] = {
537 N_("Text"),
538 N_("CSV (Comma Separated Values)"),
539 N_("DAT/MPA (Palm Archive Formats)"),
540 NULL
541 };
542 int type_int[] = {
543 IMPORT_TYPE_TEXT,
544 IMPORT_TYPE_CSV,
545 IMPORT_TYPE_DAT,
546 0
547 };
548
549 /* Hide ABA import of Memos until file format has been decoded */
550 if (memo_version == 1) {
551 type_desc[2] = NULL;
552 type_int[2] = 0;
553 }
554
555 import_gui(window, pane, type_desc, type_int, cb_memo_import);
556 return EXIT_SUCCESS;
557 }
558
559 /* End Import Code */
560
561 /* Start Export code */
562
563 static void cb_memo_export_ok(GtkWidget *export_window, GtkWidget *treeView,
564 int type, const char *filename) {
565 MyMemo *mmemo;
566 GList *list, *temp_list;
567 FILE *out;
568 struct stat statb;
569 int i, r, len;
570 const char *short_date;
571 time_t ltime;
572 struct tm *now;
573 char *button_text[] = {N_("OK")};
574 char *button_overwrite_text[] = {N_("No"), N_("Yes")};
575 char text[1024];
576 char str1[256], str2[256];
577 char date_string[1024];
578 char pref_time[40];
579 char csv_text[65550];
580 long char_set;
581 char *utf;
582 int cat;
583 int j;
584
585 /* Open file for export, including corner cases where file exists or
586 * can't be opened */
587 if (!stat(filename, &statb)) {
588 if (S_ISDIR(statb.st_mode)) {
589 g_snprintf(text, sizeof(text), _("%s is a directory"), filename);
590 dialog_generic(GTK_WINDOW(export_window),
591 _("Error Opening File"),
592 DIALOG_ERROR, text, 1, button_text);
593 return;
594 }
595 g_snprintf(text, sizeof(text), _("Do you want to overwrite file %s?"), filename);
596 r = dialog_generic(GTK_WINDOW(export_window),
597 _("Overwrite File?"),
598 DIALOG_ERROR, text, 2, button_overwrite_text);
599 if (r != DIALOG_SAID_2) {
600 return;
601 }
602 }
603
604 out = fopen(filename, "w");
605 if (!out) {
606 g_snprintf(text, sizeof(text), _("Error opening file: %s"), filename);
607 dialog_generic(GTK_WINDOW(export_window),
608 _("Error Opening File"),
609 DIALOG_ERROR, text, 1, button_text);
610 return;
611 }
612
613 /* Write a header for TEXT file */
614 if (type == EXPORT_TYPE_TEXT) {
615 get_pref(PREF_SHORTDATE, NULL, &short_date);
616 get_pref_time_no_secs(pref_time);
617 time(<ime);
618 now = localtime(<ime);
619 strftime(str1, sizeof(str1), short_date, now);
620 strftime(str2, sizeof(str2), pref_time, now);
621 g_snprintf(date_string, sizeof(date_string), "%s %s", str1, str2);
622 if (memo_version == 0) {
623 fprintf(out, _("Memo exported from %s %s on %s\n\n"),
624 PN, VERSION, date_string);
625 } else {
626 fprintf(out, _("Memos exported from %s %s on %s\n\n"),
627 PN, VERSION, date_string);
628 }
629 }
630
631 /* Write a header to the CSV file */
632 if (type == EXPORT_TYPE_CSV) {
633 if (memo_version == 0) {
634 fprintf(out, "CSV memo version "VERSION": Category, Private, Memo Text\n");
635 } else {
636 fprintf(out, "CSV memos version "VERSION": Category, Private, Memo Text\n");
637 }
638 }
639
640 /* Write a header to the BFOLDERS CSV file */
641 if (type == EXPORT_TYPE_BFOLDERS) {
642 fprintf(out, "Notes:\n");
643 fprintf(out, "Note,Folder\n");
644 }
645
646 /* Write a header to the KeePassX XML file */
647 if (type == EXPORT_TYPE_KEEPASSX) {
648 fprintf(out, "<!DOCTYPE KEEPASSX_DATABASE>\n");
649 fprintf(out, "<database>\n");
650 fprintf(out, " <group>\n");
651 fprintf(out, " <title>Memos</title>\n");
652 fprintf(out, " <icon>7</icon>\n");
653 }
654
655 get_pref(PREF_CHAR_SET, &char_set, NULL);
656 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
657 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView));
658 list = gtk_tree_selection_get_selected_rows(selection, &model);
659
660 for (i = 0, temp_list = list; temp_list; temp_list = temp_list->next, i++) {
661 GtkTreePath *path = temp_list->data;
662 GtkTreeIter iter;
663 if (gtk_tree_model_get_iter(model, &iter, path)) {
664 gtk_tree_model_get(model, &iter, MEMO_DATA_COLUMN_ENUM, &mmemo, -1);
665 if (!mmemo) {
666 continue;
667 jp_logf(JP_LOG_WARN, _("Can't export memo %d\n"), (long) temp_list->data + 1);
668 }
669 switch (type) {
670 case EXPORT_TYPE_CSV:
671 len = 0;
672 if (mmemo->memo.text) {
673 len = (unsigned int) strlen(mmemo->memo.text) * 2 + 4;
674 }
675 if (len < 256) len = 256;
676 utf = charset_p2newj(memo_app_info.category.name[mmemo->attrib & 0x0F], 16, (int) char_set);
677 str_to_csv_str(csv_text, utf);
678 fprintf(out, "\"%s\",", csv_text);
679 g_free(utf);
680 fprintf(out, "\"%s\",", (mmemo->attrib & dlpRecAttrSecret) ? "1" : "0");
681 str_to_csv_str(csv_text, mmemo->memo.text);
682 fprintf(out, "\"%s\"\n", csv_text);
683 break;
684
685 case EXPORT_TYPE_BFOLDERS:
686 len = 0;
687 str_to_csv_str(csv_text, mmemo->memo.text);
688 fprintf(out, "\"%s\",", csv_text);
689
690 if (mmemo->memo.text) {
691 len = (unsigned int) strlen(mmemo->memo.text) * 2 + 4;
692 }
693 if (len < 256) len = 256;
694 printf("\"RAW %d %s\"\n", mmemo->attrib & 0x0F, memo_app_info.category.name[mmemo->attrib & 0x0F]);
695 utf = charset_p2newj(memo_app_info.category.name[mmemo->attrib & 0x0F], 16, (int) char_set);
696 str_to_csv_str(csv_text, utf);
697 fprintf(out, "\"Memos > %s\"\n", csv_text);
698 printf("\"Memos > %s\"\n", utf);
699 g_free(utf);
700 break;
701
702 case EXPORT_TYPE_TEXT:
703 get_pref(PREF_SHORTDATE, NULL, &short_date);
704 get_pref_time_no_secs(pref_time);
705 time(<ime);
706 now = localtime(<ime);
707 strftime(str1, sizeof(str1), short_date, now);
708 strftime(str2, sizeof(str2), pref_time, now);
709 g_snprintf(text, sizeof(text), "%s %s", str1, str2);
710
711 fprintf(out, _("Memo: %ld\n"), (long) temp_list->data + 1);
712 utf = charset_p2newj(memo_app_info.category.name[mmemo->attrib & 0x0F], 16, (int) char_set);
713 fprintf(out, _("Category: %s\n"), utf);
714 g_free(utf);
715 fprintf(out, _("Private: %s\n"),
716 (mmemo->attrib & dlpRecAttrSecret) ? _("Yes") : _("No"));
717 fprintf(out, _("----- Start of Memo -----\n"));
718 fprintf(out, "%s", mmemo->memo.text);
719 fprintf(out, _("\n----- End of Memo -----\n\n"));
720 break;
721
722 case EXPORT_TYPE_KEEPASSX:
723 break;
724
725 default:
726 jp_logf(JP_LOG_WARN, _("Unknown export type\n"));
727 }
728 }
729 }
730
731 /* I'm writing a second loop for the KeePassX XML file because I want to
732 * put each category into a folder and we need to write the tag for a folder
733 * and then find each record in that category/folder
734 */
735 if (type == EXPORT_TYPE_KEEPASSX) {
736 for (cat = 0; cat < 16; cat++) {
737 if (memo_app_info.category.name[cat][0] == '\0') {
738 continue;
739 }
740 /* Write a folder XML tag */
741 utf = charset_p2newj(memo_app_info.category.name[cat], 16, (int) char_set);
742 fprintf(out, " <group>\n");
743 fprintf(out, " <title>%s</title>\n", utf);
744 fprintf(out, " <icon>7</icon>\n");
745 g_free(utf);
746 for (i = 0, temp_list = list; temp_list; temp_list = temp_list->next, i++) {
747 GtkTreePath *path = temp_list->data;
748 GtkTreeIter iter;
749 if (gtk_tree_model_get_iter(model, &iter, path)) {
750 gtk_tree_model_get(model, &iter, MEMO_DATA_COLUMN_ENUM, &mmemo, -1);
751 if (!mmemo) {
752 continue;
753 jp_logf(JP_LOG_WARN, _("Can't export memo %d\n"), (long) temp_list->data + 1);
754 }
755 if ((mmemo->attrib & 0x0F) != cat) {
756 continue;
757 }
758 fprintf(out, " <entry>\n");
759
760 /* Create a title (which is the first line of the memo) */
761 for (j = 0; j < 100; j++) {
762 str1[j] = mmemo->memo.text[j];
763 if (str1[j] == '\0') {
764 break;
765 }
766 if (str1[j] == '\n') {
767 str1[j] = '\0';
768 break;
769 }
770 }
771 str1[100] = '\0';
772 str_to_keepass_str(csv_text, str1);
773 fprintf(out, " <title>%s</title>\n", csv_text);
774 /* No keyring field for username */
775 /* No keyring field for password */
776 /* No keyring field for url */
777 str_to_keepass_str(csv_text, mmemo->memo.text);
778 fprintf(out, " <comment>%s</comment>\n", csv_text);
779 fprintf(out, " <icon>7</icon>\n");
780 /* No keyring field for creation */
781 /* No keyring field for lastaccess */
782 /* No keyring field for lastmod */
783 /* No keyring field for expire */
784 fprintf(out, " <expire>Never</expire>\n");
785 fprintf(out, " </entry>\n");
786 }
787 fprintf(out, " </group>\n");
788 }
789 }
790
791 /* Write a footer to the KeePassX XML file */
792 if (type == EXPORT_TYPE_KEEPASSX) {
793 fprintf(out, " </group>\n");
794 fprintf(out, "</database>\n");
795 }
796 }
797
798 if (out) {
799 fclose(out);
800 }
801 }
802
803 static void cb_memo_update_listStore(GtkWidget *treeView, int category) {
804 memo_update_liststore(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeView))), NULL, &export_memo_list,
805 category, FALSE);
806 }
807
808 static GtkWidget *cb_memo_init_export_treeView() {
809 GtkListStore *listStore = gtk_list_store_new(MEMO_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_RGBA,
810 G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN);
811 GtkWidget *treeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listStore));
812 treeView = GTK_WIDGET(gtk_tree_view_new_with_model(GTK_TREE_MODEL(listStore)));
813 GtkCellRenderer *columnRenderer = gtk_cell_renderer_text_new();
814 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("", columnRenderer, "text", MEMO_COLUMN_ENUM,
815 "cell-background-rgba",
816 MEMO_BACKGROUND_COLOR_ENUM,
817 "cell-background-set",
818 MEMO_BACKGROUND_COLOR_ENABLED_ENUM, NULL);
819 gtk_tree_view_column_set_fixed_width(column, (gint) 50);
820 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeView), FALSE);
821 gtk_tree_view_insert_column(GTK_TREE_VIEW(treeView), column, 0);
822 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
823 return GTK_WIDGET(treeView);
824 }
825
826 static void cb_memo_export_done(GtkWidget *widget, const char *filename) {
827 free_MemoList(&export_memo_list);
828
829 set_pref(PREF_MEMO_EXPORT_FILENAME, 0, filename, TRUE);
830 }
831
832 int memo_export(GtkWidget *window) {
833 int w, h, x, y;
834 char *type_text[] = {N_("Text"),
835 N_("CSV"),
836 N_("B-Folders CSV"),
837 N_("KeePassX XML"),
838 NULL};
839 int type_int[] = {EXPORT_TYPE_TEXT, EXPORT_TYPE_CSV, EXPORT_TYPE_BFOLDERS, EXPORT_TYPE_KEEPASSX};
840
841 w = gdk_window_get_width(gtk_widget_get_window(window));
842 h = gdk_window_get_height(gtk_widget_get_window(window));
843 gdk_window_get_root_origin(gtk_widget_get_window(window), &x, &y);
844
845 w = gtk_paned_get_position(GTK_PANED(pane));
846 x += 40;
847
848 export_gui(window,
849 w, h, x, y, 1, sort_l,
850 PREF_MEMO_EXPORT_FILENAME,
851 type_text,
852 type_int,
853 cb_memo_init_export_treeView,
854 cb_memo_update_listStore,
855 cb_memo_export_done,
856 cb_memo_export_ok
857 );
858
859 return EXIT_SUCCESS;
860 }
861
862 /* End Export Code */
863
864 /* Find position of category in sorted category array
865 * via its assigned category number */
866 static int find_sort_cat_pos(int cat) {
867 int i;
868
869 for (i = 0; i < NUM_MEMO_CAT_ITEMS; i++) {
870 if (sort_l[i].cat_num == cat) {
871 return i;
872 }
873 }
874
875 return -1;
876 }
877
878 /* Find a category's position in the category menu.
879 * This is equal to the category number except for the Unfiled category.
880 * The Unfiled category is always in the last position which changes as
881 * the number of categories changes */
882 static int find_menu_cat_pos(int cat) {
883 int i;
884
885 if (cat != NUM_MEMO_CAT_ITEMS - 1) {
886 return cat;
887 } else { /* Unfiled category */
888 /* Count how many category entries are filled */
889 for (i = 0; i < NUM_MEMO_CAT_ITEMS; i++) {
890 if (!sort_l[i].Pcat[0]) {
891 return i;
892 }
893 }
894 return 0;
895 }
896 }
897
898 gboolean deleteRecordMemo(GtkTreeModel *model,
899 GtkTreePath *path,
900 GtkTreeIter *iter,
901 gpointer data) {
902 int *i = gtk_tree_path_get_indices(path);
903 if (i[0] == row_selected) {
904 MyMemo *mmemo = NULL;
905 gtk_tree_model_get(model, iter, MEMO_DATA_COLUMN_ENUM, &mmemo, -1);
906 delete_memo(mmemo, data);
907 return TRUE;
908 }
909
910 return FALSE;
911
912
913 }
914
915 gboolean undeleteRecordMemo(GtkTreeModel *model,
916 GtkTreePath *path,
917 GtkTreeIter *iter,
918 gpointer data) {
919 int *i = gtk_tree_path_get_indices(path);
920 if (i[0] == row_selected) {
921 MyMemo *mmemo = NULL;
922 gtk_tree_model_get(model, iter, MEMO_DATA_COLUMN_ENUM, &mmemo, -1);
923 undelete_memo(mmemo, data);
924 return TRUE;
925 }
926
927 return FALSE;
928
929 }
930
931 void delete_memo(MyMemo *mmemo, gpointer data) {
932
933 int flag;
934 int show_priv;
935 long char_set;
936
937 if (mmemo < (MyMemo *) LIST_MIN_DATA) {
938 return;
939 }
940 /* Convert to Palm character set */
941 get_pref(PREF_CHAR_SET, &char_set, NULL);
942 if (char_set != CHAR_SET_LATIN1) {
943 if (mmemo->memo.text)
944 charset_j2p(mmemo->memo.text, strlen(mmemo->memo.text) + 1, (int) char_set);
945 }
946
947 /* Do masking like Palm OS 3.5 */
948 show_priv = show_privates(GET_PRIVATES);
949 if ((show_priv != SHOW_PRIVATES) &&
950 (mmemo->attrib & dlpRecAttrSecret)) {
951 return;
952 }
953 /* End Masking */
954 jp_logf(JP_LOG_DEBUG, "mmemo->unique_id = %d\n", mmemo->unique_id);
955 jp_logf(JP_LOG_DEBUG, "mmemo->rt = %d\n", mmemo->rt);
956 flag = GPOINTER_TO_INT(data);
957 if ((flag == MODIFY_FLAG) || (flag == DELETE_FLAG)) {
958 delete_pc_record(MEMO, mmemo, flag);
959 if (flag == DELETE_FLAG) {
960 /* when we redraw we want to go to the line above the deleted one */
961 if (row_selected > 0) {
962 row_selected--;
963 }
964 }
965 }
966
967 if (flag == DELETE_FLAG) {
968 memo_redraw();
969 }
970 }
971
972 static void cb_delete_memo(GtkWidget *widget,
973 const void *data) {
974 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), deleteRecordMemo, (gpointer) data);
975 return;
976
977 }
978
979 void undelete_memo(MyMemo *mmemo, gpointer data) {
980
981 int flag;
982 int show_priv;
983
984
985 if (mmemo < (MyMemo *) LIST_MIN_DATA) {
986 return;
987 }
988
989 /* Do masking like Palm OS 3.5 */
990 show_priv = show_privates(GET_PRIVATES);
991 if ((show_priv != SHOW_PRIVATES) &&
992 (mmemo->attrib & dlpRecAttrSecret)) {
993 return;
994 }
995 /* End Masking */
996
997 jp_logf(JP_LOG_DEBUG, "mmemo->unique_id = %d\n", mmemo->unique_id);
998 jp_logf(JP_LOG_DEBUG, "mmemo->rt = %d\n", mmemo->rt);
999
1000 flag = GPOINTER_TO_INT(data);
1001 if (flag == UNDELETE_FLAG) {
1002 if (mmemo->rt == DELETED_PALM_REC ||
1003 mmemo->rt == DELETED_PC_REC) {
1004 undelete_pc_record(MEMO, mmemo, flag);
1005 }
1006 /* Possible later addition of undelete for modified records
1007 else if (mmemo->rt == MODIFIED_PALM_REC)
1008 {
1009 cb_add_new_record(widget, GINT_TO_POINTER(COPY_FLAG));
1010 }
1011 */
1012 }
1013
1014 memo_redraw();
1015 }
1016
1017 static void cb_undelete_memo(GtkWidget *widget,
1018 gpointer data) {
1019
1020 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), undeleteRecordMemo, data);
1021 return;
1022
1023
1024 }
1025
1026 static void cb_cancel(GtkWidget *widget, gpointer data) {
1027 set_new_button_to(CLEAR_FLAG);
1028 memo_refresh();
1029 }
1030
1031 static void cb_edit_cats(GtkWidget *widget, gpointer data) {
1032 struct MemoAppInfo ai;
1033 char db_name[FILENAME_MAX];
1034 char pdb_name[FILENAME_MAX];
1035 char full_name[FILENAME_MAX];
1036 unsigned char buffer[65536];
1037 int num;
1038 size_t size;
1039 void *buf;
1040 struct pi_file *pf;
1041 long memo_version;
1042
1043 jp_logf(JP_LOG_DEBUG, "cb_edit_cats\n");
1044
1045 get_pref(PREF_MEMO_VERSION, &memo_version, NULL);
1046
1047 switch (memo_version) {
1048 case 0:
1049 default:
1050 strcpy(pdb_name, "MemoDB.pdb");
1051 strcpy(db_name, "MemoDB");
1052 break;
1053 case 1:
1054 strcpy(pdb_name, "MemosDB-PMem.pdb");
1055 strcpy(db_name, "MemosDB-PMem");
1056 break;
1057 case 2:
1058 strcpy(pdb_name, "Memo32DB.pdb");
1059 strcpy(db_name, "Memo32DB");
1060 break;
1061 }
1062
1063 get_home_file_name(pdb_name, full_name, sizeof(full_name));
1064
1065 buf = NULL;
1066 memset(&ai, 0, sizeof(ai));
1067
1068 pf = pi_file_open(full_name);
1069 pi_file_get_app_info(pf, &buf, &size);
1070
1071 num = unpack_MemoAppInfo(&ai, buf, size);
1072 if (num <= 0) {
1073 jp_logf(JP_LOG_WARN, _("Error reading file: %s\n"), pdb_name);
1074 return;
1075 }
1076
1077 pi_file_close(pf);
1078
1079 edit_cats(widget, db_name, &(ai.category));
1080
1081 size = pack_MemoAppInfo(&ai, buffer, sizeof(buffer));
1082
1083 pdb_file_write_app_block(db_name, buffer, size);
1084
1085
1086 cb_app_button(NULL, GINT_TO_POINTER(REDRAW));
1087 }
1088
1089 static void cb_category(GtkComboBox *item, int selection) {
1090 int b;
1091 if (!item) return;
1092 if (gtk_combo_box_get_active(GTK_COMBO_BOX(item)) < 0) {
1093 return;
1094 }
1095 int selectedItem = get_selected_category_from_combo_box(item);
1096 if (selectedItem == -1) {
1097 return;
1098 }
1099
1100 if (memo_category == selectedItem) { return; }
1101
1102 b = dialog_save_changed_record_with_cancel(pane, record_changed);
1103 if (b == DIALOG_SAID_1) { /* Cancel */
1104 int index, index2;
1105
1106 if (memo_category == CATEGORY_ALL) {
1107 index = 0;
1108 index2 = 0;
1109 } else {
1110 index = find_sort_cat_pos(memo_category);
1111 index2 = find_menu_cat_pos(index) + 1;
1112 index += 1;
1113 }
1114
1115 if (index < 0) {
1116 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1117 } else {
1118 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), index2);
1119 }
1120
1121 return;
1122 }
1123 if (b == DIALOG_SAID_3) { /* Save */
1124 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1125 }
1126
1127 if (selectedItem == CATEGORY_EDIT) {
1128 cb_edit_cats(GTK_WIDGET(item), NULL);
1129 } else {
1130 memo_category = selectedItem;
1131 }
1132 row_selected = 0;
1133 jp_logf(JP_LOG_DEBUG, "cb_category() cat=%d\n", memo_category);
1134 memo_update_liststore(listStore, category_menu1, &glob_memo_list, memo_category, TRUE);
1135 jp_logf(JP_LOG_DEBUG, "Leaving cb_category()\n");
1136
1137 }
1138
1139 static int memo_clear_details(void) {
1140 int new_cat;
1141 int sorted_position;
1142
1143 jp_logf(JP_LOG_DEBUG, "memo_clear_details()\n");
1144
1145 /* Need to disconnect signals first */
1146 connect_changed_signals(DISCONNECT_SIGNALS);
1147
1148 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(memo_text_buffer), "", -1);
1149
1150 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private_checkbox), FALSE);
1151
1152 if (memo_category == CATEGORY_ALL) {
1153 new_cat = 0;
1154 } else {
1155 new_cat = memo_category;
1156 }
1157 sorted_position = find_sort_cat_pos(new_cat);
1158 if (sorted_position < 0) {
1159 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1160 } else {
1161 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu2),find_menu_cat_pos(sorted_position));
1162 }
1163
1164 set_new_button_to(CLEAR_FLAG);
1165 connect_changed_signals(CONNECT_SIGNALS);
1166 jp_logf(JP_LOG_DEBUG, "Leaving memo_clear_details()\n");
1167
1168 return EXIT_SUCCESS;
1169 }
1170
1171 static int memo_get_details(struct Memo *new_memo, unsigned char *attrib) {
1172 GtkTextIter start_iter;
1173 GtkTextIter end_iter;
1174
1175 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(memo_text_buffer), &start_iter, &end_iter);
1176 new_memo->text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(memo_text_buffer), &start_iter, &end_iter, TRUE);
1177 if (new_memo->text[0] == '\0') {
1178 free(new_memo->text);
1179 new_memo->text = NULL;
1180 }
1181
1182 /* Get the category that is set from the menu */
1183 if (GTK_IS_WIDGET(category_menu2)) {
1184 *attrib = get_selected_category_from_combo_box(GTK_COMBO_BOX(category_menu2));
1185 }
1186 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(private_checkbox))) {
1187 *attrib |= dlpRecAttrSecret;
1188 }
1189 return EXIT_SUCCESS;
1190 }
1191
1192 gboolean
1193 addNewRecordMemo(GtkTreeModel *model,
1194 GtkTreePath *path,
1195 GtkTreeIter *iter,
1196 gpointer data) {
1197
1198 int *i = gtk_tree_path_get_indices(path);
1199
1200 if (i[0] == row_selected) {
1201 MyMemo *mmemo = NULL;
1202 gtk_tree_model_get(model, iter, MEMO_DATA_COLUMN_ENUM, &mmemo, -1);
1203 return addNewMemo(mmemo, data);
1204
1205 }
1206
1207 return FALSE;
1208
1209
1210 }
1211
1212 gboolean addNewMemo(MyMemo *mmemo, const void *data) {
1213 struct Memo new_memo;
1214 unsigned char attrib;
1215 int flag;
1216 unsigned int unique_id;
1217 int show_priv;
1218
1219 flag = GPOINTER_TO_INT(data);
1220
1221
1222 unique_id = 0;
1223
1224 /* Do masking like Palm OS 3.5 */
1225 if ((flag == COPY_FLAG) || (flag == MODIFY_FLAG)) {
1226 show_priv = show_privates(GET_PRIVATES);
1227
1228
1229 if (mmemo < (MyMemo *) LIST_MIN_DATA) {
1230 return TRUE;
1231 }
1232 if ((show_priv != SHOW_PRIVATES) &&
1233 (mmemo->attrib & dlpRecAttrSecret)) {
1234 return TRUE;
1235 }
1236 }
1237 /* End Masking */
1238 if (flag == CLEAR_FLAG) {
1239 /* Clear button was hit */
1240 memo_clear_details();
1241 connect_changed_signals(DISCONNECT_SIGNALS);
1242 set_new_button_to(NEW_FLAG);
1243 gtk_widget_grab_focus(GTK_WIDGET(memo_text));
1244 return TRUE;
1245 }
1246 if ((flag != NEW_FLAG) && (flag != MODIFY_FLAG) && (flag != COPY_FLAG)) {
1247 return TRUE;
1248 }
1249 if (flag == MODIFY_FLAG) {
1250
1251 unique_id = mmemo->unique_id;
1252 if (mmemo < (MyMemo *) LIST_MIN_DATA) {
1253 return TRUE;
1254 }
1255 if ((mmemo->rt == DELETED_PALM_REC) ||
1256 (mmemo->rt == DELETED_PC_REC) ||
1257 (mmemo->rt == MODIFIED_PALM_REC)) {
1258 jp_logf(JP_LOG_INFO, _("You can't modify a record that is deleted\n"));
1259 return TRUE;
1260 }
1261 }
1262 memo_get_details(&new_memo, &attrib);
1263
1264 set_new_button_to(CLEAR_FLAG);
1265
1266 /* Keep unique ID intact */
1267 if (flag == MODIFY_FLAG) {
1268 cb_delete_memo(NULL, data);
1269 if ((mmemo->rt == PALM_REC) || (mmemo->rt == REPLACEMENT_PALM_REC)) {
1270 pc_memo_write(&new_memo, REPLACEMENT_PALM_REC, attrib, &unique_id);
1271 } else {
1272 unique_id = 0;
1273 pc_memo_write(&new_memo, NEW_PC_REC, attrib, &unique_id);
1274 }
1275 } else {
1276 unique_id = 0;
1277 pc_memo_write(&new_memo, NEW_PC_REC, attrib, &unique_id);
1278 }
1279
1280 free_Memo(&new_memo);
1281 /* Don't return to modified record if search gui active */
1282 if (!glob_find_id) {
1283 glob_find_id = unique_id;
1284 }
1285 memo_redraw();
1286 return TRUE;
1287 }
1288
1289 static void cb_add_new_record(GtkWidget *widget, gpointer data) {
1290
1291 if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL) != 0) {
1292 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), addNewRecordMemo, data);
1293 } else {
1294 //no records exist in category yet.
1295 addNewMemo(NULL, data);
1296 }
1297 }
1298
1299 /* Do masking like Palm OS 3.5 */
1300 static void clear_mymemo(MyMemo *mmemo) {
1301 mmemo->unique_id = 0;
1302 mmemo->attrib = (unsigned char) (mmemo->attrib & 0xF8);
1303 if (mmemo->memo.text) {
1304 free(mmemo->memo.text);
1305 mmemo->memo.text = strdup("");
1306 }
1307
1308 return;
1309 }
1310
1311 /* End Masking */
1312
1313
1314 static gboolean cb_key_pressed_left_side(GtkWidget *widget,
1315 GdkEventKey *event,
1316 gpointer next_widget) {
1317 GtkTextBuffer *text_buffer;
1318 GtkTextIter iter;
1319
1320 if (event->keyval == GDK_KEY_Return) {
1321 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1322 gtk_widget_grab_focus(GTK_WIDGET(next_widget));
1323 /* Position cursor at start of text */
1324 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(next_widget));
1325 gtk_text_buffer_get_start_iter(text_buffer, &iter);
1326 gtk_text_buffer_place_cursor(text_buffer, &iter);
1327 return TRUE;
1328 }
1329
1330 return FALSE;
1331 }
1332
1333 static gboolean cb_key_pressed_right_side(GtkWidget *widget,
1334 GdkEventKey *event,
1335 gpointer next_widget) {
1336 /* Switch to treeView */
1337 if ((event->keyval == GDK_KEY_Return) && (event->state & GDK_SHIFT_MASK)) {
1338 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1339 gtk_widget_grab_focus(GTK_WIDGET(next_widget));
1340 return TRUE;
1341 }
1342 /* Call external editor for memo_text */
1343 if ((event->keyval == GDK_KEY_e) && (event->state & GDK_CONTROL_MASK)) {
1344 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1345
1346 /* Get current text and place in temporary file */
1347 GtkTextIter start_iter;
1348 GtkTextIter end_iter;
1349 char *text_out;
1350
1351 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(memo_text_buffer),
1352 &start_iter, &end_iter);
1353 text_out = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(memo_text_buffer),
1354 &start_iter, &end_iter, TRUE);
1355
1356
1357 char tmp_fname[] = "jpilot.XXXXXX";
1358 int tmpfd = mkstemp(tmp_fname);
1359 if (tmpfd < 0) {
1360 jp_logf(JP_LOG_WARN, _("Could not get temporary file name\n"));
1361 if (text_out)
1362 free(text_out);
1363 return TRUE;
1364 }
1365
1366 FILE *fptr = fdopen(tmpfd, "w");
1367 if (!fptr) {
1368 jp_logf(JP_LOG_WARN, _("Could not open temporary file for external editor\n"));
1369 if (text_out)
1370 free(text_out);
1371 return TRUE;
1372 }
1373 fwrite(text_out, strlen(text_out), 1, fptr);
1374 fwrite("\n", 1, 1, fptr);
1375 fclose(fptr);
1376
1377 /* Call external editor */
1378 char command[1024];
1379 const char *ext_editor;
1380
1381 get_pref(PREF_EXTERNAL_EDITOR, NULL, &ext_editor);
1382 if (!ext_editor) {
1383 jp_logf(JP_LOG_INFO, "External Editor command empty\n");
1384 if (text_out)
1385 free(text_out);
1386 return TRUE;
1387 }
1388
1389 if ((strlen(ext_editor) + strlen(tmp_fname) + 1) > sizeof(command)) {
1390 jp_logf(JP_LOG_WARN, _("External editor command too long to execute\n"));
1391 if (text_out)
1392 free(text_out);
1393 return TRUE;
1394 }
1395 g_snprintf(command, sizeof(command), "%s %s", ext_editor, tmp_fname);
1396
1397 /* jp_logf(JP_LOG_STDOUT|JP_LOG_FILE, _("executing command = [%s]\n"), command); */
1398 if (system(command) == -1) {
1399 /* Read data back from temporary file into memo */
1400 char text_in[0xFFFF];
1401 size_t bytes_read;
1402
1403 fptr = fopen(tmp_fname, "rb");
1404 if (!fptr) {
1405 jp_logf(JP_LOG_WARN, _("Could not open temporary file from external editor\n"));
1406 return TRUE;
1407 }
1408 bytes_read = fread(text_in, 1, 0xFFFF, fptr);
1409 fclose(fptr);
1410 unlink(tmp_fname);
1411
1412 text_in[--bytes_read] = '\0'; /* Strip final newline */
1413 /* Only update text if it has changed */
1414 if (strcmp(text_out, text_in)) {
1415 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(memo_text_buffer),
1416 text_in, -1);
1417 }
1418 }
1419
1420 if (text_out)
1421 free(text_out);
1422
1423 return TRUE;
1424 } /* End of external editor if */
1425
1426 return FALSE;
1427 }
1428
1429 gboolean
1430 selectRecordByRowMemo(GtkTreeModel *model,
1431 GtkTreePath *path,
1432 GtkTreeIter *iter,
1433 gpointer data) {
1434 int *i = gtk_tree_path_get_indices(path);
1435 if (i[0] == row_selected) {
1436 GtkTreeSelection *selection = NULL;
1437 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
1438 gtk_tree_selection_select_path(selection, path);
1439 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeView), path,
1440 gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), MEMO_COLUMN_ENUM),
1441 FALSE, 1.0, 0.0);
1442 return TRUE;
1443 }
1444
1445 return FALSE;
1446 }
1447
1448 static void memo_update_liststore(GtkListStore *pListStore, GtkWidget *tooltip_widget,
1449 MemoList **memo_list, int category, int main) {
1450 int num_entries, entries_shown;
1451 GtkTreeIter iter;
1452 size_t copy_max_length;
1453 char *last;
1454 char str2[MEMO_MAX_COLUMN_LEN];
1455 MemoList *temp_memo;
1456 char str[MEMO_LIST_CHAR_WIDTH + 10];
1457 int len, len1;
1458 int show_priv;
1459 long show_tooltips;
1460
1461 jp_logf(JP_LOG_DEBUG, "memo_update_liststore()\n");
1462
1463 free_MemoList(memo_list);
1464
1465 /* Need to get all records including private ones for the tooltips calculation */
1466 num_entries = get_memos2(memo_list, SORT_ASCENDING, 2, 2, 1, CATEGORY_ALL);
1467
1468 /* Start by clearing existing entry if in main window */
1469 if (main) {
1470 memo_clear_details();
1471 }
1472 GtkTreeSelection* treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
1473 gtk_tree_selection_set_select_function(treeSelection, NULL, NULL, NULL);
1474 gtk_list_store_clear(GTK_LIST_STORE(pListStore));
1475
1476 show_priv = show_privates(GET_PRIVATES);
1477
1478 entries_shown = 0;
1479 for (temp_memo = *memo_list; temp_memo; temp_memo = temp_memo->next) {
1480 if (((temp_memo->mmemo.attrib & 0x0F) != category) &&
1481 category != CATEGORY_ALL) {
1482 continue;
1483 }
1484
1485 /* Do masking like Palm OS 3.5 */
1486 if ((show_priv == MASK_PRIVATES) &&
1487 (temp_memo->mmemo.attrib & dlpRecAttrSecret)) {
1488 clear_mymemo(&temp_memo->mmemo);
1489 gtk_list_store_append(pListStore, &iter);
1490 gtk_list_store_set(pListStore, &iter,
1491 MEMO_COLUMN_ENUM, "----------------------------------------",
1492 MEMO_DATA_COLUMN_ENUM, &(temp_memo->mmemo),
1493 -1);
1494 entries_shown++;
1495 continue;
1496 }
1497 /* End Masking */
1498
1499 /* Hide the private records if need be */
1500 if ((show_priv != SHOW_PRIVATES) &&
1501 (temp_memo->mmemo.attrib & dlpRecAttrSecret)) {
1502 continue;
1503 }
1504
1505 sprintf(str, "%d. ", entries_shown + 1);
1506
1507 len1 = (int) strlen(str);
1508 len = ((int) strlen(temp_memo->mmemo.memo.text)) + 1;
1509 /* ..memo treeView does not display '/n' */
1510 if ((copy_max_length = (size_t) len) > MEMO_LIST_CHAR_WIDTH) {
1511 copy_max_length = MEMO_LIST_CHAR_WIDTH;
1512 }
1513 last = multibyte_safe_memccpy(str + len1, temp_memo->mmemo.memo.text, '\n', copy_max_length);
1514 if (last) {
1515 *(last - 1) = '\0';
1516 } else {
1517 str[copy_max_length + len1] = '\0';
1518 }
1519 lstrncpy_remove_cr_lfs(str2, str, MEMO_MAX_COLUMN_LEN);
1520 gtk_list_store_append(pListStore, &iter);
1521
1522 /* Highlight row background depending on status */
1523 GdkRGBA bgColor;
1524 gboolean showBgColor = FALSE;
1525 switch (temp_memo->mmemo.rt) {
1526 case NEW_PC_REC:
1527 case REPLACEMENT_PALM_REC:
1528 bgColor = get_color(LIST_NEW_RED, LIST_NEW_GREEN, LIST_NEW_BLUE);
1529 showBgColor = TRUE;
1530 break;
1531 case DELETED_PALM_REC:
1532 case DELETED_PC_REC:
1533 bgColor = get_color(LIST_DEL_RED, LIST_DEL_GREEN, LIST_DEL_BLUE);
1534 showBgColor = TRUE;
1535 break;
1536 case MODIFIED_PALM_REC:
1537 bgColor = get_color(LIST_MOD_RED, LIST_MOD_GREEN, LIST_MOD_BLUE);
1538 showBgColor = TRUE;
1539 break;
1540 default:
1541 if (temp_memo->mmemo.attrib & dlpRecAttrSecret) {
1542 bgColor = get_color(LIST_PRIVATE_RED, LIST_PRIVATE_GREEN, LIST_PRIVATE_BLUE);
1543 showBgColor = TRUE;
1544 } else {
1545 showBgColor = FALSE;
1546 }
1547 }
1548 gtk_list_store_set(pListStore, &iter,
1549 MEMO_COLUMN_ENUM, str2,
1550 MEMO_DATA_COLUMN_ENUM, &(temp_memo->mmemo),
1551 MEMO_BACKGROUND_COLOR_ENUM, showBgColor ? &bgColor : NULL,
1552 MEMO_BACKGROUND_COLOR_ENABLED_ENUM, showBgColor,
1553 -1);
1554 entries_shown++;
1555 }
1556
1557 jp_logf(JP_LOG_DEBUG, "entries_shown=%d\n", entries_shown);
1558
1559 // Set callback for a row selected
1560 gtk_tree_selection_set_select_function(treeSelection, handleRowSelectionForMemo, NULL, NULL);
1561
1562 /* If there are items in the list, highlight the selected row */
1563 if ((main) && (entries_shown > 0)) {
1564 /* First, select any record being searched for */
1565 if (glob_find_id) {
1566 memo_find();
1567 }
1568 /* Second, try the currently selected row */
1569 else if (row_selected < entries_shown) {
1570 gtk_tree_model_foreach(GTK_TREE_MODEL(pListStore), selectRecordByRowMemo, NULL);
1571 }
1572 /* Third, select row 0 if nothing else is possible */
1573 else {
1574 row_selected = 0;
1575 gtk_tree_model_foreach(GTK_TREE_MODEL(pListStore), selectRecordByRowMemo, NULL);
1576 }
1577 }
1578
1579
1580 if (tooltip_widget) {
1581 get_pref(PREF_SHOW_TOOLTIPS, &show_tooltips, NULL);
1582 if (memo_list == NULL) {
1583 set_tooltip((int) show_tooltips, tooltip_widget, _("0 records"));
1584 } else {
1585 sprintf(str, _("%d of %d records"), entries_shown, num_entries);
1586 set_tooltip((int) show_tooltips, tooltip_widget, str);
1587 }
1588 }
1589
1590 if (main) {
1591 connect_changed_signals(CONNECT_SIGNALS);
1592 }
1593
1594 /* return focus to treeView after any big operation which requires a redraw */
1595 gtk_widget_grab_focus(GTK_WIDGET(treeView));
1596 jp_logf(JP_LOG_DEBUG, "Leaving memo_update_liststore()\n");
1597 }
1598
1599 gboolean
1600 findRecordMemo(GtkTreeModel *model,
1601 GtkTreePath *path,
1602 GtkTreeIter *iter,
1603 gpointer data) {
1604
1605 if (glob_find_id) {
1606 MyMemo *mmemo = NULL;
1607 gtk_tree_model_get(model, iter, MEMO_DATA_COLUMN_ENUM, &mmemo, -1);
1608
1609 if (mmemo->unique_id == glob_find_id) {
1610 GtkTreeSelection *selection = NULL;
1611 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
1612 gtk_tree_selection_set_select_function(selection, handleRowSelectionForMemo, NULL, NULL);
1613 gtk_tree_selection_select_path(selection, path);
1614 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeView), path,
1615 gtk_tree_view_get_column(GTK_TREE_VIEW(treeView), MEMO_DATA_COLUMN_ENUM),
1616 FALSE, 1.0, 0.0);
1617 glob_find_id = 0;
1618 return TRUE;
1619 }
1620 }
1621 return FALSE;
1622 }
1623
1624 static int memo_find(void) {
1625 gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), findRecordMemo, NULL);
1626 return EXIT_SUCCESS;
1627 }
1628
1629 static int memo_redraw(void) {
1630 memo_update_liststore(listStore, category_menu1, &glob_memo_list, memo_category, TRUE);
1631 return EXIT_SUCCESS;
1632 }
1633
1634 int memo_cycle_cat(void) {
1635 int b;
1636 int i, new_cat;
1637
1638 b = dialog_save_changed_record(pane, record_changed);
1639 if (b == DIALOG_SAID_2) {
1640 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1641 }
1642
1643 if (memo_category == CATEGORY_ALL) {
1644 new_cat = -1;
1645 } else {
1646 new_cat = find_sort_cat_pos(memo_category);
1647 }
1648
1649 for (i = 0; i < NUM_MEMO_CAT_ITEMS; i++) {
1650 new_cat++;
1651 if (new_cat >= NUM_MEMO_CAT_ITEMS) {
1652 memo_category = CATEGORY_ALL;
1653 break;
1654 }
1655 if ((sort_l[new_cat].Pcat) && (sort_l[new_cat].Pcat[0])) {
1656 memo_category = sort_l[new_cat].cat_num;
1657 break;
1658 }
1659 }
1660
1661 row_selected = 0;
1662
1663 return EXIT_SUCCESS;
1664 }
1665
1666 int memo_refresh(void) {
1667 int index, index2;
1668
1669 if (glob_find_id) {
1670 memo_category = CATEGORY_ALL;
1671 }
1672 if (memo_category == CATEGORY_ALL) {
1673 index = 0;
1674 index2 = 0;
1675 } else {
1676 index = find_sort_cat_pos(memo_category);
1677 index2 = find_menu_cat_pos(index) + 1;
1678 index += 1;
1679 }
1680 memo_update_liststore(listStore, category_menu1, &glob_memo_list, memo_category, TRUE);
1681 if (index < 0) {
1682 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
1683 } else {
1684 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), index2);
1685 }
1686
1687 return EXIT_SUCCESS;
1688 }
1689
1690 int memo_gui_cleanup(void) {
1691 int b;
1692
1693 b = dialog_save_changed_record(pane, record_changed);
1694 if (b == DIALOG_SAID_2) {
1695 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1696 }
1697 free_MemoList(&glob_memo_list);
1698 connect_changed_signals(DISCONNECT_SIGNALS);
1699 set_pref(PREF_MEMO_PANE, gtk_paned_get_position(GTK_PANED(pane)), NULL, TRUE);
1700 set_pref(PREF_LAST_MEMO_CATEGORY, memo_category, NULL, TRUE);
1701 GtkTreeSelection* treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
1702 gtk_tree_selection_set_select_function(treeSelection, NULL, NULL, NULL);
1703 gtk_list_store_clear(GTK_LIST_STORE(listStore));
1704
1705 return EXIT_SUCCESS;
1706 }
1707
1708 /* Main function */
1709 int memo_gui(GtkWidget *vbox, GtkWidget *hbox) {
1710 int i;
1711 GtkWidget *scrolled_window;
1712 GtkWidget *vbox1, *vbox2, *hbox_temp;
1713 GtkWidget *separator;
1714 long ivalue;
1715 GtkAccelGroup *accel_group;
1716 long char_set;
1717 long show_tooltips;
1718 char *cat_name;
1719
1720 get_pref(PREF_MEMO_VERSION, &memo_version, NULL);
1721
1722 /* Do some initialization */
1723 row_selected = 0;
1724
1725 record_changed = CLEAR_FLAG;
1726
1727 get_memo_app_info(&memo_app_info);
1728 listStore = gtk_list_store_new(MEMO_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_RGBA,
1729 G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN);
1730
1731 /* Initialize categories */
1732 get_pref(PREF_CHAR_SET, &char_set, NULL);
1733 for (i = 1; i < NUM_MEMO_CAT_ITEMS; i++) {
1734 cat_name = charset_p2newj(memo_app_info.category.name[i], 31, (int) char_set);
1735 strcpy(sort_l[i - 1].Pcat, cat_name);
1736 free(cat_name);
1737 sort_l[i - 1].cat_num = i;
1738 }
1739 /* put reserved 'Unfiled' category at end of list */
1740 cat_name = charset_p2newj(memo_app_info.category.name[0], 31, (int) char_set);
1741 strcpy(sort_l[NUM_MEMO_CAT_ITEMS - 1].Pcat, cat_name);
1742 free(cat_name);
1743 sort_l[NUM_MEMO_CAT_ITEMS - 1].cat_num = 0;
1744
1745 qsort(sort_l, NUM_MEMO_CAT_ITEMS - 1, sizeof(struct sorted_cats), cat_compare);
1746
1747 #ifdef JPILOT_DEBUG
1748 for (i=0; i<NUM_MEMO_CAT_ITEMS; i++) {
1749 printf("cat %d [%s]\n", sort_l[i].cat_num, sort_l[i].Pcat);
1750 }
1751 #endif
1752
1753 get_pref(PREF_LAST_MEMO_CATEGORY, &ivalue, NULL);
1754 memo_category = (int) ivalue;
1755
1756 if ((memo_category != CATEGORY_ALL)
1757 && (memo_app_info.category.name[memo_category][0] == '\0')) {
1758 memo_category = CATEGORY_ALL;
1759 }
1760
1761 /* Create basic GUI with left and right boxes and sliding pane */
1762 accel_group = gtk_accel_group_new();
1763 gtk_window_add_accel_group(GTK_WINDOW(gtk_widget_get_toplevel(vbox)),
1764 accel_group);
1765 get_pref(PREF_SHOW_TOOLTIPS, &show_tooltips, NULL);
1766
1767 pane = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
1768 get_pref(PREF_MEMO_PANE, &ivalue, NULL);
1769 gtk_paned_set_position(GTK_PANED(pane), (gint) ivalue);
1770
1771 gtk_box_pack_start(GTK_BOX(hbox), pane, TRUE, TRUE, 5);
1772
1773 vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1774 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1775
1776 gtk_paned_pack1(GTK_PANED(pane), vbox1, TRUE, FALSE);
1777 gtk_paned_pack2(GTK_PANED(pane), vbox2, TRUE, FALSE);
1778
1779 /* Left side of GUI */
1780
1781 /* Separator */
1782 separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1783 gtk_box_pack_start(GTK_BOX(vbox1), separator, FALSE, FALSE, 5);
1784
1785 /* 'Today is:' label */
1786 glob_date_label = gtk_label_new(" ");
1787 gtk_box_pack_start(GTK_BOX(vbox1), glob_date_label, FALSE, FALSE, 0);
1788 timeout_date(NULL);
1789 glob_date_timer_tag = g_timeout_add(CLOCK_TICK, timeout_sync_up, NULL);
1790
1791 /* Separator */
1792 separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1793 gtk_box_pack_start(GTK_BOX(vbox1), separator, FALSE, FALSE, 5);
1794
1795 /* Left-side Category menu */
1796 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1797 gtk_box_pack_start(GTK_BOX(vbox1), hbox_temp, FALSE, FALSE, 0);
1798
1799 make_category_menu(&category_menu1,
1800 sort_l, cb_category, TRUE, TRUE);
1801 gtk_box_pack_start(GTK_BOX(hbox_temp), category_menu1, TRUE, TRUE, 0);
1802
1803 /* Memo list scrolled window */
1804 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1805 gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 0);
1806 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1807 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1808 gtk_box_pack_start(GTK_BOX(vbox1), scrolled_window, TRUE, TRUE, 0);
1809
1810
1811 initializeTreeView();
1812 gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(treeView));
1813
1814 /* Right side of GUI */
1815
1816 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
1817 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
1818
1819 /* Cancel button */
1820 CREATE_BUTTON(cancel_record_button, _("Cancel"), CANCEL, _("Cancel the modifications"), GDK_KEY_Escape, 0, "ESC")
1821 g_signal_connect(G_OBJECT(cancel_record_button), "clicked",
1822 G_CALLBACK(cb_cancel), NULL);
1823
1824 /* Delete Button */
1825 CREATE_BUTTON(delete_record_button, _("Delete"), DELETE, _("Delete the selected record"), GDK_d, GDK_CONTROL_MASK,
1826 "Ctrl+D")
1827 g_signal_connect(G_OBJECT(delete_record_button), "clicked",
1828 G_CALLBACK(cb_delete_memo),
1829 GINT_TO_POINTER(DELETE_FLAG));
1830
1831 /* Undelete Button */
1832 CREATE_BUTTON(undelete_record_button, _("Undelete"), UNDELETE, _("Undelete the selected record"), 0, 0, "")
1833 g_signal_connect(G_OBJECT(undelete_record_button), "clicked",
1834 G_CALLBACK(cb_undelete_memo),
1835 GINT_TO_POINTER(UNDELETE_FLAG));
1836
1837 /* Copy button */
1838 CREATE_BUTTON(copy_record_button, _("Copy"), COPY, _("Copy the selected record"), GDK_c,
1839 GDK_CONTROL_MASK | GDK_SHIFT_MASK, "Ctrl+Shift+C")
1840 g_signal_connect(G_OBJECT(copy_record_button), "clicked",
1841 G_CALLBACK(cb_add_new_record),
1842 GINT_TO_POINTER(COPY_FLAG));
1843
1844 /* New button */
1845 CREATE_BUTTON(new_record_button, _("New Record"), NEW, _("Add a new record"), GDK_n, GDK_CONTROL_MASK, "Ctrl+N")
1846 g_signal_connect(G_OBJECT(new_record_button), "clicked",
1847 G_CALLBACK(cb_add_new_record),
1848 GINT_TO_POINTER(CLEAR_FLAG));
1849
1850 /* "Add Record" button */
1851 CREATE_BUTTON(add_record_button, _("Add Record"), ADD, _("Add the new record"), GDK_KEY_Return, GDK_CONTROL_MASK,
1852 "Ctrl+Enter")
1853 g_signal_connect(G_OBJECT(add_record_button), "clicked",
1854 G_CALLBACK(cb_add_new_record),
1855 GINT_TO_POINTER(NEW_FLAG));
1856 #ifndef ENABLE_STOCK_BUTTONS
1857 gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(gtk_bin_get_child(GTK_BIN(add_record_button)))),
1858 "label_high");
1859 #endif
1860
1861 /* "Apply Changes" button */
1862 CREATE_BUTTON(apply_record_button, _("Apply Changes"), APPLY, _("Commit the modifications"), GDK_KEY_Return,
1863 GDK_CONTROL_MASK, "Ctrl+Enter")
1864 g_signal_connect(G_OBJECT(apply_record_button), "clicked",
1865 G_CALLBACK(cb_add_new_record),
1866 GINT_TO_POINTER(MODIFY_FLAG));
1867 #ifndef ENABLE_STOCK_BUTTONS
1868 gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(gtk_bin_get_child(GTK_BIN(apply_record_button)))),
1869 "label_high");
1870 #endif
1871
1872 /* Separator */
1873 separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1874 gtk_box_pack_start(GTK_BOX(vbox2), separator, FALSE, FALSE, 5);
1875
1876 /* Private check box */
1877 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1878 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
1879 private_checkbox = gtk_check_button_new_with_label(_("Private"));
1880 gtk_box_pack_end(GTK_BOX(hbox_temp), private_checkbox, FALSE, FALSE, 0);
1881
1882 /* Right-side Category menu */
1883 make_category_menu(&category_menu2,
1884 sort_l, NULL, FALSE, FALSE);
1885 gtk_box_pack_start(GTK_BOX(hbox_temp), category_menu2, TRUE, TRUE, 0);
1886
1887 /* Description text box */
1888 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1889 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
1890
1891 hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1892 gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, TRUE, TRUE, 0);
1893
1894 memo_text = gtk_text_view_new();
1895 memo_text_buffer = G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(memo_text)));
1896 gtk_text_view_set_editable(GTK_TEXT_VIEW(memo_text), TRUE);
1897 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(memo_text), GTK_WRAP_WORD);
1898
1899 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1900 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1901 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1902 gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 1);
1903 gtk_container_add(GTK_CONTAINER(scrolled_window), memo_text);
1904 gtk_box_pack_start(GTK_BOX(hbox_temp), scrolled_window,TRUE,TRUE,0);
1905 /* Capture the Enter & Shift-Enter key combinations to move back and
1906 * forth between the left- and right-hand sides of the display. */
1907 g_signal_connect(G_OBJECT(treeView), "key_press_event",
1908 G_CALLBACK(cb_key_pressed_left_side), memo_text);
1909
1910 g_signal_connect(G_OBJECT(memo_text), "key_press_event",
1911 G_CALLBACK(cb_key_pressed_right_side), treeView);
1912
1913 /**********************************************************************/
1914
1915 gtk_widget_show_all(vbox);
1916 gtk_widget_show_all(hbox);
1917
1918 gtk_widget_hide(add_record_button);
1919 gtk_widget_hide(apply_record_button);
1920 gtk_widget_hide(undelete_record_button);
1921 gtk_widget_hide(cancel_record_button);
1922
1923 memo_refresh();
1924
1925 return EXIT_SUCCESS;
1926 }
1927
1928 void initializeTreeView() {
1929 GtkTreeSelection *treeSelection = NULL;
1930 treeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listStore));
1931 GtkCellRenderer *columnRenderer = gtk_cell_renderer_text_new();
1932 // gtk_cell_renderer_set_fixed_size(columnRenderer, -1, 1);
1933 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("", columnRenderer, "text", MEMO_COLUMN_ENUM,
1934 "cell-background-rgba",
1935 MEMO_BACKGROUND_COLOR_ENUM,
1936 "cell-background-set",
1937 MEMO_BACKGROUND_COLOR_ENABLED_ENUM, NULL);
1938 gtk_tree_view_column_set_fixed_width(column, (gint) 50);
1939 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeView), FALSE);
1940 gtk_tree_view_insert_column(GTK_TREE_VIEW(treeView), column, 0);
1941 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1942 treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
1943 gtk_tree_selection_set_select_function(treeSelection, handleRowSelectionForMemo, NULL, NULL);
1944 gtk_widget_set_events(treeView, GDK_BUTTON1_MOTION_MASK);
1945 g_signal_connect (G_OBJECT(treeView), "motion_notify_event",
1946 G_CALLBACK(motion_notify_event), NULL);
1947 g_signal_connect (G_OBJECT(treeView), "button-press-event",
1948 G_CALLBACK(button_pressed_for_motion), NULL);
1949 g_signal_connect (G_OBJECT(treeView), "button-release-event",
1950 G_CALLBACK(button_released_for_motion), NULL);
1951
1952 }
1953
1954 gboolean handleRowSelectionForMemo(GtkTreeSelection *selection,
1955 GtkTreeModel *model,
1956 GtkTreePath *path,
1957 gboolean path_currently_selected,
1958 gpointer userdata) {
1959 GtkTreeIter iter;
1960
1961 struct Memo *memo;
1962 MyMemo *mmemo;
1963 int b;
1964 int index, sorted_position;
1965 // int unique_id;
1966
1967 if ((gtk_tree_model_get_iter(model, &iter, path)) && (!path_currently_selected)) {
1968 int *i = gtk_tree_path_get_indices(path);
1969 row_selected = i[0];
1970 gtk_tree_model_get(model, &iter, MEMO_DATA_COLUMN_ENUM, &mmemo, -1);
1971 if ((record_changed == MODIFY_FLAG) || (record_changed == NEW_FLAG)) {
1972 //if (mmemo != NULL) {
1973 // unique_id = mmemo->unique_id;
1974 //}
1975
1976 // We need to turn this "scroll with mouse held down" thing off
1977 button_set_for_motion(0);
1978
1979 b = dialog_save_changed_record_with_cancel(pane, record_changed);
1980 if (b == DIALOG_SAID_1) { /* Cancel */
1981 return TRUE;
1982 }
1983 if (b == DIALOG_SAID_3) { /* Save */
1984 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
1985 }
1986
1987 set_new_button_to(CLEAR_FLAG);
1988 return TRUE;
1989 }
1990 if (mmemo == NULL) {
1991 return TRUE;
1992 }
1993
1994 if (mmemo->rt == DELETED_PALM_REC ||
1995 (mmemo->rt == DELETED_PC_REC))
1996 /* Possible later addition of undelete code for modified deleted records
1997 || mmemo->rt == MODIFIED_PALM_REC
1998 */
1999 {
2000 set_new_button_to(UNDELETE_FLAG);
2001 } else {
2002 set_new_button_to(CLEAR_FLAG);
2003 }
2004
2005 connect_changed_signals(DISCONNECT_SIGNALS);
2006
2007 memo = &(mmemo->memo);
2008
2009 index = mmemo->attrib & 0x0F;
2010 sorted_position = find_sort_cat_pos(index);
2011 int pos = findSortedPostion(sorted_position, GTK_COMBO_BOX(category_menu2));
2012 if (pos != sorted_position && index != 0) {
2013 /* Illegal category */
2014 jp_logf(JP_LOG_DEBUG, "Category is not legal\n");
2015 index = sorted_position = 0;
2016 }
2017
2018 if (sorted_position < 0) {
2019 jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
2020 }
2021 gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu2),find_menu_cat_pos(sorted_position));
2022
2023 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(memo_text_buffer), memo->text, -1);
2024
2025 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private_checkbox),
2026 mmemo->attrib & dlpRecAttrSecret);
2027
2028 connect_changed_signals(CONNECT_SIGNALS);
2029 return TRUE; /* allow selection state to change */
2030 }
2031 return TRUE; /* allow selection state to change */
2032 }