"Fossies" - the Fresh Open Source Software Archive

Member "jpilot-2_0_1/address_gui.c" (3 Apr 2021, 193050 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 "address_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  * address_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 <signal.h>
   26 #include <ctype.h>
   27 #include <sys/stat.h>
   28 #include <gtk/gtk.h>
   29 #include <gdk/gdkkeysyms.h>
   30 #include "address.h"
   31 #include "i18n.h"
   32 #include "prefs.h"
   33 #include "print.h"
   34 #include "password.h"
   35 #include "export.h"
   36 #include "stock_buttons.h"
   37 
   38 /********************************* Constants **********************************/
   39 #define NUM_ADDRESS_CAT_ITEMS 16
   40 #define NUM_PHONE_ENTRIES 7
   41 #define NUM_PHONE_LABELS 8
   42 #define MAX_NUM_TEXTS contNote+1
   43 #define NUM_IM_LABELS 5
   44 
   45 #define ADDRESS_MAX_LIST_NAME 30
   46 #define ADDRESS_MAX_COLUMN_LEN 80
   47 #define NUM_CONT_CSV_FIELDS 56
   48 #define NUM_ADDR_CSV_FIELDS 27
   49 
   50 /* Size of photo to display in Jpilot.  Actual photo can be larger */
   51 #define PHOTO_X_SZ 139
   52 #define PHOTO_Y_SZ 144
   53 
   54 /* Many RFCs require that the line termination be CRLF rather than just \n.
   55  * For conformance to standards this requires adding the two-byte string to
   56  * the end of strings destined for export */
   57 #define CRLF "\x0D\x0A"
   58 
   59 #define CONNECT_SIGNALS 400
   60 #define DISCONNECT_SIGNALS 401
   61 
   62 /******************************* Global vars **********************************/
   63 static address_schema_entry *schema;
   64 static int schema_size;
   65 
   66 static address_schema_entry contact_schema[NUM_CONTACT_FIELDS] = {
   67         {contLastname,  0, ADDRESS_GUI_LABEL_TEXT},
   68         {contFirstname, 0, ADDRESS_GUI_LABEL_TEXT},
   69         {contCompany,   0, ADDRESS_GUI_LABEL_TEXT},
   70         {contTitle,     0, ADDRESS_GUI_LABEL_TEXT},
   71         {contPhone1,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
   72         {contPhone2,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
   73         {contPhone3,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
   74         {contPhone4,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
   75         {contPhone5,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
   76         {contPhone6,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
   77         {contPhone7,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
   78         {contIM1,       0, ADDRESS_GUI_IM_MENU_TEXT},
   79         {contIM2,       0, ADDRESS_GUI_IM_MENU_TEXT},
   80         {contWebsite,   0, ADDRESS_GUI_WEBSITE_TEXT},
   81         {contAddress1,  1, ADDRESS_GUI_ADDR_MENU_TEXT},
   82         {contCity1,     1, ADDRESS_GUI_LABEL_TEXT},
   83         {contState1,    1, ADDRESS_GUI_LABEL_TEXT},
   84         {contZip1,      1, ADDRESS_GUI_LABEL_TEXT},
   85         {contCountry1,  1, ADDRESS_GUI_LABEL_TEXT},
   86         {contAddress2,  2, ADDRESS_GUI_ADDR_MENU_TEXT},
   87         {contCity2,     2, ADDRESS_GUI_LABEL_TEXT},
   88         {contState2,    2, ADDRESS_GUI_LABEL_TEXT},
   89         {contZip2,      2, ADDRESS_GUI_LABEL_TEXT},
   90         {contCountry2,  2, ADDRESS_GUI_LABEL_TEXT},
   91         {contAddress3,  3, ADDRESS_GUI_ADDR_MENU_TEXT},
   92         {contCity3,     3, ADDRESS_GUI_LABEL_TEXT},
   93         {contState3,    3, ADDRESS_GUI_LABEL_TEXT},
   94         {contZip3,      3, ADDRESS_GUI_LABEL_TEXT},
   95         {contCountry3,  3, ADDRESS_GUI_LABEL_TEXT},
   96         {contBirthday,  4, ADDRESS_GUI_BIRTHDAY},
   97         {contCustom1,   4, ADDRESS_GUI_LABEL_TEXT},
   98         {contCustom2,   4, ADDRESS_GUI_LABEL_TEXT},
   99         {contCustom3,   4, ADDRESS_GUI_LABEL_TEXT},
  100         {contCustom4,   4, ADDRESS_GUI_LABEL_TEXT},
  101         {contCustom5,   4, ADDRESS_GUI_LABEL_TEXT},
  102         {contCustom6,   4, ADDRESS_GUI_LABEL_TEXT},
  103         {contCustom7,   4, ADDRESS_GUI_LABEL_TEXT},
  104         {contCustom8,   4, ADDRESS_GUI_LABEL_TEXT},
  105         {contCustom9,   4, ADDRESS_GUI_LABEL_TEXT},
  106         {contNote,      5, ADDRESS_GUI_LABEL_TEXT}
  107 };
  108 
  109 static address_schema_entry address_schema[NUM_ADDRESS_FIELDS] = {
  110         {contLastname,  0, ADDRESS_GUI_LABEL_TEXT},
  111         {contFirstname, 0, ADDRESS_GUI_LABEL_TEXT},
  112         {contTitle,     0, ADDRESS_GUI_LABEL_TEXT},
  113         {contCompany,   0, ADDRESS_GUI_LABEL_TEXT},
  114         {contPhone1,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
  115         {contPhone2,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
  116         {contPhone3,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
  117         {contPhone4,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
  118         {contPhone5,    0, ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT},
  119         {contAddress1,  1, ADDRESS_GUI_LABEL_TEXT},
  120         {contCity1,     1, ADDRESS_GUI_LABEL_TEXT},
  121         {contState1,    1, ADDRESS_GUI_LABEL_TEXT},
  122         {contZip1,      1, ADDRESS_GUI_LABEL_TEXT},
  123         {contCountry1,  1, ADDRESS_GUI_LABEL_TEXT},
  124         {contCustom1,   2, ADDRESS_GUI_LABEL_TEXT},
  125         {contCustom2,   2, ADDRESS_GUI_LABEL_TEXT},
  126         {contCustom3,   2, ADDRESS_GUI_LABEL_TEXT},
  127         {contCustom4,   2, ADDRESS_GUI_LABEL_TEXT},
  128         {contNote,      3, ADDRESS_GUI_LABEL_TEXT}
  129 };
  130 
  131 /* Keeps track of whether code is using Address or Contacts database.
  132  * 0 is AddressDB, 1 is ContactsDB */
  133 static long address_version = 0;
  134 
  135 static GtkWidget *treeView;
  136 static GtkTreeSelection *treeSelection;
  137 static GtkListStore *listStore;
  138 static GtkWidget *addr_text[MAX_NUM_TEXTS];
  139 static GObject *addr_text_buffer[MAX_NUM_TEXTS];
  140 static GtkWidget *addr_all;
  141 static GObject *addr_all_buffer;
  142 static GtkWidget *notebook_label[NUM_CONTACT_NOTEBOOK_PAGES];
  143 static GtkWidget *phone_type_list_menu[NUM_PHONE_ENTRIES];
  144 static GtkWidget *address_type_list_menu[NUM_ADDRESSES];
  145 static GtkWidget *IM_type_list_menu[NUM_IMS];
  146 static int address_phone_label_selected[NUM_PHONE_ENTRIES];
  147 static int address_type_selected[NUM_ADDRESSES];
  148 static int IM_type_selected[NUM_IMS];
  149 
  150 /* Need two extra slots for the ALL category and Edit Categories... */
  151 //static GtkWidget *address_cat_menu_item1[NUM_ADDRESS_CAT_ITEMS + 2];
  152 //static GtkWidget *address_cat_menu_item2[NUM_ADDRESS_CAT_ITEMS];
  153 static GtkWidget *category_menu1;
  154 static GtkWidget *category_menu2;
  155 static GtkWidget *address_quickfind_entry;
  156 static GtkWidget *notebook;
  157 static GtkWidget *pane;
  158 static GtkWidget *radio_button[NUM_PHONE_ENTRIES];
  159 static GtkWidget *dial_button[NUM_PHONE_ENTRIES];
  160 
  161 static struct AddressAppInfo address_app_info;
  162 static struct ContactAppInfo contact_app_info;
  163 static struct sorted_cats sort_l[NUM_ADDRESS_CAT_ITEMS];
  164 static int address_category = CATEGORY_ALL;
  165 static int rowSelected;
  166 
  167 static ContactList *glob_contact_list = NULL;
  168 static ContactList *export_contact_list = NULL;
  169 
  170 static GtkWidget *new_record_button;
  171 static GtkWidget *apply_record_button;
  172 static GtkWidget *add_record_button;
  173 static GtkWidget *delete_record_button;
  174 static GtkWidget *undelete_record_button;
  175 static GtkWidget *copy_record_button;
  176 static GtkWidget *cancel_record_button;
  177 static int record_changed;
  178 
  179 static GtkWidget *private_checkbox;
  180 static GtkWidget *picture_button;
  181 static GtkWidget *birthday_checkbox;
  182 static GtkWidget *birthday_button;
  183 static GtkWidget *birthday_box;
  184 static GtkWidget *reminder_checkbox;
  185 static GtkWidget *reminder_entry;
  186 static GtkWidget *reminder_box;
  187 static struct tm birthday;
  188 static GtkWidget *image = NULL;
  189 static struct ContactPicture contact_picture;
  190 static GList *changed_list = NULL;
  191 
  192 extern GtkWidget *glob_date_label;
  193 extern int glob_date_timer_tag;
  194 
  195 /****************************** Prototypes ************************************/
  196 static void connect_changed_signals(int con_or_dis);
  197 
  198 static void address_update_listStore(GtkListStore *listStore, GtkWidget *tooltip_widget,
  199                                      ContactList **cont_list, int category,
  200                                      int main);
  201 
  202 gboolean
  203 findAddressRecordAndSelect(GtkTreeModel *model,
  204                            GtkTreePath *path,
  205                            GtkTreeIter *iter,
  206                            gpointer data);
  207 
  208 gboolean
  209 findAddressRecordByTextAndSelect(GtkTreeModel *model,
  210                                  GtkTreePath *path,
  211                                  GtkTreeIter *iter,
  212                                  const gpointer data);
  213 
  214 gboolean
  215 selectRecordAddressByRow(GtkTreeModel *model,
  216                          GtkTreePath *path,
  217                          GtkTreeIter *iter,
  218                          gpointer data);
  219 
  220 gboolean
  221 findAndSetGlobalAddressId(GtkTreeModel *model,
  222                           GtkTreePath *path,
  223                           GtkTreeIter *iter,
  224                           gpointer data);
  225 
  226 gboolean printAddressRecord(GtkTreeModel *model,
  227                             GtkTreePath *path,
  228                             GtkTreeIter *iter,
  229                             gpointer data);
  230 
  231 gboolean deleteAddressRecord(GtkTreeModel *model,
  232                              GtkTreePath *path,
  233                              GtkTreeIter *iter,
  234                              gpointer data);
  235 
  236 gboolean addNewAddressRecord(GtkTreeModel *model,
  237                              GtkTreePath *path,
  238                              GtkTreeIter *iter,
  239                              gpointer data);
  240 
  241 gboolean deleteAddressContactRecord(GtkTreeModel *model,
  242                                     GtkTreePath *path,
  243                                     GtkTreeIter *iter,
  244                                     gpointer data);
  245 
  246 gboolean undeleteAddressRecord(GtkTreeModel *model,
  247                                GtkTreePath *path,
  248                                GtkTreeIter *iter,
  249                                gpointer data);
  250 
  251 void undeleteAddress(MyContact *mcont, gpointer data);
  252 
  253 static void cb_delete_address_or_contact(GtkWidget *widget, gpointer data);
  254 
  255 void deleteAddress(MyContact *mcont, gpointer data);
  256 
  257 void deleteAddressContact(MyContact *mcont, gpointer data);
  258 
  259 void addNewAddressRecordToDataStructure(MyContact *mcont, gpointer data);
  260 
  261 static int address_redraw(void);
  262 
  263 int printAddress(MyContact *mcont, gpointer data);
  264 
  265 static int address_find(void);
  266 
  267 static void get_address_attrib(unsigned char *attrib);
  268 
  269 
  270 static gboolean handleRowSelectionForAddress(GtkTreeSelection *selection,
  271                                              GtkTreeModel *model,
  272                                              GtkTreePath *path,
  273                                              gboolean path_currently_selected,
  274                                              gpointer userdata);
  275 
  276 
  277 enum {
  278     ADDRESS_NAME_COLUMN_ENUM,
  279     ADDRESS_NOTE_COLUMN_ENUM,
  280     ADDRESS_PHONE_COLUMN_ENUM,
  281     ADDRESS_DATA_COLUMN_ENUM,
  282     ADDRESS_BACKGROUND_COLOR_ENUM,
  283     ADDRESS_BACKGROUND_COLOR_ENABLED_ENUM,
  284     ADDRESS_FOREGROUND_COLOR_ENUM,
  285     ADDRESSS_FOREGROUND_COLOR_ENABLED_ENUM,
  286     ADDRESS_NUM_COLS
  287 };
  288 /****************************** Main Code *************************************/
  289 /* Called once on initialization of GUI */
  290 static void init(void) {
  291     time_t ltime;
  292     struct tm *now;
  293 
  294     if (address_version) {
  295         jp_logf(JP_LOG_DEBUG, "setting schema to contacts\n");
  296         schema = contact_schema;
  297         schema_size = NUM_CONTACT_FIELDS;
  298     } else {
  299         jp_logf(JP_LOG_DEBUG, "setting schema to addresses\n");
  300         schema = address_schema;
  301         schema_size = NUM_ADDRESS_FIELDS;
  302     }
  303 
  304     time(&ltime);
  305     now = localtime(&ltime);
  306     memcpy(&birthday, now, sizeof(struct tm));
  307 
  308     contact_picture.dirty = 0;
  309     contact_picture.length = 0;
  310     contact_picture.data = NULL;
  311 
  312     rowSelected = 0;
  313 
  314     changed_list = NULL;
  315     record_changed = CLEAR_FLAG;
  316 }
  317 
  318 static void set_new_button_to(int new_state) {
  319     jp_logf(JP_LOG_DEBUG, "set_new_button_to new %d old %d\n", new_state, record_changed);
  320 
  321     if (record_changed == new_state) {
  322         return;
  323     }
  324 
  325     switch (new_state) {
  326         case MODIFY_FLAG:
  327             gtk_widget_show(cancel_record_button);
  328             gtk_widget_show(copy_record_button);
  329             gtk_widget_show(apply_record_button);
  330 
  331             gtk_widget_hide(add_record_button);
  332             gtk_widget_hide(delete_record_button);
  333             gtk_widget_hide(new_record_button);
  334             gtk_widget_hide(undelete_record_button);
  335 
  336             break;
  337         case NEW_FLAG:
  338             gtk_widget_show(cancel_record_button);
  339             gtk_widget_show(add_record_button);
  340 
  341             gtk_widget_hide(apply_record_button);
  342             gtk_widget_hide(copy_record_button);
  343             gtk_widget_hide(delete_record_button);
  344             gtk_widget_hide(new_record_button);
  345             gtk_widget_hide(undelete_record_button);
  346 
  347             break;
  348         case CLEAR_FLAG:
  349             gtk_widget_show(delete_record_button);
  350             gtk_widget_show(copy_record_button);
  351             gtk_widget_show(new_record_button);
  352 
  353             gtk_widget_hide(add_record_button);
  354             gtk_widget_hide(apply_record_button);
  355             gtk_widget_hide(cancel_record_button);
  356             gtk_widget_hide(undelete_record_button);
  357 
  358             break;
  359         case UNDELETE_FLAG:
  360             gtk_widget_show(undelete_record_button);
  361             gtk_widget_show(copy_record_button);
  362             gtk_widget_show(new_record_button);
  363 
  364             gtk_widget_hide(add_record_button);
  365             gtk_widget_hide(apply_record_button);
  366             gtk_widget_hide(cancel_record_button);
  367             gtk_widget_hide(delete_record_button);
  368             break;
  369 
  370         default:
  371             return;
  372     }
  373 
  374     record_changed = new_state;
  375 }
  376 
  377 static void cb_record_changed(GtkWidget *widget,
  378                               gpointer data) {
  379     jp_logf(JP_LOG_DEBUG, "cb_record_changed\n");
  380     if (record_changed == CLEAR_FLAG) {
  381         connect_changed_signals(DISCONNECT_SIGNALS);
  382         if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL) > 0) {
  383             set_new_button_to(MODIFY_FLAG);
  384         } else {
  385             set_new_button_to(NEW_FLAG);
  386         }
  387     } else if (record_changed == UNDELETE_FLAG) {
  388         jp_logf(JP_LOG_INFO | JP_LOG_GUI,
  389                 _("This record is deleted.\n"
  390                   "Undelete it or copy it to make changes.\n"));
  391     }
  392 }
  393 
  394 static void connect_changed_signals(int con_or_dis) {
  395     GtkWidget *w;
  396     GList *temp_list;
  397     static int connected = 0;
  398 
  399     /* Connect signals */
  400     if ((con_or_dis == CONNECT_SIGNALS)) {
  401         if (connected) return;
  402         connected = 1;
  403         for (temp_list = changed_list; temp_list; temp_list = temp_list->next) {
  404             if (!(w = temp_list->data)) {
  405                 continue;
  406             }
  407             if (GTK_IS_TEXT_BUFFER(w) ||
  408                 GTK_IS_ENTRY(w) ||
  409                 GTK_IS_TEXT_VIEW(w) ||
  410                 GTK_IS_COMBO_BOX(w)
  411                     ) {
  412                 g_signal_connect(w, "changed", G_CALLBACK(cb_record_changed), NULL);
  413                 continue;
  414             }
  415             if (GTK_IS_CHECK_MENU_ITEM(w) ||
  416                 GTK_IS_RADIO_BUTTON(w) ||
  417                 GTK_IS_CHECK_BUTTON(w)
  418                     ) {
  419                 g_signal_connect(w, "toggled", G_CALLBACK(cb_record_changed), NULL);
  420                 continue;
  421             }
  422             if (GTK_IS_BUTTON(w)) {
  423                 g_signal_connect(w, "pressed", G_CALLBACK(cb_record_changed), NULL);
  424                 continue;
  425             }
  426             jp_logf(JP_LOG_DEBUG, "connect_changed_signals(): Encountered unknown object type.  Skipping\n");
  427         }
  428         return;
  429     }
  430 
  431     /* Disconnect signals */
  432     if ((con_or_dis == DISCONNECT_SIGNALS)) {
  433         if (!connected) return;
  434         connected = 0;
  435         for (temp_list = changed_list; temp_list; temp_list = temp_list->next) {
  436             if (!(temp_list->data)) {
  437                 continue;
  438             }
  439             w = temp_list->data;
  440             g_signal_handlers_disconnect_by_func(w, G_CALLBACK(cb_record_changed), NULL);
  441         }
  442     }
  443 }
  444 
  445 gboolean printAddressRecord(GtkTreeModel *model,
  446                             GtkTreePath *path,
  447                             GtkTreeIter *iter,
  448                             gpointer data) {
  449     int *i = gtk_tree_path_get_indices(path);
  450     if (i[0] == rowSelected) {
  451         MyContact *myContact = NULL;
  452         gtk_tree_model_get(model, iter, ADDRESS_DATA_COLUMN_ENUM, &myContact, -1);
  453         printAddress(myContact, data);
  454         return TRUE;
  455     }
  456 
  457     return FALSE;
  458 
  459 
  460 }
  461 
  462 int printAddress(MyContact *mcont, gpointer data) {
  463     long this_many;
  464     AddressList *addr_list;
  465     ContactList *cont_list;
  466     ContactList cont_list1;
  467     int get_category;
  468 
  469     get_pref(PREF_PRINT_THIS_MANY, &this_many, NULL);
  470 
  471     cont_list = NULL;
  472     if (this_many == 1) {
  473         if (mcont < (MyContact *) LIST_MIN_DATA) {
  474             return EXIT_FAILURE;
  475         }
  476         memcpy(&(cont_list1.mcont), mcont, sizeof(MyContact));
  477         cont_list1.next = NULL;
  478         cont_list = &cont_list1;
  479     }
  480 
  481     /* Get Contacts, or Addresses */
  482     if ((this_many == 2) || (this_many == 3)) {
  483         get_category = CATEGORY_ALL;
  484         if (this_many == 2) {
  485             get_category = address_category;
  486         }
  487         if (address_version == 0) {
  488             addr_list = NULL;
  489             get_addresses2(&addr_list, SORT_ASCENDING, 2, 2, 1, get_category);
  490             copy_addresses_to_contacts(addr_list, &cont_list);
  491             free_AddressList(&addr_list);
  492         } else {
  493             get_contacts2(&cont_list, SORT_ASCENDING, 2, 2, 1, get_category);
  494         }
  495     }
  496 
  497     print_contacts(cont_list, &contact_app_info, schema, schema_size);
  498 
  499     if ((this_many == 2) || (this_many == 3)) {
  500         free_ContactList(&cont_list);
  501     }
  502 
  503     return EXIT_SUCCESS;
  504 }
  505 
  506 int address_print(void) {
  507     gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), printAddressRecord, NULL);
  508     return EXIT_SUCCESS;
  509 
  510 }
  511 
  512 static GString *contact_to_gstring(struct Contact *cont) {
  513     GString *s;
  514     int i;
  515     int address_i, IM_i, phone_i;
  516     char birthday_str[255];
  517     const char *pref_date;
  518     char NL[2];
  519     char *utf;
  520     long char_set;
  521 
  522     get_pref(PREF_CHAR_SET, &char_set, NULL);
  523 
  524     s = g_string_sized_new(4096);
  525     NL[0] = '\0';
  526     NL[1] = '\0';
  527 
  528     address_i = IM_i = phone_i = 0;
  529     for (i = 0; i < schema_size; i++) {
  530         switch (schema[i].type) {
  531             case ADDRESS_GUI_LABEL_TEXT:
  532             case ADDRESS_GUI_WEBSITE_TEXT:
  533                 if (cont->entry[schema[i].record_field] == NULL) continue;
  534                 if (address_version) {
  535                     g_string_append_printf(s, _("%s%s: %s"),
  536                                            NL, contact_app_info.labels[schema[i].record_field],
  537                                            cont->entry[schema[i].record_field]);
  538                 } else {
  539                     utf = charset_p2newj(contact_app_info.labels[schema[i].record_field], 16, char_set);
  540                     g_string_append_printf(s, _("%s%s: %s"),
  541                                       NL, utf, cont->entry[schema[i].record_field]);
  542                     g_free(utf);
  543                 }
  544                 NL[0] = '\n';
  545                 break;
  546             case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
  547                 if (cont->entry[schema[i].record_field] == NULL) {
  548                     phone_i++;
  549                     continue;
  550                 }
  551                 utf = charset_p2newj(contact_app_info.phoneLabels[cont->phoneLabel[phone_i]], 16, char_set);
  552                 g_string_append_printf(s, _("%s%s: %s"),
  553                                   NL, utf,
  554                                   cont->entry[schema[i].record_field]);
  555                 g_free(utf);
  556                 NL[0] = '\n';
  557                 phone_i++;
  558                 break;
  559             case ADDRESS_GUI_IM_MENU_TEXT:
  560                 if (cont->entry[schema[i].record_field] == NULL) {
  561                     IM_i++;
  562                     continue;
  563                 }
  564                 utf = charset_p2newj(contact_app_info.IMLabels[cont->IMLabel[IM_i]], 16, char_set);
  565                 g_string_append_printf(s, _("%s%s: %s"),
  566                                   NL, utf,
  567                                   cont->entry[schema[i].record_field]);
  568                 g_free(utf);
  569                 NL[0] = '\n';
  570                 IM_i++;
  571                 break;
  572             case ADDRESS_GUI_ADDR_MENU_TEXT:
  573                 if (cont->entry[schema[i].record_field] == NULL) {
  574                     address_i++;
  575                     continue;
  576                 }
  577                 utf = charset_p2newj(contact_app_info.addrLabels[cont->addressLabel[address_i]], 16, char_set);
  578                 g_string_append_printf(s, _("%s%s: %s"),
  579                                   NL, utf,
  580                                   cont->entry[schema[i].record_field]);
  581                 g_free(utf);
  582                 NL[0] = '\n';
  583                 address_i++;
  584                 break;
  585             case ADDRESS_GUI_BIRTHDAY:
  586                 if (cont->birthdayFlag == 0) continue;
  587                 get_pref(PREF_LONGDATE, NULL, &pref_date);
  588                 strftime(birthday_str, sizeof(birthday_str), pref_date, &cont->birthday);
  589 
  590                 utf = charset_p2newj(contact_app_info.labels[schema[i].record_field], 16, char_set);
  591                 g_string_append_printf(s, _("%s%s: %s"), NL, utf, birthday_str);
  592                 g_free(utf);
  593                 NL[0] = '\n';
  594                 break;
  595             default:
  596                 break;
  597         }
  598     }
  599     return s;
  600 }
  601 
  602 /* Start Import Code */
  603 static int cb_addr_import(GtkWidget *parent_window,
  604                           const char *file_path,
  605                           int type) {
  606     FILE *in;
  607     char text[65536];
  608     struct Address new_addr;
  609     struct Contact new_cont;
  610     struct CategoryAppInfo *p_cai;
  611 
  612     unsigned char attrib;
  613     int i, j, ret, index;
  614     int address_i, IM_i, phone_i;
  615     int import_all;
  616     char old_cat_name[32];
  617     int new_cat_num, suggested_cat_num;
  618     int priv;
  619     int year, month, day;
  620     GString *cont_text;
  621 
  622     AddressList *addrlist;
  623     AddressList *temp_addrlist;
  624     struct CategoryAppInfo cai;
  625 
  626     in = fopen(file_path, "r");
  627     if (!in) {
  628         jp_logf(JP_LOG_WARN, _("Unable to open file: %s\n"), file_path);
  629         return EXIT_FAILURE;
  630     }
  631 
  632     switch (type) {
  633 
  634         case IMPORT_TYPE_CSV:
  635             jp_logf(JP_LOG_DEBUG, "Address import CSV [%s]\n", file_path);
  636 
  637             /* Switch between contacts & address data structures */
  638             if (address_version) {
  639                 p_cai = &contact_app_info.category;
  640             } else {
  641                 p_cai = &address_app_info.category;
  642             }
  643 
  644             /* Get the first line containing the format and check for reasonableness */
  645             if (fgets(text, sizeof(text), in) == NULL) {
  646                 jp_logf(JP_LOG_WARN, _("Unable to read file: %s\n"), file_path);
  647             }
  648             if (address_version) {
  649                 ret = verify_csv_header(text, NUM_CONT_CSV_FIELDS, file_path);
  650             } else {
  651                 ret = verify_csv_header(text, NUM_ADDR_CSV_FIELDS, file_path);
  652             }
  653             if (EXIT_FAILURE == ret) return EXIT_FAILURE;
  654 
  655             import_all = FALSE;
  656             while (1) {
  657                 /* Read the category field */
  658                 read_csv_field(in, text, sizeof(text));
  659                 if (feof(in)) break;
  660 #ifdef JPILOT_DEBUG
  661                 printf("category is [%s]\n", text);
  662 #endif
  663                 g_strlcpy(old_cat_name, text, 16);
  664                 /* Try to match imported category name to an existing category number */
  665                 suggested_cat_num = 0;
  666                 for (i = 0; i < NUM_ADDRESS_CAT_ITEMS; i++) {
  667                     if (!p_cai->name[i][0]) continue;
  668                     if (!strcmp(p_cai->name[i], old_cat_name)) {
  669                         suggested_cat_num = i;
  670                         break;
  671                     }
  672                 }
  673 
  674                 /* Read the private field */
  675                 read_csv_field(in, text, sizeof(text));
  676 #ifdef JPILOT_DEBUG
  677                 printf("private is [%s]\n", text);
  678 #endif
  679                 sscanf(text, "%d", &priv);
  680 
  681                 /* Need to clear record if doing multiple imports */
  682                 memset(&new_cont, 0, sizeof(new_cont));
  683                 address_i = phone_i = IM_i = 0;
  684                 for (i = 0; i < schema_size; i++) {
  685                     read_csv_field(in, text, sizeof(text));
  686                     switch (schema[i].type) {
  687                         case ADDRESS_GUI_IM_MENU_TEXT:
  688                             for (j = 0; j < NUM_IM_LABELS; j++) {
  689                                 if (strcmp(text, contact_app_info.IMLabels[j]) == 0) {
  690                                     new_cont.IMLabel[IM_i] = j;
  691                                     break;
  692                                 }
  693                             }
  694                             read_csv_field(in, text, sizeof(text));
  695                             new_cont.entry[schema[i].record_field] = strdup(text);
  696                             IM_i++;
  697                             break;
  698                         case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
  699                             for (j = 0; j < NUM_PHONE_LABELS; j++) {
  700                                 if (address_version) {
  701                                     if (strcmp(text, contact_app_info.phoneLabels[j]) == 0) {
  702                                         new_cont.phoneLabel[phone_i] = j;
  703                                         break;
  704                                     }
  705                                 } else {
  706                                     if (strcmp(text, address_app_info.phoneLabels[j]) == 0) {
  707                                         new_cont.phoneLabel[phone_i] = j;
  708                                         break;
  709                                     }
  710                                 }
  711                             }
  712                             read_csv_field(in, text, sizeof(text));
  713                             new_cont.entry[schema[i].record_field] = strdup(text);
  714                             phone_i++;
  715                             break;
  716                         case ADDRESS_GUI_ADDR_MENU_TEXT:
  717                             for (j = 0; j < NUM_ADDRESSES; j++) {
  718                                 if (strcmp(text, contact_app_info.addrLabels[j]) == 0) {
  719                                     new_cont.addressLabel[address_i] = j;
  720                                     break;
  721                                 }
  722                             }
  723                             read_csv_field(in, text, sizeof(text));
  724                             new_cont.entry[schema[i].record_field] = strdup(text);
  725                             address_i++;
  726                             break;
  727                         case ADDRESS_GUI_LABEL_TEXT:
  728                         case ADDRESS_GUI_WEBSITE_TEXT:
  729                             new_cont.entry[schema[i].record_field] = strdup(text);
  730                             break;
  731                         case ADDRESS_GUI_BIRTHDAY:
  732                             if (text[0]) {
  733                                 new_cont.birthdayFlag = 1;
  734                                 sscanf(text, "%d/%d/%d", &year, &month, &day);
  735                                 memset(&(new_cont.birthday), 0, sizeof(new_cont.birthday));
  736                                 new_cont.birthday.tm_year = year - 1900;
  737                                 new_cont.birthday.tm_mon = month - 1;
  738                                 new_cont.birthday.tm_mday = day;
  739                             }
  740                             /* Also get Reminder Advance field */
  741                             read_csv_field(in, text, sizeof(text));
  742                             if (text[0]) {
  743                                 new_cont.reminder = TRUE;
  744                                 sscanf(text, "%d", &(new_cont.advance));
  745                                 new_cont.advanceUnits = 1;  /* Days */
  746                             }
  747                             break;
  748                         default:
  749                             break;
  750                     }
  751                 }
  752 
  753                 ret = read_csv_field(in, text, sizeof(text));
  754                 sscanf(text, "%d", &(new_cont.showPhone));
  755 
  756                 cont_text = contact_to_gstring(&new_cont);
  757                 if (!import_all) {
  758                     ret = import_record_ask(parent_window, pane,
  759                                             cont_text->str,
  760                                             p_cai,
  761                                             old_cat_name,
  762                                             priv,
  763                                             suggested_cat_num,
  764                                             &new_cat_num);
  765                 } else {
  766                     new_cat_num = suggested_cat_num;
  767                 }
  768                 g_string_free(cont_text, TRUE);
  769 
  770                 if (ret == DIALOG_SAID_IMPORT_QUIT) {
  771                     jp_free_Contact(&new_cont);
  772                     break;
  773                 }
  774                 if (ret == DIALOG_SAID_IMPORT_SKIP) {
  775                     jp_free_Contact(&new_cont);
  776                     continue;
  777                 }
  778                 if (ret == DIALOG_SAID_IMPORT_ALL) {
  779                     import_all = TRUE;
  780                 }
  781 
  782                 attrib = (new_cat_num & 0x0F) |
  783                          (priv ? dlpRecAttrSecret : 0);
  784                 if ((ret == DIALOG_SAID_IMPORT_YES) || import_all) {
  785                     if (address_version) {
  786                         pc_contact_write(&new_cont, NEW_PC_REC, attrib, NULL);
  787                         jp_free_Contact(&new_cont);
  788                     } else {
  789                         copy_contact_to_address(&new_cont, &new_addr);
  790                         jp_free_Contact(&new_cont);
  791                         pc_address_write(&new_addr, NEW_PC_REC, attrib, NULL);
  792                         free_Address(&new_addr);
  793                     }
  794                 }
  795             }
  796             break;
  797 
  798         case IMPORT_TYPE_DAT:  /* Palm Desktop DAT format */
  799             jp_logf(JP_LOG_DEBUG, "Address import DAT [%s]\n", file_path);
  800             if (dat_check_if_dat_file(in) != DAT_ADDRESS_FILE) {
  801                 jp_logf(JP_LOG_WARN, _("File doesn't appear to be address.dat format\n"));
  802                 fclose(in);
  803                 return EXIT_FAILURE;
  804             }
  805             addrlist = NULL;
  806             dat_get_addresses(in, &addrlist, &cai);
  807             import_all = FALSE;
  808             for (temp_addrlist = addrlist; temp_addrlist; temp_addrlist = temp_addrlist->next) {
  809                 index = temp_addrlist->maddr.unique_id - 1;
  810                 if (index < 0) {
  811                     g_strlcpy(old_cat_name, _("Unfiled"), 16);
  812                 } else {
  813                     g_strlcpy(old_cat_name, cai.name[index], 16);
  814                 }
  815                 /* Figure out what category it was in the dat file */
  816                 index = temp_addrlist->maddr.unique_id - 1;
  817                 suggested_cat_num = 0;
  818                 if (index > -1) {
  819                     for (i = 0; i < NUM_ADDRESS_CAT_ITEMS; i++) {
  820                         if (!address_app_info.category.name[i][0]) continue;
  821                         if (!strcmp(address_app_info.category.name[i], old_cat_name)) {
  822                             suggested_cat_num = i;
  823                             break;
  824                         }
  825                     }
  826                 }
  827 
  828                 ret = 0;
  829                 if (!import_all) {
  830                     copy_address_to_contact(&(temp_addrlist->maddr.addr), &new_cont);
  831                     cont_text = contact_to_gstring(&new_cont);
  832                     ret = import_record_ask(parent_window, pane,
  833                                             cont_text->str,
  834                                             &(address_app_info.category),
  835                                             old_cat_name,
  836                                             (temp_addrlist->maddr.attrib & 0x10),
  837                                             suggested_cat_num,
  838                                             &new_cat_num);
  839                     g_string_free(cont_text, TRUE);
  840                     jp_free_Contact(&new_cont);
  841                 } else {
  842                     new_cat_num = suggested_cat_num;
  843                 }
  844                 if (ret == DIALOG_SAID_IMPORT_QUIT) break;
  845                 if (ret == DIALOG_SAID_IMPORT_SKIP) continue;
  846                 if (ret == DIALOG_SAID_IMPORT_ALL) import_all = TRUE;
  847 
  848                 attrib = (new_cat_num & 0x0F) |
  849                          ((temp_addrlist->maddr.attrib & 0x10) ? dlpRecAttrSecret : 0);
  850                 if ((ret == DIALOG_SAID_IMPORT_YES) || (import_all)) {
  851                     pc_address_write(&(temp_addrlist->maddr.addr), NEW_PC_REC, attrib, NULL);
  852                 }
  853             }
  854             free_AddressList(&addrlist);
  855             break;
  856         default:
  857             break;
  858     }  /* end switch for import types */
  859 
  860     address_refresh();
  861     fclose(in);
  862     return EXIT_SUCCESS;
  863 }
  864 
  865 int address_import(GtkWidget *window) {
  866     char *type_desc[] = {
  867             N_("CSV (Comma Separated Values)"),
  868             N_("DAT/ABA (Palm Archive Formats)"),
  869             NULL
  870     };
  871     int type_int[] = {
  872             IMPORT_TYPE_CSV,
  873             IMPORT_TYPE_DAT,
  874             0
  875     };
  876 
  877     /* Hide ABA import of Contacts until file format has been decoded */
  878     if (address_version == 1) {
  879         type_desc[1] = NULL;
  880         type_int[1] = 0;
  881 
  882     }
  883 
  884     import_gui(window, pane, type_desc, type_int, cb_addr_import);
  885     return EXIT_SUCCESS;
  886 }
  887 
  888 /* End Import Code */
  889 
  890 /* Start Export code */
  891 
  892 static const char *ldifMapType(int label) {
  893     switch (label) {
  894         case 0:
  895             return "telephoneNumber";
  896         case 1:
  897             return "homePhone";
  898         case 2:
  899             return "facsimileTelephoneNumber";
  900         case 3:
  901             return "xotherTelephoneNumber";
  902         case 4:
  903             return "mail";
  904         case 5:
  905             return "xmainTelephoneNumber";
  906         case 6:
  907             return "pager";
  908         case 7:
  909             return "mobile";
  910         default:
  911             return "xunknownTelephoneNumber";
  912     }
  913 }
  914 
  915 static const char *vCardMapType(int label) {
  916     switch (label) {
  917         case 0:
  918             return "work";
  919         case 1:
  920             return "home";
  921         case 2:
  922             return "fax";
  923         case 3:
  924             return "x-other";
  925         case 4:
  926             return "email";
  927         case 5:
  928             return "x-main";
  929         case 6:
  930             return "pager";
  931         case 7:
  932             return "cell";
  933         default:
  934             return "x-unknown";
  935     }
  936 }
  937 
  938 
  939 static void cb_addr_export_ok(GtkWidget *export_window, GtkWidget *treeView,
  940                               int type, const char *filename) {
  941     MyContact *mcont;
  942     GList *list, *temp_list;
  943     FILE *out;
  944     struct stat statb;
  945     const char *short_date;
  946     time_t ltime;
  947     struct tm *now;
  948     char str1[256], str2[256];
  949     char pref_time[40];
  950     int i, r, n;
  951     int record_num;
  952     char *button_text[] = {N_("OK")};
  953     char *button_overwrite_text[] = {N_("No"), N_("Yes")};
  954     char text[1024];
  955     char date_string[1024];
  956     char csv_text[65550];
  957     long char_set;
  958     char username[256];
  959     char hostname[256];
  960     const char *svalue;
  961     long userid;
  962     char birthday_str[255];
  963     const char *pref_date;
  964     int address_i, IM_i, phone_i;
  965     int index = 0;
  966     char *utf;
  967 
  968     /* Open file for export, including corner cases where file exists or
  969     * can't be opened */
  970     if (!stat(filename, &statb)) {
  971         if (S_ISDIR(statb.st_mode)) {
  972             g_snprintf(text, sizeof(text), _("%s is a directory"), filename);
  973             dialog_generic(GTK_WINDOW(export_window),
  974                            _("Error Opening File"),
  975                            DIALOG_ERROR, text, 1, button_text);
  976             return;
  977         }
  978         g_snprintf(text, sizeof(text), _("Do you want to overwrite file %s?"), filename);
  979         r = dialog_generic(GTK_WINDOW(export_window),
  980                            _("Overwrite File?"),
  981                            DIALOG_QUESTION, text, 2, button_overwrite_text);
  982         if (r != DIALOG_SAID_2) {
  983             return;
  984         }
  985     }
  986 
  987     out = fopen(filename, "w");
  988     if (!out) {
  989         g_snprintf(text, sizeof(text), _("Error opening file: %s"), filename);
  990         dialog_generic(GTK_WINDOW(export_window),
  991                        _("Error Opening File"),
  992                        DIALOG_ERROR, text, 1, button_text);
  993         return;
  994     }
  995 
  996     /* Write a header for TEXT file */
  997     if (type == EXPORT_TYPE_TEXT) {
  998         get_pref(PREF_SHORTDATE, NULL, &short_date);
  999         get_pref_time_no_secs(pref_time);
 1000         time(&ltime);
 1001         now = localtime(&ltime);
 1002         strftime(str1, sizeof(str1), short_date, now);
 1003         strftime(str2, sizeof(str2), pref_time, now);
 1004         g_snprintf(date_string, sizeof(date_string), "%s %s", str1, str2);
 1005         if (address_version == 0) {
 1006             fprintf(out, _("Address exported from %s %s on %s\n\n"),
 1007                     PN, VERSION, date_string);
 1008         } else {
 1009             fprintf(out, _("Contact exported from %s %s on %s\n\n"),
 1010                     PN, VERSION, date_string);
 1011         }
 1012     }
 1013 
 1014     /* Write a header to the CSV file */
 1015     if (type == EXPORT_TYPE_CSV) {
 1016         if (address_version) {
 1017             fprintf(out, "CSV contacts version "VERSION": Category, Private, ");
 1018         } else {
 1019             fprintf(out, "CSV address version "VERSION": Category, Private, ");
 1020         }
 1021 
 1022         address_i = phone_i = IM_i = 0;
 1023         for (i = 0; i < schema_size; i++) {
 1024             switch (schema[i].type) {
 1025                 case ADDRESS_GUI_IM_MENU_TEXT:
 1026                     fprintf(out, "IM %d label, ", IM_i);
 1027                     fprintf(out, "IM %d, ", IM_i);
 1028                     IM_i++;
 1029                     break;
 1030                 case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 1031                     fprintf(out, "Phone %d label, ", phone_i);
 1032                     fprintf(out, "Phone %d, ", phone_i);
 1033                     phone_i++;
 1034                     break;
 1035                 case ADDRESS_GUI_ADDR_MENU_TEXT:
 1036                     fprintf(out, "Address %d label, ", address_i);
 1037                     fprintf(out, "Address %d, ", address_i);
 1038                     address_i++;
 1039                     break;
 1040                 case ADDRESS_GUI_BIRTHDAY:
 1041                     fprintf(out, "%s, ", contact_app_info.labels[schema[i].record_field]);
 1042                     fprintf(out, "Reminder Advance, ");
 1043                     break;
 1044                 case ADDRESS_GUI_LABEL_TEXT:
 1045                 case ADDRESS_GUI_WEBSITE_TEXT:
 1046                     fprintf(out, "%s, ", contact_app_info.labels[schema[i].record_field]);
 1047                     break;
 1048                 default:
 1049                     break;
 1050             }
 1051         }
 1052 
 1053         fprintf(out, "Show in List\n");
 1054     }  /* end writing CSV header */
 1055 
 1056     /* Write a header to the B-Folders CSV file */
 1057     if (type == EXPORT_TYPE_BFOLDERS) {
 1058         fprintf(out, "%s",
 1059                 "Contacts:\nName, Email, Phone (mobile), Company, Title, Website, Phone (work),"
 1060                 "Phone 2(work), Fax (work), Address (work), Phone (home), Address (home), "
 1061                 "\"Custom Label 1\", \"Custom Value 1\", \"Custom Label 2\", \"Custom Value 2\","
 1062                 "\"Custom Label 3\",\"Custom Value 3\",\"Custom Label 4\",\"Custom Value 4\","
 1063                 "\"Custom Label 5\",\"Custom Value 5\",Note,Folder");
 1064     }  /* end writing CSV header */
 1065 
 1066 
 1067     /* Special setup for VCARD export */
 1068     if ((type == EXPORT_TYPE_VCARD) ||
 1069         (type == EXPORT_TYPE_VCARD_GMAIL)) {
 1070         get_pref(PREF_CHAR_SET, &char_set, NULL);
 1071         get_pref(PREF_USER, NULL, &svalue);
 1072         /* Convert User Name stored in Palm character set */
 1073         g_strlcpy(text, svalue, 128);
 1074         charset_p2j(text, 128, char_set);
 1075         str_to_ical_str(username, sizeof(username), text);
 1076         get_pref(PREF_USER_ID, &userid, NULL);
 1077         gethostname(text, sizeof(text));
 1078         text[sizeof(text) - 1] = '\0';
 1079         str_to_ical_str(hostname, sizeof(hostname), text);
 1080     }
 1081 
 1082     /* Check encoding for LDIF output */
 1083     if (type == EXPORT_TYPE_LDIF) {
 1084         get_pref(PREF_CHAR_SET, &char_set, NULL);
 1085         if (char_set < CHAR_SET_UTF) {
 1086             jp_logf(JP_LOG_WARN, _("Host character encoding is not UTF-8 based.\n"
 1087                                    " Exported ldif file may not be standards-compliant\n"));
 1088         }
 1089     }
 1090 
 1091     get_pref(PREF_CHAR_SET, &char_set, NULL);
 1092     GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
 1093     GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView));
 1094     list = gtk_tree_selection_get_selected_rows(selection, &model);
 1095 
 1096     /* Loop over list of records to export */
 1097     for (record_num = 0, temp_list = list; temp_list; temp_list = temp_list->next, record_num++) {
 1098         GtkTreePath *path = temp_list->data;
 1099         GtkTreeIter iter;
 1100         if (gtk_tree_model_get_iter(model, &iter, path)) {
 1101             gtk_tree_model_get(model, &iter, ADDRESS_DATA_COLUMN_ENUM, &mcont, -1);
 1102             if (!mcont) {
 1103                 continue;
 1104                 jp_logf(JP_LOG_WARN, _("Can't export address %d\n"), (long) temp_list->data + 1);
 1105             }
 1106 
 1107             switch (type) {
 1108                 case EXPORT_TYPE_TEXT:
 1109                     utf = charset_p2newj(contact_app_info.category.name[mcont->attrib & 0x0F], 16, char_set);
 1110                     fprintf(out, _("Category: %s\n"), utf);
 1111                     g_free(utf);
 1112                     fprintf(out, _("Private: %s\n"),
 1113                             (mcont->attrib & dlpRecAttrSecret) ? _("Yes") : _("No"));
 1114 
 1115                     for (i = 0; i < schema_size; i++) {
 1116                         /* Special handling for birthday which doesn't have an entry
 1117                  * field but instead has a flag and a tm struct field */
 1118                         if (schema[i].type == ADDRESS_GUI_BIRTHDAY) {
 1119                             if (mcont->cont.birthdayFlag) {
 1120                                 fprintf(out, _("%s: "), contact_app_info.labels[schema[i].record_field]
 1121                                                         ? contact_app_info.labels[schema[i].record_field] : "");
 1122                                 birthday_str[0] = '\0';
 1123                                 get_pref(PREF_SHORTDATE, NULL, &pref_date);
 1124                                 strftime(birthday_str, sizeof(birthday_str), pref_date, &(mcont->cont.birthday));
 1125                                 fprintf(out, _("%s\n"), birthday_str);
 1126                                 continue;
 1127                             }
 1128                         }
 1129 
 1130                         if (mcont->cont.entry[schema[i].record_field]) {
 1131                             /* Print labels for menu selectable fields (Work, Fax, etc.) */
 1132                             switch (schema[i].type) {
 1133                                 case ADDRESS_GUI_IM_MENU_TEXT:
 1134                                     index = mcont->cont.IMLabel[i - contIM1];
 1135                                     fprintf(out, _("%s: "), contact_app_info.IMLabels[index]);
 1136                                     break;
 1137                                 case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 1138                                     index = mcont->cont.phoneLabel[i - contPhone1];
 1139                                     fprintf(out, _("%s: "), contact_app_info.phoneLabels[index]);
 1140                                     break;
 1141                                 case ADDRESS_GUI_ADDR_MENU_TEXT:
 1142                                     switch (schema[i].record_field) {
 1143                                         case contAddress1 :
 1144                                             index = 0;
 1145                                             break;
 1146                                         case contAddress2 :
 1147                                             index = 1;
 1148                                             break;
 1149                                         case contAddress3 :
 1150                                             index = 2;
 1151                                             break;
 1152                                         default:
 1153                                             break;
 1154                                     }
 1155                                     index = mcont->cont.addressLabel[index];
 1156                                     fprintf(out, _("%s: "),
 1157                                             contact_app_info.addrLabels[mcont->cont.addressLabel[index]]);
 1158                                     break;
 1159                                 default:
 1160                                     fprintf(out, _("%s: "), contact_app_info.labels[schema[i].record_field]
 1161                                                             ? contact_app_info.labels[schema[i].record_field] : "");
 1162                             }
 1163                             /* Next print the entry field */
 1164                             switch (schema[i].type) {
 1165                                 case ADDRESS_GUI_LABEL_TEXT:
 1166                                 case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 1167                                 case ADDRESS_GUI_IM_MENU_TEXT:
 1168                                 case ADDRESS_GUI_ADDR_MENU_TEXT:
 1169                                 case ADDRESS_GUI_WEBSITE_TEXT:
 1170                                     fprintf(out, "%s\n", mcont->cont.entry[schema[i].record_field]);
 1171                                     break;
 1172                                 default:
 1173                                     break;
 1174                             }
 1175                         }
 1176                     }
 1177                     fprintf(out, "\n");
 1178 
 1179                     break;
 1180 
 1181                 case EXPORT_TYPE_CSV:
 1182                     /* Category name */
 1183                     utf = charset_p2newj(contact_app_info.category.name[mcont->attrib & 0x0F], 16, char_set);
 1184                     str_to_csv_str(csv_text, utf);
 1185                     fprintf(out, "\"%s\",", csv_text);
 1186                     g_free(utf);
 1187 
 1188                     /* Private */
 1189                     fprintf(out, "\"%s\",", (mcont->attrib & dlpRecAttrSecret) ? "1" : "0");
 1190 
 1191                     address_i = phone_i = IM_i = 0;
 1192                     /* The Contact entry values */
 1193                     for (i = 0; i < schema_size; i++) {
 1194                         switch (schema[i].type) {
 1195                             /* For labels that are menu selectable ("Work", Fax", etc)
 1196                   * we list what they are set to in the record */
 1197                             case ADDRESS_GUI_IM_MENU_TEXT:
 1198                                 str_to_csv_str(csv_text, contact_app_info.IMLabels[mcont->cont.IMLabel[IM_i]]);
 1199                                 fprintf(out, "\"%s\",", csv_text);
 1200                                 str_to_csv_str(csv_text, mcont->cont.entry[schema[i].record_field] ?
 1201                                                          mcont->cont.entry[schema[i].record_field] : "");
 1202                                 fprintf(out, "\"%s\",", csv_text);
 1203                                 IM_i++;
 1204                                 break;
 1205                             case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 1206                                 str_to_csv_str(csv_text, contact_app_info.phoneLabels[mcont->cont.phoneLabel[phone_i]]);
 1207                                 fprintf(out, "\"%s\",", csv_text);
 1208                                 str_to_csv_str(csv_text, mcont->cont.entry[schema[i].record_field] ?
 1209                                                          mcont->cont.entry[schema[i].record_field] : "");
 1210                                 fprintf(out, "\"%s\",", csv_text);
 1211                                 phone_i++;
 1212                                 break;
 1213                             case ADDRESS_GUI_ADDR_MENU_TEXT:
 1214                                 str_to_csv_str(csv_text,
 1215                                                contact_app_info.addrLabels[mcont->cont.addressLabel[address_i]]);
 1216                                 fprintf(out, "\"%s\",", csv_text);
 1217                                 str_to_csv_str(csv_text, mcont->cont.entry[schema[i].record_field] ?
 1218                                                          mcont->cont.entry[schema[i].record_field] : "");
 1219                                 fprintf(out, "\"%s\",", csv_text);
 1220                                 address_i++;
 1221                                 break;
 1222                             case ADDRESS_GUI_LABEL_TEXT:
 1223                             case ADDRESS_GUI_WEBSITE_TEXT:
 1224                                 str_to_csv_str(csv_text, mcont->cont.entry[schema[i].record_field] ?
 1225                                                          mcont->cont.entry[schema[i].record_field] : "");
 1226                                 fprintf(out, "\"%s\",", csv_text);
 1227                                 break;
 1228                             case ADDRESS_GUI_BIRTHDAY:
 1229                                 if (mcont->cont.birthdayFlag) {
 1230                                     birthday_str[0] = '\0';
 1231                                     strftime(birthday_str, sizeof(birthday_str), "%Y/%02m/%02d",
 1232                                              &(mcont->cont.birthday));
 1233                                     fprintf(out, "\"%s\",", birthday_str);
 1234 
 1235                                     if (mcont->cont.reminder) {
 1236                                         fprintf(out, "\"%d\",", mcont->cont.advance);
 1237                                     } else {
 1238                                         fprintf(out, "\"\",");
 1239                                     }
 1240 
 1241                                 } else {
 1242                                     fprintf(out, "\"\",");  /* for null Birthday field */
 1243                                     fprintf(out, "\"\",");  /* for null Birthday Reminder field */
 1244                                 }
 1245                                 break;
 1246                             default:
 1247                                 break;
 1248                         }
 1249                     }
 1250 
 1251                     fprintf(out, "\"%d\"\n", mcont->cont.showPhone);
 1252                     break;
 1253 
 1254                 case EXPORT_TYPE_BFOLDERS:
 1255                     /* fprintf(out, "%s",
 1256                      "Name, Email, Phone (mobile), Company, Title, Website, Phone (work),"
 1257                      "Phone 2(work), Fax (work), Address (work), Phone (home), Address (home), "
 1258                      "\"Custom Label 1\", \"Custom Value 1\", \"Custom Label 2\", \"Custom Value 2\","
 1259                      "\"Custom Label 3\",\"Custom Value 3\",\"Custom Label 4\",\"Custom Value 4\","
 1260                      "\"Custom Label 5\",\"Custom Value 5\",Note,Folder");
 1261               */
 1262                     //address_i = phone_i = IM_i = 0;
 1263                     for (i = 0; i < schema_size; i++) {
 1264                         if (schema[i].record_field == contLastname) {
 1265                             str_to_csv_str(csv_text, mcont->cont.entry[schema[i].record_field] ?
 1266                                                      mcont->cont.entry[schema[i].record_field] : "");
 1267                             fprintf(out, "\"%s, ", csv_text);
 1268                         }
 1269                     }
 1270                     for (i = 0; i < schema_size; i++) {
 1271                         if (schema[i].record_field == contFirstname) {
 1272                             str_to_csv_str(csv_text, mcont->cont.entry[schema[i].record_field] ?
 1273                                                      mcont->cont.entry[schema[i].record_field] : "");
 1274                             fprintf(out, "%s\",", csv_text);
 1275                         }
 1276                     }
 1277                     /* E-Mail */
 1278                     /*
 1279              for (i=0; i<schema_size; i++) {
 1280                 if (!strcasecmp(contact_app_info.phoneLabels[cont->phoneLabel[phone_i]], _("E-mail"))) {
 1281                     g_object_set_data(G_OBJECT(dial_button[phone_i]), "mail", GINT_TO_POINTER(1));
 1282                    gtk_button_set_label(GTK_BUTTON(dial_button[phone_i]), _("Mail"));
 1283                 }
 1284                 fprintf(out, "%s\",", csv_text);
 1285              }
 1286     */
 1287                     fprintf(out, "%s",
 1288                             "\"\", \"\", \"\", \"\", \"\","
 1289                             "\"\", \"\", \"\", \"\", \"\", "
 1290                             "\"Custom Label 1\", \"Custom Value 1\", \"Custom Label 2\", \"Custom Value 2\","
 1291                             "\"Custom Label 3\",\"Custom Value 3\",\"Custom Label 4\",\"Custom Value 4\","
 1292                             "\"Custom Label 5\",\"Custom Value 5\",\"Note\",\"Contacts\"");
 1293                     fprintf(out, "\n");
 1294 
 1295                     break;
 1296 
 1297                 case EXPORT_TYPE_VCARD:
 1298                 case EXPORT_TYPE_VCARD_GMAIL:
 1299                     /* RFC 2426: vCard MIME Directory Profile */
 1300                     fprintf(out, "BEGIN:VCARD"CRLF);
 1301                     fprintf(out, "VERSION:3.0"CRLF);
 1302                     fprintf(out, "PRODID:%s"CRLF, FPI_STRING);
 1303                     if (mcont->attrib & dlpRecAttrSecret) {
 1304                         fprintf(out, "CLASS:PRIVATE"CRLF);
 1305                     }
 1306                     fprintf(out, "UID:palm-addressbook-%08x-%08lx-%s@%s"CRLF,
 1307                             mcont->unique_id, userid, username, hostname);
 1308                     utf = charset_p2newj(contact_app_info.category.name[mcont->attrib & 0x0F], 16, char_set);
 1309                     str_to_vcard_str(csv_text, sizeof(csv_text), utf);
 1310                     fprintf(out, "CATEGORIES:%s"CRLF, csv_text);
 1311                     g_free(utf);
 1312                     if (mcont->cont.entry[contLastname] || mcont->cont.entry[contFirstname]) {
 1313                         char *last = mcont->cont.entry[contLastname];
 1314                         char *first = mcont->cont.entry[contFirstname];
 1315                         fprintf(out, "FN:");
 1316                         if (first) {
 1317                             str_to_vcard_str(csv_text, sizeof(csv_text), first);
 1318                             fprintf(out, "%s", csv_text);
 1319                         }
 1320                         if (first && last) {
 1321                             fprintf(out, " ");
 1322                         }
 1323                         if (last) {
 1324                             str_to_vcard_str(csv_text, sizeof(csv_text), last);
 1325                             fprintf(out, "%s", csv_text);
 1326                         }
 1327                         fprintf(out, CRLF);
 1328                         fprintf(out, "N:");
 1329                         if (last) {
 1330                             str_to_vcard_str(csv_text, sizeof(csv_text), last);
 1331                             fprintf(out, "%s", csv_text);
 1332                         }
 1333                         fprintf(out, ";");
 1334                         /* split up first into first + middle and do first;middle,middle*/
 1335                         if (first) {
 1336                             str_to_vcard_str(csv_text, sizeof(csv_text), first);
 1337                             fprintf(out, "%s", csv_text);
 1338                         }
 1339                         fprintf(out, CRLF);
 1340                     } else if (mcont->cont.entry[contCompany]) {
 1341                         str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[contCompany]);
 1342                         fprintf(out, "FN:%s"CRLF"N:%s"CRLF, csv_text, csv_text);
 1343                     } else {
 1344                         fprintf(out, "FN:-Unknown-"CRLF"N:known-;-Un"CRLF);
 1345                     }
 1346                     if (mcont->cont.entry[contTitle]) {
 1347                         str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[contTitle]);
 1348                         fprintf(out, "TITLE:%s"CRLF, csv_text);
 1349                     }
 1350                     if (mcont->cont.entry[contCompany]) {
 1351                         str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[contCompany]);
 1352                         fprintf(out, "ORG:%s"CRLF, csv_text);
 1353                     }
 1354                     for (n = contPhone1; n < contPhone7 + 1; n++) {
 1355                         if (mcont->cont.entry[n]) {
 1356                             str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[n]);
 1357                             /* E-mail should be the Palm dropdown menu item for email */
 1358                             if (!strcasecmp(contact_app_info.phoneLabels[mcont->cont.phoneLabel[n - contPhone1]],
 1359                                             _("E-mail"))) {
 1360                                 fprintf(out, "EMAIL:%s"CRLF, csv_text);
 1361                             } else {
 1362                                 fprintf(out, "TEL;TYPE=%s", vCardMapType(mcont->cont.phoneLabel[n - contPhone1]));
 1363                                 if (mcont->cont.showPhone == n - contPhone1) {
 1364                                     fprintf(out, ",pref");
 1365                                 }
 1366                                 fprintf(out, ":%s"CRLF, csv_text);
 1367                             }
 1368                         }
 1369                     }
 1370                     for (i = 0; i < NUM_ADDRESSES; i++) {
 1371                         int address_il = 0, city_i = 0, state_i = 0, zip_i = 0, country_i = 0;
 1372                         switch (i) {
 1373                             case 0:
 1374                                 address_il = contAddress1;
 1375                                 city_i = contCity1;
 1376                                 state_i = contState1;
 1377                                 zip_i = contZip1;
 1378                                 country_i = contCountry1;
 1379                                 break;
 1380                             case 1:
 1381                                 address_il = contAddress2;
 1382                                 city_i = contCity2;
 1383                                 state_i = contState2;
 1384                                 zip_i = contZip2;
 1385                                 country_i = contCountry2;
 1386                                 break;
 1387                             case 2:
 1388                                 address_il = contAddress3;
 1389                                 city_i = contCity3;
 1390                                 state_i = contState3;
 1391                                 zip_i = contZip3;
 1392                                 country_i = contCountry3;
 1393                                 break;
 1394                             default:
 1395                                 break;
 1396                         }
 1397                         if (mcont->cont.entry[address_il] ||
 1398                             mcont->cont.entry[city_i] ||
 1399                             mcont->cont.entry[state_i] ||
 1400                             mcont->cont.entry[zip_i] ||
 1401                             mcont->cont.entry[country_i]) {
 1402 
 1403                             /* Should we rely on the label, or the label index, for the addr
 1404                     * type?  The label depends on the translated text.  I'll go
 1405                     * with index for now.  The text is here:
 1406                    contact_app_info.addrLabels[mcont->cont.addressLabel[i]] */
 1407 
 1408                             switch (mcont->cont.addressLabel[i]) {
 1409                                 case 0:
 1410                                     fprintf(out, "ADR;TYPE=WORK:;;");
 1411                                     break;
 1412                                 case 1:
 1413                                     fprintf(out, "ADR;TYPE=HOME:;;");
 1414                                     break;
 1415                                 default:
 1416                                     fprintf(out, "ADR:;;");
 1417                             }
 1418 
 1419                             for (n = address_il; n < country_i + 1; n++) {
 1420                                 if (mcont->cont.entry[n]) {
 1421                                     str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[n]);
 1422                                     fprintf(out, "%s", csv_text);
 1423                                 }
 1424                                 if (n < country_i) {
 1425                                     fprintf(out, ";");
 1426                                 }
 1427                             }
 1428                             fprintf(out, CRLF);
 1429                         }
 1430                     }
 1431                     for (i = 0; i < NUM_IMS; i++) {
 1432                         int im_i = 0;
 1433                         switch (i) {
 1434                             case 0:
 1435                                 im_i = contIM1;
 1436                                 break;
 1437                             case 1:
 1438                                 im_i = contIM2;
 1439                                 break;
 1440                             default:
 1441                                 break;
 1442                         }
 1443                         if (mcont->cont.entry[im_i]) {
 1444                             int i_label = mcont->cont.IMLabel[i];
 1445                             const gchar *label = contact_app_info.IMLabels[i_label];
 1446                             gchar *vlabel;
 1447                             if (strcmp(label, "AOL ICQ") == 0)
 1448                                 label = "ICQ";
 1449                             vlabel = g_strcanon(g_ascii_strup(label, -1),
 1450                                                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ-", '-');
 1451                             fprintf(out, "X-%s:", vlabel);
 1452                             g_free(vlabel);
 1453                             str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[im_i]);
 1454                             fprintf(out, "%s"CRLF, csv_text);
 1455                         }
 1456                     }
 1457                     if (mcont->cont.entry[contWebsite]) {
 1458                         str_to_vcard_str(csv_text, sizeof(csv_text),
 1459                                          mcont->cont.entry[contWebsite]);
 1460                         fprintf(out, "URL:%s"CRLF, csv_text);
 1461                     }
 1462                     if (mcont->cont.birthdayFlag) {
 1463                         char birthday_str_l[255];
 1464                         strftime(birthday_str_l, sizeof(birthday_str), "%F", &mcont->cont.birthday);
 1465                         str_to_vcard_str(csv_text, sizeof(csv_text), birthday_str_l);
 1466                         fprintf(out, "BDAY:%s"CRLF, birthday_str);
 1467                     }
 1468                     if (type == EXPORT_TYPE_VCARD_GMAIL) {
 1469                         /* Gmail contacts don't have fields for the custom fields,
 1470                  * rather than lose them we can stick them all in a note field */
 1471                         int printed_note = 0;
 1472                         for (n = contCustom1; n <= contCustom9; n++) {
 1473                             if (mcont->cont.entry[n]) {
 1474                                 if (!printed_note) {
 1475                                     printed_note = 1;
 1476                                     fprintf(out, "NOTE:");
 1477                                 } else {
 1478                                     fprintf(out, " ");
 1479                                 }
 1480                                 str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[n]);
 1481                                 fprintf(out, "%s:%s\\n"CRLF, contact_app_info.customLabels[n - contCustom1], csv_text);
 1482                             }
 1483                         }
 1484                         if (mcont->cont.entry[contNote]) {
 1485                             if (!printed_note) {
 1486                                 fprintf(out, "NOTE:");
 1487                             } else {
 1488                                 fprintf(out, " note:");
 1489                             }
 1490                             str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[contNote]);
 1491                             fprintf(out, "%s\\n"CRLF, csv_text);
 1492                         }
 1493                     } else { /* Not a Gmail optimized export */
 1494                         if (mcont->cont.entry[contCustom1] ||
 1495                             mcont->cont.entry[contCustom2] ||
 1496                             mcont->cont.entry[contCustom3] ||
 1497                             mcont->cont.entry[contCustom4] ||
 1498                             mcont->cont.entry[contCustom5] ||
 1499                             mcont->cont.entry[contCustom6] ||
 1500                             mcont->cont.entry[contCustom7] ||
 1501                             mcont->cont.entry[contCustom8] ||
 1502                             mcont->cont.entry[contCustom9]) {
 1503                             for (n = contCustom1; n <= contCustom9; n++) {
 1504                                 if (mcont->cont.entry[n]) {
 1505                                     const gchar *label = contact_app_info.customLabels[n - contCustom1];
 1506                                     gchar *vlabel;
 1507                                     vlabel = g_strcanon(g_ascii_strup(label, -1),
 1508                                                         "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-", '-');
 1509                                     fprintf(out, "X-%s:", vlabel);
 1510                                     g_free(vlabel);
 1511                                     str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[n]);
 1512                                     fprintf(out, "%s"CRLF, csv_text);
 1513                                 }
 1514                             }
 1515                         }
 1516                         if (mcont->cont.entry[contNote]) {
 1517                             fprintf(out, "NOTE:");
 1518                             str_to_vcard_str(csv_text, sizeof(csv_text), mcont->cont.entry[contNote]);
 1519                             fprintf(out, "%s\\n"CRLF, csv_text);
 1520                         }
 1521                     }
 1522 
 1523                     fprintf(out, "END:VCARD"CRLF);
 1524                     break;
 1525 
 1526                 case EXPORT_TYPE_LDIF:
 1527                     /* RFC 2256 - organizationalPerson */
 1528                     /* RFC 2798 - inetOrgPerson */
 1529                     /* RFC 2849 - LDIF file format */
 1530                     if (record_num == 0) {
 1531                         fprintf(out, "version: 1\n");
 1532                     }
 1533                     {
 1534                         char *cn;
 1535                         char *email = NULL;
 1536                         char *last = mcont->cont.entry[contLastname];
 1537                         char *first = mcont->cont.entry[contFirstname];
 1538                         for (n = contPhone1; n <= contPhone7; n++) {
 1539                             if (mcont->cont.entry[n] && mcont->cont.phoneLabel[n - contPhone1] == 4) {
 1540                                 email = mcont->cont.entry[n];
 1541                                 break;
 1542                             }
 1543                         }
 1544                         if (first || last) {
 1545                             cn = csv_text;
 1546                             snprintf(csv_text, sizeof(csv_text), "%s%s%s", first ? first : "",
 1547                                      first && last ? " " : "", last ? last : "");
 1548                             if (!last) {
 1549                                 last = first;
 1550                                 first = NULL;
 1551                             }
 1552                         } else if (mcont->cont.entry[contCompany]) {
 1553                             last = mcont->cont.entry[contCompany];
 1554                             cn = last;
 1555                         } else {
 1556                             last = "Unknown";
 1557                             cn = last;
 1558                         }
 1559                         /* maybe add dc=%s for each part of the email address? */
 1560                         /* Mozilla just does mail=%s */
 1561                         ldif_out(out, "dn", "cn=%s%s%s", cn, email ? ",mail=" : "",
 1562                                  email ? email : "");
 1563                         fprintf(out, "dnQualifier: %s\n", PN);
 1564                         fprintf(out, "objectClass: top\nobjectClass: person\n");
 1565                         fprintf(out, "objectClass: organizationalPerson\n");
 1566                         fprintf(out, "objectClass: inetOrgPerson\n");
 1567                         ldif_out(out, "cn", "%s", cn);
 1568                         ldif_out(out, "sn", "%s", last);
 1569                         if (first)
 1570                             ldif_out(out, "givenName", "%s", first);
 1571                         if (mcont->cont.entry[contCompany])
 1572                             ldif_out(out, "o", "%s", mcont->cont.entry[contCompany]);
 1573                         for (n = contPhone1; n <= contPhone7; n++) {
 1574                             if (mcont->cont.entry[n]) {
 1575                                 ldif_out(out, ldifMapType(mcont->cont.phoneLabel[n - contPhone1]), "%s",
 1576                                          mcont->cont.entry[n]);
 1577                             }
 1578                         }
 1579                         if (mcont->cont.entry[contAddress1])
 1580                             ldif_out(out, "postalAddress", "%s", mcont->cont.entry[contAddress1]);
 1581                         if (mcont->cont.entry[contCity1])
 1582                             ldif_out(out, "l", "%s", mcont->cont.entry[contCity1]);
 1583                         if (mcont->cont.entry[contState1])
 1584                             ldif_out(out, "st", "%s", mcont->cont.entry[contState1]);
 1585                         if (mcont->cont.entry[contZip1])
 1586                             ldif_out(out, "postalCode", "%s", mcont->cont.entry[contZip1]);
 1587                         if (mcont->cont.entry[contCountry1])
 1588                             ldif_out(out, "c", "%s", mcont->cont.entry[contCountry1]);
 1589 
 1590                         if (mcont->cont.entry[contAddress2])
 1591                             ldif_out(out, "postalAddress", "%s", mcont->cont.entry[contAddress2]);
 1592                         if (mcont->cont.entry[contCity2])
 1593                             ldif_out(out, "l", "%s", mcont->cont.entry[contCity2]);
 1594                         if (mcont->cont.entry[contState2])
 1595                             ldif_out(out, "st", "%s", mcont->cont.entry[contState2]);
 1596                         if (mcont->cont.entry[contZip2])
 1597                             ldif_out(out, "postalCode", "%s", mcont->cont.entry[contZip2]);
 1598                         if (mcont->cont.entry[contCountry2])
 1599                             ldif_out(out, "c", "%s", mcont->cont.entry[contCountry2]);
 1600 
 1601                         if (mcont->cont.entry[contAddress3])
 1602                             ldif_out(out, "postalAddress", "%s", mcont->cont.entry[contAddress3]);
 1603                         if (mcont->cont.entry[contCity3])
 1604                             ldif_out(out, "l", "%s", mcont->cont.entry[contCity3]);
 1605                         if (mcont->cont.entry[contState3])
 1606                             ldif_out(out, "st", "%s", mcont->cont.entry[contState3]);
 1607                         if (mcont->cont.entry[contZip3])
 1608                             ldif_out(out, "postalCode", "%s", mcont->cont.entry[contZip3]);
 1609                         if (mcont->cont.entry[contCountry3])
 1610                             ldif_out(out, "c", "%s", mcont->cont.entry[contCountry3]);
 1611 
 1612                         if (mcont->cont.entry[contIM1]) {
 1613                             strncpy(text, contact_app_info.IMLabels[mcont->cont.IMLabel[0]], 100);
 1614                             ldif_out(out, text, "%s", mcont->cont.entry[contIM1]);
 1615                         }
 1616                         if (mcont->cont.entry[contIM2]) {
 1617                             strncpy(text, contact_app_info.IMLabels[mcont->cont.IMLabel[1]], 100);
 1618                             ldif_out(out, text, "%s", mcont->cont.entry[contIM2]);
 1619                         }
 1620 
 1621                         if (mcont->cont.entry[contWebsite])
 1622                             ldif_out(out, "website", "%s", mcont->cont.entry[contWebsite]);
 1623                         if (mcont->cont.entry[contTitle])
 1624                             ldif_out(out, "title", "%s", mcont->cont.entry[contTitle]);
 1625                         if (mcont->cont.entry[contCustom1])
 1626                             ldif_out(out, "custom1", "%s", mcont->cont.entry[contCustom1]);
 1627                         if (mcont->cont.entry[contCustom2])
 1628                             ldif_out(out, "custom2", "%s", mcont->cont.entry[contCustom2]);
 1629                         if (mcont->cont.entry[contCustom3])
 1630                             ldif_out(out, "custom3", "%s", mcont->cont.entry[contCustom3]);
 1631                         if (mcont->cont.entry[contCustom4])
 1632                             ldif_out(out, "custom4", "%s", mcont->cont.entry[contCustom4]);
 1633                         if (mcont->cont.entry[contCustom5])
 1634                             ldif_out(out, "custom5", "%s", mcont->cont.entry[contCustom5]);
 1635                         if (mcont->cont.entry[contCustom6])
 1636                             ldif_out(out, "custom6", "%s", mcont->cont.entry[contCustom6]);
 1637                         if (mcont->cont.entry[contCustom7])
 1638                             ldif_out(out, "custom7", "%s", mcont->cont.entry[contCustom7]);
 1639                         if (mcont->cont.entry[contCustom8])
 1640                             ldif_out(out, "custom8", "%s", mcont->cont.entry[contCustom8]);
 1641                         if (mcont->cont.entry[contCustom9])
 1642                             ldif_out(out, "custom9", "%s", mcont->cont.entry[contCustom9]);
 1643                         if (mcont->cont.entry[contNote])
 1644                             ldif_out(out, "description", "%s", mcont->cont.entry[contNote]);
 1645                         fprintf(out, "\n");
 1646                         break;
 1647                     }
 1648 
 1649                 default:
 1650                     jp_logf(JP_LOG_WARN, _("Unknown export type\n"));
 1651             }
 1652         }
 1653     }
 1654     if (out) {
 1655         fclose(out);
 1656     }
 1657 }
 1658 
 1659 static GtkWidget *cb_addr_export_init_treeView() {
 1660     GtkListStore *localListStore = gtk_list_store_new(ADDRESS_NUM_COLS, G_TYPE_STRING, GDK_TYPE_PIXBUF,
 1661                                                       G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_RGBA,
 1662                                                       G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN);
 1663     GtkTreeModel *model = GTK_TREE_MODEL(localListStore);
 1664     GtkTreeView *localTreeView = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
 1665     GtkCellRenderer *nameRenderer = gtk_cell_renderer_text_new();
 1666     GtkTreeViewColumn *nameColumn = gtk_tree_view_column_new_with_attributes(ADDRESS_LAST_NAME_COMPANY, nameRenderer,
 1667                                                                              "text",
 1668                                                                              ADDRESS_NAME_COLUMN_ENUM,
 1669                                                                              "cell-background-rgba",
 1670                                                                              ADDRESS_BACKGROUND_COLOR_ENUM,
 1671                                                                              "cell-background-set",
 1672                                                                              ADDRESS_BACKGROUND_COLOR_ENABLED_ENUM,
 1673                                                                              NULL);
 1674     gtk_tree_view_column_set_clickable(nameColumn, FALSE);
 1675 
 1676     GtkCellRenderer *noteRenderer = gtk_cell_renderer_pixbuf_new();
 1677     GtkTreeViewColumn *noteColumn = gtk_tree_view_column_new_with_attributes("", noteRenderer, "pixbuf",
 1678                                                                              ADDRESS_NOTE_COLUMN_ENUM,
 1679                                                                              "cell-background-rgba",
 1680                                                                              ADDRESS_BACKGROUND_COLOR_ENUM,
 1681                                                                              "cell-background-set",
 1682                                                                              ADDRESS_BACKGROUND_COLOR_ENABLED_ENUM,
 1683                                                                              NULL);
 1684     gtk_tree_view_column_set_clickable(noteColumn, FALSE);
 1685 
 1686     GtkCellRenderer *phoneRenderer = gtk_cell_renderer_text_new();
 1687     GtkTreeViewColumn *phoneColumn = gtk_tree_view_column_new_with_attributes("Phone", phoneRenderer, "text",
 1688                                                                               ADDRESS_PHONE_COLUMN_ENUM,
 1689                                                                               "cell-background-rgba",
 1690                                                                               ADDRESS_BACKGROUND_COLOR_ENUM,
 1691                                                                               "cell-background-set",
 1692                                                                               ADDRESS_BACKGROUND_COLOR_ENABLED_ENUM,
 1693                                                                               NULL);
 1694     gtk_tree_view_column_set_clickable(phoneColumn, FALSE);
 1695     gtk_tree_view_column_set_sizing(nameColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
 1696     gtk_tree_view_column_set_sizing(noteColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
 1697     gtk_tree_view_column_set_sizing(phoneColumn, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
 1698     gtk_tree_view_insert_column(GTK_TREE_VIEW(localTreeView), nameColumn, ADDRESS_NAME_COLUMN_ENUM);
 1699     gtk_tree_view_insert_column(GTK_TREE_VIEW(localTreeView), noteColumn, ADDRESS_NOTE_COLUMN_ENUM);
 1700     gtk_tree_view_insert_column(GTK_TREE_VIEW(localTreeView), phoneColumn, ADDRESS_PHONE_COLUMN_ENUM);
 1701     return GTK_WIDGET(localTreeView);
 1702 }
 1703 
 1704 static void cb_addr_update_listStore(GtkWidget *ptreeView, int category) {
 1705     address_update_listStore(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ptreeView))), NULL,
 1706                              &export_contact_list, category, FALSE);
 1707 }
 1708 
 1709 
 1710 static void cb_addr_export_done(GtkWidget *widget, const char *filename) {
 1711     free_ContactList(&export_contact_list);
 1712     set_pref(PREF_ADDRESS_EXPORT_FILENAME, 0, filename, TRUE);
 1713 }
 1714 
 1715 
 1716 //TODO: fix this when working on exports.
 1717 int address_export(GtkWidget *window) {
 1718     int w, h, x, y;
 1719     char *type_text[] = {N_("Text"),
 1720                          N_("CSV"),
 1721                          N_("vCard"),
 1722                          N_("vCard (Optimized for Gmail/Android Import)"),
 1723                          N_("ldif"),
 1724                          N_("B-Folders CSV"),
 1725                          NULL};
 1726     int type_int[] = {EXPORT_TYPE_TEXT,
 1727                       EXPORT_TYPE_CSV,
 1728                       EXPORT_TYPE_VCARD,
 1729                       EXPORT_TYPE_VCARD_GMAIL,
 1730                       EXPORT_TYPE_LDIF,
 1731                       EXPORT_TYPE_BFOLDERS};
 1732 
 1733     w = gdk_window_get_width(gtk_widget_get_window(window));
 1734     h = gdk_window_get_height(gtk_widget_get_window(window));
 1735     gdk_window_get_root_origin(gtk_widget_get_window(window), &x, &y);
 1736 
 1737     w = gtk_paned_get_position(GTK_PANED(pane));
 1738     x += 40;
 1739 
 1740     export_gui(window,
 1741                w, h, x, y, 3, sort_l,
 1742                PREF_ADDRESS_EXPORT_FILENAME,
 1743                type_text,
 1744                type_int,
 1745                cb_addr_export_init_treeView,
 1746                cb_addr_update_listStore,
 1747                cb_addr_export_done,
 1748                cb_addr_export_ok
 1749     );
 1750 
 1751     return EXIT_SUCCESS;
 1752 }
 1753 
 1754 /* End Export Code */
 1755 
 1756 static void cb_resize_name_column(GtkTreeViewColumn *column) {
 1757     if (column == NULL) {
 1758         return;
 1759     }
 1760     int width = gtk_tree_view_column_get_width(column);
 1761     set_pref(PREF_ADDR_NAME_COL_SZ, width, NULL, TRUE);
 1762 }
 1763 
 1764 /* Find position of category in sorted category array
 1765  * via its assigned category number */
 1766 static int find_sort_cat_pos(int cat) {
 1767     int i;
 1768 
 1769     for (i = 0; i < NUM_ADDRESS_CAT_ITEMS; i++) {
 1770         if (sort_l[i].cat_num == cat) {
 1771             return i;
 1772         }
 1773     }
 1774 
 1775     return -1;
 1776 }
 1777 
 1778 /* Find a category's position in the category menu.
 1779  * This is equal to the category number except for the Unfiled category.
 1780  * The Unfiled category is always in the last position which changes as
 1781  * the number of categories changes */
 1782 static int find_menu_cat_pos(int cat) {
 1783     int i;
 1784 
 1785     if (cat != NUM_ADDRESS_CAT_ITEMS - 1) {
 1786         return cat;
 1787     } else { /* Unfiled category */
 1788         /* Count how many category entries are filled */
 1789         for (i = 0; i < NUM_ADDRESS_CAT_ITEMS; i++) {
 1790             if (!sort_l[i].Pcat[0]) {
 1791                 return i;
 1792             }
 1793         }
 1794         return 0;
 1795     }
 1796 }
 1797 
 1798 gboolean deleteAddressRecord(GtkTreeModel *model,
 1799                              GtkTreePath *path,
 1800                              GtkTreeIter *iter,
 1801                              gpointer data) {
 1802     int *i = gtk_tree_path_get_indices(path);
 1803     if (i[0] == rowSelected) {
 1804         MyContact *mcont = NULL;
 1805         gtk_tree_model_get(model, iter, ADDRESS_DATA_COLUMN_ENUM, &mcont, -1);
 1806         deleteAddress(mcont, data);
 1807         return TRUE;
 1808     }
 1809 
 1810     return FALSE;
 1811 
 1812 
 1813 }
 1814 
 1815 gboolean addNewAddressRecord(GtkTreeModel *model,
 1816                              GtkTreePath *path,
 1817                              GtkTreeIter *iter,
 1818                              gpointer data) {
 1819     int *i = gtk_tree_path_get_indices(path);
 1820     if (i[0] == rowSelected) {
 1821         MyContact *mcont = NULL;
 1822         gtk_tree_model_get(model, iter, ADDRESS_DATA_COLUMN_ENUM, &mcont, -1);
 1823         addNewAddressRecordToDataStructure(mcont, data);
 1824         return TRUE;
 1825     }
 1826     return FALSE;
 1827 }
 1828 
 1829 void addNewAddressRecordToDataStructure(MyContact *mcont, gpointer data) {
 1830     int i;
 1831     struct Contact cont;
 1832     struct Address addr;
 1833     unsigned char attrib;
 1834     int address_i, IM_i, phone_i;
 1835     int flag, type;
 1836     unsigned int unique_id;
 1837     int show_priv;
 1838     GtkTextIter start_iter;
 1839     GtkTextIter end_iter;
 1840 
 1841     memset(&cont, 0, sizeof(cont));
 1842     flag = GPOINTER_TO_INT(data);
 1843     unique_id = 0;
 1844 
 1845     /* Do masking like Palm OS 3.5 */
 1846     if ((flag == COPY_FLAG) || (flag == MODIFY_FLAG)) {
 1847         show_priv = show_privates(GET_PRIVATES);
 1848         if (mcont < (MyContact *) LIST_MIN_DATA) {
 1849             return;
 1850         }
 1851         if ((show_priv != SHOW_PRIVATES) &&
 1852             (mcont->attrib & dlpRecAttrSecret)) {
 1853             return;
 1854         }
 1855     }
 1856     /* End Masking */
 1857     if ((flag == NEW_FLAG) || (flag == COPY_FLAG) || (flag == MODIFY_FLAG)) {
 1858         /* These rec_types are both the same for now */
 1859         if (flag == MODIFY_FLAG) {
 1860             unique_id = mcont->unique_id;
 1861             if (mcont < (MyContact *) LIST_MIN_DATA) {
 1862                 return;
 1863             }
 1864             if ((mcont->rt == DELETED_PALM_REC) ||
 1865                 (mcont->rt == DELETED_PC_REC) ||
 1866                 (mcont->rt == MODIFIED_PALM_REC)) {
 1867                 jp_logf(JP_LOG_INFO, _("You can't modify a record that is deleted\n"));
 1868                 return;
 1869             }
 1870         }
 1871 
 1872         cont.showPhone = 0;
 1873 
 1874         /* Get the menu labels and settings */
 1875         address_i = IM_i = phone_i = 0;
 1876         for (i = 0; i < schema_size; i++) {
 1877             switch (schema[i].type) {
 1878                 case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 1879                     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio_button[phone_i]))) {
 1880                         cont.showPhone = phone_i;
 1881                     }
 1882                     cont.phoneLabel[phone_i] = address_phone_label_selected[phone_i];
 1883                     phone_i++;
 1884                     break;
 1885                 case ADDRESS_GUI_IM_MENU_TEXT:
 1886                     cont.IMLabel[IM_i] = IM_type_selected[IM_i];
 1887                     IM_i++;
 1888                     break;
 1889                 case ADDRESS_GUI_ADDR_MENU_TEXT:
 1890                     cont.addressLabel[address_i] = address_type_selected[address_i];
 1891                     address_i++;
 1892                     break;
 1893                 case ADDRESS_GUI_BIRTHDAY:
 1894                     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(birthday_checkbox))) {
 1895                         cont.birthdayFlag = 1;
 1896                         memcpy(&cont.birthday, &birthday, sizeof(struct tm));
 1897                     }
 1898                     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(reminder_checkbox))) {
 1899                         cont.reminder = 1;
 1900                         cont.advance = atoi(gtk_entry_get_text(GTK_ENTRY(reminder_entry)));
 1901                         cont.advanceUnits = 1; /* Days */
 1902                     }
 1903                     break;
 1904                 case ADDRESS_GUI_LABEL_TEXT:
 1905                 case ADDRESS_GUI_WEBSITE_TEXT:
 1906                     break;
 1907                 default:
 1908                     break;
 1909             }
 1910         }
 1911 
 1912         /* Get the entry texts */
 1913         for (i = 0; i < schema_size; i++) {
 1914             switch (schema[i].type) {
 1915                 case ADDRESS_GUI_LABEL_TEXT:
 1916                 case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 1917                 case ADDRESS_GUI_IM_MENU_TEXT:
 1918                 case ADDRESS_GUI_ADDR_MENU_TEXT:
 1919                 case ADDRESS_GUI_WEBSITE_TEXT:
 1920                     gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(addr_text_buffer[schema[i].record_field]), &start_iter,
 1921                                                &end_iter);
 1922                     cont.entry[schema[i].record_field] =
 1923                             gtk_text_buffer_get_text(GTK_TEXT_BUFFER(addr_text_buffer[schema[i].record_field]),
 1924                                                      &start_iter, &end_iter, TRUE);
 1925                     break;
 1926                 case ADDRESS_GUI_BIRTHDAY:
 1927                     break;
 1928                 default:
 1929                     break;
 1930             }
 1931         }
 1932 
 1933         /* Get the picture */
 1934         if (contact_picture.data) {
 1935             jp_Contact_add_picture(&cont, &contact_picture);
 1936         }
 1937 
 1938         /* Get the attributes */
 1939         get_address_attrib(&attrib);
 1940 
 1941         set_new_button_to(CLEAR_FLAG);
 1942 
 1943         if (flag == MODIFY_FLAG) {
 1944             cb_delete_address_or_contact(NULL, data);
 1945             if ((mcont->rt == PALM_REC) || (mcont->rt == REPLACEMENT_PALM_REC)) {
 1946                 type = REPLACEMENT_PALM_REC;
 1947             } else {
 1948                 unique_id = 0;
 1949                 type = NEW_PC_REC;
 1950             }
 1951         } else {
 1952             unique_id = 0;
 1953             type = NEW_PC_REC;
 1954         }
 1955 
 1956         if (address_version == 0) {
 1957             copy_contact_to_address(&cont, &addr);
 1958             jp_free_Contact(&cont);
 1959             pc_address_write(&addr, type, attrib, &unique_id);
 1960             free_Address(&addr);
 1961         } else {
 1962             pc_contact_write(&cont, type, attrib, &unique_id);
 1963             jp_free_Contact(&cont);
 1964         }
 1965 
 1966         /* Don't return to modified record if search gui active */
 1967         if (!glob_find_id) {
 1968             glob_find_id = unique_id;
 1969         }
 1970         address_redraw();
 1971     }
 1972 }
 1973 
 1974 gboolean deleteAddressContactRecord(GtkTreeModel *model,
 1975                                     GtkTreePath *path,
 1976                                     GtkTreeIter *iter,
 1977                                     gpointer data) {
 1978     int *i = gtk_tree_path_get_indices(path);
 1979     if (i[0] == rowSelected) {
 1980         MyContact *mcont = NULL;
 1981         gtk_tree_model_get(model, iter, ADDRESS_DATA_COLUMN_ENUM, &mcont, -1);
 1982         deleteAddressContact(mcont, data);
 1983         return TRUE;
 1984     }
 1985 
 1986     return FALSE;
 1987 
 1988 
 1989 }
 1990 
 1991 gboolean undeleteAddressRecord(GtkTreeModel *model,
 1992                                GtkTreePath *path,
 1993                                GtkTreeIter *iter,
 1994                                gpointer data) {
 1995     int *i = gtk_tree_path_get_indices(path);
 1996     if (i[0] == rowSelected) {
 1997         MyContact *mcont = NULL;
 1998         gtk_tree_model_get(model, iter, ADDRESS_DATA_COLUMN_ENUM, &mcont, -1);
 1999         undeleteAddress(mcont, data);
 2000         return TRUE;
 2001     }
 2002 
 2003     return FALSE;
 2004 
 2005 
 2006 }
 2007 
 2008 void deleteAddressContact(MyContact *mcont, gpointer data) {
 2009     int flag;
 2010     int show_priv;
 2011     long char_set;
 2012     int i;
 2013 
 2014     if (mcont < (MyContact *) LIST_MIN_DATA) {
 2015         return;
 2016     }
 2017     /* convert to Palm character set */
 2018     get_pref(PREF_CHAR_SET, &char_set, NULL);
 2019     if (char_set != CHAR_SET_LATIN1) {
 2020         for (i = 0; i < NUM_CONTACT_ENTRIES; i++) {
 2021             if (mcont->cont.entry[i]) {
 2022                 charset_j2p(mcont->cont.entry[i],
 2023                             strlen(mcont->cont.entry[i]) + 1, char_set);
 2024             }
 2025         }
 2026     }
 2027 
 2028     /* Do masking like Palm OS 3.5 */
 2029     show_priv = show_privates(GET_PRIVATES);
 2030     if ((show_priv != SHOW_PRIVATES) &&
 2031         (mcont->attrib & dlpRecAttrSecret)) {
 2032         return;
 2033     }
 2034     /* End Masking */
 2035     flag = GPOINTER_TO_INT(data);
 2036     if ((flag == MODIFY_FLAG) || (flag == DELETE_FLAG)) {
 2037         delete_pc_record(CONTACTS, mcont, flag);
 2038         if (flag == DELETE_FLAG) {
 2039             /* when we redraw we want to go to the line above the deleted one */
 2040             if (rowSelected > 0) {
 2041                 rowSelected--;
 2042             }
 2043         }
 2044     }
 2045 
 2046     if (flag == DELETE_FLAG) {
 2047         address_redraw();
 2048     }
 2049 }
 2050 
 2051 void deleteAddress(MyContact *mcont, gpointer data) {
 2052     MyAddress maddr;
 2053     int flag;
 2054     int show_priv;
 2055     long char_set;
 2056     int i;
 2057 
 2058     if (mcont < (MyContact *) LIST_MIN_DATA) {
 2059         return;
 2060     }
 2061 
 2062     copy_contact_to_address(&(mcont->cont), &(maddr.addr));
 2063     maddr.rt = mcont->rt;
 2064     maddr.unique_id = mcont->unique_id;
 2065     maddr.attrib = mcont->attrib;
 2066 
 2067     /* convert to Palm character set */
 2068     get_pref(PREF_CHAR_SET, &char_set, NULL);
 2069     if (char_set != CHAR_SET_LATIN1) {
 2070         for (i = 0; i < NUM_ADDRESS_FIELDS; i++) {
 2071             if (maddr.addr.entry[i]) {
 2072                 charset_j2p(maddr.addr.entry[i],
 2073                             strlen(maddr.addr.entry[i]) + 1, char_set);
 2074             }
 2075         }
 2076     }
 2077 
 2078     /* Do masking like Palm OS 3.5 */
 2079     show_priv = show_privates(GET_PRIVATES);
 2080     if ((show_priv != SHOW_PRIVATES) &&
 2081         (maddr.attrib & dlpRecAttrSecret)) {
 2082         free_Address(&(maddr.addr));
 2083         return;
 2084     }
 2085     /* End Masking */
 2086     flag = GPOINTER_TO_INT(data);
 2087     if ((flag == MODIFY_FLAG) || (flag == DELETE_FLAG)) {
 2088         delete_pc_record(ADDRESS, &maddr, flag);
 2089         if (flag == DELETE_FLAG) {
 2090             /* when we redraw we want to go to the line above the deleted one */
 2091             if (rowSelected > 0) {
 2092                 rowSelected--;
 2093             }
 2094         }
 2095     }
 2096 
 2097     free_Address(&(maddr.addr));
 2098 
 2099     if (flag == DELETE_FLAG) {
 2100         address_redraw();
 2101     }
 2102 }
 2103 
 2104 static void cb_delete_address(GtkWidget *widget, gpointer data) {
 2105     gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), deleteAddressRecord, data);
 2106 }
 2107 
 2108 static void cb_delete_contact(GtkWidget *widget, gpointer data) {
 2109     gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), deleteAddressContactRecord, data);
 2110 }
 2111 
 2112 static void cb_delete_address_or_contact(GtkWidget *widget, gpointer data) {
 2113     if (address_version == 0) {
 2114         cb_delete_address(widget, data);
 2115     } else {
 2116         cb_delete_contact(widget, data);
 2117     }
 2118 }
 2119 
 2120 void undeleteAddress(MyContact *mcont, gpointer data) {
 2121     int flag;
 2122     int show_priv;
 2123 
 2124     if (mcont < (MyContact *) LIST_MIN_DATA) {
 2125         return;
 2126     }
 2127 
 2128     /* Do masking like Palm OS 3.5 */
 2129     show_priv = show_privates(GET_PRIVATES);
 2130     if ((show_priv != SHOW_PRIVATES) &&
 2131         (mcont->attrib & dlpRecAttrSecret)) {
 2132         return;
 2133     }
 2134     /* End Masking */
 2135 
 2136     jp_logf(JP_LOG_DEBUG, "mcont->unique_id = %d\n", mcont->unique_id);
 2137     jp_logf(JP_LOG_DEBUG, "mcont->rt = %d\n", mcont->rt);
 2138 
 2139     flag = GPOINTER_TO_INT(data);
 2140     if (flag == UNDELETE_FLAG) {
 2141         if (mcont->rt == DELETED_PALM_REC ||
 2142             (mcont->rt == DELETED_PC_REC)) {
 2143             if (address_version == 0) {
 2144                 MyAddress maddr;
 2145                 maddr.unique_id = mcont->unique_id;
 2146                 undelete_pc_record(ADDRESS, &maddr, flag);
 2147             } else {
 2148                 undelete_pc_record(CONTACTS, mcont, flag);
 2149             }
 2150         }
 2151         /* Possible later addition of undelete for modified records
 2152       else if (mcont->rt == MODIFIED_PALM_REC) {
 2153          cb_add_new_record(widget, GINT_TO_POINTER(COPY_FLAG));
 2154       }
 2155       */
 2156     }
 2157 
 2158     address_redraw();
 2159 }
 2160 
 2161 static void cb_undelete_address(GtkWidget *widget,
 2162                                 gpointer data) {
 2163 
 2164     gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), undeleteAddressRecord, data);
 2165 
 2166 }
 2167 
 2168 static void cb_cancel(GtkWidget *widget, gpointer data) {
 2169     set_new_button_to(CLEAR_FLAG);
 2170     address_refresh();
 2171 }
 2172 
 2173 /**
 2174  * This is a bit hacky.
 2175  * The sort should probably be done on the column sort,
 2176  * but since there are 3 ways to sort, I'm not positive
 2177  * it will work well.
 2178  * @param nameColumn
 2179  */
 2180 static void cb_resortNameColumn(GtkTreeViewColumn *nameColumn) {
 2181     addr_sort_order = addr_sort_order << 1;
 2182     if (!(addr_sort_order & 0x07)) addr_sort_order = SORT_BY_LNAME;
 2183     set_pref(PREF_ADDR_SORT_ORDER, addr_sort_order, NULL, TRUE);
 2184     //find Id to keep selected.
 2185     gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), findAndSetGlobalAddressId, NULL);
 2186     address_redraw();
 2187     switch (addr_sort_order) {
 2188         case SORT_BY_LNAME:
 2189         default:
 2190             addr_sort_order = SORT_BY_LNAME;  /* Initialize variable if default case taken */
 2191             gtk_tree_view_column_set_title(nameColumn, ADDRESS_LAST_NAME_COMPANY);
 2192             break;
 2193         case SORT_BY_FNAME:
 2194             gtk_tree_view_column_set_title(nameColumn, ADDRESS_FIRST_NAME_COMPANY);
 2195             break;
 2196         case SORT_BY_COMPANY:
 2197             gtk_tree_view_column_set_title(nameColumn, ADDRESS_COMPANY_LAST_NAME);
 2198             break;
 2199     }
 2200 
 2201 
 2202 }
 2203 
 2204 
 2205 static void cb_phone_menu(GtkComboBox *item, unsigned int value) {
 2206     if (!item)
 2207         return;
 2208     if (gtk_combo_box_get_active(GTK_COMBO_BOX(item)) < 0) {
 2209         return;
 2210     }
 2211     int selectedIndex = gtk_combo_box_get_active(GTK_COMBO_BOX(item));
 2212     address_phone_label_selected[value] = selectedIndex;
 2213 
 2214 }
 2215 
 2216 static void cb_IM_type_menu(GtkComboBox *item, unsigned int value) {
 2217     if (!item)
 2218         return;
 2219     if (gtk_combo_box_get_active(GTK_COMBO_BOX(item)) < 0) {
 2220         return;
 2221     }
 2222     int selectedIndex = gtk_combo_box_get_active(GTK_COMBO_BOX(item));
 2223     IM_type_selected[value] = selectedIndex;
 2224 }
 2225 
 2226 /* The least significant byte of value is the selection of the menu,
 2227  * i.e., which item is chosen (Work, Office, Home).
 2228  * The next to least significant byte is the address type menu
 2229  * that is being selected (there are 3 addresses and 3 pulldown menus) */
 2230 static void cb_address_type_menu(GtkComboBox *item, unsigned int value) {
 2231     int menu, selection;
 2232     int address_i, i;
 2233 
 2234     if (!item)
 2235         return;
 2236     if (gtk_combo_box_get_active(GTK_COMBO_BOX(item)) < 0) {
 2237         return;
 2238     }
 2239     int selectedIndex = gtk_combo_box_get_active(GTK_COMBO_BOX(item));
 2240     menu = value;
 2241     selection = selectedIndex;
 2242     jp_logf(JP_LOG_DEBUG, "addr_type_menu = %d\n", menu);
 2243     jp_logf(JP_LOG_DEBUG, "selection = %d\n", selection);
 2244     address_type_selected[menu] = selection;
 2245     /* We want to make the notebook page tab label match the type of
 2246    * address from the menu.  So, we'll find the nth address menu
 2247    * and set whatever page the schema says it resides on */
 2248     address_i = 0;
 2249     for (i = 0; i < schema_size; i++) {
 2250         if (schema[i].type == ADDRESS_GUI_ADDR_MENU_TEXT) {
 2251             if (address_i == menu) {
 2252                 gtk_label_set_text(GTK_LABEL(notebook_label[schema[i].notebook_page]),
 2253                                    contact_app_info.addrLabels[selection]);
 2254             }
 2255             address_i++;
 2256         }
 2257     }
 2258 }
 2259 
 2260 static void cb_notebook_changed(GtkWidget *widget,
 2261                                 GtkWidget *widget2,
 2262                                 int page,
 2263                                 gpointer data) {
 2264     int prev_page;
 2265 
 2266     /* GTK calls this function while it is destroying the notebook
 2267     * I use this function to tell if it is being destroyed */
 2268     prev_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget));
 2269     if (prev_page < 0) return;
 2270 
 2271     jp_logf(JP_LOG_DEBUG, "cb_notebook_changed(), prev_page=%d, page=%d\n", prev_page, page);
 2272     set_pref(PREF_ADDRESS_NOTEBOOK_PAGE, page, NULL, TRUE);
 2273 }
 2274 
 2275 static void get_address_attrib(unsigned char *attrib) {
 2276     /* Get the category that is set from the menu */
 2277     *attrib = 0;
 2278     if (GTK_IS_WIDGET(category_menu2)) {
 2279         *attrib = get_selected_category_from_combo_box(GTK_COMBO_BOX(category_menu2));
 2280     }
 2281 
 2282     /* Get private flag */
 2283     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(private_checkbox))) {
 2284         *attrib |= dlpRecAttrSecret;
 2285     }
 2286 }
 2287 
 2288 static void cb_add_new_record(GtkWidget *widget, gpointer data) {
 2289 
 2290     if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL) != 0) {
 2291         gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), addNewAddressRecord, data);
 2292     } else {
 2293         //no records exist in category yet.
 2294         addNewAddressRecordToDataStructure(NULL, data);
 2295     }
 2296 }
 2297 
 2298 static void addr_clear_details(void) {
 2299     int i;
 2300     int new_cat;
 2301     int sorted_position;
 2302     int address_i, IM_i, phone_i;
 2303     long ivalue;
 2304     char reminder_str[10];
 2305     /* Palm has phone popup menus in one order and the display of phone tabs
 2306     * in another. This reorders the tabs to produce the more usable order of
 2307     * the Palm desktop software */
 2308     int phone_btn_order[NUM_PHONE_ENTRIES] = {0, 1, 4, 7, 5, 3, 2};
 2309 
 2310     /* Need to disconnect signals first */
 2311     connect_changed_signals(DISCONNECT_SIGNALS);
 2312 
 2313     /* Clear the quickview */
 2314     gtk_text_buffer_set_text(GTK_TEXT_BUFFER(addr_all_buffer), "", -1);
 2315 
 2316     /* Clear all of the text fields */
 2317     for (i = 0; i < schema_size; i++) {
 2318         switch (schema[i].type) {
 2319             case ADDRESS_GUI_LABEL_TEXT:
 2320             case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 2321             case ADDRESS_GUI_ADDR_MENU_TEXT:
 2322             case ADDRESS_GUI_IM_MENU_TEXT:
 2323             case ADDRESS_GUI_WEBSITE_TEXT:
 2324                 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(addr_text_buffer[schema[i].record_field]), "", -1);
 2325             default:
 2326                 break;
 2327         }
 2328     }
 2329 
 2330     address_i = IM_i = phone_i = 0;
 2331     for (i = 0; i < schema_size; i++) {
 2332         switch (schema[i].type) {
 2333             case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 2334                 if (phone_i == 0) {
 2335                     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button[0]), TRUE);
 2336                 }
 2337                 if (address_version) {
 2338                     gtk_combo_box_set_active(GTK_COMBO_BOX(phone_type_list_menu[phone_i]), phone_btn_order[phone_i]);
 2339                 } else {
 2340                     gtk_combo_box_set_active(GTK_COMBO_BOX(phone_type_list_menu[phone_i]), phone_i);
 2341                 }
 2342                 phone_i++;
 2343                 break;
 2344             case ADDRESS_GUI_IM_MENU_TEXT:
 2345                 gtk_combo_box_set_active(GTK_COMBO_BOX(IM_type_list_menu[IM_i]), IM_i);
 2346                 IM_i++;
 2347                 break;
 2348             case ADDRESS_GUI_ADDR_MENU_TEXT:
 2349                 gtk_combo_box_set_active(GTK_COMBO_BOX(address_type_list_menu[address_i]), address_i);
 2350                 address_i++;
 2351                 break;
 2352             case ADDRESS_GUI_WEBSITE_TEXT:
 2353                 break;
 2354             case ADDRESS_GUI_BIRTHDAY:
 2355                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(birthday_checkbox), 0);
 2356                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reminder_checkbox), 0);
 2357                 get_pref(PREF_TODO_DAYS_TILL_DUE, &ivalue, NULL);
 2358                 reminder_str[0] = '\0';
 2359                 g_snprintf(reminder_str, sizeof(reminder_str), "%ld", ivalue);
 2360                 gtk_entry_set_text(GTK_ENTRY(reminder_entry), reminder_str);
 2361                 break;
 2362             default:
 2363                 break;
 2364         }
 2365     }
 2366 
 2367     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private_checkbox), FALSE);
 2368 
 2369     if (image) {
 2370         gtk_widget_destroy(image);
 2371         image = NULL;
 2372     }
 2373     if (contact_picture.data) {
 2374         free(contact_picture.data);
 2375         contact_picture.dirty = 0;
 2376         contact_picture.length = 0;
 2377         contact_picture.data = NULL;
 2378     }
 2379 
 2380     if (address_category == CATEGORY_ALL) {
 2381         new_cat = 0;
 2382     } else {
 2383         new_cat = address_category;
 2384     }
 2385     sorted_position = find_sort_cat_pos(new_cat);
 2386     if (sorted_position < 0) {
 2387         jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
 2388     } else {
 2389         gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu2), find_menu_cat_pos(sorted_position));
 2390     }
 2391 
 2392     set_new_button_to(CLEAR_FLAG);
 2393 
 2394     connect_changed_signals(CONNECT_SIGNALS);
 2395 }
 2396 
 2397 static void cb_address_clear(GtkWidget *widget,
 2398                              gpointer data) {
 2399     addr_clear_details();
 2400     gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
 2401     connect_changed_signals(DISCONNECT_SIGNALS);
 2402     set_new_button_to(NEW_FLAG);
 2403     gtk_widget_grab_focus(GTK_WIDGET(addr_text[0]));
 2404 }
 2405 
 2406 /* Attempt to make the best possible string out of whatever garbage we find
 2407  * Remove illegal characters, stop at carriage return and at least 1 digit
 2408  */
 2409 static void parse_phone_str(char *dest, char *src, int max_len) {
 2410     int i1, i2;
 2411 
 2412     for (i1 = 0, i2 = 0; (i1 < max_len) && src[i1]; i1++) {
 2413         if (isdigit(src[i1]) || (src[i1] == ',')
 2414             || (src[i1] == 'A') || (src[i1] == 'B') || (src[i1] == 'C')
 2415             || (src[i1] == 'D') || (src[i1] == '*') || (src[i1] == '#')
 2416                 ) {
 2417             dest[i2] = src[i1];
 2418             i2++;
 2419         } else if (((src[i1] == '\n') || (src[i1] == '\r') ||
 2420                     (src[i1] == 'x')) && i2) {
 2421             break;
 2422         }
 2423     }
 2424     dest[i2] = '\0';
 2425 }
 2426 
 2427 static void email_contact(GtkWidget *widget, gchar *str) {
 2428     char command[1024];
 2429     const char *pref_command;
 2430 
 2431     get_pref(PREF_MAIL_COMMAND, NULL, &pref_command);
 2432     if (!pref_command) {
 2433         jp_logf(JP_LOG_DEBUG, "email command empty\n");
 2434         return;
 2435     }
 2436 
 2437     /* Make a system call command string */
 2438     g_snprintf(command, sizeof(command), pref_command, str);
 2439     command[1023] = '\0';
 2440 
 2441     jp_logf(JP_LOG_STDOUT | JP_LOG_FILE, _("executing command = [%s]\n"), command);
 2442     if (system(command) == -1) {
 2443         jp_logf(JP_LOG_STDOUT | JP_LOG_FILE, _("Failed to execute [%s] at %s %d\n"), command, __FILE__, __LINE__);
 2444     }
 2445 
 2446 }
 2447 
 2448 static void dial_contact(GtkWidget *widget, gchar *str) {
 2449     char *Px;
 2450     char number[100];
 2451     char ext[100];
 2452 
 2453     number[0] = ext[0] = '\0';
 2454 
 2455     parse_phone_str(number, str, sizeof(number));
 2456 
 2457     Px = strstr(str, "x");
 2458     if (Px) {
 2459         parse_phone_str(ext, Px, sizeof(ext));
 2460     }
 2461 
 2462     dialog_dial(GTK_WINDOW(gtk_widget_get_toplevel(widget)), number, ext);
 2463 }
 2464 
 2465 static void cb_dial_or_mail(GtkWidget *widget, gpointer data) {
 2466     GtkWidget *text;
 2467     gchar *str;
 2468     int is_mail;
 2469     GtkTextIter start_iter;
 2470     GtkTextIter end_iter;
 2471     GtkTextBuffer *text_buffer;
 2472     text = data;
 2473 
 2474     text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
 2475     gtk_text_buffer_get_bounds(text_buffer, &start_iter, &end_iter);
 2476     str = gtk_text_buffer_get_text(text_buffer, &start_iter, &end_iter, TRUE);
 2477 
 2478     if (!str) return;
 2479     printf("[%s]\n", str);
 2480 
 2481     is_mail = GPOINTER_TO_INT( g_object_get_data(G_OBJECT(widget), "mail"));
 2482     if (is_mail) {
 2483         email_contact(widget, str);
 2484     } else {
 2485         dial_contact(widget, str);
 2486     }
 2487 
 2488     g_free(str);
 2489 }
 2490 
 2491 static void cb_address_quickfind(GtkWidget *widget,
 2492                                  gpointer data) {
 2493     const gchar *entry_text;
 2494     gchar *copied_text;
 2495     jp_logf(JP_LOG_DEBUG, "cb_address_quickfind\n");
 2496 
 2497     entry_text = gtk_entry_get_text(GTK_ENTRY(widget));
 2498     if (!strlen(entry_text)) {
 2499         return;
 2500     } else {
 2501         copied_text = g_strdup(entry_text);
 2502     }
 2503     gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), findAddressRecordByTextAndSelect, copied_text);
 2504     g_free(copied_text);
 2505 }
 2506 
 2507 static void cb_edit_cats_contacts(GtkWidget *widget, gpointer data) {
 2508     struct ContactAppInfo cai;
 2509     char full_name[FILENAME_MAX];
 2510     int num;
 2511     size_t size;
 2512     void *buf;
 2513     struct pi_file *pf;
 2514     pi_buffer_t *pi_buf;
 2515 
 2516     jp_logf(JP_LOG_DEBUG, "cb_edit_cats_contacts\n");
 2517 
 2518     get_home_file_name("ContactsDB-PAdd.pdb", full_name, sizeof(full_name));
 2519 
 2520     buf = NULL;
 2521     memset(&cai, 0, sizeof(cai));
 2522 
 2523     /* Contacts App Info is 1152 or so */
 2524     pi_buf = pi_buffer_new(1500);
 2525 
 2526     pf = pi_file_open(full_name);
 2527     pi_file_get_app_info(pf, &buf, &size);
 2528 
 2529     pi_buf = pi_buffer_append(pi_buf, buf, size);
 2530 
 2531     num = jp_unpack_ContactAppInfo(&cai, pi_buf);
 2532     if (num <= 0) {
 2533         jp_logf(JP_LOG_WARN, _("Error reading file: %s\n"), "ContactsDB-PAdd.pdb");
 2534         pi_buffer_free(pi_buf);
 2535         return;
 2536     }
 2537 
 2538     pi_file_close(pf);
 2539 
 2540     edit_cats(widget, "ContactsDB-PAdd", &(cai.category));
 2541 
 2542     size = jp_pack_ContactAppInfo(&cai, pi_buf);
 2543 
 2544     pdb_file_write_app_block("ContactsDB-PAdd", pi_buf->data, pi_buf->used);
 2545 
 2546     pi_buffer_free(pi_buf);
 2547 }
 2548 
 2549 static void cb_edit_cats_address(GtkWidget *widget, gpointer data) {
 2550     struct AddressAppInfo aai;
 2551     char full_name[FILENAME_MAX];
 2552     char buffer[65536];
 2553     int num;
 2554     size_t size;
 2555     void *buf;
 2556     struct pi_file *pf;
 2557 
 2558     jp_logf(JP_LOG_DEBUG, "cb_edit_cats_address\n");
 2559 
 2560     get_home_file_name("AddressDB.pdb", full_name, sizeof(full_name));
 2561 
 2562     buf = NULL;
 2563     memset(&aai, 0, sizeof(aai));
 2564 
 2565     pf = pi_file_open(full_name);
 2566     pi_file_get_app_info(pf, &buf, &size);
 2567 
 2568     num = unpack_AddressAppInfo(&aai, buf, size);
 2569 
 2570     if (num <= 0) {
 2571         jp_logf(JP_LOG_WARN, _("Error reading file: %s\n"), "AddressDB.pdb");
 2572         return;
 2573     }
 2574 
 2575     pi_file_close(pf);
 2576 
 2577     edit_cats(widget, "AddressDB", &(aai.category));
 2578 
 2579     size = pack_AddressAppInfo(&aai, (unsigned char *) buffer, sizeof(buffer));
 2580 
 2581     pdb_file_write_app_block("AddressDB", buffer, size);
 2582 }
 2583 
 2584 static void cb_edit_cats(GtkWidget *widget, gpointer data) {
 2585     if (address_version) {
 2586         cb_edit_cats_contacts(widget, data);
 2587     } else {
 2588         cb_edit_cats_address(widget, data);
 2589     }
 2590 
 2591     cb_app_button(NULL, GINT_TO_POINTER(REDRAW));
 2592 }
 2593 
 2594 static void cb_category(GtkComboBox *item, int selection) {
 2595     int b;
 2596 
 2597     if (!item) return;
 2598     if (gtk_combo_box_get_active(GTK_COMBO_BOX(item)) < 0) {
 2599         return;
 2600     }
 2601     int selectedItem = get_selected_category_from_combo_box(item);
 2602     if (selectedItem == -1) {
 2603         return;
 2604     }
 2605 
 2606     if (address_category == selectedItem) { return; }
 2607 
 2608     b = dialog_save_changed_record_with_cancel(pane, record_changed);
 2609     if (b == DIALOG_SAID_1) { /* Cancel */
 2610         int index, index2;
 2611 
 2612         if (address_category == CATEGORY_ALL) {
 2613             index = 0;
 2614             index2 = 0;
 2615         } else {
 2616             index = find_sort_cat_pos(address_category);
 2617             index2 = find_menu_cat_pos(index) + 1;
 2618             index += 1;
 2619         }
 2620 
 2621         if (index < 0) {
 2622             jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
 2623         } else {
 2624             gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), index2);
 2625         }
 2626 
 2627         return;
 2628     }
 2629     if (b == DIALOG_SAID_3) { /* Save */
 2630         cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
 2631     }
 2632 
 2633     if (selectedItem == CATEGORY_EDIT) {
 2634         cb_edit_cats(GTK_WIDGET(item), NULL);
 2635     } else {
 2636         address_category = selectedItem;
 2637     }
 2638     rowSelected = 0;
 2639     jp_logf(JP_LOG_DEBUG, "address_category = %d\n", address_category);
 2640     address_update_listStore(listStore, category_menu1, &glob_contact_list,
 2641                              address_category, TRUE);
 2642     /* gives the focus to the search field */
 2643     gtk_widget_grab_focus(address_quickfind_entry);
 2644 
 2645 }
 2646 
 2647 static void clear_mycontact(MyContact *mcont) {
 2648     mcont->unique_id = 0;
 2649     mcont->attrib = mcont->attrib & 0xF8;
 2650     jp_free_Contact(&(mcont->cont));
 2651     memset(&(mcont->cont), 0, sizeof(struct Contact));
 2652 
 2653     return;
 2654 }
 2655 
 2656 static void set_button_label_to_date(GtkWidget *button, struct tm *date) {
 2657     char birthday_str[255];
 2658     const char *pref_date;
 2659 
 2660     birthday_str[0] = '\0';
 2661     get_pref(PREF_SHORTDATE, NULL, &pref_date);
 2662     strftime(birthday_str, sizeof(birthday_str), pref_date, date);
 2663     gtk_button_set_label(GTK_BUTTON(button), birthday_str);
 2664 }
 2665 
 2666 static void cb_button_birthday(GtkWidget *widget, gpointer data) {
 2667     long fdow;
 2668     int r;
 2669 
 2670     get_pref(PREF_FDOW, &fdow, NULL);
 2671     r = cal_dialog(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
 2672                    _("Birthday"), fdow,
 2673                    &(birthday.tm_mon),
 2674                    &(birthday.tm_mday),
 2675                    &(birthday.tm_year));
 2676     if (r == CAL_DONE) {
 2677         set_button_label_to_date(birthday_button, &birthday);
 2678     }
 2679 }
 2680 
 2681 static void cb_check_button_birthday(GtkWidget *widget, gpointer data) {
 2682     time_t ltime;
 2683     struct tm *now;
 2684 
 2685     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
 2686         gtk_widget_show(birthday_box);
 2687         set_button_label_to_date(birthday_button, &birthday);
 2688     } else {
 2689         gtk_widget_hide(birthday_box);
 2690         gtk_widget_hide(reminder_box);
 2691         time(&ltime);
 2692         now = localtime(&ltime);
 2693         memcpy(&birthday, now, sizeof(struct tm));
 2694     }
 2695 }
 2696 
 2697 static void cb_check_button_reminder(GtkWidget *widget, gpointer data) {
 2698     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
 2699         gtk_widget_show(reminder_box);
 2700     } else {
 2701         gtk_widget_hide(reminder_box);
 2702     }
 2703 }
 2704 
 2705 /* Photo Code */
 2706 
 2707 static GtkWidget *image_from_data(void *buf, size_t size) {
 2708     GdkPixbufLoader *loader;
 2709     GError *error;
 2710     GdkPixbuf *pb;
 2711     GtkWidget *tmp_image = NULL;
 2712 
 2713     error = NULL;
 2714     loader = gdk_pixbuf_loader_new();
 2715     gdk_pixbuf_loader_write(loader, buf, size, &error);
 2716     pb = gdk_pixbuf_loader_get_pixbuf(loader);
 2717     tmp_image = g_object_ref(gtk_image_new_from_pixbuf(pb));
 2718 
 2719     if (loader) {
 2720         gdk_pixbuf_loader_close(loader, &error);
 2721         g_object_unref(loader);
 2722     }
 2723 
 2724     /* Force down reference count to prevent memory leak */
 2725     if (tmp_image) {
 2726         g_object_unref(tmp_image);
 2727     }
 2728 
 2729     return tmp_image;
 2730 }
 2731 
 2732 typedef void (*sighandler_t)(int);
 2733 
 2734 static int change_photo(char *filename) {
 2735     FILE *in;
 2736     char command[FILENAME_MAX + 256];
 2737     char buf[0xFFFF];
 2738     int total_read, count, r;
 2739     sighandler_t old_sighandler;
 2740 
 2741     /* SIGCHLD handler installed by sync process interferes with pclose.
 2742     * Temporarily restore SIGCHLD to its default value (null) while
 2743     * processing command through pipe */
 2744     old_sighandler = signal(SIGCHLD, SIG_DFL);
 2745 
 2746     sprintf(command, "convert -resize %dx%d %s jpg:-",
 2747             PHOTO_X_SZ, PHOTO_Y_SZ, filename);
 2748 
 2749     in = popen(command, "r");
 2750 
 2751     if (!in) {
 2752         return EXIT_FAILURE;
 2753     }
 2754 
 2755     total_read = 0;
 2756     while (!feof(in)) {
 2757         count = fread(buf + total_read, 1, 0xFFFF - total_read, in);
 2758         total_read += count;
 2759         if ((count == 0) || (total_read >= 0xFFFF)) break;
 2760     }
 2761     r = pclose(in);
 2762 
 2763     if (r) {
 2764         dialog_generic_ok(gtk_widget_get_toplevel(notebook),
 2765                           _("External program not found, or other error"),
 2766                           DIALOG_ERROR,
 2767                           _("J-Pilot can not find the external program \"convert\"\nor an error occurred while executing convert.\nYou may need to install package ImageMagick"));
 2768         jp_logf(JP_LOG_WARN, _("Command executed was \"%s\"\n"), command);
 2769         jp_logf(JP_LOG_WARN, _("return code was %d\n"), r);
 2770         return EXIT_FAILURE;
 2771     }
 2772 
 2773     if (image) {
 2774         gtk_widget_destroy(image);
 2775         image = NULL;
 2776     }
 2777     if (contact_picture.data) {
 2778         free(contact_picture.data);
 2779         contact_picture.dirty = 0;
 2780         contact_picture.length = 0;
 2781         contact_picture.data = NULL;
 2782     }
 2783     contact_picture.data = malloc(total_read);
 2784     memcpy(contact_picture.data, buf, total_read);
 2785     contact_picture.length = total_read;
 2786     contact_picture.dirty = 0;
 2787     image = image_from_data(contact_picture.data, contact_picture.length);
 2788     gtk_container_add(GTK_CONTAINER(picture_button), image);
 2789     gtk_widget_show(image);
 2790 
 2791     signal(SIGCHLD, old_sighandler);
 2792 
 2793     return EXIT_SUCCESS;
 2794 }
 2795 
 2796 // TODO: make a common filesel function
 2797 static void cb_photo_browse_cancel(GtkWidget *widget, gpointer data) {
 2798     gtk_widget_destroy(widget);
 2799 }
 2800 
 2801 static void cb_photo_browse_ok(GtkWidget *widget, gpointer data) {
 2802     const char *sel;
 2803     char **Pselection;
 2804 
 2805     sel = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (widget));
 2806     set_pref(PREF_CONTACTS_PHOTO_FILENAME, 0, sel, TRUE);
 2807 
 2808     Pselection = g_object_get_data(G_OBJECT(GTK_FILE_CHOOSER(widget)), "selection");
 2809     if (Pselection) {
 2810         jp_logf(JP_LOG_DEBUG, "setting selection to %s\n", sel);
 2811         *Pselection = strdup(sel);
 2812     }
 2813 
 2814     gtk_widget_destroy(widget);
 2815 }
 2816 
 2817 static int browse_photo(GtkWidget *main_window) {
 2818     GtkWidget *fileChooserWidget;
 2819     const char *svalue;
 2820     char dir[MAX_PREF_LEN + 2];
 2821     int i;
 2822     char *selection;
 2823 
 2824     get_pref(PREF_CONTACTS_PHOTO_FILENAME, NULL, &svalue);
 2825     g_strlcpy(dir, svalue, sizeof(dir));
 2826     i = strlen(dir) - 1;
 2827     if (i < 0) i = 0;
 2828     if (dir[i] != '/') {
 2829         for (i = strlen(dir); i >= 0; i--) {
 2830             if (dir[i] == '/') {
 2831                 dir[i + 1] = '\0';
 2832                 break;
 2833             }
 2834         }
 2835     }
 2836 
 2837     if (chdir(dir)) {
 2838         jp_logf(JP_LOG_WARN, _("chdir() failed\n"));
 2839     }
 2840     selection = NULL;
 2841     fileChooserWidget = gtk_file_chooser_dialog_new(_("Add Photo"), GTK_WINDOW(main_window), GTK_FILE_CHOOSER_ACTION_OPEN,
 2842                                                     "Cancel", GTK_RESPONSE_CANCEL, "Open",
 2843                                                     GTK_RESPONSE_ACCEPT, NULL);
 2844     g_object_set_data(G_OBJECT(GTK_FILE_CHOOSER(fileChooserWidget)), "selection", &selection);
 2845     if (gtk_dialog_run(GTK_DIALOG (fileChooserWidget)) == GTK_RESPONSE_ACCEPT) {
 2846         cb_photo_browse_ok(fileChooserWidget, NULL);
 2847     } else {
 2848         cb_photo_browse_cancel(fileChooserWidget, NULL);
 2849     }
 2850     if (selection) {
 2851         jp_logf(JP_LOG_DEBUG, "browse_photo(): selection = %s\n", selection);
 2852         change_photo(selection);
 2853         free(selection);
 2854         return 1;
 2855     }
 2856 
 2857     return 0;
 2858 }
 2859 
 2860 static void cb_photo_menu_select(GtkWidget *item,
 2861                                  GtkPositionType selected) {
 2862     if (selected == 1) {
 2863         if (0 == browse_photo(gtk_widget_get_toplevel(treeView)))
 2864             /* change photo canceled */
 2865             return;
 2866     }
 2867     if (selected == 2) {
 2868         if (image) {
 2869             gtk_widget_destroy(image);
 2870             image = NULL;
 2871         }
 2872         if (contact_picture.data) {
 2873             free(contact_picture.data);
 2874             contact_picture.dirty = 0;
 2875             contact_picture.length = 0;
 2876             contact_picture.data = NULL;
 2877         }
 2878     }
 2879 
 2880     cb_record_changed(NULL, NULL);
 2881 }
 2882 
 2883 static gint cb_photo_menu_popup(GtkWidget *widget, GdkEvent *event) {
 2884     GtkMenu *menu;
 2885     GdkEventButton *event_button;
 2886 
 2887     g_return_val_if_fail(widget != NULL, FALSE);
 2888     g_return_val_if_fail(GTK_IS_MENU(widget), FALSE);
 2889     g_return_val_if_fail(event != NULL, FALSE);
 2890 
 2891     if (event->type == GDK_BUTTON_PRESS) {
 2892         event_button = (GdkEventButton *) event;
 2893         if (event_button->button == 1) {
 2894             menu = GTK_MENU (widget);
 2895             // TODO verify that this change is correct
 2896             //gtk_menu_popup(menu, NULL, NULL, NULL, NULL,
 2897             //               event_button->button, event_button->time);
 2898             gtk_menu_popup_at_pointer(menu, event);
 2899             return TRUE;
 2900         }
 2901     }
 2902 
 2903     return FALSE;
 2904 }
 2905 
 2906 /* End Photo code */
 2907 
 2908 static gboolean cb_key_pressed_left_side(GtkWidget *widget,
 2909                                          GdkEventKey *event) {
 2910     GtkWidget *entry_widget;
 2911     GtkTextBuffer *text_buffer;
 2912     GtkTextIter iter;
 2913 
 2914     if (event->keyval == GDK_KEY_Return) {
 2915         g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
 2916 
 2917         if (address_version == 0) {
 2918             switch (gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))) {
 2919                 case 0 :
 2920                     entry_widget = addr_text[contLastname];
 2921                     break;
 2922                 case 1 :
 2923                     entry_widget = addr_text[contAddress1];
 2924                     break;
 2925                 case 2 :
 2926                     entry_widget = addr_text[contCustom1];
 2927                     break;
 2928                 case 3 :
 2929                     entry_widget = addr_text[contNote];
 2930                     break;
 2931                 default:
 2932                     entry_widget = addr_text[0];
 2933             }
 2934         } else {
 2935             switch (gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))) {
 2936                 case 0 :
 2937                     entry_widget = addr_text[contLastname];
 2938                     break;
 2939                 case 1 :
 2940                     entry_widget = addr_text[contAddress1];
 2941                     break;
 2942                 case 2 :
 2943                     entry_widget = addr_text[contAddress2];
 2944                     break;
 2945                 case 3 :
 2946                     entry_widget = addr_text[contAddress3];
 2947                     break;
 2948                 case 4 :
 2949                     entry_widget = addr_text[contCustom1];
 2950                     break;
 2951                 case 5 :
 2952                     entry_widget = addr_text[contNote];
 2953                     break;
 2954                 default:
 2955                     entry_widget = addr_text[0];
 2956             }
 2957         }
 2958 
 2959         gtk_widget_grab_focus(entry_widget);
 2960         /* Position cursor at start of text */
 2961         text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry_widget));
 2962         gtk_text_buffer_get_start_iter(text_buffer, &iter);
 2963         gtk_text_buffer_place_cursor(text_buffer, &iter);
 2964 
 2965         return TRUE;
 2966     }
 2967 
 2968     return FALSE;
 2969 }
 2970 
 2971 static gboolean cb_key_pressed_right_side(GtkWidget *widget,
 2972                                           GdkEventKey *event,
 2973                                           gpointer data) {
 2974     if ((event->keyval == GDK_KEY_Return) && (event->state & GDK_SHIFT_MASK)) {
 2975         g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
 2976         gtk_widget_grab_focus(GTK_WIDGET(treeView));
 2977         return TRUE;
 2978     }
 2979     /* Call external editor for note text */
 2980     if (data != NULL &&
 2981         (event->keyval == GDK_KEY_e) && (event->state & GDK_CONTROL_MASK)) {
 2982         g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
 2983 
 2984         /* Get current text and place in temporary file */
 2985         GtkTextIter start_iter;
 2986         GtkTextIter end_iter;
 2987         char *text_out;
 2988         GObject *note_buffer = addr_text_buffer[schema[GPOINTER_TO_INT(data)].record_field];
 2989 
 2990         gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(note_buffer),
 2991                                    &start_iter, &end_iter);
 2992         text_out = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(note_buffer),
 2993                                             &start_iter, &end_iter, TRUE);
 2994 
 2995 
 2996         char tmp_fname[] = "jpilot.XXXXXX";
 2997         int tmpfd = mkstemp(tmp_fname);
 2998         if (tmpfd < 0) {
 2999             jp_logf(JP_LOG_WARN, _("Could not get temporary file name\n"));
 3000             if (text_out)
 3001                 free(text_out);
 3002             return TRUE;
 3003         }
 3004 
 3005         FILE *fptr = fdopen(tmpfd, "w");
 3006         if (!fptr) {
 3007             jp_logf(JP_LOG_WARN, _("Could not open temporary file for external editor\n"));
 3008             if (text_out)
 3009                 free(text_out);
 3010             return TRUE;
 3011         }
 3012         fwrite(text_out, strlen(text_out), 1, fptr);
 3013         fwrite("\n", 1, 1, fptr);
 3014         fclose(fptr);
 3015 
 3016         /* Call external editor */
 3017         char command[1024];
 3018         const char *ext_editor;
 3019 
 3020         get_pref(PREF_EXTERNAL_EDITOR, NULL, &ext_editor);
 3021         if (!ext_editor) {
 3022             jp_logf(JP_LOG_INFO, "External Editor command empty\n");
 3023             if (text_out)
 3024                 free(text_out);
 3025             return TRUE;
 3026         }
 3027 
 3028         if ((strlen(ext_editor) + strlen(tmp_fname) + 1) > sizeof(command)) {
 3029             if (text_out)
 3030                 free(text_out);
 3031             return TRUE;
 3032         }
 3033         g_snprintf(command, sizeof(command), "%s %s", ext_editor, tmp_fname);
 3034 
 3035         /* jp_logf(JP_LOG_STDOUT|JP_LOG_FILE, _("executing command = [%s]\n"), command); */
 3036         if (system(command) == -1) {
 3037             /* Read data back from temporary file into memo */
 3038             char text_in[0xFFFF];
 3039             size_t bytes_read;
 3040 
 3041             fptr = fopen(tmp_fname, "rb");
 3042             if (!fptr) {
 3043                 jp_logf(JP_LOG_WARN, _("Could not open temporary file from external editor\n"));
 3044                 return TRUE;
 3045             }
 3046             bytes_read = fread(text_in, 1, 0xFFFF, fptr);
 3047             fclose(fptr);
 3048             unlink(tmp_fname);
 3049 
 3050             text_in[--bytes_read] = '\0';  /* Strip final newline */
 3051             /* Only update text if it has changed */
 3052             if (strcmp(text_out, text_in)) {
 3053                 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(note_buffer), text_in, -1);
 3054             }
 3055         }
 3056 
 3057         if (text_out)
 3058             free(text_out);
 3059 
 3060         return TRUE;
 3061     }   /* End of external editor if */
 3062 
 3063     return FALSE;
 3064 }
 3065 
 3066 gboolean
 3067 findAddressRecordByTextAndSelect(GtkTreeModel *model,
 3068                                  GtkTreePath *path,
 3069                                  GtkTreeIter *iter,
 3070                                  gpointer data) {
 3071     int *i = gtk_tree_path_get_indices(path);
 3072     char *list_text;
 3073     char *entry_text = data;
 3074 
 3075     gtk_tree_model_get(model, iter, ADDRESS_NAME_COLUMN_ENUM, &list_text, -1);
 3076     int result = strncasecmp(list_text, entry_text, strlen(entry_text));
 3077     if (!result) {
 3078         GtkTreeSelection *selection = NULL;
 3079         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
 3080         gtk_tree_selection_set_select_function(selection, handleRowSelectionForAddress, NULL, NULL);
 3081         gtk_tree_selection_select_path(selection, path);
 3082         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeView), path, ADDRESS_NAME_COLUMN_ENUM, FALSE, 1.0, 0.0);
 3083         rowSelected = i[0];
 3084         return TRUE;
 3085     }
 3086     return FALSE;
 3087 }
 3088 
 3089 gboolean
 3090 findAddressRecordAndSelect(GtkTreeModel *model,
 3091                            GtkTreePath *path,
 3092                            GtkTreeIter *iter,
 3093                            gpointer data) {
 3094 
 3095     if (glob_find_id) {
 3096         MyContact *maddr = NULL;
 3097 
 3098         gtk_tree_model_get(model, iter, ADDRESS_DATA_COLUMN_ENUM, &maddr, -1);
 3099         if (maddr->unique_id == glob_find_id) {
 3100             GtkTreeSelection *selection = NULL;
 3101             selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
 3102             gtk_tree_selection_select_path(selection, path);
 3103             gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeView), path, (GtkTreeViewColumn *)ADDRESS_PHONE_COLUMN_ENUM, FALSE, 1.0, 0.0);
 3104             glob_find_id = 0;
 3105             return TRUE;
 3106         }
 3107     }
 3108     return FALSE;
 3109 }
 3110 
 3111 gboolean
 3112 findAndSetGlobalAddressId(GtkTreeModel *model,
 3113                           GtkTreePath *path,
 3114                           GtkTreeIter *iter,
 3115                           gpointer data) {
 3116     int *i = gtk_tree_path_get_indices(path);
 3117     if (i[0] == rowSelected) {
 3118         MyContact *maddr = NULL;
 3119 
 3120         gtk_tree_model_get(model, iter, ADDRESS_DATA_COLUMN_ENUM, &maddr, -1);
 3121         if (maddr != NULL) {
 3122             glob_find_id = maddr->unique_id;
 3123         } else {
 3124             glob_find_id = 0;
 3125         }
 3126         return TRUE;
 3127     }
 3128 
 3129     return FALSE;
 3130 }
 3131 
 3132 gboolean
 3133 selectRecordAddressByRow(GtkTreeModel *model,
 3134                          GtkTreePath *path,
 3135                          GtkTreeIter *iter,
 3136                          gpointer data) {
 3137     int *i = gtk_tree_path_get_indices(path);
 3138     if (i[0] == rowSelected) {
 3139         GtkTreeSelection *selection = NULL;
 3140         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
 3141         gtk_tree_selection_select_path(selection, path);
 3142         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeView), path, (GtkTreeViewColumn *)ADDRESS_PHONE_COLUMN_ENUM, FALSE, 1.0, 0.0);
 3143         return TRUE;
 3144     }
 3145 
 3146     return FALSE;
 3147 }
 3148 
 3149 static void address_update_listStore(GtkListStore *pListStore, GtkWidget *tooltip_widget,
 3150                                      ContactList **cont_list, int category,
 3151                                      int main) {
 3152     GtkTreeIter iter;
 3153     int num_entries, entries_shown;
 3154     int show1, show2, show3;
 3155     GdkPixbuf *pixbuf_note;
 3156     GdkPixbuf *noteColumnDisplay;
 3157     ContactList *temp_cl;
 3158     char str[ADDRESS_MAX_COLUMN_LEN + 2];
 3159     char phone[ADDRESS_MAX_COLUMN_LEN + 2];
 3160     char name[ADDRESS_MAX_COLUMN_LEN + 2];
 3161     int show_priv;
 3162     long use_jos, char_set, show_tooltips;
 3163     char *tmp_p1, *tmp_p2, *tmp_p3;
 3164     char blank[] = "";
 3165     char slash[] = " / ";
 3166     char comma_space[] = ", ";
 3167     char *field1, *field2, *field3;
 3168     char *delim1, *delim2;
 3169     char *tmp_delim1, *tmp_delim2;
 3170     AddressList *addr_list;
 3171 
 3172     free_ContactList(cont_list);
 3173 
 3174     if (address_version == 0) {
 3175         addr_list = NULL;
 3176         num_entries = get_addresses2(&addr_list, SORT_ASCENDING, 2, 2, 1, CATEGORY_ALL);
 3177         copy_addresses_to_contacts(addr_list, cont_list);
 3178         free_AddressList(&addr_list);
 3179     } else {
 3180         /* Need to get all records including private ones for the tooltips calculation */
 3181         num_entries = get_contacts2(cont_list, SORT_ASCENDING, 2, 2, 1, CATEGORY_ALL);
 3182     }
 3183 
 3184     /* Start by clearing existing entry if in main window */
 3185     if (main) {
 3186         addr_clear_details();
 3187         gtk_text_buffer_set_text(GTK_TEXT_BUFFER(addr_all_buffer), "", -1);
 3188     }
 3189     GtkTreeSelection* treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
 3190     gtk_tree_selection_set_select_function(treeSelection, NULL, NULL, NULL);
 3191     gtk_list_store_clear(GTK_LIST_STORE(pListStore));
 3192     /* Collect preferences and pixmaps before loop */
 3193     get_pref(PREF_CHAR_SET, &char_set, NULL);
 3194     get_pref(PREF_USE_JOS, &use_jos, NULL);
 3195     show_priv = show_privates(GET_PRIVATES);
 3196     get_pixbufs(PIXMAP_NOTE, &pixbuf_note);
 3197 
 3198 
 3199     switch (addr_sort_order) {
 3200         case SORT_BY_LNAME:
 3201         default:
 3202             show1 = contLastname;
 3203             show2 = contFirstname;
 3204             show3 = contCompany;
 3205             delim1 = comma_space;
 3206             delim2 = slash;
 3207             break;
 3208         case SORT_BY_FNAME:
 3209             show1 = contFirstname;
 3210             show2 = contLastname;
 3211             show3 = contCompany;
 3212             delim1 = comma_space;
 3213             delim2 = slash;
 3214             break;
 3215         case SORT_BY_COMPANY:
 3216             show1 = contCompany;
 3217             show2 = contLastname;
 3218             show3 = contFirstname;
 3219             delim1 = slash;
 3220             delim2 = comma_space;
 3221             break;
 3222     }
 3223 
 3224     entries_shown = 0;
 3225 
 3226     for (temp_cl = *cont_list; temp_cl; temp_cl = temp_cl->next) {
 3227         if (((temp_cl->mcont.attrib & 0x0F) != category) &&
 3228             category != CATEGORY_ALL) {
 3229             continue;
 3230         }
 3231 
 3232         /* Do masking like Palm OS 3.5 */
 3233         if ((show_priv == MASK_PRIVATES) &&
 3234             (temp_cl->mcont.attrib & dlpRecAttrSecret)) {
 3235             gtk_list_store_append(pListStore, &iter);
 3236             clear_mycontact(&temp_cl->mcont);
 3237             gtk_list_store_set(pListStore, &iter,
 3238                                ADDRESS_NAME_COLUMN_ENUM, "---------------",
 3239                                ADDRESS_PHONE_COLUMN_ENUM, "---------------",
 3240                                ADDRESS_DATA_COLUMN_ENUM, &temp_cl->mcont,
 3241                                -1);
 3242             entries_shown++;
 3243             continue;
 3244         }
 3245         /* End Masking */
 3246 
 3247         /* Hide the private records if need be */
 3248         if ((show_priv != SHOW_PRIVATES) &&
 3249             (temp_cl->mcont.attrib & dlpRecAttrSecret)) {
 3250             continue;
 3251         }
 3252 
 3253         if (!use_jos && (char_set == CHAR_SET_JAPANESE || char_set == CHAR_SET_SJIS_UTF)) {
 3254             str[0] = '\0';
 3255             if (temp_cl->mcont.cont.entry[show1] || temp_cl->mcont.cont.entry[show2]) {
 3256                 if (temp_cl->mcont.cont.entry[show1] && temp_cl->mcont.cont.entry[show2]) {
 3257                     if ((tmp_p1 = strchr(temp_cl->mcont.cont.entry[show1], '\1'))) *tmp_p1 = '\0';
 3258                     if ((tmp_p2 = strchr(temp_cl->mcont.cont.entry[show2], '\1'))) *tmp_p2 = '\0';
 3259                     g_snprintf(str, ADDRESS_MAX_LIST_NAME, "%s, %s", temp_cl->mcont.cont.entry[show1],
 3260                                temp_cl->mcont.cont.entry[show2]);
 3261                     if (tmp_p1) *tmp_p1 = '\1';
 3262                     if (tmp_p2) *tmp_p2 = '\1';
 3263                 }
 3264                 if (temp_cl->mcont.cont.entry[show1] && !temp_cl->mcont.cont.entry[show2]) {
 3265                     if ((tmp_p1 = strchr(temp_cl->mcont.cont.entry[show1], '\1'))) *tmp_p1 = '\0';
 3266                     if (temp_cl->mcont.cont.entry[show3]) {
 3267                         if ((tmp_p3 = strchr(temp_cl->mcont.cont.entry[show3], '\1'))) *tmp_p3 = '\0';
 3268                         g_snprintf(str, ADDRESS_MAX_LIST_NAME, "%s, %s", temp_cl->mcont.cont.entry[show1],
 3269                                    temp_cl->mcont.cont.entry[show3]);
 3270                         if (tmp_p3) *tmp_p3 = '\1';
 3271                     } else {
 3272                         multibyte_safe_strncpy(str, temp_cl->mcont.cont.entry[show1], ADDRESS_MAX_LIST_NAME);
 3273                     }
 3274                     if (tmp_p1) *tmp_p1 = '\1';
 3275                 }
 3276                 if (!temp_cl->mcont.cont.entry[show1] && temp_cl->mcont.cont.entry[show2]) {
 3277                     if ((tmp_p2 = strchr(temp_cl->mcont.cont.entry[show2], '\1'))) *tmp_p2 = '\0';
 3278                     multibyte_safe_strncpy(str, temp_cl->mcont.cont.entry[show2], ADDRESS_MAX_LIST_NAME);
 3279                     if (tmp_p2) *tmp_p2 = '\1';
 3280                 }
 3281             } else if (temp_cl->mcont.cont.entry[show3]) {
 3282                 if ((tmp_p3 = strchr(temp_cl->mcont.cont.entry[show3], '\1'))) *tmp_p3 = '\0';
 3283                 multibyte_safe_strncpy(str, temp_cl->mcont.cont.entry[show3], ADDRESS_MAX_LIST_NAME);
 3284                 if (tmp_p3) *tmp_p3 = '\1';
 3285             } else {
 3286                 strcpy(str, _("-Unnamed-"));
 3287             }
 3288         } else {
 3289             str[0] = '\0';
 3290             field1 = field2 = field3 = blank;
 3291             tmp_delim1 = delim1;
 3292             tmp_delim2 = delim2;
 3293             if (temp_cl->mcont.cont.entry[show1]) field1 = temp_cl->mcont.cont.entry[show1];
 3294             if (temp_cl->mcont.cont.entry[show2]) field2 = temp_cl->mcont.cont.entry[show2];
 3295             if (temp_cl->mcont.cont.entry[show3]) field3 = temp_cl->mcont.cont.entry[show3];
 3296             switch (addr_sort_order) {
 3297                 case SORT_BY_LNAME:
 3298                 default:
 3299                     if ((!field1[0]) || (!field2[0])) tmp_delim1 = blank;
 3300                     if (!(field3[0])) tmp_delim2 = blank;
 3301                     if ((!field1[0]) && (!field2[0])) tmp_delim2 = blank;
 3302                     break;
 3303                 case SORT_BY_FNAME:
 3304                     if ((!field1[0]) || (!field2[0])) tmp_delim1 = blank;
 3305                     if (!(field3[0])) tmp_delim2 = blank;
 3306                     if ((!field1[0]) && (!field2[0])) tmp_delim2 = blank;
 3307                     break;
 3308                 case SORT_BY_COMPANY:
 3309                     if (!(field1[0])) tmp_delim1 = blank;
 3310                     if ((!field2[0]) || (!field3[0])) tmp_delim2 = blank;
 3311                     if ((!field2[0]) && (!field3[0])) tmp_delim1 = blank;
 3312                     break;
 3313             }
 3314             g_snprintf(str, ADDRESS_MAX_COLUMN_LEN, "%s%s%s%s%s",
 3315                        field1, tmp_delim1, field2, tmp_delim2, field3);
 3316             if (strlen(str) < 1) strcpy(str, _("-Unnamed-"));
 3317             str[ADDRESS_MAX_COLUMN_LEN] = '\0';
 3318 
 3319 
 3320         }
 3321 
 3322         lstrncpy_remove_cr_lfs(name, str, ADDRESS_MAX_COLUMN_LEN);
 3323         phone[0] = '\0';
 3324         lstrncpy_remove_cr_lfs(phone, temp_cl->mcont.cont.entry[temp_cl->mcont.cont.showPhone + 4],
 3325                                ADDRESS_MAX_COLUMN_LEN);
 3326         GdkRGBA bgColor;
 3327         gboolean showBgColor = FALSE;
 3328         GdkRGBA fgColor;
 3329         gboolean showFgColor = FALSE;
 3330         /* Highlight row background depending on status */
 3331         switch (temp_cl->mcont.rt) {
 3332             case NEW_PC_REC:
 3333             case REPLACEMENT_PALM_REC:
 3334                 bgColor = get_color(LIST_NEW_RED, LIST_NEW_GREEN, LIST_NEW_BLUE);
 3335                 showBgColor = TRUE;
 3336                 break;
 3337             case DELETED_PALM_REC:
 3338             case DELETED_PC_REC:
 3339                 bgColor = get_color(LIST_DEL_RED, LIST_DEL_GREEN, LIST_DEL_BLUE);
 3340                 showBgColor = TRUE;
 3341                 break;
 3342             case MODIFIED_PALM_REC:
 3343                 bgColor = get_color(LIST_MOD_RED, LIST_MOD_GREEN, LIST_MOD_BLUE);
 3344                 showBgColor = TRUE;
 3345                 break;
 3346             default:
 3347                 if (temp_cl->mcont.attrib & dlpRecAttrSecret) {
 3348                     bgColor = get_color(LIST_PRIVATE_RED, LIST_PRIVATE_GREEN, LIST_PRIVATE_BLUE);
 3349                     showBgColor = TRUE;
 3350                 } else {
 3351                     showBgColor = FALSE;
 3352                 }
 3353         }
 3354 
 3355         /* Put a note pixmap up */
 3356         if (temp_cl->mcont.cont.entry[contNote]) {
 3357             noteColumnDisplay = pixbuf_note;
 3358         } else {
 3359             noteColumnDisplay = NULL;
 3360         }
 3361         gtk_list_store_append(pListStore, &iter);
 3362         gtk_list_store_set(pListStore, &iter, ADDRESS_NAME_COLUMN_ENUM, name,
 3363                            ADDRESS_NOTE_COLUMN_ENUM, noteColumnDisplay,
 3364                            ADDRESS_PHONE_COLUMN_ENUM, phone,
 3365                            ADDRESS_DATA_COLUMN_ENUM, &(temp_cl->mcont),
 3366                            ADDRESS_BACKGROUND_COLOR_ENUM, showBgColor ? &bgColor : NULL,
 3367                            ADDRESS_BACKGROUND_COLOR_ENABLED_ENUM, showBgColor,
 3368                            ADDRESS_FOREGROUND_COLOR_ENUM, showFgColor ? gdk_rgba_to_string(&fgColor) : NULL,
 3369                            ADDRESSS_FOREGROUND_COLOR_ENABLED_ENUM, showFgColor, -1);
 3370         entries_shown++;
 3371     }
 3372 
 3373     // Set callback for a row selected
 3374     gtk_tree_selection_set_select_function(treeSelection, handleRowSelectionForAddress, NULL, NULL);
 3375 
 3376     /* If there are items in the list, highlight the selected row */
 3377     if ((main) && (entries_shown > 0)) {
 3378         /* First, select any record being searched for */
 3379         if (glob_find_id) {
 3380             address_find();
 3381         }
 3382         /* Second, try the currently selected row */
 3383         else if (rowSelected < entries_shown) {
 3384             gtk_tree_model_foreach(GTK_TREE_MODEL(pListStore), selectRecordAddressByRow, NULL);
 3385         } else
 3386             /* Third, select row 0 if nothing else is possible */
 3387         {
 3388             rowSelected = 0;
 3389             gtk_tree_model_foreach(GTK_TREE_MODEL(pListStore), selectRecordAddressByRow, NULL);
 3390         }
 3391     }
 3392 
 3393     if (tooltip_widget) {
 3394         get_pref(PREF_SHOW_TOOLTIPS, &show_tooltips, NULL);
 3395         if (cont_list == NULL) {
 3396             set_tooltip(show_tooltips, category_menu1, _("0 records"));
 3397         } else {
 3398             sprintf(str, _("%d of %d records"), entries_shown, num_entries);
 3399             set_tooltip(show_tooltips, category_menu1, str);
 3400         }
 3401     }
 3402     /* return focus to treeView after any big operation which requires a redraw */
 3403     gtk_widget_grab_focus(GTK_WIDGET(treeView));
 3404 
 3405 }
 3406 
 3407 /* default set is which menu item is to be set on by default */
 3408 /* set is which set in the phone_type_menu_item array to use */
 3409 static int make_IM_type_menu(int default_set, unsigned int callback_id, int set) {
 3410     int i;
 3411 
 3412     IM_type_list_menu[set] = gtk_combo_box_text_new();
 3413 
 3414     for (i = 0; i < NUM_IM_LABELS; i++) {
 3415         if (contact_app_info.IMLabels[i][0]) {
 3416             gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT (IM_type_list_menu[set]), contact_app_info.IMLabels[i]);
 3417             changed_list = g_list_prepend(changed_list, IM_type_list_menu[set]);
 3418         }
 3419     }
 3420     g_signal_connect(G_OBJECT(IM_type_list_menu[set]), "changed", G_CALLBACK(cb_IM_type_menu),
 3421                      GINT_TO_POINTER(set));
 3422 
 3423     gtk_combo_box_set_active(GTK_COMBO_BOX(IM_type_list_menu[set]), default_set);
 3424 
 3425     return EXIT_SUCCESS;
 3426 }
 3427 
 3428 
 3429 // TODO: rewrite this crappy function
 3430 /* default set is which menu item is to be set on by default */
 3431 /* set is which set in the menu_item array to use */
 3432 static int make_address_type_menu(int default_set, int set) {
 3433     int i;
 3434 
 3435     address_type_list_menu[set] = gtk_combo_box_text_new();
 3436     for (i = 0; i < NUM_ADDRESSES; i++) {
 3437         if (contact_app_info.addrLabels[i][0]) {
 3438             gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT (address_type_list_menu[set]),
 3439                                            contact_app_info.addrLabels[i]);
 3440             changed_list = g_list_prepend(changed_list, address_type_list_menu[set]);
 3441         }
 3442     }
 3443     g_signal_connect(G_OBJECT(address_type_list_menu[set]), "changed", G_CALLBACK(cb_address_type_menu),
 3444                      GINT_TO_POINTER(set));
 3445 
 3446     gtk_combo_box_set_active(GTK_COMBO_BOX(address_type_list_menu[set]), default_set);
 3447     return EXIT_SUCCESS;
 3448 }
 3449 
 3450 /* default set is which menu item is to be set on by default */
 3451 /* set is which set in the phone_type_menu_item array to use */
 3452 static int make_phone_menu(int default_set, unsigned int callback_id, int set) {
 3453     int i;
 3454     char *utf;
 3455     long char_set;
 3456 
 3457     get_pref(PREF_CHAR_SET, &char_set, NULL);
 3458 
 3459     phone_type_list_menu[set] = gtk_combo_box_text_new();
 3460 
 3461     for (i = 0; i < NUM_PHONE_LABELS; i++) {
 3462         utf = charset_p2newj(contact_app_info.phoneLabels[i], 16, char_set);
 3463         if (contact_app_info.phoneLabels[i][0]) {
 3464             gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT (phone_type_list_menu[set]), utf);
 3465             changed_list = g_list_prepend(changed_list, phone_type_list_menu[set]);
 3466         }
 3467         g_free(utf);
 3468     }
 3469     g_signal_connect(G_OBJECT(phone_type_list_menu[set]), "changed", G_CALLBACK(cb_phone_menu),
 3470                      GINT_TO_POINTER(set));
 3471 
 3472     /* Set this one to active */
 3473     gtk_combo_box_set_active(GTK_COMBO_BOX(phone_type_list_menu[set]), default_set);
 3474 
 3475     return EXIT_SUCCESS;
 3476 }
 3477 
 3478 
 3479 /* returns 1 if found, 0 if not */
 3480 static int address_find(void) {
 3481     gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), findAddressRecordAndSelect, NULL);
 3482     return EXIT_SUCCESS;
 3483 }
 3484 
 3485 static int address_redraw(void) {
 3486     address_update_listStore(listStore, category_menu1, &glob_contact_list,
 3487                              address_category, TRUE);
 3488     return EXIT_SUCCESS;
 3489 }
 3490 
 3491 int address_cycle_cat(void) {
 3492     int b;
 3493     int i, new_cat;
 3494 
 3495     b = dialog_save_changed_record(pane, record_changed);
 3496     if (b == DIALOG_SAID_2) {
 3497         cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
 3498     }
 3499 
 3500     if (address_category == CATEGORY_ALL) {
 3501         new_cat = -1;
 3502     } else {
 3503         new_cat = find_sort_cat_pos(address_category);
 3504     }
 3505 
 3506     for (i = 0; i < NUM_ADDRESS_CAT_ITEMS; i++) {
 3507         new_cat++;
 3508         if (new_cat >= NUM_ADDRESS_CAT_ITEMS) {
 3509             address_category = CATEGORY_ALL;
 3510             break;
 3511         }
 3512         if ((sort_l[new_cat].Pcat) && (sort_l[new_cat].Pcat[0])) {
 3513             address_category = sort_l[new_cat].cat_num;
 3514             break;
 3515         }
 3516     }
 3517 
 3518     rowSelected = 0;
 3519 
 3520     return EXIT_SUCCESS;
 3521 }
 3522 
 3523 int address_refresh(void) {
 3524     int index, index2;
 3525 
 3526     if (glob_find_id) {
 3527         address_category = CATEGORY_ALL;
 3528     }
 3529     if (address_category == CATEGORY_ALL) {
 3530         index = 0;
 3531         index2 = 0;
 3532     } else {
 3533         index = find_sort_cat_pos(address_category);
 3534         index2 = find_menu_cat_pos(index) + 1;
 3535         index += 1;
 3536     }
 3537     address_update_listStore(listStore, category_menu1, &glob_contact_list,
 3538                              address_category, TRUE);
 3539     if (index < 0) {
 3540         jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
 3541     } else {
 3542         //gtk_check_menu_item_set_active
 3543         //   (GTK_CHECK_MENU_ITEM(address_cat_menu_item1[index]), TRUE);
 3544         // gtk_option_menu_set_history(GTK_OPTION_MENU(category_menu1), index2);
 3545         gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu1), index2);
 3546     }
 3547 
 3548     /* gives the focus to the search field */
 3549     gtk_widget_grab_focus(address_quickfind_entry);
 3550 
 3551     return EXIT_SUCCESS;
 3552 }
 3553 
 3554 
 3555 static gboolean
 3556 cb_key_pressed_quickfind(GtkWidget *widget, GdkEventKey *event, gpointer data) {
 3557     int row_count;
 3558     int select_row;
 3559     int add;
 3560 
 3561     add = 0;
 3562     if ((event->keyval == GDK_KEY_KP_Down) || (event->keyval == GDK_KEY_Down)) {
 3563         add = 1;
 3564     }
 3565     if ((event->keyval == GDK_KEY_KP_Up) || (event->keyval == GDK_KEY_Up)) {
 3566         add = -1;
 3567     }
 3568     if (!add) return FALSE;
 3569     row_count = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStore), NULL);
 3570     if (!row_count) return FALSE;
 3571 
 3572     g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
 3573 
 3574     select_row = rowSelected + add;
 3575     if (select_row > row_count - 1) {
 3576         select_row = 0;
 3577     }
 3578     if (select_row < 0) {
 3579         select_row = row_count - 1;
 3580     }
 3581     rowSelected = select_row;
 3582     gtk_tree_model_foreach(GTK_TREE_MODEL(listStore), selectRecordAddressByRow, NULL);
 3583     return TRUE;
 3584 }
 3585 
 3586 static gboolean cb_key_pressed(GtkWidget *widget, GdkEventKey *event) {
 3587     GtkTextIter cursor_pos_iter;
 3588     GtkTextBuffer *text_buffer;
 3589     int page, next;
 3590     int i, j, found, break_loop;
 3591 
 3592     if ((event->keyval != GDK_KEY_Tab) &&
 3593         (event->keyval != GDK_KEY_ISO_Left_Tab)) {
 3594         return FALSE;
 3595     }
 3596 
 3597     if (event->keyval == GDK_KEY_Tab) {
 3598         /* See if they are at the end of the text */
 3599         text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
 3600         gtk_text_buffer_get_iter_at_mark(text_buffer, &cursor_pos_iter, gtk_text_buffer_get_insert(text_buffer));
 3601         if (!(gtk_text_iter_is_end(&cursor_pos_iter))) {
 3602             return FALSE;
 3603         }
 3604     }
 3605     g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
 3606 
 3607     /* Initialize page and next widget in case search fails */
 3608     page = schema[0].notebook_page;
 3609     next = schema[0].record_field;
 3610 
 3611     found = break_loop = 0;
 3612     for (i = j = 0; i < schema_size && !break_loop; i++) {
 3613         switch (schema[i].type) {
 3614             case ADDRESS_GUI_LABEL_TEXT:
 3615             case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 3616             case ADDRESS_GUI_ADDR_MENU_TEXT:
 3617             case ADDRESS_GUI_IM_MENU_TEXT:
 3618             case ADDRESS_GUI_WEBSITE_TEXT:
 3619                 if (found) {
 3620                     page = schema[i].notebook_page;
 3621                     next = schema[i].record_field;
 3622                     break_loop = 1;
 3623                     break;
 3624                 }
 3625                 if (addr_text[schema[i].record_field] == widget) {
 3626                     found = 1;
 3627                 } else {
 3628                     j = i;
 3629                 }
 3630                 break;
 3631             default:
 3632                 break;
 3633         }
 3634     }
 3635 
 3636     if (event->keyval == GDK_KEY_ISO_Left_Tab) {
 3637         j = (j < 0 ? 0 : j);
 3638         page = schema[j].notebook_page;
 3639         next = schema[j].record_field;
 3640     }
 3641 
 3642     gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page);
 3643     gtk_widget_grab_focus(GTK_WIDGET(addr_text[next]));
 3644 
 3645     return TRUE;
 3646 }
 3647 
 3648 int address_gui_cleanup(void) {
 3649     int b;
 3650 
 3651     b = dialog_save_changed_record(pane, record_changed);
 3652     if (b == DIALOG_SAID_2) {
 3653         cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
 3654     }
 3655 
 3656     g_list_free(changed_list);
 3657     changed_list = NULL;
 3658 
 3659     free_ContactList(&glob_contact_list);
 3660     free_ContactList(&export_contact_list);
 3661     connect_changed_signals(DISCONNECT_SIGNALS);
 3662     set_pref(PREF_ADDRESS_PANE, gtk_paned_get_position(GTK_PANED(pane)), NULL, TRUE);
 3663     set_pref(PREF_LAST_ADDR_CATEGORY, address_category, NULL, TRUE);
 3664     GtkTreeSelection* treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
 3665     gtk_tree_selection_set_select_function(treeSelection, NULL, NULL, NULL);
 3666     gtk_list_store_clear(GTK_LIST_STORE(listStore));
 3667     if (image) {
 3668         gtk_widget_destroy(image);
 3669         image = NULL;
 3670     }
 3671     if (contact_picture.data) {
 3672         free(contact_picture.data);
 3673     }
 3674     contact_picture.dirty = 0;
 3675     contact_picture.length = 0;
 3676     contact_picture.data = NULL;
 3677 
 3678     return EXIT_SUCCESS;
 3679 }
 3680 
 3681 
 3682 static gboolean handleRowSelectionForAddress(GtkTreeSelection *selection,
 3683                                              GtkTreeModel *model,
 3684                                              GtkTreePath *path,
 3685                                              gboolean path_currently_selected,
 3686                                              gpointer userdata) {
 3687     GtkTreeIter iter;
 3688     /* The rename-able phone entries are indexes 3,4,5,6,7 */
 3689     struct Contact *cont;
 3690     MyContact *mcont;
 3691     int b;
 3692     int i, index, sorted_position;
 3693     unsigned int unique_id = 0;
 3694     char *list_text;
 3695     const char *entry_text;
 3696     int address_i, IM_i, phone_i;
 3697     char birthday_str[255];
 3698     long ivalue;
 3699     char reminder_str[10];
 3700     GString *s;
 3701     long char_set;
 3702 
 3703     if ((gtk_tree_model_get_iter(model, &iter, path)) && (!path_currently_selected)) {
 3704 
 3705         int *path_index = gtk_tree_path_get_indices(path);
 3706         rowSelected = path_index[0];
 3707         get_pref(PREF_CHAR_SET, &char_set, NULL);
 3708 
 3709         gtk_tree_model_get(model, &iter, ADDRESS_DATA_COLUMN_ENUM, &mcont, -1);
 3710 
 3711         if ((record_changed == MODIFY_FLAG) || (record_changed == NEW_FLAG)) {
 3712 
 3713             if (mcont != NULL) {
 3714                 unique_id = mcont->unique_id;
 3715             }
 3716 
 3717             // We need to turn this "scroll with mouse held down" thing off
 3718             button_set_for_motion(0);
 3719             b = dialog_save_changed_record_with_cancel(pane, record_changed);
 3720             if (b == DIALOG_SAID_1) { /* Cancel */
 3721                 // https://developer.gnome.org/gtk3/stable/GtkTreeSelection.html#gtk-tree-selection-set-select-function
 3722                 // return false is the node selected should not be changed
 3723                 return FALSE;
 3724             }
 3725             if (b == DIALOG_SAID_2) { /* No */
 3726                 set_new_button_to(CLEAR_FLAG);
 3727                 return TRUE;
 3728             }
 3729             if (b == DIALOG_SAID_3) { /* Save */
 3730                 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
 3731             }
 3732 
 3733             set_new_button_to(CLEAR_FLAG);
 3734 
 3735             if (unique_id) {
 3736                 glob_find_id = unique_id;
 3737                 address_find();
 3738             }
 3739             return TRUE;
 3740         }
 3741 
 3742         if (mcont == NULL) {
 3743             return TRUE;
 3744         }
 3745 
 3746         if (mcont->rt == DELETED_PALM_REC ||
 3747             (mcont->rt == DELETED_PC_REC))
 3748             /* Possible later addition of undelete code for modified deleted records
 3749              || mcont->rt == MODIFIED_PALM_REC
 3750           */
 3751         {
 3752             set_new_button_to(UNDELETE_FLAG);
 3753         } else {
 3754             set_new_button_to(CLEAR_FLAG);
 3755         }
 3756 
 3757         connect_changed_signals(DISCONNECT_SIGNALS);
 3758 
 3759         if (mcont->cont.picture && mcont->cont.picture->data) {
 3760             if (contact_picture.data) {
 3761                 free(contact_picture.data);
 3762             }
 3763             /* Set global variables to keep the picture data */
 3764             contact_picture.data = malloc(mcont->cont.picture->length);
 3765             memcpy(contact_picture.data, mcont->cont.picture->data, mcont->cont.picture->length);
 3766             contact_picture.length = mcont->cont.picture->length;
 3767             contact_picture.dirty = 0;
 3768             if (image) {
 3769                 gtk_widget_destroy(image);
 3770             }
 3771             image = image_from_data(contact_picture.data, contact_picture.length);
 3772             gtk_container_add(GTK_CONTAINER(picture_button), image);
 3773             gtk_widget_show(image);
 3774         } else {
 3775             if (image) {
 3776                 gtk_widget_destroy(image);
 3777                 image = NULL;
 3778             }
 3779             if (contact_picture.data) {
 3780                 free(contact_picture.data);
 3781                 contact_picture.dirty = 0;
 3782                 contact_picture.length = 0;
 3783                 contact_picture.data = NULL;
 3784             }
 3785         }
 3786 
 3787         cont = &(mcont->cont);
 3788         list_text = NULL;
 3789         gtk_tree_model_get(model, &iter, ADDRESS_NAME_COLUMN_ENUM, &list_text, -1);
 3790         entry_text = gtk_entry_get_text(GTK_ENTRY(address_quickfind_entry));
 3791         if (strncasecmp(list_text, entry_text, strlen(entry_text))) {
 3792             gtk_entry_set_text(GTK_ENTRY(address_quickfind_entry), "");
 3793         }
 3794 
 3795         /* category menu */
 3796         index = mcont->attrib & 0x0F;
 3797         sorted_position = find_sort_cat_pos(index);
 3798         int pos = findSortedPostion(sorted_position, GTK_COMBO_BOX(category_menu2));
 3799         if (pos != sorted_position && index != 0) {
 3800             /* Illegal category, Assume that category 0 is Unfiled and valid */
 3801             jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
 3802             index = 0;
 3803             sorted_position = find_sort_cat_pos(index);
 3804         }
 3805 
 3806         if (sorted_position < 0) {
 3807             jp_logf(JP_LOG_WARN, _("Category is not legal\n"));
 3808         } else {
 3809             gtk_combo_box_set_active(GTK_COMBO_BOX(category_menu2), find_menu_cat_pos(sorted_position));
 3810         }
 3811         /* End category menu */
 3812 
 3813         /* Freeze the "All" text buffer to prevent flicker while updating */
 3814         gtk_widget_freeze_child_notify(addr_all);
 3815 
 3816         gtk_text_buffer_set_text(GTK_TEXT_BUFFER(addr_all_buffer), "", -1);
 3817 
 3818         /* Fill out the "All" text buffer */
 3819         s = contact_to_gstring(cont);
 3820         if (s->len) {
 3821             gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(addr_all_buffer), _("Category: "), -1);
 3822             char *utf;
 3823             utf = charset_p2newj(contact_app_info.category.name[mcont->attrib & 0x0F], 16, char_set);
 3824             gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(addr_all_buffer), utf, -1);
 3825             g_free(utf);
 3826             gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(addr_all_buffer), "\n", -1);
 3827 
 3828             gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(addr_all_buffer), s->str, -1);
 3829         }
 3830         g_string_free(s, TRUE);
 3831 
 3832         address_i = phone_i = IM_i = 0;
 3833         for (i = 0; i < schema_size; i++) {
 3834             switch (schema[i].type) {
 3835                 case ADDRESS_GUI_LABEL_TEXT:
 3836                     goto set_text;
 3837                 case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 3838                     /* Set dial/email button text and callback data */
 3839                     if (!strcasecmp(contact_app_info.phoneLabels[cont->phoneLabel[phone_i]], _("E-mail"))) {
 3840                          g_object_set_data(G_OBJECT(dial_button[phone_i]), "mail", GINT_TO_POINTER(1));
 3841                         gtk_button_set_label(GTK_BUTTON(dial_button[phone_i]), _("Mail"));
 3842                     } else {
 3843                          g_object_set_data(G_OBJECT(dial_button[phone_i]), "mail", 0);
 3844                         gtk_button_set_label(GTK_BUTTON(dial_button[phone_i]), _("Dial"));
 3845                     }
 3846                     if ((phone_i < NUM_PHONE_ENTRIES) && (cont->phoneLabel[phone_i] < NUM_PHONE_LABELS)) {
 3847                         if (GTK_IS_WIDGET(phone_type_list_menu[phone_i])) {
 3848                             gtk_combo_box_set_active(GTK_COMBO_BOX(phone_type_list_menu[phone_i]),
 3849                                                      cont->phoneLabel[phone_i]);
 3850                         }
 3851 
 3852                     }
 3853                     phone_i++;
 3854                     goto set_text;
 3855                 case ADDRESS_GUI_IM_MENU_TEXT:
 3856                     if (GTK_IS_WIDGET(IM_type_list_menu[IM_i])) {
 3857                         gtk_combo_box_set_active(GTK_COMBO_BOX(IM_type_list_menu[IM_i]), cont->IMLabel[IM_i]);
 3858                     }
 3859                     IM_i++;
 3860                     goto set_text;
 3861                 case ADDRESS_GUI_ADDR_MENU_TEXT:
 3862                     if (GTK_IS_WIDGET(address_type_list_menu[address_i])) {
 3863                         gtk_combo_box_set_active(GTK_COMBO_BOX(address_type_list_menu[address_i]),
 3864                                                  cont->addressLabel[address_i]);
 3865                         /* We want to make the notebook page tab label match the type of
 3866                  * address from the menu.  So, we'll find the nth address menu
 3867                  * and set whatever page the schema says it resides on */
 3868                         if (GTK_IS_LABEL(notebook_label[schema[i].notebook_page])) {
 3869                             gtk_label_set_text(GTK_LABEL(notebook_label[schema[i].notebook_page]),
 3870                                                contact_app_info.addrLabels[cont->addressLabel[address_i]]);
 3871                         }
 3872                     }
 3873                     address_i++;
 3874                     goto set_text;
 3875                 case ADDRESS_GUI_WEBSITE_TEXT:
 3876                 set_text:
 3877                     if (cont->entry[schema[i].record_field]) {
 3878                         gtk_text_buffer_set_text(GTK_TEXT_BUFFER(addr_text_buffer[schema[i].record_field]),
 3879                                                  cont->entry[schema[i].record_field], -1);
 3880                     } else {
 3881                         gtk_text_buffer_set_text(GTK_TEXT_BUFFER(addr_text_buffer[schema[i].record_field]), "", -1);
 3882                     }
 3883                     break;
 3884                 case ADDRESS_GUI_BIRTHDAY:
 3885                     get_pref(PREF_TODO_DAYS_TILL_DUE, &ivalue, NULL);
 3886                     reminder_str[0] = '\0';
 3887                     g_snprintf(reminder_str, sizeof(reminder_str), "%ld", ivalue);
 3888 
 3889                     if (cont->birthdayFlag) {
 3890                         memcpy(&birthday, &cont->birthday, sizeof(struct tm));
 3891                         set_button_label_to_date(birthday_button, &birthday);
 3892 
 3893                         /* Birthday checkbox */
 3894                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(birthday_checkbox),
 3895                                                      TRUE);
 3896 
 3897                         if (cont->reminder) {
 3898                             sprintf(birthday_str, "%d", cont->advance);
 3899                             gtk_entry_set_text(GTK_ENTRY(reminder_entry), birthday_str);
 3900 
 3901                             /* Reminder checkbox */
 3902                             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reminder_checkbox),
 3903                                                          cont->reminder);
 3904                         } else {
 3905                             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reminder_checkbox), FALSE);
 3906                             gtk_entry_set_text(GTK_ENTRY(reminder_entry), reminder_str);
 3907                         }
 3908                     } else {
 3909                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(birthday_checkbox),
 3910                                                      FALSE);
 3911                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reminder_checkbox),
 3912                                                      FALSE);
 3913                         gtk_entry_set_text(GTK_ENTRY(reminder_entry), reminder_str);
 3914                     }
 3915                     break;
 3916                 default:
 3917                     break;
 3918             }
 3919         }
 3920 
 3921         /* Set phone grouped radio buttons */
 3922         if ((cont->showPhone > -1) && (cont->showPhone < NUM_PHONE_ENTRIES)) {
 3923             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button[cont->showPhone]), TRUE);
 3924         }
 3925 
 3926         /* Private checkbox */
 3927         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private_checkbox),
 3928                                      mcont->attrib & dlpRecAttrSecret);
 3929 
 3930         gtk_widget_thaw_child_notify(addr_all);
 3931         connect_changed_signals(CONNECT_SIGNALS);
 3932     }
 3933     return TRUE;
 3934 }
 3935 
 3936 
 3937 /* Main function */
 3938 int address_gui(GtkWidget *vbox, GtkWidget *hbox) {
 3939     GtkWidget *scrolled_window;
 3940     GtkWidget *pixmapwid;
 3941     GdkPixbuf *pixmap;
 3942     GtkWidget *vbox1, *vbox2;
 3943     GtkWidget *hbox_temp;
 3944     GtkWidget *vbox_temp;
 3945     GtkWidget *separator;
 3946     GtkWidget *label;
 3947     GtkWidget *button;
 3948     GtkWidget *grid;
 3949     GtkWidget *notebook_tab;
 3950     GSList *group;
 3951     long ivalue, notebook_page;
 3952     long show_tooltips;
 3953     GtkAccelGroup *accel_group;
 3954     int address_type_i, IM_type_i, page_i, grid_y_i;
 3955     int x, y;
 3956 
 3957     int i, j, phone_i, num_pages;
 3958     long char_set;
 3959     char *cat_name;
 3960 
 3961     /* Note that the contact pages labeled "Address" will change
 3962     * dynamically if the address type pulldown is selected */
 3963     char *contact_page_names[] = {
 3964             N_("Name"),
 3965             N_("Address"),
 3966             N_("Address"),
 3967             N_("Address"),
 3968             N_("Other"),
 3969             N_("Note")
 3970     };
 3971     char *address_page_names[] = {
 3972             N_("Name"),
 3973             N_("Address"),
 3974             N_("Other"),
 3975             N_("Note")
 3976     };
 3977     char **page_names;
 3978 
 3979     get_pref(PREF_ADDRESS_VERSION, &address_version, NULL);
 3980     if (address_version) {
 3981         unsigned char *buf;
 3982         int rec_size;
 3983 
 3984         if ((EXIT_SUCCESS != jp_get_app_info("ContactsDB-PAdd", &buf, &rec_size)) || (0 == rec_size)) {
 3985             jp_logf(JP_LOG_WARN, _("Reverting to Address database\n"));
 3986             address_version = 0;
 3987         } else {
 3988             if (buf) free(buf);
 3989         }
 3990     }
 3991 
 3992     init();
 3993 
 3994     if (address_version) {
 3995         page_names = contact_page_names;
 3996         num_pages = NUM_CONTACT_NOTEBOOK_PAGES;
 3997         get_contact_app_info(&contact_app_info);
 3998     } else {
 3999         page_names = address_page_names;
 4000         num_pages = NUM_ADDRESS_NOTEBOOK_PAGES;
 4001         get_address_app_info(&address_app_info);
 4002         copy_address_ai_to_contact_ai(&address_app_info, &contact_app_info);
 4003     }
 4004     listStore = gtk_list_store_new(ADDRESS_NUM_COLS, G_TYPE_STRING, GDK_TYPE_PIXBUF,
 4005                                    G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_RGBA,
 4006                                    G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN);
 4007     /* Initialize categories */
 4008     get_pref(PREF_CHAR_SET, &char_set, NULL);
 4009     for (i = 1; i < NUM_ADDRESS_CAT_ITEMS; i++) {
 4010         cat_name = charset_p2newj(contact_app_info.category.name[i], 31, char_set);
 4011         strcpy(sort_l[i - 1].Pcat, cat_name);
 4012         free(cat_name);
 4013         sort_l[i - 1].cat_num = i;
 4014     }
 4015     /* put reserved 'Unfiled' category at end of list */
 4016     cat_name = charset_p2newj(contact_app_info.category.name[0], 31, char_set);
 4017     strcpy(sort_l[NUM_ADDRESS_CAT_ITEMS - 1].Pcat, cat_name);
 4018     free(cat_name);
 4019     sort_l[NUM_ADDRESS_CAT_ITEMS - 1].cat_num = 0;
 4020 
 4021     qsort(sort_l, NUM_ADDRESS_CAT_ITEMS - 1, sizeof(struct sorted_cats), cat_compare);
 4022 #ifdef JPILOT_DEBUG
 4023                                                                                                                             for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
 4024       printf("cat %d [%s]\n", sort_l[i].cat_num, sort_l[i].Pcat);
 4025    }
 4026 #endif
 4027 
 4028     get_pref(PREF_LAST_ADDR_CATEGORY, &ivalue, NULL);
 4029     address_category = ivalue;
 4030 
 4031     if ((address_category != CATEGORY_ALL)
 4032         && (contact_app_info.category.name[address_category][0] == '\0')) {
 4033         address_category = CATEGORY_ALL;
 4034     }
 4035 
 4036     /* Create basic GUI with left and right boxes and sliding pane */
 4037     accel_group = gtk_accel_group_new();
 4038     gtk_window_add_accel_group(GTK_WINDOW(gtk_widget_get_toplevel(vbox)),
 4039                                accel_group);
 4040     get_pref(PREF_SHOW_TOOLTIPS, &show_tooltips, NULL);
 4041 
 4042     pane = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
 4043     get_pref(PREF_ADDRESS_PANE, &ivalue, NULL);
 4044     gtk_paned_set_position(GTK_PANED(pane), ivalue);
 4045 
 4046     gtk_box_pack_start(GTK_BOX(hbox), pane, TRUE, TRUE, 5);
 4047 
 4048     vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 4049     vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 4050     gtk_paned_pack1(GTK_PANED(pane), vbox1, TRUE, FALSE);
 4051     gtk_paned_pack2(GTK_PANED(pane), vbox2, TRUE, FALSE);
 4052 
 4053     /* Left side of GUI */
 4054 
 4055     /* Separator */
 4056     separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
 4057     gtk_box_pack_start(GTK_BOX(vbox1), separator, FALSE, FALSE, 5);
 4058 
 4059     /* Make the 'Today is:' label */
 4060     glob_date_label = gtk_label_new(" ");
 4061     gtk_box_pack_start(GTK_BOX(vbox1), glob_date_label, FALSE, FALSE, 0);
 4062     timeout_date(NULL);
 4063     glob_date_timer_tag = g_timeout_add(CLOCK_TICK, timeout_sync_up, NULL);
 4064 
 4065     /* Separator */
 4066     separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
 4067     gtk_box_pack_start(GTK_BOX(vbox1), separator, FALSE, FALSE, 5);
 4068 
 4069     /* Left-side Category box */
 4070     hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 4071     gtk_box_pack_start(GTK_BOX(vbox1), hbox_temp, FALSE, FALSE, 0);
 4072 
 4073     /* Left-side Category menu */
 4074     make_category_menu(&category_menu1,
 4075                        sort_l, cb_category, TRUE, TRUE);
 4076     gtk_box_pack_start(GTK_BOX(hbox_temp), category_menu1, TRUE, TRUE, 0);
 4077 
 4078     /* Address list scrolled window */
 4079     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
 4080     gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 0);
 4081     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
 4082                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 4083     gtk_box_pack_start(GTK_BOX(vbox1), scrolled_window, TRUE, TRUE, 0);
 4084     get_pref(PREF_ADDR_SORT_ORDER, &ivalue, NULL);
 4085     addr_sort_order = (int) ivalue;
 4086     GtkTreeModel *model = GTK_TREE_MODEL(listStore);
 4087     treeView = gtk_tree_view_new_with_model(model);
 4088     get_pref(PREF_ADDR_NAME_COL_SZ, &ivalue, NULL);
 4089     GtkCellRenderer *nameRenderer = gtk_cell_renderer_text_new();
 4090     gtk_cell_renderer_set_fixed_size(nameRenderer, ivalue, 18);
 4091     GtkTreeViewColumn *nameColumn = gtk_tree_view_column_new_with_attributes(ADDRESS_LAST_NAME_COMPANY, nameRenderer,
 4092                                                                              "text",
 4093                                                                              ADDRESS_NAME_COLUMN_ENUM,
 4094                                                                              "cell-background-rgba",
 4095                                                                              ADDRESS_BACKGROUND_COLOR_ENUM,
 4096                                                                              "cell-background-set",
 4097                                                                              ADDRESS_BACKGROUND_COLOR_ENABLED_ENUM,
 4098                                                                              NULL);
 4099     gtk_tree_view_column_set_clickable(nameColumn, TRUE);
 4100 
 4101     GtkCellRenderer *noteRenderer = gtk_cell_renderer_pixbuf_new();
 4102     gtk_cell_renderer_set_alignment(noteRenderer,0,0);
 4103     gtk_cell_renderer_set_padding(noteRenderer,4,0);
 4104     GtkTreeViewColumn *noteColumn = gtk_tree_view_column_new_with_attributes("", noteRenderer, "pixbuf",
 4105                                                                              ADDRESS_NOTE_COLUMN_ENUM,
 4106                                                                              "cell-background-rgba",
 4107                                                                              ADDRESS_BACKGROUND_COLOR_ENUM,
 4108                                                                              "cell-background-set",
 4109                                                                              ADDRESS_BACKGROUND_COLOR_ENABLED_ENUM,
 4110                                                                              NULL);
 4111     gtk_tree_view_column_set_clickable(noteColumn, FALSE);
 4112     GtkCellRenderer *phoneRenderer = gtk_cell_renderer_text_new();
 4113     // Set the phone column width to something small and let it expand to the pane
 4114     // Set the height to 1 so we do not see line wraps making verticle gaps in the view
 4115     gtk_cell_renderer_set_fixed_size(phoneRenderer, 100, 18);
 4116     GtkTreeViewColumn *phoneColumn = gtk_tree_view_column_new_with_attributes("Phone", phoneRenderer, "text",
 4117                                                                               ADDRESS_PHONE_COLUMN_ENUM,
 4118                                                                               "cell-background-rgba",
 4119                                                                               ADDRESS_BACKGROUND_COLOR_ENUM,
 4120                                                                               "cell-background-set",
 4121                                                                               ADDRESS_BACKGROUND_COLOR_ENABLED_ENUM,
 4122                                                                               NULL);
 4123     gtk_tree_view_column_set_clickable(phoneColumn, FALSE);
 4124 
 4125     gtk_tree_view_insert_column(GTK_TREE_VIEW(treeView), nameColumn, ADDRESS_NAME_COLUMN_ENUM);
 4126     gtk_tree_view_insert_column(GTK_TREE_VIEW(treeView), noteColumn, ADDRESS_NOTE_COLUMN_ENUM);
 4127     gtk_tree_view_insert_column(GTK_TREE_VIEW(treeView), phoneColumn, ADDRESS_PHONE_COLUMN_ENUM);
 4128     treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
 4129 
 4130     gtk_tree_selection_set_select_function(treeSelection, handleRowSelectionForAddress, NULL, NULL);
 4131     gtk_widget_set_events(treeView, GDK_BUTTON1_MOTION_MASK);
 4132     g_signal_connect (G_OBJECT(treeView), "motion_notify_event",
 4133                       G_CALLBACK(motion_notify_event), NULL);
 4134     g_signal_connect (G_OBJECT(treeView), "button-press-event",
 4135                       G_CALLBACK(button_pressed_for_motion), NULL);
 4136     g_signal_connect (G_OBJECT(treeView), "button-release-event",
 4137                       G_CALLBACK(button_released_for_motion), NULL);
 4138 
 4139     switch (addr_sort_order) {
 4140         case SORT_BY_LNAME:
 4141         default:
 4142             addr_sort_order = SORT_BY_LNAME;  /* Initialize variable if default case taken */
 4143             gtk_tree_view_column_set_title(nameColumn, ADDRESS_LAST_NAME_COMPANY);
 4144             break;
 4145         case SORT_BY_FNAME:
 4146             gtk_tree_view_column_set_title(nameColumn, ADDRESS_FIRST_NAME_COMPANY);
 4147             break;
 4148         case SORT_BY_COMPANY:
 4149             gtk_tree_view_column_set_title(nameColumn, ADDRESS_COMPANY_LAST_NAME);
 4150             break;
 4151     }
 4152     g_signal_connect (nameColumn, "clicked", G_CALLBACK(cb_resortNameColumn), NULL);
 4153 
 4154 
 4155 
 4156     /* Put pretty pictures in the list column headings */
 4157     get_pixbufs(PIXMAP_NOTE, &pixmap);
 4158     pixmapwid = gtk_image_new_from_pixbuf(pixmap);
 4159     gtk_widget_show(GTK_WIDGET(pixmapwid));
 4160     gtk_tree_view_column_set_widget(noteColumn, pixmapwid);
 4161 
 4162     gtk_tree_view_column_set_alignment(noteColumn, GTK_JUSTIFY_LEFT);
 4163 
 4164     gtk_tree_view_column_set_min_width(nameColumn, 60);
 4165 
 4166 
 4167     gtk_tree_view_column_set_resizable(nameColumn, TRUE);
 4168     gtk_tree_view_column_set_resizable(noteColumn, FALSE);
 4169     gtk_tree_view_column_set_resizable(phoneColumn, TRUE);
 4170 
 4171 
 4172 
 4173     g_signal_connect(G_OBJECT(nameColumn), "notify::width",
 4174                      G_CALLBACK(cb_resize_name_column), NULL);
 4175 
 4176     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolled_window),
 4177                                     GTK_POLICY_NEVER,
 4178                                     GTK_POLICY_AUTOMATIC);
 4179     gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(treeView));
 4180     hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 4181     gtk_box_pack_start(GTK_BOX(vbox1), hbox_temp, FALSE, FALSE, 0);
 4182 
 4183     label = gtk_label_new(_("Quick Find: "));
 4184     gtk_box_pack_start(GTK_BOX(hbox_temp), label, FALSE, FALSE, 0);
 4185 
 4186     address_quickfind_entry = gtk_entry_new();
 4187     entry_set_multiline_truncate(GTK_ENTRY(address_quickfind_entry), TRUE);
 4188     g_signal_connect(G_OBJECT(address_quickfind_entry), "key_press_event",
 4189                        G_CALLBACK(cb_key_pressed_quickfind), NULL);
 4190     g_signal_connect(G_OBJECT(address_quickfind_entry), "changed",
 4191                      G_CALLBACK(cb_address_quickfind),
 4192                        NULL);
 4193     gtk_box_pack_start(GTK_BOX(hbox_temp), address_quickfind_entry, TRUE, TRUE, 0);
 4194 
 4195     /* Right side of GUI */
 4196 
 4197     hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
 4198     gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
 4199 
 4200     /* Cancel button */
 4201     CREATE_BUTTON(cancel_record_button, _("Cancel"), CANCEL, _("Cancel the modifications"), GDK_KEY_Escape, 0, "ESC")
 4202     g_signal_connect(G_OBJECT(cancel_record_button), "clicked",
 4203                      G_CALLBACK(cb_cancel), NULL);
 4204 
 4205     /* Delete Button */
 4206     CREATE_BUTTON(delete_record_button, _("Delete"), DELETE, _("Delete the selected record"), GDK_d, GDK_CONTROL_MASK,
 4207                   "Ctrl+D")
 4208     g_signal_connect(G_OBJECT(delete_record_button), "clicked",
 4209                        G_CALLBACK(cb_delete_address_or_contact),
 4210                        GINT_TO_POINTER(DELETE_FLAG));
 4211 
 4212     /* Undelete Button */
 4213     CREATE_BUTTON(undelete_record_button, _("Undelete"), UNDELETE, _("Undelete the selected record"), 0, 0, "")
 4214     g_signal_connect(G_OBJECT(undelete_record_button), "clicked",
 4215                        G_CALLBACK(cb_undelete_address),
 4216                        GINT_TO_POINTER(UNDELETE_FLAG));
 4217 
 4218     /* Copy button */
 4219     CREATE_BUTTON(copy_record_button, _("Copy"), COPY, _("Copy the selected record"), GDK_c,
 4220                   GDK_CONTROL_MASK | GDK_SHIFT_MASK, "Ctrl+Shift+C")
 4221     g_signal_connect(G_OBJECT(copy_record_button), "clicked",
 4222                        G_CALLBACK(cb_add_new_record),
 4223                        GINT_TO_POINTER(COPY_FLAG));
 4224 
 4225     /* New button */
 4226     CREATE_BUTTON(new_record_button, _("New Record"), NEW, _("Add a new record"), GDK_n, GDK_CONTROL_MASK, "Ctrl+N")
 4227     g_signal_connect(G_OBJECT(new_record_button), "clicked",
 4228                        G_CALLBACK(cb_address_clear), NULL);
 4229 
 4230     /* "Add Record" button */
 4231     CREATE_BUTTON(add_record_button, _("Add Record"), ADD, _("Add the new record"), GDK_KEY_Return, GDK_CONTROL_MASK,
 4232                   "Ctrl+Enter")
 4233     g_signal_connect(G_OBJECT(add_record_button), "clicked",
 4234                        G_CALLBACK(cb_add_new_record),
 4235                        GINT_TO_POINTER(NEW_FLAG));
 4236 
 4237 
 4238 #ifndef ENABLE_STOCK_BUTTONS
 4239     gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(gtk_bin_get_child(GTK_BIN(add_record_button)))),
 4240                     "label_high");
 4241 #endif
 4242 
 4243     /* "Apply Changes" button */
 4244     CREATE_BUTTON(apply_record_button, _("Apply Changes"), APPLY, _("Commit the modifications"), GDK_KEY_Return,
 4245                   GDK_CONTROL_MASK, "Ctrl+Enter")
 4246     g_signal_connect(G_OBJECT(apply_record_button), "clicked",
 4247                        G_CALLBACK(cb_add_new_record),
 4248                        GINT_TO_POINTER(MODIFY_FLAG));
 4249 #ifndef ENABLE_STOCK_BUTTONS
 4250     gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(gtk_bin_get_child(GTK_BIN(apply_record_button)))),
 4251          "label_high");
 4252 #endif
 4253 
 4254     /* Separator */
 4255     separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
 4256     gtk_box_pack_start(GTK_BOX(vbox2), separator, FALSE, FALSE, 5);
 4257 
 4258     hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 4259     gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
 4260 
 4261     /* Right-side Category menu */
 4262     make_category_menu(&category_menu2,
 4263                        sort_l, NULL, FALSE, FALSE);
 4264 
 4265     gtk_box_pack_start(GTK_BOX(hbox_temp), category_menu2, TRUE, TRUE, 0);
 4266 
 4267     //for (i = 0; i < NUM_ADDRESS_CAT_ITEMS; i++) {
 4268     //  changed_list = g_list_prepend(changed_list, address_cat_menu_item2[i]);
 4269     // }
 4270     changed_list = g_list_prepend(changed_list, GTK_COMBO_BOX(category_menu2));
 4271     /* Private check box */
 4272     private_checkbox = gtk_check_button_new_with_label(_("Private"));
 4273     gtk_box_pack_end(GTK_BOX(hbox_temp), private_checkbox, FALSE, FALSE, 0);
 4274 
 4275     changed_list = g_list_prepend(changed_list, private_checkbox);
 4276 
 4277     /* Notebook for new entries */
 4278     notebook = gtk_notebook_new();
 4279     gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
 4280     gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
 4281     g_signal_connect(G_OBJECT(notebook), "switch-page",
 4282                        G_CALLBACK(cb_notebook_changed), NULL);
 4283 
 4284     gtk_box_pack_start(GTK_BOX(vbox2), notebook, TRUE, TRUE, 0);
 4285 
 4286     /* Clear GTK option menus before use */
 4287     for (i = 0; i < NUM_ADDRESSES; i++) {
 4288         for (j = 0; j < NUM_PHONE_LABELS; j++) {
 4289             if (GTK_IS_COMBO_BOX(phone_type_list_menu[i]) &&
 4290                 gtk_combo_box_get_has_entry(GTK_COMBO_BOX(phone_type_list_menu[i]))) {
 4291                 gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(phone_type_list_menu[i]), j);
 4292             }
 4293         }
 4294     }
 4295 
 4296     /* Add notebook pages and their widgets */
 4297     phone_i = address_type_i = IM_type_i = 0;
 4298     for (page_i = 0; page_i < num_pages; page_i++) {
 4299         x = y = 0;
 4300         for (i = 0; i < schema_size; i++) {
 4301             /* Figure out the table X and Y size */
 4302             if (schema[i].notebook_page != page_i) continue;
 4303             switch (schema[i].type) {
 4304                 case ADDRESS_GUI_LABEL_TEXT:
 4305                     if (x < 2) x = 2;
 4306                     y++;
 4307                     break;
 4308                 case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 4309                     if (x < 4) x = 4;
 4310                     y++;
 4311                     break;
 4312                 case ADDRESS_GUI_ADDR_MENU_TEXT:
 4313                     if (x < 2) x = 2;
 4314                     y++;
 4315                     break;
 4316                 case ADDRESS_GUI_IM_MENU_TEXT:
 4317                     if (x < 2) x = 2;
 4318                     y++;
 4319                     break;
 4320                 case ADDRESS_GUI_WEBSITE_TEXT:
 4321                     if (x < 2) x = 2;
 4322                     y++;
 4323                     break;
 4324                 case ADDRESS_GUI_BIRTHDAY:
 4325                     if (x < 2) x = 2;
 4326                     y++;
 4327                     break;
 4328                 default:
 4329                     break;
 4330             }
 4331         }
 4332 
 4333         if ((x == 0) || (y == 0)) {
 4334             continue;
 4335         }
 4336 
 4337         /* Add a notebook page */
 4338         grid_y_i = 0;
 4339         notebook_label[page_i] = gtk_label_new(_(page_names[page_i]));
 4340         grid = gtk_grid_new();
 4341         gtk_grid_set_row_spacing(GTK_GRID(grid), 2);
 4342         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), grid, notebook_label[page_i]);
 4343 
 4344         gtk_widget_show(label);
 4345 
 4346         /* Grid on the right side */
 4347 
 4348         if ((page_i == 0) && (grid_y_i == 0) && (address_version == 1)) {
 4349             GtkWidget *menu, *menu_item;
 4350 
 4351             picture_button = gtk_button_new();
 4352             gtk_widget_set_size_request(GTK_WIDGET(picture_button), PHOTO_X_SZ + 10, PHOTO_Y_SZ + 10);
 4353             gtk_container_set_border_width(GTK_CONTAINER(picture_button), 0);
 4354             gtk_widget_set_vexpand(GTK_WIDGET(picture_button), FALSE);
 4355             gtk_widget_set_valign(GTK_WIDGET(picture_button), GTK_ALIGN_START);
 4356             gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(picture_button),
 4357                             0, 0, 2, 4);
 4358 
 4359             /* Create a photo menu */
 4360             menu = gtk_menu_new();
 4361 
 4362             menu_item = gtk_menu_item_new_with_label(_("Change Photo"));
 4363             gtk_widget_show(menu_item);
 4364             g_signal_connect(G_OBJECT(menu_item), "activate",
 4365                                G_CALLBACK(cb_photo_menu_select), GINT_TO_POINTER(1));
 4366             gtk_menu_attach(GTK_MENU(menu), menu_item, 0, 1, 0, 1);
 4367             menu_item = gtk_menu_item_new_with_label(_("Remove Photo"));
 4368             gtk_widget_show(menu_item);
 4369             g_signal_connect(G_OBJECT(menu_item), "activate",
 4370                                G_CALLBACK(cb_photo_menu_select), GINT_TO_POINTER(2));
 4371             gtk_menu_attach(GTK_MENU(menu), menu_item, 0, 1, 1, 2);
 4372 
 4373             g_signal_connect_swapped(picture_button, "button_press_event",
 4374                                      G_CALLBACK(cb_photo_menu_popup), menu);
 4375 
 4376         }
 4377 
 4378         /* Add widgets for each notebook page */
 4379 
 4380         group = NULL;
 4381         for (i = 0; i < schema_size; i++) {
 4382             char *utf;
 4383 
 4384             if (schema[i].notebook_page != page_i) continue;
 4385             switch (schema[i].type) {
 4386                 case ADDRESS_GUI_LABEL_TEXT:
 4387                     /* special case for Note which has a scrollbar and no label */
 4388                     if (schema[i].record_field != contNote) {
 4389                         if (address_version) {
 4390                             label = gtk_label_new(contact_app_info.labels[schema[i].record_field]);
 4391                         } else {
 4392                             utf = charset_p2newj(contact_app_info.labels[schema[i].record_field], 16, char_set);
 4393                             label = gtk_label_new(utf);
 4394                             g_free(utf);
 4395                         }
 4396                         gtk_widget_set_margin_end(GTK_WIDGET(label), 5);
 4397                         gtk_label_set_xalign(GTK_LABEL(label), 1);
 4398                         gtk_label_set_yalign(GTK_LABEL(label), 0);
 4399                         // These are the labels "Last Name", "First Name", "Company", "Title" just to the right of the picture
 4400                         gtk_widget_set_vexpand(GTK_WIDGET(label), FALSE);
 4401                         gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_START);
 4402                         gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(label),
 4403                                         x - 2, grid_y_i, 1, 1);
 4404                     }
 4405                     /* Text */
 4406                     addr_text[schema[i].record_field] = gtk_text_view_new();
 4407                     addr_text_buffer[schema[i].record_field] = G_OBJECT(
 4408                             gtk_text_view_get_buffer(GTK_TEXT_VIEW(addr_text[schema[i].record_field])));
 4409                     gtk_text_view_set_editable(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), TRUE);
 4410                     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), GTK_WRAP_CHAR);
 4411                     gtk_widget_set_hexpand(addr_text[schema[i].record_field], TRUE);
 4412                     gtk_widget_set_halign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4413                     gtk_widget_set_vexpand(addr_text[schema[i].record_field], TRUE);
 4414                     gtk_widget_set_valign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4415                     gtk_container_set_border_width(GTK_CONTAINER(addr_text[schema[i].record_field]), 1);
 4416 
 4417                     /* special case for Note which has a scrollbar and no label */
 4418                     if (schema[i].record_field == contNote) {
 4419                         scrolled_window = gtk_scrolled_window_new(NULL, NULL);
 4420                         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
 4421                                                        GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
 4422                         gtk_container_add(GTK_CONTAINER(scrolled_window), addr_text[schema[i].record_field]);
 4423                     }
 4424 
 4425 
 4426                     /* special case for Note which has a scrollbar and no label */
 4427                     if (schema[i].record_field == contNote) {
 4428                         gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(scrolled_window),
 4429                                         x - 1, grid_y_i, 10, 1);
 4430 
 4431                     } else {
 4432                         gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(addr_text[schema[i].record_field]),
 4433                                         x - 1, grid_y_i, 10, 1);
 4434                     }
 4435 
 4436                     changed_list = g_list_prepend(changed_list, addr_text_buffer[schema[i].record_field]);
 4437                     break;
 4438                 case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 4439                     if (!strcasecmp(contact_app_info.phoneLabels[phone_i], _("E-mail"))) {
 4440                         dial_button[phone_i] = gtk_button_new_with_label(_("Mail"));
 4441                          g_object_set_data(G_OBJECT(dial_button[phone_i]), "mail", GINT_TO_POINTER(1));
 4442                     } else {
 4443                         dial_button[phone_i] = gtk_button_new_with_label(_("Dial"));
 4444                          g_object_set_data(G_OBJECT(dial_button[phone_i]), "mail", 0);
 4445                     }
 4446                     gtk_widget_set_vexpand(GTK_WIDGET(dial_button[phone_i]), FALSE);
 4447                     gtk_widget_set_valign(GTK_WIDGET(dial_button[phone_i]), GTK_ALIGN_START);
 4448                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(dial_button[phone_i]),
 4449                                     x - 4, grid_y_i, 1, 1);
 4450 
 4451                     radio_button[phone_i] = gtk_radio_button_new_with_label(group, _("Show In List"));
 4452                     group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_button[phone_i]));
 4453                     gtk_widget_set_vexpand(GTK_WIDGET(radio_button[phone_i]), FALSE);
 4454                     gtk_widget_set_valign(GTK_WIDGET(radio_button[phone_i]), GTK_ALIGN_START);
 4455                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(radio_button[phone_i]),
 4456                                     x - 3, grid_y_i, 1, 1);
 4457 
 4458                     changed_list = g_list_prepend(changed_list, radio_button[phone_i]);
 4459 
 4460                     make_phone_menu(phone_i, phone_i, phone_i);
 4461                     gtk_widget_set_vexpand(GTK_WIDGET(phone_type_list_menu[phone_i]), FALSE);
 4462                     gtk_widget_set_valign(GTK_WIDGET(phone_type_list_menu[phone_i]), GTK_ALIGN_START);
 4463                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(phone_type_list_menu[phone_i]),
 4464                                     x - 2, grid_y_i, 1, 1);
 4465 
 4466                     /* Text */
 4467                     addr_text[schema[i].record_field] = gtk_text_view_new();
 4468                     addr_text_buffer[schema[i].record_field] = G_OBJECT(
 4469                     gtk_text_view_get_buffer(GTK_TEXT_VIEW(addr_text[schema[i].record_field])));
 4470                     gtk_text_view_set_editable(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), TRUE);
 4471                     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), GTK_WRAP_CHAR);
 4472                     gtk_container_set_border_width(GTK_CONTAINER(addr_text[schema[i].record_field]), 1);
 4473                     gtk_widget_set_hexpand(addr_text[schema[i].record_field], TRUE);
 4474                     gtk_widget_set_halign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4475                     gtk_widget_set_vexpand(addr_text[schema[i].record_field], TRUE);
 4476                     gtk_widget_set_valign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4477                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(addr_text[schema[i].record_field]),
 4478                                     x - 1, grid_y_i, 1, 1);
 4479 
 4480                     g_signal_connect(G_OBJECT(dial_button[phone_i]), "clicked",
 4481                                        G_CALLBACK(cb_dial_or_mail),
 4482                                        addr_text[schema[i].record_field]);
 4483                     changed_list = g_list_prepend(changed_list, addr_text_buffer[schema[i].record_field]);
 4484                     phone_i++;
 4485                     break;
 4486                 case ADDRESS_GUI_ADDR_MENU_TEXT:
 4487                     make_address_type_menu(address_type_i, address_type_i);
 4488                     gtk_widget_set_vexpand(GTK_WIDGET(address_type_list_menu[address_type_i]), FALSE);
 4489                     gtk_widget_set_valign(GTK_WIDGET(address_type_list_menu[address_type_i]), GTK_ALIGN_START);
 4490                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(address_type_list_menu[address_type_i]),
 4491                                     x - 2, grid_y_i, 1, 1);
 4492                     address_type_i++;
 4493 
 4494                     /* Text */
 4495                     addr_text[schema[i].record_field] = gtk_text_view_new();
 4496                     addr_text_buffer[schema[i].record_field] = G_OBJECT(
 4497                             gtk_text_view_get_buffer(GTK_TEXT_VIEW(addr_text[schema[i].record_field])));
 4498                     gtk_text_view_set_editable(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), TRUE);
 4499                     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), GTK_WRAP_CHAR);
 4500                     gtk_container_set_border_width(GTK_CONTAINER(addr_text[schema[i].record_field]), 1);
 4501                     gtk_widget_set_hexpand(addr_text[schema[i].record_field], TRUE);
 4502                     gtk_widget_set_halign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4503                     gtk_widget_set_vexpand(addr_text[schema[i].record_field], TRUE);
 4504                     gtk_widget_set_valign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4505                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(addr_text[schema[i].record_field]),
 4506                                     x - 1, grid_y_i, 1, 1);
 4507 
 4508                     changed_list = g_list_prepend(changed_list, addr_text_buffer[schema[i].record_field]);
 4509                     break;
 4510                 case ADDRESS_GUI_IM_MENU_TEXT:
 4511                     make_IM_type_menu(IM_type_i, IM_type_i, IM_type_i);
 4512                     gtk_widget_set_vexpand(GTK_WIDGET(IM_type_list_menu[IM_type_i]), FALSE);
 4513                     gtk_widget_set_valign(GTK_WIDGET(IM_type_list_menu[IM_type_i]), GTK_ALIGN_START);
 4514                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(IM_type_list_menu[IM_type_i]),
 4515                                     x - 2, grid_y_i, 1, 1);
 4516                     IM_type_i++;
 4517 
 4518                     /* Text */
 4519                     addr_text[schema[i].record_field] = gtk_text_view_new();
 4520                     addr_text_buffer[schema[i].record_field] = G_OBJECT(
 4521                             gtk_text_view_get_buffer(GTK_TEXT_VIEW(addr_text[schema[i].record_field])));
 4522                     gtk_text_view_set_editable(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), TRUE);
 4523                     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), GTK_WRAP_CHAR);
 4524                     gtk_container_set_border_width(GTK_CONTAINER(addr_text[schema[i].record_field]), 1);
 4525                     gtk_widget_set_hexpand(addr_text[schema[i].record_field], TRUE);
 4526                     gtk_widget_set_halign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4527                     gtk_widget_set_vexpand(addr_text[schema[i].record_field], TRUE);
 4528                     gtk_widget_set_valign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4529                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(addr_text[schema[i].record_field]),
 4530                                     x - 1, grid_y_i, 1, 1);
 4531 
 4532                     changed_list = g_list_prepend(changed_list, addr_text_buffer[schema[i].record_field]);
 4533                     break;
 4534                 case ADDRESS_GUI_WEBSITE_TEXT:
 4535                     /* Button used as label */
 4536                     button = gtk_button_new_with_label(contact_app_info.labels[schema[i].record_field]);
 4537                     /* Remove normal button behavior to accept focus */
 4538                     gtk_widget_set_focus_on_click(GTK_WIDGET(button), FALSE);
 4539                     gtk_widget_set_vexpand(GTK_WIDGET(button), FALSE);
 4540                     gtk_widget_set_valign(GTK_WIDGET(button), GTK_ALIGN_START);
 4541                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(button),
 4542                                     x - 2, grid_y_i, 1, 1);
 4543                     /* Text */
 4544                     addr_text[schema[i].record_field] = gtk_text_view_new();
 4545                     addr_text_buffer[schema[i].record_field] = G_OBJECT(
 4546                             gtk_text_view_get_buffer(GTK_TEXT_VIEW(addr_text[schema[i].record_field])));
 4547                     gtk_text_view_set_editable(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), TRUE);
 4548                     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(addr_text[schema[i].record_field]), GTK_WRAP_CHAR);
 4549                     gtk_container_set_border_width(GTK_CONTAINER(addr_text[schema[i].record_field]), 1);
 4550                     gtk_widget_set_hexpand(addr_text[schema[i].record_field], TRUE);
 4551                     gtk_widget_set_halign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4552                     gtk_widget_set_vexpand(addr_text[schema[i].record_field], TRUE);
 4553                     gtk_widget_set_valign(addr_text[schema[i].record_field], GTK_ALIGN_FILL);
 4554                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(addr_text[schema[i].record_field]),
 4555                                     x - 1, grid_y_i, 10, 1);
 4556 
 4557                     changed_list = g_list_prepend(changed_list, addr_text_buffer[schema[i].record_field]);
 4558                     break;
 4559                 case ADDRESS_GUI_BIRTHDAY:
 4560                     hbox_temp = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 4561                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(hbox_temp),
 4562                                     0, grid_y_i, 1, 1);
 4563 
 4564                     birthday_checkbox = gtk_check_button_new_with_label(
 4565                             contact_app_info.labels[schema[i].record_field]);
 4566                     gtk_box_pack_start(GTK_BOX(hbox_temp), birthday_checkbox, FALSE, FALSE, 0);
 4567                     g_signal_connect(G_OBJECT(birthday_checkbox), "clicked",
 4568                                        G_CALLBACK(cb_check_button_birthday), NULL);
 4569 
 4570                     changed_list = g_list_prepend(changed_list, birthday_checkbox);
 4571 
 4572                     birthday_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
 4573                     gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(birthday_box),
 4574                                     1, grid_y_i, 1, 1);
 4575 
 4576                     birthday_button = gtk_button_new_with_label("");
 4577                     gtk_box_pack_start(GTK_BOX(birthday_box), birthday_button, FALSE, FALSE, 0);
 4578                     g_signal_connect(G_OBJECT(birthday_button), "clicked",
 4579                                        G_CALLBACK(cb_button_birthday), NULL);
 4580 
 4581                     changed_list = g_list_prepend(changed_list, birthday_button);
 4582 
 4583                     reminder_checkbox = gtk_check_button_new_with_label(_("Reminder"));
 4584                     gtk_box_pack_start(GTK_BOX(birthday_box), reminder_checkbox, FALSE, FALSE, 0);
 4585                     g_signal_connect(G_OBJECT(reminder_checkbox), "clicked",
 4586                                        G_CALLBACK(cb_check_button_reminder), NULL);
 4587 
 4588                     changed_list = g_list_prepend(changed_list, reminder_checkbox);
 4589 
 4590                     reminder_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 4591                     gtk_box_pack_start(GTK_BOX(birthday_box), reminder_box, FALSE, FALSE, 0);
 4592 
 4593                     reminder_entry = gtk_entry_new();
 4594                     gtk_entry_set_max_length(GTK_ENTRY(reminder_entry), 2);
 4595                     entry_set_multiline_truncate(GTK_ENTRY(reminder_entry), TRUE);
 4596                     gtk_box_pack_start(GTK_BOX(reminder_box), reminder_entry, FALSE, FALSE, 0);
 4597 
 4598                     changed_list = g_list_prepend(changed_list, reminder_entry);
 4599 
 4600                     label = gtk_label_new(_("Days"));
 4601                     gtk_box_pack_start(GTK_BOX(reminder_box), label, FALSE, FALSE, 0);
 4602 
 4603                     break;
 4604                 default:
 4605                     break;
 4606             }
 4607             grid_y_i++;
 4608         }
 4609     }
 4610 
 4611     /* Connect keypress signals to callbacks */
 4612 
 4613     /* Capture the Enter key to move to the left-hand side of the display */
 4614     g_signal_connect(G_OBJECT(treeView), "key_press_event",
 4615                        G_CALLBACK(cb_key_pressed_left_side),
 4616                        NULL);
 4617 
 4618     for (i = 0; i < schema_size; i++) {
 4619         switch (schema[i].type) {
 4620             case ADDRESS_GUI_LABEL_TEXT:
 4621             case ADDRESS_GUI_DIAL_SHOW_PHONE_MENU_TEXT:
 4622             case ADDRESS_GUI_ADDR_MENU_TEXT:
 4623             case ADDRESS_GUI_IM_MENU_TEXT:
 4624             case ADDRESS_GUI_WEBSITE_TEXT:
 4625                 /* Capture the Shift-Enter key combination to move back to
 4626           * the right-hand side of the display. */
 4627                 if (schema[i].record_field != contNote) {
 4628                     g_signal_connect(G_OBJECT(addr_text[schema[i].record_field]),
 4629                                        "key_press_event",
 4630                                        G_CALLBACK(cb_key_pressed_right_side),
 4631                                        NULL);
 4632                 } else {
 4633                     g_signal_connect(G_OBJECT(addr_text[schema[i].record_field]),
 4634                                        "key_press_event",
 4635                                        G_CALLBACK(cb_key_pressed_right_side),
 4636                                        GINT_TO_POINTER(i));
 4637                 }
 4638