"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. For more information about "memo_gui.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.8.2_vs_2_0_1.

    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(&ltime);
  618         now = localtime(&ltime);
  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(&ltime);
  706                     now = localtime(&ltime);
  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 }