Filewatcher File Search
FTP Search
  
Directory (beta)
  
Content Search (beta)
   
pkg://jpilot-0.99.2-8.src.rpm:872711/jpilot-0.99.2.tar.gz  info  downloads

jpilot-0.99.2/0040755000175000001440000000000007430305566012177 5ustar  mouseusersjpilot-0.99.2/address.c0100644000175000001440000002430007423403442013756 0ustar  mouseusers/* address.c
 * A module of J-Pilot http://jpilot.org
 *
 * Copyright (C) 1999-2001 by Judd Montgomery
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "config.h"
#include "i18n.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <pi-source.h>
#include <pi-socket.h>
#include <pi-address.h>
#include <pi-dlp.h>
#include "address.h"
#include "utils.h"
#include "log.h"
#include "prefs.h"
#include "libplugin.h"
#include "password.h"

#define ADDRESS_EOF 7

static int glob_sort_by_company;
int sort_override=0;

#ifdef JPILOT_DEBUG
int print_address_list(AddressList **al)
{
   AddressList *temp_al, *prev_al, *next;

   for (prev_al=NULL, temp_al=*al; temp_al;
	prev_al=temp_al, temp_al=temp_al->next) {
      jpilot_logf(LOG_FILE | LOG_STDOUT, "entry[0]=[%s]\n", temp_al->ma.a.entry[0]);
   }
}
#endif

int address_compare(const void *v1, const void *v2)
{
   char *str1, *str2;
   int sort1, sort2, sort3;
   AddressList **al1, **al2;
   struct Address *a1, *a2;
   int i;

   al1=(AddressList **)v1;
   al2=(AddressList **)v2;

   a1=&((*al1)->ma.a);
   a2=&((*al2)->ma.a);

   if (glob_sort_by_company) {
      sort1=2; /*company */
      sort2=0; /*last name */
      sort3=1; /*first name */
   } else {
      sort1=0; /*last name */
      sort2=1; /*first name */
      sort3=2; /*company */
   }
   /*sort_by_company: */
   /*0 last, first or */
   /*1 company, last */

   str1=str2=NULL;

   if (a1->entry[sort1] || a1->entry[sort2]) {
      if (a1->entry[sort1] && a1->entry[sort2]) {
	 if ((str1 = (char *)malloc(strlen(a1->entry[sort1])+strlen(a1->entry[sort2])+1)) == NULL) {
	    return 0;
	 }	      
	 strcpy(str1, a1->entry[sort1]);
	 strcat(str1, a1->entry[sort2]);
      }
      if (a1->entry[sort1] && (!a1->entry[sort2])) {
	 if ((str1 = (char *)malloc(strlen(a1->entry[sort1])+1)) == NULL) {
	    return 0;
	 }
	 strcpy(str1, a1->entry[sort1]);
      }
      if ((!a1->entry[sort1]) && a1->entry[sort2]) {
	 if ((str1 = (char *)malloc(strlen(a1->entry[sort2])+1)) == NULL) {
	    return 0;
	 }
	 strcpy(str1, a1->entry[sort2]);
      }
   } else if (a1->entry[sort3]) {
      if ((str1 = (char *)malloc(strlen(a1->entry[sort3])+1)) == NULL) {
	 return 0;
      }
      strcpy(str1, a1->entry[sort3]);
   } else {
      return -1;
   }

   if (a2->entry[sort1] || a2->entry[sort2]) {
      if (a2->entry[sort1] && a2->entry[sort2]) {
	 if ((str2 = (char *)malloc(strlen(a2->entry[sort1])+strlen(a2->entry[sort2])+1)) == NULL) {
	    return 0;
	 }	      
	 strcpy(str2, a2->entry[sort1]);
	 strcat(str2, a2->entry[sort2]);
      }
      if (a2->entry[sort1] && (!a2->entry[sort2])) {
	 if ((str2 = (char *)malloc(strlen(a2->entry[sort1])+1)) == NULL) {
	    return 0;
	 }	      
	 strcpy(str2, a2->entry[sort1]);
      }
      if ((!a2->entry[sort1]) && a2->entry[sort2]) {
	 if ((str2 = (char *)malloc(strlen(a2->entry[sort2])+1)) == NULL) {
	    return 0;
	 }	      
	 strcpy(str2, a2->entry[sort2]);
      }
   } else if (a2->entry[sort3]) {
      if ((str2 = (char *)malloc(strlen(a2->entry[sort3])+1)) == NULL) {
	 return 0;
      }	      
      strcpy(str2, a2->entry[sort3]);
   } else {
      free(str1);
      return 1;
   }

   /* lower case the strings for a better compare */
   for (i=strlen(str1)-1; i >= 0; i--) {
      str1[i] = tolower(str1[i]);
   }
   for (i=strlen(str2)-1; i >= 0; i--) {
      str2[i] = tolower(str2[i]);
   }

   i = strcoll(str2, str1);
   if (str1) {
      free(str1);
   }
   if (str2) {
      free(str2);
   }
   return i;
}

/* 
 * sort_order: 0=descending,  1=ascending
 */
int address_sort(AddressList **al, int sort_order)
{
   AddressList *temp_al;
   AddressList **sort_al;
   struct AddressAppInfo ai;
   int count, i;

   /* Count the entries in the list */
   for (count=0, temp_al=*al; temp_al; temp_al=temp_al->next, count++) {
      ;
   }

   if (count<2) {
      /* We don't have to sort less than 2 items */
      return 0;
   }

   get_address_app_info(&ai);
   glob_sort_by_company = ai.sortByCompany;
   if (sort_override) {
      glob_sort_by_company = !(ai.sortByCompany & 1);
   }

   /* Allocate an array to be qsorted */
   sort_al = calloc(count, sizeof(AddressList *));
   if (!sort_al) {
      jpilot_logf(LOG_WARN, "address_sort(): Out of Memory\n");
      return 0;
   }

   /* Set our array to be a list of pointers to the nodes in the linked list */
   for (i=0, temp_al=*al; temp_al; temp_al=temp_al->next, i++) {
      sort_al[i] = temp_al;
   }

   /* qsort them */
   qsort(sort_al, count, sizeof(AddressList *), address_compare);

   /* Put the linked list in the order of the array */
   if (sort_order==SORT_ASCENDING) {
      for (i=count-1; i>0; i--) {
	 sort_al[i]->next=sort_al[i-1];
      }
      sort_al[0]->next = NULL;
      *al = sort_al[count-1];
   } else {
      /* Descending order */
      sort_al[count-1]->next = NULL;
      for (i=count-1; i; i--) {
	 sort_al[i-1]->next=sort_al[i];
      }
      *al = sort_al[0];
   }

   free(sort_al);

   return 0;
}

int pc_address_write(struct Address *a, PCRecType rt, unsigned char attrib,
		     unsigned int *unique_id)
{
   char record[65536];
   int rec_len,i;
   buf_rec br;
   long char_set;

   get_pref(PREF_CHAR_SET, &char_set, NULL);
   if (char_set != CHAR_SET_ENGLISH) {
      for (i = 0; i < 19; i++) {
	 if (a->entry[i]) charset_j2p(a->entry[i], strlen(a->entry[i])+1, char_set);
      }
   }

   rec_len = pack_Address(a, record, 65535);
   if (!rec_len) {
      PRINT_FILE_LINE;
      jpilot_logf(LOG_WARN, "pack_Address %s\n", _("error"));
      return -1;
   }
   br.rt=rt;
   br.attrib = attrib;
   br.buf = record;
   br.size = rec_len;

   jp_pc_write("AddressDB", &br);
   *unique_id = br.unique_id;

   return 0;
}

void free_AddressList(AddressList **al)
{
   AddressList *temp_al, *temp_al_next;

   for (temp_al = *al; temp_al; temp_al=temp_al_next) {
      free_Address(&(temp_al->ma.a));
      temp_al_next = temp_al->next;
      free(temp_al);
   }
   *al = NULL;
}

int get_address_app_info(struct AddressAppInfo *ai)
{
   int num;
   unsigned int rec_size;
   unsigned char *buf;
   long char_set;

   bzero(ai, sizeof(*ai));

   jp_get_app_info("AddressDB", &buf, &rec_size);
   num = unpack_AddressAppInfo(ai, buf, rec_size);
   if (buf) {
      free(buf);
   }
   if (num <= 0) {
      jpilot_logf(LOG_WARN, _("Error reading"), "AddressDB.pdb");
      return -1;
   }

   get_pref(PREF_CHAR_SET, &char_set, NULL); 
   if (char_set != CHAR_SET_ENGLISH) {
      /* Convert to character set */
      int i;
      for (i = 0; i < 16; i++)
	if (ai->category.name[i][0] != '\0') {
	   charset_p2j(ai->category.name[i], 16, char_set);
	}
      for (i = 0; i < 19 + 3; i++)
	if (ai->labels[i][0] != '\0') {
	   charset_p2j(ai->labels[i],16, char_set);
	}
      for (i = 0; i < 8; i++)
	if (ai->phoneLabels[i][0] != '\0') {
	   charset_p2j(ai->phoneLabels[i],16, char_set);
	}
   }

   return 0;
}

int get_addresses(AddressList **address_list, int sort_order)
{
   return get_addresses2(address_list, sort_order, 1, 1, 1, CATEGORY_ALL);
}
/* 
 * sort_order: 0=descending,  1=ascending
 * modified, deleted and private, 0 for no, 1 for yes, 2 for use prefs
 */
int get_addresses2(AddressList **address_list, int sort_order,
		  int modified, int deleted, int privates, int category)
{
   GList *records;
   GList *temp_list;
   int recs_returned, i, num;
   struct Address a;
   AddressList *temp_a_list;
   long keep_modified, keep_deleted;
   int keep_priv;
   long char_set;
   buf_rec *br;

   jpilot_logf(LOG_DEBUG, "get_addresses2()\n");
   if (modified==2) {
      get_pref(PREF_SHOW_MODIFIED, &keep_modified, NULL);
   } else {
      keep_modified = modified;
   }
   if (deleted==2) {
      get_pref(PREF_SHOW_DELETED, &keep_deleted, NULL);
   } else {
      keep_deleted = deleted;
   }
   if (privates==2) {
      keep_priv = show_privates(GET_PRIVATES, NULL);
   } else {
      keep_priv = privates;
   }

   *address_list=NULL;
   recs_returned = 0;

   num = jp_read_DB_files("AddressDB", &records);
   /* Go to first entry in the list */
   for (temp_list = records; temp_list; temp_list = temp_list->prev) {
      records = temp_list;
   }
   for (i=0, temp_list = records; temp_list; temp_list = temp_list->next, i++) {
      if (temp_list->data) {
	 br=temp_list->data;
      } else {
	 continue;
      }
      if (!br->buf) {
	 continue;
      }

      if ( ((br->rt==DELETED_PALM_REC) && (!keep_deleted)) ||
	  ((br->rt==MODIFIED_PALM_REC) && (!keep_modified)) ) {
	 continue;
      }
      if ((keep_priv != SHOW_PRIVATES) && 
	  (br->attrib & dlpRecAttrSecret)) {
	 continue;
      }

      num = unpack_Address(&a, br->buf, br->size);

      if (num <= 0) {
	 continue;
      }

      if ( ((br->attrib & 0x0F) != category) && category != CATEGORY_ALL) {
	 continue;
      }

      get_pref(PREF_CHAR_SET, &char_set, NULL);
      if (char_set != CHAR_SET_ENGLISH) {
	 for (i = 0; i < 19; i++) {
	    if ((a.entry[i] != NULL) && (a.entry[i][0] != '\0')) {
	       charset_p2j(a.entry[i], strlen(a.entry[i])+1, char_set);
	    }
	 }
      }

      temp_a_list = malloc(sizeof(AddressList));
      if (!temp_a_list) {
	 jpilot_logf(LOG_WARN, "get_addresses2(): Out of memory\n");
	 break;
      }
      memcpy(&(temp_a_list->ma.a), &a, sizeof(struct Address));
      temp_a_list->app_type = ADDRESS;
      temp_a_list->ma.rt = br->rt;
      temp_a_list->ma.attrib = br->attrib;
      temp_a_list->ma.unique_id = br->unique_id;
      temp_a_list->next = *address_list;
      *address_list = temp_a_list;
      recs_returned++;
   }

   jp_free_DB_records(&records);

#ifdef JPILOT_DEBUG
   print_address_list(address_list);
#endif
   address_sort(address_list, sort_order);

   jpilot_logf(LOG_DEBUG, "Leaving get_addresses2()\n");

   return recs_returned;
}
jpilot-0.99.2/address_gui.c0100644000175000001440000016171007424211157014632 0ustar  mouseusers/* address_gui.c
 * A module of J-Pilot http://jpilot.org
 * 
 * Copyright (C) 1999-2001 by Judd Montgomery
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "config.h"
#include "i18n.h"
#include <sys/stat.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdk.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
#include "address.h"
#include "log.h"
#include "prefs.h"
#include "print.h"
#include "password.h"
#include "export.h"
#include <pi-dlp.h>


/*#define SHADOW GTK_SHADOW_IN */
/*#define SHADOW GTK_SHADOW_OUT */
/*#define SHADOW GTK_SHADOW_ETCHED_IN */
#define SHADOW GTK_SHADOW_ETCHED_OUT

#define CONNECT_SIGNALS 400
#define DISCONNECT_SIGNALS 401

#define NUM_MENU_ITEM1 8
#define NUM_MENU_ITEM2 8
#define NUM_ADDRESS_CAT_ITEMS 16
#define NUM_ADDRESS_ENTRIES 19
#define NUM_ADDRESS_LABELS 22
#define NUM_PHONE_ENTRIES 5

#define ADDRESS_NAME_COLUMN  0
#define ADDRESS_NOTE_COLUMN  1
#define ADDRESS_PHONE_COLUMN 2

#define ADDRESS_MAX_CLIST_NAME 24

GtkWidget *clist;
GtkWidget *address_text[22];
GtkWidget *text;
GtkWidget *vscrollbar;
static GtkWidget *private_checkbox;
GtkWidget *phone_list_menu[NUM_PHONE_ENTRIES];
GtkWidget *menu;
GtkWidget *menu_item[NUM_MENU_ITEM1][NUM_MENU_ITEM2];
/*We need an extra one for the ALL category */
GtkWidget *address_cat_menu_item1[NUM_ADDRESS_CAT_ITEMS+1];
GtkWidget *address_cat_menu_item2[NUM_ADDRESS_CAT_ITEMS];
static GtkWidget *category_menu1;
static GtkWidget *category_menu2;
static GtkWidget *scrolled_window;
GtkWidget *address_quickfind_entry;
static GtkWidget *notebook;
static GtkWidget *pane;
static GtkWidget *radio_button[NUM_PHONE_ENTRIES];

static struct AddressAppInfo address_app_info;
static struct sorted_cats sort_l[NUM_ADDRESS_CAT_ITEMS];
int address_category=CATEGORY_ALL;
int address_phone_label_selected[NUM_PHONE_ENTRIES];
static int clist_row_selected;
extern GtkTooltips *glob_tooltips;

static AddressList *glob_address_list=NULL;
static AddressList *export_address_list=NULL;

static GtkWidget *new_record_button;
static GtkWidget *apply_record_button;
static GtkWidget *add_record_button;
static int record_changed;
static int clist_hack;

static void connect_changed_signals(int con_or_dis);
static void address_update_clist(GtkWidget *clist, GtkWidget *tooltip_widget,
				 AddressList *addr_list, int category, int main);
int address_clist_redraw();
static int address_find();
static void get_address_attrib(unsigned char *attrib);
static void cb_clist_selection(GtkWidget      *clist,
			       gint           row,
			       gint           column,
			       GdkEventButton *event,
			       gpointer       data);

static void init()
{
   int i, j;
   record_changed=CLEAR_FLAG;
   for (i=0; i<NUM_MENU_ITEM1; i++) {
      for (j=0; j<NUM_MENU_ITEM2; j++) {
	 menu_item[i][j] = NULL;
      }
   }
   for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
      address_cat_menu_item2[i] = NULL;
   }
}

static void
set_new_button_to(int new_state)
{
   jpilot_logf(LOG_DEBUG, "set_new_button_to new %d old %d\n", new_state, record_changed);

   if (record_changed==new_state) {
      return;
   }

   switch (new_state) {
    case MODIFY_FLAG:
      gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_SINGLE);
      clist_hack=TRUE;
      /* The line selected on the clist becomes unhighlighted, so we do this */
      gtk_clist_select_row(GTK_CLIST(clist), clist_row_selected, 0);
      gtk_widget_show(apply_record_button);
      break;
    case NEW_FLAG:
      gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_SINGLE);
      clist_hack=TRUE;
      /* The line selected on the clist becomes unhighlighted, so we do this */
      gtk_clist_select_row(GTK_CLIST(clist), clist_row_selected, 0);
      gtk_widget_show(add_record_button);
      break;
    case CLEAR_FLAG:
      gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE);
      clist_hack=FALSE;
      gtk_widget_show(new_record_button);
      break;
    default:
      return;
   }
   switch (record_changed) {
    case MODIFY_FLAG:
      gtk_widget_hide(apply_record_button);
      break;
    case NEW_FLAG:
      gtk_widget_hide(add_record_button);
      break;
    case CLEAR_FLAG:
      gtk_widget_hide(new_record_button);
      break;
   }
   record_changed=new_state;
}

static void
cb_record_changed(GtkWidget *widget,
		  gpointer   data)
{
   jpilot_logf(LOG_DEBUG, "cb_record_changed\n");
   if (record_changed==CLEAR_FLAG) {
      connect_changed_signals(DISCONNECT_SIGNALS);
      if (((GtkCList *)clist)->rows > 0) {
	 set_new_button_to(MODIFY_FLAG);
      } else {
	 set_new_button_to(NEW_FLAG);
      }
   }
   return;
}

static void connect_changed_signals(int con_or_dis)
{
   int i, j;
   static int connected=0;

   /* CONNECT */
   if ((con_or_dis==CONNECT_SIGNALS) && (!connected)) {
      connected=1;

      for (i=0; i<NUM_MENU_ITEM1; i++) {
	 for (j=0; j<NUM_MENU_ITEM2; j++) {
	    if (menu_item[i][j]) {
	       gtk_signal_connect(GTK_OBJECT(menu_item[i][j]), "toggled",
				  GTK_SIGNAL_FUNC(cb_record_changed), NULL);
	    }
	 }
      }
      for (i=0; i<NUM_PHONE_ENTRIES; i++) {
	 if (radio_button[i]) {
	    gtk_signal_connect(GTK_OBJECT(radio_button[i]), "toggled",
			       GTK_SIGNAL_FUNC(cb_record_changed), NULL);
	 }
      }
      for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
	 if (address_cat_menu_item2[i]) {
	    gtk_signal_connect(GTK_OBJECT(address_cat_menu_item2[i]), "toggled",
			       GTK_SIGNAL_FUNC(cb_record_changed), NULL);
	 }
      }
      for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
	 gtk_signal_connect(GTK_OBJECT(address_text[i]), "changed",
			    GTK_SIGNAL_FUNC(cb_record_changed), NULL);
      }
      gtk_signal_connect(GTK_OBJECT(private_checkbox), "toggled",
			 GTK_SIGNAL_FUNC(cb_record_changed), NULL);
   }

   /* DISCONNECT */
   if ((con_or_dis==DISCONNECT_SIGNALS) && (connected)) {
      connected=0;
      for (i=0; i<NUM_MENU_ITEM1; i++) {
	 for (j=0; j<NUM_MENU_ITEM2; j++) {
	    if (menu_item[i][j]) {
	       gtk_signal_disconnect_by_func(GTK_OBJECT(menu_item[i][j]),
					     GTK_SIGNAL_FUNC(cb_record_changed), NULL);     
	    }
	 }
      }
      for (i=0; i<NUM_PHONE_ENTRIES; i++) {
	 if (radio_button[i]) {
	    gtk_signal_disconnect_by_func(GTK_OBJECT(radio_button[i]),
					  GTK_SIGNAL_FUNC(cb_record_changed), NULL);
	 }
      }
      for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
	 if (address_cat_menu_item2[i]) {
	    gtk_signal_disconnect_by_func(GTK_OBJECT(address_cat_menu_item2[i]),
					  GTK_SIGNAL_FUNC(cb_record_changed), NULL);     
	 }
      }
      for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
	 gtk_signal_disconnect_by_func(GTK_OBJECT(address_text[i]),
				       GTK_SIGNAL_FUNC(cb_record_changed), NULL);     
      }
      gtk_signal_disconnect_by_func(GTK_OBJECT(private_checkbox),
				    GTK_SIGNAL_FUNC(cb_record_changed), NULL);
   }
}

int address_print()
{
   long this_many;
   MyAddress *ma;
   AddressList *address_list;
   AddressList address_list1;

   get_pref(PREF_PRINT_THIS_MANY, &this_many, NULL);

   address_list=NULL;
   if (this_many==1) {
      ma = gtk_clist_get_row_data(GTK_CLIST(clist), clist_row_selected);
      if (ma < (MyAddress *)CLIST_MIN_DATA) {
	 return -1;
      }
      memcpy(&(address_list1.ma), ma, sizeof(MyAddress));
      address_list1.next=NULL;
      address_list = &address_list1;
   }
   if (this_many==2) {
      get_addresses2(&address_list, SORT_ASCENDING, 2, 2, 2, address_category);
   }
   if (this_many==3) {
      get_addresses2(&address_list, SORT_ASCENDING, 2, 2, 2, CATEGORY_ALL);
   }

   print_addresses(address_list);

   if ((this_many==2) || (this_many==3)) {
      free_AddressList(&address_list);
   }

   return 0;
}

int address_to_text(struct Address *addr, char *text, int len)
{
   int order[NUM_ADDRESS_LABELS]={
      0,1,13,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,19,20,21
   };
   char *field_names[]={"Last", "First", "Title", "Company", "Phone1",
	"Phone2", "Phone3", "Phone4", "Phone5", "Address", "City", "State",
	"ZipCode", "Country", "Custom1", "Custom2", "Custom3", "Custom4",
	"Note", "phoneLabel1", "phoneLabel2", "phoneLabel3", "phoneLabel4",
	"phoneLabel5", "showPhone", NULL};

   g_snprintf(text, len,
	      "%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
	      "%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
	      "%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
	      "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
	      "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n"
	      "%s: %d\n",
	      field_names[0], addr->entry[order[0]],
	      field_names[1], addr->entry[order[1]],
	      field_names[2], addr->entry[order[2]],
	      field_names[3], addr->entry[order[3]],
	      field_names[4], addr->entry[order[4]],
	      field_names[5], addr->entry[order[5]],
	      field_names[6], addr->entry[order[6]],
	      field_names[7], addr->entry[order[7]],
	      field_names[8], addr->entry[order[8]],
	      field_names[9], addr->entry[order[9]],
	      field_names[10], addr->entry[order[10]],
	      field_names[11], addr->entry[order[11]],
	      field_names[12], addr->entry[order[12]],
	      field_names[13], addr->entry[order[13]],
	      field_names[14], addr->entry[order[14]],
	      field_names[15], addr->entry[order[15]],
	      field_names[16], addr->entry[order[16]],
	      field_names[17], addr->entry[order[17]],
	      field_names[18], addr->entry[order[18]],
	      field_names[19], addr->phoneLabel[0],
	      field_names[20], addr->phoneLabel[1],
	      field_names[21], addr->phoneLabel[2],
	      field_names[22], addr->phoneLabel[3],
	      field_names[23], addr->phoneLabel[4],
	      field_names[24], addr->showPhone
	      );
   text[len-1]='\0';
   return 0;
}

/*
 * Start Import Code
 */
int address_import_callback(GtkWidget *parent_window, char *file_path, int type)
{
   FILE *in;
   char text[65536];
   struct Address new_addr;
   unsigned char attrib;
   unsigned int unique_id;
   int i, ret, index;
   int import_all;
   AddressList *addrlist;
   AddressList *temp_addrlist;
   struct CategoryAppInfo cai;
   char old_cat_name[32];
   int suggested_cat_num;
   int new_cat_num;
   int priv;
   int order[NUM_ADDRESS_LABELS]={
      0,1,13,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,19,20,21
   };

   get_address_attrib(&attrib);

   in=fopen(file_path, "r");
   if (!in) {
      jpilot_logf(LOG_WARN, _("Could not open file %s\n"), file_path);
      return -1;
   }

   /* CSV */
   if (type==IMPORT_TYPE_CSV) {
      jpilot_logf(LOG_DEBUG, "Address import CSV [%s]\n", file_path);
      /* The first line is format, so we don't need it */
      fgets(text, 1000, in);
      import_all=FALSE;
      while (1) {
	 /* Read the category field */
	 ret = read_csv_field(in, text, 65535);
	 if (feof(in)) break;
#ifdef JPILOT_DEBUG
	 printf("category is [%s]\n", text);
#endif
	 strncpy(old_cat_name, text, 16);
	 old_cat_name[16]='\0';
	 attrib=0;
	 /* Figure out what the best category number is */
	 index=temp_addrlist->ma.unique_id-1;
	 suggested_cat_num=0;
	 for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
	    if (address_app_info.category.name[i][0]=='\0') continue;
	    if (!strcmp(address_app_info.category.name[i], old_cat_name)) {
	       suggested_cat_num=i;
	       i=1000;
	       break;
	    }
	 }

	 /* Read the private field */
	 ret = read_csv_field(in, text, 65535);
#ifdef JPILOT_DEBUG
	 printf("private is [%s]\n", text);
#endif
	 sscanf(text, "%d", &priv);

	 for (i=0; i<19; i++) {
	    new_addr.entry[order[i]]=NULL;
	    ret = read_csv_field(in, text, 65535);
	    text[65535]='\0';
	    new_addr.entry[order[i]]=strdup(text);
	 }
	 for (i=0; i<5; i++) {
	    ret = read_csv_field(in, text, 65535);
	    text[65535]='\0';
	    sscanf(text, "%d", &(new_addr.phoneLabel[i]));
	 }
	 ret = read_csv_field(in, text, 65535);
	 text[65535]='\0';
	 sscanf(text, "%d", &(new_addr.showPhone));

	 address_to_text(&new_addr, text, 65535);
	 if (!import_all) {
	    ret=import_record_ask(parent_window, pane,
				  text,
				  &(address_app_info.category),
				  old_cat_name,
				  priv,
				  suggested_cat_num,
				  &new_cat_num);
	 } else {
	    new_cat_num=suggested_cat_num;
	 }
	 if (ret==DIALOG_SAID_IMPORT_QUIT) break;
	 if (ret==DIALOG_SAID_IMPORT_SKIP) continue;
	 if (ret==DIALOG_SAID_IMPORT_ALL) {
	    import_all=TRUE;
	 }
	 attrib = (new_cat_num & 0x0F) |
	   (priv ? dlpRecAttrSecret : 0);
	 if ((ret==DIALOG_SAID_IMPORT_YES) || (import_all)) {
	    pc_address_write(&new_addr, NEW_PC_REC, attrib, &unique_id);
	 }
      }
   }

   /* Palm Desktop DAT format */
   if (type==IMPORT_TYPE_DAT) {
      jpilot_logf(LOG_DEBUG, "Address import DAT [%s]\n", file_path);
      if (dat_check_if_dat_file(in)!=DAT_ADDRESS_FILE) {
	 jpilot_logf(LOG_WARN, _("File doesn't appear to be address.dat format\n"));
	 fclose(in);
	 return 1;
      }
      addrlist=NULL;
      dat_get_addresses(in, &addrlist, &cai);
      import_all=FALSE;
      for (temp_addrlist=addrlist; temp_addrlist; temp_addrlist=temp_addrlist->next) {
	 index=temp_addrlist->ma.unique_id-1;
	 if (index<0) {
	    strncpy(old_cat_name, _("Unfiled"), 16);
	    old_cat_name[16]='\0';
	    index=0;
	 } else {
	    strncpy(old_cat_name, cai.name[index], 16);
	    old_cat_name[16]='\0';
	 }
	 attrib=0;
	 /* Figure out what category it was in the dat file */
	 index=temp_addrlist->ma.unique_id-1;
	 suggested_cat_num=0;
	 if (index>-1) {
	    for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
	       if (address_app_info.category.name[i][0]=='\0') continue;
	       if (!strcmp(address_app_info.category.name[i], old_cat_name)) {
		  suggested_cat_num=i;
		  i=1000;
		  break;
	       }
	    }
	 }

	 ret=0;
	 if (!import_all) {
	    address_to_text(&(temp_addrlist->ma.a), text, 65535);
	    ret=import_record_ask(parent_window, pane,
				  text,
				  &(address_app_info.category),
				  old_cat_name,
				  (temp_addrlist->ma.attrib & 0x10),
				  suggested_cat_num,
				  &new_cat_num);
	 } else {
	    new_cat_num=suggested_cat_num;
	 }
	 if (ret==DIALOG_SAID_IMPORT_QUIT) break;
	 if (ret==DIALOG_SAID_IMPORT_SKIP) continue;
	 if (ret==DIALOG_SAID_IMPORT_ALL) {
	    import_all=TRUE;
	 }
	 attrib = (new_cat_num & 0x0F) |
	   ((temp_addrlist->ma.attrib & 0x10) ? dlpRecAttrSecret : 0);
	 if ((ret==DIALOG_SAID_IMPORT_YES) || (import_all)) {
	    pc_address_write(&(temp_addrlist->ma.a), NEW_PC_REC, attrib, &unique_id);
	 }
      }
      free_AddressList(&addrlist);
   }

   address_refresh();
   fclose(in);
   return 0;
}

int address_import(GtkWidget *window)
{
   char *type_desc[] = {
      "CSV (Comma Separated Values)",
      "DAT/ABA (Palm Archive Formats)",
      NULL
   };
   int type_int[] = {
      IMPORT_TYPE_CSV,
      IMPORT_TYPE_DAT,
      0
   };

   import_gui(window, pane, type_desc, type_int, address_import_callback);
   return 0;
}
/*
 * End Import Code
 */

/*
 * Start Export code
 */

void cb_addr_export_ok(GtkWidget *export_window, GtkWidget *clist,
		       int type, const char *filename)
{
   MyAddress *ma;
   GList *list, *temp_list;
   FILE *out;
   struct stat statb;
   const char *short_date;
   time_t ltime;
   struct tm *now;
   char str1[256], str2[256];
   char pref_time[40];
   int i, r, n, len;
   char *button_text[]={gettext_noop("OK")};
   char *button_overwrite_text[]={gettext_noop("Yes"), gettext_noop("No")};
   char text[1024];
   char csv_text[65550];
   int order[NUM_ADDRESS_LABELS]={
      0,1,13,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,19,20,21
   };
   char *field_names[]={"Last", "First", "Title", "Company", "Phone1",
	"Phone2", "Phone3", "Phone4", "Phone5", "Address", "City", "State",
	"ZipCode", "Country", "Custom1", "Custom2", "Custom3", "Custom4",
	"Note", "phoneLabel1", "phoneLabel2", "phoneLabel3", "phoneLabel4",
	"phoneLabel5", "showPhone", NULL};

   list=GTK_CLIST(clist)->selection;

   if (!stat(filename, &statb)) {
      if (S_ISDIR(statb.st_mode)) {
	 g_snprintf(text, 1024, _("%s is a directory"), filename);
	 dialog_generic(GTK_WIDGET(export_window)->window,
			0, 0, _("Error Opening File"),
			"Directory", text, 1, button_text);
	 return;
      }
      g_snprintf(text, 1024, _("Do you want to overwrite file %s?"), filename);
      r = dialog_generic(GTK_WIDGET(export_window)->window,
			 0, 0, _("Overwrite File?"),
			 _("Overwrite File"), text, 2, button_overwrite_text);
      if (r!=DIALOG_SAID_1) {
	 return;
      }
   }

   out = fopen(filename, "w");
   if (!out) {
      g_snprintf(text, 1024, "Error Opening File: %s", filename);
      dialog_generic(GTK_WIDGET(export_window)->window,
		     0, 0, _("Error Opening File"),
		     "Filename", text, 1, button_text);
      return;
   }

   for (i=0, temp_list=list; temp_list; temp_list = temp_list->next, i++) {
      ma = gtk_clist_get_row_data(GTK_CLIST(clist), (int) temp_list->data);
      if (!ma) {
	 continue;
	 jpilot_logf(LOG_WARN, "Can't export address %d\n", (long) temp_list->data + 1);
      }
      switch (type) {
       case EXPORT_TYPE_TEXT:
	 get_pref(PREF_SHORTDATE, NULL, &short_date);
	 get_pref_time_no_secs(pref_time);
	 time(&ltime);
	 now = localtime(&ltime);
	 strftime(str1, 50, short_date, now);
	 strftime(str2, 50, pref_time, now);
	 g_snprintf(text, 100, "%s %s", str1, str2);
	 text[100]='\0';

	 /* Todo Should I translate these? */
	 fprintf(out, "Address: exported from %s on %s\n", PN, text);
	 fprintf(out, "Category: %s\n", address_app_info.category.name[ma->attrib & 0x0F]);
	 fprintf(out, "Private: %s\n",
		 (ma->attrib & dlpRecAttrSecret) ? "Yes":"No");
	 for (n=0; (field_names[n]) && (n<19); n++) {
	    fprintf(out, "%s: ", field_names[n]);
	    fprintf(out, "%s\n", ma->a.entry[order[n]] ?
		    ma->a.entry[order[n]] : "");
	 }
	 for (n=19; (field_names[n]) && (n<24); n++) {
	    fprintf(out, "%s: ", field_names[n]);
	    fprintf(out, "%d\n", ma->a.phoneLabel[n-19]);
	 }
	 fprintf(out, "Show Phone: %d\n\n", ma->a.showPhone);
	 break;
       case EXPORT_TYPE_CSV:
	 if (i==0) {
	    fprintf(out, "CSV address: Category, Private, ");
	    for (n=0; (field_names[n]) && (n<30); n++) {
	       fprintf(out, "%s", field_names[n]);
	       if (field_names[n+1]) {
		  fprintf(out, ", ");
	       }
	    }
	    fprintf(out, "\n");
	 }
	 len=0;
	 str_to_csv_str(csv_text,
			address_app_info.category.name[ma->attrib & 0x0F]);
	 fprintf(out, "\"%s\", ", csv_text);
	 fprintf(out, "\"%s\", ", (ma->attrib & dlpRecAttrSecret) ? "1":"0");
	 for (n=0; n<19; n++) {
	    csv_text[0]='\0';
	    if (ma->a.entry[order[n]]) {
	       if (strlen(ma->a.entry[order[n]])>65535) {
		  jpilot_logf(LOG_WARN, "Field > 65535\n");
	       } else {
		  str_to_csv_str(csv_text, ma->a.entry[order[n]]);
	       }
	    }
	    fprintf(out, "\"%s\", ", csv_text);
	 }
	 for (n=0; n<5; n++) {
	    fprintf(out, "\"%d\", ", ma->a.phoneLabel[n]);
	 }
	 fprintf(out, "\"%d\"", ma->a.showPhone);
	 fprintf(out, "\n");
	 break;
       default:
	 jpilot_logf(LOG_WARN, "Unknown export type\n");
      }
   }

   if (out) {
      fclose(out);
   }
}


static void cb_addr_update_clist(GtkWidget *clist, int category)
{
   address_update_clist(clist, NULL, export_address_list, category, FALSE);
}


static void cb_addr_export_done(GtkWidget *widget, const char *filename)
{
   free_AddressList(&export_address_list);

   set_pref(PREF_ADDRESS_EXPORT_FILENAME, 0, filename);
}

int address_export(GtkWidget *window)
{
   int w, h, x, y;

   gdk_window_get_size(window->window, &w, &h);
   gdk_window_get_root_origin(window->window, &x, &y);

   w = GTK_PANED(pane)->handle_xpos;
   x+=40;

   export_gui(w, h, x, y, 3, sort_l,
	      PREF_ADDRESS_EXPORT_FILENAME,
	      cb_addr_update_clist,
	      cb_addr_export_done,
	      cb_addr_export_ok
	      );

   return 0;
}

/*
 * End Export Code
 */

static int find_sorted_cat(int cat)
{
   int i;
   for (i=0; i< NUM_ADDRESS_CAT_ITEMS; i++) {
      if (sort_l[i].cat_num==cat) {
	 return i;
      }
   }
   return 0;
}


void cb_delete_address(GtkWidget *widget,
		       gpointer   data)
{
   MyAddress *ma;
   int flag;
   int show_priv;

   ma = gtk_clist_get_row_data(GTK_CLIST(clist), clist_row_selected);
   if (ma < (MyAddress *)CLIST_MIN_DATA) {
      return;
   }
   /* Do masking like Palm OS 3.5 */
   show_priv = show_privates(GET_PRIVATES, NULL);
   if ((show_priv != SHOW_PRIVATES) &&
       (ma->attrib & dlpRecAttrSecret)) {
      return;
   }
   /* End Masking */
   flag = GPOINTER_TO_INT(data);
   if ((flag==MODIFY_FLAG) || (flag==DELETE_FLAG)) {
      delete_pc_record(ADDRESS, ma, flag);
      if (flag==DELETE_FLAG) {
	 /* when we redraw we want to go to the line above the deleted one */
	 if (clist_row_selected>0) {
	    clist_row_selected--;
	 }
      }
   }

   address_clist_redraw();
}

void cb_resort(GtkWidget *widget,
	       gpointer   data)
{
   int by_company;

   by_company=GPOINTER_TO_INT(data);

   if (sort_override) {
      sort_override=0;
   } else {
      sort_override=1;
      by_company=!(by_company & 1);
   }

   if (by_company) {
      gtk_clist_set_column_title(GTK_CLIST(clist), ADDRESS_NAME_COLUMN, _("Company/Name"));
   } else {
      gtk_clist_set_column_title(GTK_CLIST(clist), ADDRESS_NAME_COLUMN, _("Name/Company"));
   }

   address_clist_redraw();
}


void cb_phone_menu(GtkWidget *item, unsigned int value)
{
   if (!item)
     return;
   if ((GTK_CHECK_MENU_ITEM(item))->active) {
      jpilot_logf(LOG_DEBUG, "phone_menu = %d\n", (value & 0xF0) >> 4);
      jpilot_logf(LOG_DEBUG, "selection = %d\n", value & 0x0F);
      address_phone_label_selected[(value & 0xF0) >> 4] = value & 0x0F;
   }
}

void cb_notebook_changed(GtkWidget *widget,
			 GtkWidget *widget2,
			 int        page,
			 gpointer   data)
{
   int prev_page;

   /* GTK calls this function while it is destroying the notebook
    * I use this function to tell if it is being destroyed */
   prev_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget));
   if (prev_page<0) {
      return;
   }
   jpilot_logf(LOG_DEBUG, "cb_notebook_changed(), prev_page=%d, page=%d\n", prev_page, page);
   set_pref(PREF_ADDRESS_NOTEBOOK_PAGE, page, NULL);
}

static void get_address_attrib(unsigned char *attrib)
{
   int i;
   /*Get the category that is set from the menu */
   *attrib = 0;
   for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
      if (GTK_IS_WIDGET(address_cat_menu_item2[i])) {
	 if (GTK_CHECK_MENU_ITEM(address_cat_menu_item2[i])->active) {
	    *attrib = sort_l[i].cat_num;
	    break;
	 }
      }
   }
   /* Get private flag */
   if (GTK_TOGGLE_BUTTON(private_checkbox)->active) {
      *attrib |= dlpRecAttrSecret;
   }
}

static void cb_add_new_record(GtkWidget *widget,
			      gpointer   data)
{
   int i;
   struct Address a;
   MyAddress *ma;
   unsigned char attrib;
   unsigned int unique_id;
   int show_priv;

   bzero(&a, sizeof(a));

   /* Do masking like Palm OS 3.5 */
   if ((GPOINTER_TO_INT(data)==COPY_FLAG) || 
       (GPOINTER_TO_INT(data)==MODIFY_FLAG)) {
      show_priv = show_privates(GET_PRIVATES, NULL);
      ma = gtk_clist_get_row_data(GTK_CLIST(clist), clist_row_selected);
      if (ma < (MyAddress *)CLIST_MIN_DATA) {
	 return;
      }
      if ((show_priv != SHOW_PRIVATES) &&
	  (ma->attrib & dlpRecAttrSecret)) {
	 return;
      }
   }
   /* End Masking */
   if ((GPOINTER_TO_INT(data)==NEW_FLAG) || 
       (GPOINTER_TO_INT(data)==COPY_FLAG) ||
       (GPOINTER_TO_INT(data)==MODIFY_FLAG)) {
      /*These rec_types are both the same for now */
      if (GPOINTER_TO_INT(data)==MODIFY_FLAG) {
	 ma = gtk_clist_get_row_data(GTK_CLIST(clist), clist_row_selected);
	 if (ma < (MyAddress *)CLIST_MIN_DATA) {
	    return;
	 }
	 if ((ma->rt==DELETED_PALM_REC) || (ma->rt==MODIFIED_PALM_REC)) {
	    jpilot_logf(LOG_INFO, "You can't modify a record that is deleted\n");
	    return;
	 }
      }
      a.showPhone=0;
      for (i=0; i<NUM_PHONE_ENTRIES; i++) {
	 if (GTK_TOGGLE_BUTTON(radio_button[i])->active) {
	    a.showPhone=i;
	 }
      }
      for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
	 a.entry[i] = 
	   gtk_editable_get_chars(GTK_EDITABLE(address_text[i]), 0, -1);
      }
      for (i=0; i<NUM_PHONE_ENTRIES; i++) {
	 a.phoneLabel[i]=address_phone_label_selected[i];
      }

      /*Get the attributes */
      get_address_attrib(&attrib);

      set_new_button_to(CLEAR_FLAG);

      pc_address_write(&a, NEW_PC_REC, attrib, &unique_id);
      free_Address(&a);
      if (GPOINTER_TO_INT(data) == MODIFY_FLAG) {
	 cb_delete_address(NULL, data);
      } else {
	 address_clist_redraw();
      }
      glob_find_id = unique_id;
      address_find();
   }
}

void clear_details()
{
   int i;
   int new_cat;
   int sorted_position;

   /* Need to disconnect these signals first */
   set_new_button_to(NEW_FLAG);
   connect_changed_signals(DISCONNECT_SIGNALS);

   /* Clear the quickview */
   gtk_text_set_point(GTK_TEXT(text), 0);
   gtk_text_forward_delete(GTK_TEXT(text),
			   gtk_text_get_length(GTK_TEXT(text)));

   /*Clear all the address entry texts */
   for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
      gtk_text_set_point(GTK_TEXT(address_text[i]), 0);
      gtk_text_forward_delete(GTK_TEXT(address_text[i]),
			      gtk_text_get_length(GTK_TEXT(address_text[i])));
   }
   for (i=0; i<NUM_PHONE_ENTRIES; i++) {
      if (GTK_IS_WIDGET(menu_item[i][i])) {
	 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
					(menu_item[i][i]), TRUE);
	 gtk_option_menu_set_history(GTK_OPTION_MENU(phone_list_menu[i]), i);
      }
   }
   if (address_category==CATEGORY_ALL) {
      new_cat = 0;
   } else {
      new_cat = address_category;
   }
   sorted_position = find_sorted_cat(new_cat);
   if (sorted_position<0) {
      jpilot_logf(LOG_WARN, "Category is not legal\n");
   } else {
      gtk_check_menu_item_set_active
	(GTK_CHECK_MENU_ITEM(address_cat_menu_item2[sorted_position]), TRUE);
      gtk_option_menu_set_history(GTK_OPTION_MENU(category_menu2), sorted_position);
   }

   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private_checkbox), FALSE);

   connect_changed_signals(CONNECT_SIGNALS);
}

void cb_address_clear(GtkWidget *widget,
		      gpointer   data)
{
   clear_details();
   gtk_notebook_set_page(GTK_NOTEBOOK(notebook), 0);
   gtk_widget_grab_focus(GTK_WIDGET(address_text[0]));
}

void cb_address_quickfind(GtkWidget *widget,
			  gpointer   data)
{
   char *entry_text;
   int i, r, found, found_at, line_count;
   char *clist_text;

   found_at = 0;
   entry_text = gtk_entry_get_text(GTK_ENTRY(widget));
   if (!strlen(entry_text)) {
      return;
   }
   /*100000 is just to prevent ininite looping during a solar flare */
   for (found = i = 0; i<100000; i++) {
      r = gtk_clist_get_text(GTK_CLIST(clist), i, ADDRESS_NAME_COLUMN, &clist_text);
      if (!r) {
	 break;
      }
      if (found) {
	 continue;
      }
      if (!strncasecmp(clist_text, entry_text, strlen(entry_text))) {
	 found = 1;
	 found_at = i;
	 gtk_clist_select_row(GTK_CLIST(clist), i, ADDRESS_NAME_COLUMN);
	 cb_clist_selection(clist, i, ADDRESS_NAME_COLUMN, (GdkEventButton *)455, NULL);
      }
   }
   line_count = i;

   if (found) {
      move_scrolled_window(scrolled_window,
			   ((float)found_at)/((float)line_count));
   }
}

static void cb_category(GtkWidget *item, int selection)
{
   int b;

   if (!item) return;
   b=dialog_save_changed_record(pane, record_changed);
   if (b==DIALOG_SAID_1) {
      cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
   }
   if ((GTK_CHECK_MENU_ITEM(item))->active) {
      address_category = selection;
      jpilot_logf(LOG_DEBUG, "address_category = %d\n",address_category);
      address_update_clist(clist, category_menu1, glob_address_list,
			   address_category, TRUE);
   }
}


/* Do masking like Palm OS 3.5 */
static void clear_myaddress(MyAddress *ma)
{
   int i;

   ma->unique_id=0;
   ma->attrib=ma->attrib & 0xF8;
   for (i=0; i<5; i++) {
      ma->a.phoneLabel[i]=0;
   }
   ma->a.showPhone=0;
   for (i=0; i<19; i++) {
      if (ma->a.entry) {
	 free(ma->a.entry[i]);
	 ma->a.entry[i]=NULL;
      }
   }
   return;
}
/* End Masking */

static void cb_clist_selection(GtkWidget      *clist,
			       gint           row,
			       gint           column,
			       GdkEventButton *event,
			       gpointer       data)
{
   /* The rename-able phone entries are indexes 3,4,5,6,7 */
   struct Address *a;
   MyAddress *ma;
   int cat, count, sorted_position;
   int i, i2;
   int keep, b;
   /* This is because the palm doesn't show the address entries in order */
   int order[NUM_ADDRESS_LABELS]={
      0,1,13,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,19,20,21
   };
   char *clist_text, *entry_text;

   if ((!event) && (clist_hack)) return;

   /* HACK */
   if (clist_hack) {
      keep=record_changed;
      gtk_clist_select_row(GTK_CLIST(clist), clist_row_selected, column);
      b=dialog_save_changed_record(pane, record_changed);
      if (b==DIALOG_SAID_1) {
	 cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
      }
      set_new_button_to(CLEAR_FLAG);
      /* This doesn't cause an event to occur, it does highlight
       * the line, so we do the next call also */
      gtk_clist_select_row(GTK_CLIST(clist), row, column);
      cb_clist_selection(clist, row, column, GINT_TO_POINTER(1), NULL);
      return;
   }

   clist_row_selected=row;

   ma = gtk_clist_get_row_data(GTK_CLIST(clist), row);
   if (ma==NULL) {
      return;
   }

   set_new_button_to(CLEAR_FLAG);

   connect_changed_signals(DISCONNECT_SIGNALS);

   a=&(ma->a);
   clist_text = NULL;
   gtk_clist_get_text(GTK_CLIST(clist), row, ADDRESS_NAME_COLUMN, &clist_text);
   entry_text = gtk_entry_get_text(GTK_ENTRY(address_quickfind_entry));
   if (strncasecmp(clist_text, entry_text, strlen(entry_text))) {
      gtk_entry_set_text(GTK_ENTRY(address_quickfind_entry), "");
   }

   gtk_text_freeze(GTK_TEXT(text));

   gtk_text_set_point(GTK_TEXT(text), 0);
   gtk_text_forward_delete(GTK_TEXT(text),
			   gtk_text_get_length(GTK_TEXT(text)));

   gtk_text_insert(GTK_TEXT(text), NULL,NULL,NULL, _("Category: "), -1);
   gtk_text_insert(GTK_TEXT(text), NULL,NULL,NULL,
		   address_app_info.category.name[ma->attrib & 0x0F], -1);
   gtk_text_insert(GTK_TEXT(text), NULL,NULL,NULL, "\n", -1);
   for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
      i2=order[i];
      if (a->entry[i2]) {
	 if (i2>2 && i2<8) {
	    gtk_text_insert(GTK_TEXT(text), NULL,NULL,NULL,
			    address_app_info.phoneLabels[a->phoneLabel[i2-3]],
			    -1);
	 } else {
	    gtk_text_insert(GTK_TEXT(text), NULL,NULL,NULL, address_app_info.labels[i2], -1);
	 }
	 gtk_text_insert(GTK_TEXT(text), NULL,NULL,NULL, ": ", -1);
	 gtk_text_insert(GTK_TEXT(text), NULL,NULL,NULL, a->entry[i2], -1);
	 gtk_text_insert(GTK_TEXT(text), NULL,NULL,NULL, "\n", -1);
      }
   }
   gtk_text_thaw(GTK_TEXT(text));

   cat = ma->attrib & 0x0F;
   sorted_position = find_sorted_cat(cat);
   if (address_cat_menu_item2[sorted_position]==NULL) {
      /* Illegal category, Assume that category 0 is Unfiled and valid*/
      jpilot_logf(LOG_DEBUG, "Category is not legal\n");
      cat = sorted_position = 0;
      sorted_position = find_sorted_cat(cat);
   }
   /* We need to count how many items down in the list this is */
   for (i=sorted_position, count=0; i>=0; i--) {
      if (address_cat_menu_item2[i]) {
	 count++;
      }
   }
   count--;

   if (sorted_position<0) {
      jpilot_logf(LOG_WARN, "Category is not legal\n");
   } else {
      if (address_cat_menu_item2[sorted_position]) {
	 gtk_check_menu_item_set_active
	   (GTK_CHECK_MENU_ITEM(address_cat_menu_item2[sorted_position]), TRUE);
      }
      gtk_option_menu_set_history(GTK_OPTION_MENU(category_menu2), count);
   }

   for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
      gtk_text_set_point(GTK_TEXT(address_text[i]), 0);
      gtk_text_forward_delete(GTK_TEXT(address_text[i]),
			      gtk_text_get_length(GTK_TEXT(address_text[i])));
      if (a->entry[i]) {
	 gtk_text_insert(GTK_TEXT(address_text[i]), NULL,NULL,NULL, a->entry[i], -1);
      }
   }
   for (i=0; i<NUM_PHONE_ENTRIES; i++) {
      if (GTK_IS_WIDGET(menu_item[i][a->phoneLabel[i]])) {
	 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
					(menu_item[i][a->phoneLabel[i]]), TRUE);
	 gtk_option_menu_set_history(GTK_OPTION_MENU(phone_list_menu[i]),
				     a->phoneLabel[i]);
      }
   }
   if ((a->showPhone > -1) && (a->showPhone < NUM_PHONE_ENTRIES)) {
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button[a->showPhone]),
				   TRUE);
   }
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private_checkbox),
				ma->attrib & dlpRecAttrSecret);

   connect_changed_signals(CONNECT_SIGNALS);
}

static void address_update_clist(GtkWidget *clist, GtkWidget *tooltip_widget,
				 AddressList *addr_list, int category, int main)
{
   int num_entries, entries_shown, i;
   int row_count;
   int show1, show2, show3;
   gchar *empty_line[] = { "","","" };
   GdkPixmap *pixmap_note;
   GdkBitmap *mask_note;
   GdkColor color;
   GdkColormap *colormap;
   AddressList *temp_al;
   char str[ADDRESS_MAX_CLIST_NAME+8];
   int by_company;
   int show_priv;

   row_count=((GtkCList *)clist)->rows;

   free_AddressList(&addr_list);
#ifdef JPILOT_DEBUG
    for (i=0;i<NUM_ADDRESS_CAT_ITEMS;i++) {
      jpilot_logf(LOG_DEBUG, "renamed:[%02d]:\n",address_app_info.category.renamed[i]);
      jpilot_logf(LOG_DEBUG, "category name:[%02d]:",i);
      print_string(address_app_info.category.name[i],16);
      jpilot_logf(LOG_DEBUG, "category ID:%d\n", address_app_info.category.ID[i]);
   }

   for (i=0;i<NUM_ADDRESS_LABELS;i++) {
      jpilot_logf(LOG_DEBUG, "labels[%02d]:",i);
      print_string(address_app_info.labels[i],16);
   }
   for (i=0;i<8;i++) {
      jpilot_logf(LOG_DEBUG, "phoneLabels[%d]:",i);
      print_string(address_app_info.phoneLabels[i],16);
   }
   jpilot_logf(LOG_DEBUG, "country %d\n",address_app_info.country);
   jpilot_logf(LOG_DEBUG, "sortByCompany %d\n",address_app_info.sortByCompany);
#endif

   num_entries = get_addresses2(&addr_list, SORT_ASCENDING, 2, 2, 1, CATEGORY_ALL);

   if (addr_list==NULL) {
      if (tooltip_widget) {
	 gtk_tooltips_set_tip(glob_tooltips, category_menu1, _("0 records"), NULL);
      }
      return;
   }

   /*Clear the text box to make things look nice */
   if (main) {
      gtk_text_set_point(GTK_TEXT(text), 0);
      gtk_text_forward_delete(GTK_TEXT(text),
			      gtk_text_get_length(GTK_TEXT(text)));
   }

   gtk_clist_freeze(GTK_CLIST(clist));

   by_company = address_app_info.sortByCompany;
   if (sort_override) {
      by_company=!(by_company & 1);
   }
   if (by_company) {
      show1=2; /*company */
      show2=0; /*last name */
      show3=1; /*first name */
   } else {
      show1=0; /*last name */
      show2=1; /*first name */
      show3=2; /*company */
   }

   entries_shown=0;

   show_priv = show_privates(GET_PRIVATES, NULL);
   for (temp_al = addr_list, i=0; temp_al; temp_al=temp_al->next) {
      if ( ((temp_al->ma.attrib & 0x0F) != address_category) &&
	  address_category != CATEGORY_ALL) {
	 continue;
      }
      /* Do masking like Palm OS 3.5 */
      if ((show_priv == MASK_PRIVATES) && 
	  (temp_al->ma.attrib & dlpRecAttrSecret)) {
	 if (entries_shown+1>row_count) {
	    gtk_clist_append(GTK_CLIST(clist), empty_line);
	 }
	 gtk_clist_set_text(GTK_CLIST(clist), entries_shown, ADDRESS_NAME_COLUMN, "---------------");
	 gtk_clist_set_text(GTK_CLIST(clist), entries_shown, ADDRESS_PHONE_COLUMN, "---------------");
	 clear_myaddress(&temp_al->ma);
	 gtk_clist_set_row_data(GTK_CLIST(clist), entries_shown, &(temp_al->ma));
	 gtk_clist_set_background(GTK_CLIST(clist), entries_shown, NULL);
	 entries_shown++;
	 continue;
      }
      /* End Masking */
      if ((show_priv != SHOW_PRIVATES) && 
	  (temp_al->ma.attrib & dlpRecAttrSecret)) {
	 continue;
      }

      str[0]='\0';
      if (temp_al->ma.a.entry[show1] || temp_al->ma.a.entry[show2]) {
	 if (temp_al->ma.a.entry[show1] && temp_al->ma.a.entry[show2]) {
	    g_snprintf(str, ADDRESS_MAX_CLIST_NAME, "%s, %s", temp_al->ma.a.entry[show1], temp_al->ma.a.entry[show2]);
	 }
	 if (temp_al->ma.a.entry[show1] && ! temp_al->ma.a.entry[show2]) {
	    strncpy(str, temp_al->ma.a.entry[show1], ADDRESS_MAX_CLIST_NAME);
	 }
	 if (! temp_al->ma.a.entry[show1] && temp_al->ma.a.entry[show2]) {
	    strncpy(str, temp_al->ma.a.entry[show2], ADDRESS_MAX_CLIST_NAME);
	 }
      } else if (temp_al->ma.a.entry[show3]) {
	 strncpy(str, temp_al->ma.a.entry[show3], ADDRESS_MAX_CLIST_NAME);
      } else {
	 strcpy(str, "-Unnamed-");
      }
      str[ADDRESS_MAX_CLIST_NAME]='\0';
      if (entries_shown+1>row_count) {
	 gtk_clist_append(GTK_CLIST(clist), empty_line);
      }
      gtk_clist_set_text(GTK_CLIST(clist), entries_shown, ADDRESS_NAME_COLUMN, str);
      gtk_clist_set_text(GTK_CLIST(clist), entries_shown, ADDRESS_PHONE_COLUMN, temp_al->ma.a.entry[temp_al->ma.a.showPhone+3]);
      gtk_clist_set_row_data(GTK_CLIST(clist), entries_shown, &(temp_al->ma));

      switch (temp_al->ma.rt) {
       case NEW_PC_REC:
	 colormap = gtk_widget_get_colormap(clist);
	 color.red=CLIST_NEW_RED;
	 color.green=CLIST_NEW_GREEN;
	 color.blue=CLIST_NEW_BLUE;
	 gdk_color_alloc(colormap, &color);
	 gtk_clist_set_background(GTK_CLIST(clist), entries_shown, &color);
	 break;
       case DELETED_PALM_REC:
	 colormap = gtk_widget_get_colormap(clist);
	 color.red=CLIST_DEL_RED;
	 color.green=CLIST_DEL_GREEN;
	 color.blue=CLIST_DEL_BLUE;
	 gdk_color_alloc(colormap, &color);
	 gtk_clist_set_background(GTK_CLIST(clist), entries_shown, &color);
	 break;
       case MODIFIED_PALM_REC:
	 colormap = gtk_widget_get_colormap(clist);
	 color.red=CLIST_MOD_RED;
	 color.green=CLIST_MOD_GREEN;
	 color.blue=CLIST_MOD_BLUE;
	 gdk_color_alloc(colormap, &color);
	 gtk_clist_set_background(GTK_CLIST(clist), entries_shown, &color);
	 break;
       default:
	 if (temp_al->ma.attrib & dlpRecAttrSecret) {
	    colormap = gtk_widget_get_colormap(clist);
	    color.red=CLIST_PRIVATE_RED;
	    color.green=CLIST_PRIVATE_GREEN;
	    color.blue=CLIST_PRIVATE_BLUE;
	    gdk_color_alloc(colormap, &color);
	    gtk_clist_set_background(GTK_CLIST(clist), entries_shown, &color);
	 } else {
	    gtk_clist_set_background(GTK_CLIST(clist), entries_shown, NULL);
	 }
      }

      if (temp_al->ma.a.entry[18]) {
	 /*Put a note pixmap up */
	 get_pixmaps(clist, PIXMAP_NOTE, &pixmap_note, &mask_note);
	 gtk_clist_set_pixmap(GTK_CLIST(clist), entries_shown, ADDRESS_NOTE_COLUMN, pixmap_note, mask_note);
      } else {
	 gtk_clist_set_text(GTK_CLIST(clist), entries_shown, ADDRESS_NOTE_COLUMN, "");
      }

      entries_shown++;
   }

   /* If there is an item in the list, select the first one */
   if ((main) && (entries_shown>0)) {
      gtk_clist_select_row(GTK_CLIST(clist), 0, ADDRESS_PHONE_COLUMN);
      cb_clist_selection(clist, 0, ADDRESS_PHONE_COLUMN, (GdkEventButton *)455, NULL);
   }

   for (i=row_count-1; i>=entries_shown; i--) {
      gtk_clist_remove(GTK_CLIST(clist), i);
   }

   gtk_clist_thaw(GTK_CLIST(clist));

   sprintf(str, _("%d of %d records"), entries_shown, num_entries);
   if (tooltip_widget) {
      gtk_tooltips_set_tip(glob_tooltips, category_menu1, str, NULL);
   }

   if (main) {
      set_new_button_to(CLEAR_FLAG);
   }
}

/*default set is which menu item is to be set on by default */
/*set is which set in the menu_item array to use */
static int make_phone_menu(int default_set, unsigned int callback_id, int set)
{
   int i;
   GSList *group;

   phone_list_menu[set] = gtk_option_menu_new();

   menu = gtk_menu_new();
   group = NULL;

   for (i=0; i<8; i++) {
      if (address_app_info.phoneLabels[i][0]) {
	 menu_item[set][i] = gtk_radio_menu_item_new_with_label(
			group, address_app_info.phoneLabels[i]);
	 gtk_signal_connect(GTK_OBJECT(menu_item[set][i]), "activate",
			    cb_phone_menu, GINT_TO_POINTER(callback_id + i));
	 group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menu_item[set][i]));
	 gtk_menu_append(GTK_MENU(menu), menu_item[set][i]);
	 gtk_widget_show(menu_item[set][i]);
      }
   }
   /*Set this one to active */
   if (GTK_IS_WIDGET(menu_item[set][default_set])) {
      gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
				     menu_item[set][default_set]), TRUE);
   }

   gtk_option_menu_set_menu(GTK_OPTION_MENU(phone_list_menu[set]), menu);
   /*Make this one show up by default */
   gtk_option_menu_set_history(GTK_OPTION_MENU(phone_list_menu[set]),
			       default_set);

   gtk_widget_show(phone_list_menu[set]);

   return 0;
}

/* returns 1 if found, 0 if not */
static int address_find()
{
   int r, found_at, total_count;

   r = 0;
   if (glob_find_id) {
      r = clist_find_id(clist,
			glob_find_id,
			&found_at,
			&total_count);
      if (r) {
	 /*avoid dividing by zero */
	 if (total_count == 0) {
	    total_count = 1;
	 }
	 gtk_clist_select_row(GTK_CLIST(clist), found_at, ADDRESS_PHONE_COLUMN);
	 cb_clist_selection(clist, found_at, ADDRESS_PHONE_COLUMN, (GdkEventButton *)455, NULL);
	 if (!gtk_clist_row_is_visible(GTK_CLIST(clist), found_at)) {
	    move_scrolled_window_hack(scrolled_window,
				      (float)found_at/(float)total_count);
	 }
      }
      glob_find_id = 0;
   }
   return r;
}

/* This redraws the clist and goes back to the same line number */
int address_clist_redraw()
{
   int line_num;

   line_num = clist_row_selected;

   address_update_clist(clist, category_menu1, glob_address_list,
			address_category, TRUE);

   gtk_clist_select_row(GTK_CLIST(clist), line_num, ADDRESS_NAME_COLUMN);
   cb_clist_selection(clist, line_num, ADDRESS_NAME_COLUMN, (GdkEventButton *)455, NULL);

   return 0;
}

int address_refresh()
{
   int index;

   if (glob_find_id) {
      address_category = CATEGORY_ALL;
   }
   if (address_category==CATEGORY_ALL) {
      index=0;
   } else {
      index=find_sorted_cat(address_category)+1;
   }
   address_update_clist(clist, category_menu1, glob_address_list,
			address_category, TRUE);
   if (index<0) {
      jpilot_logf(LOG_WARN, "Category not legal\n");
   } else {
      gtk_option_menu_set_history(GTK_OPTION_MENU(category_menu1), index);
      gtk_check_menu_item_set_active
	(GTK_CHECK_MENU_ITEM(address_cat_menu_item1[index]), TRUE);
   }
   address_find();
   return 0;
}


static gboolean
cb_key_pressed_quickfind(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
   int row_count;
   int select_row;
   int add;

   add=0;
   if ((event->keyval == GDK_KP_Down) || (event->keyval == GDK_Down)) {
      add=1;
   }
   if ((event->keyval == GDK_KP_Up) || (event->keyval == GDK_Up)) {
      add=-1;
   }
   if (!add) return FALSE;
   row_count=((GtkCList *)clist)->rows;
   if (!row_count) return FALSE;

   gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event"); 

   select_row=clist_row_selected+add;
   if (select_row>row_count-1) {
      select_row=0;
   }
   if (select_row<0) {
      select_row=row_count-1;
   }
   gtk_clist_select_row(GTK_CLIST(clist), select_row, ADDRESS_NAME_COLUMN);
   cb_clist_selection(clist, select_row, ADDRESS_PHONE_COLUMN, (GdkEventButton *)455, NULL);
   if (!gtk_clist_row_is_visible(GTK_CLIST(clist), select_row)) {
      move_scrolled_window_hack(scrolled_window,
				(float)select_row/(float)row_count);
   }
   return TRUE;
}

static gboolean
  cb_key_pressed(GtkWidget *widget, GdkEventKey *event,
		 gpointer next_widget) 
{
   /* This is needed because the text boxes aren't shown on the 
    * screen in the same order as the array.  I show them in the
    * same order that the palm does */
   int order[NUM_ADDRESS_ENTRIES]={
      0,1,13,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18
   };
   int page[NUM_ADDRESS_ENTRIES]={
      0,0,0,0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2
   };
   int i;

   if ((event->keyval == GDK_Tab) ||
       (event->keyval == GDK_ISO_Left_Tab)) {
      /* See if they are at the end of the text */
      if (gtk_text_get_point(GTK_TEXT(widget)) ==
	  gtk_text_get_length(GTK_TEXT(widget))) {
	 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event"); 
	 /* Find the next/prev widget */
	 for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
	    if (address_text[order[i]] == widget) {
	       break;
	    }
	 }
	 if (event->keyval == GDK_Tab)  i++;
	 if (event->keyval == GDK_ISO_Left_Tab)  i--;
	 if (i>=NUM_ADDRESS_ENTRIES)  i=0;
	 if (i<0)  i=NUM_ADDRESS_ENTRIES-1;
	 gtk_notebook_set_page(GTK_NOTEBOOK(notebook), page[i]);
	 gtk_widget_grab_focus(GTK_WIDGET(address_text[order[i]]));
	 return TRUE;
      }
   }
   return FALSE;
}

int address_gui_cleanup()
{
   int b;

   free_AddressList(&glob_address_list);
   b=dialog_save_changed_record(pane, record_changed);
   if (b==DIALOG_SAID_1) {
      cb_add_new_record(NULL, GINT_TO_POINTER(record_changed));
   }
   connect_changed_signals(DISCONNECT_SIGNALS);
   set_pref(PREF_ADDRESS_PANE, GTK_PANED(pane)->handle_xpos, NULL);
   return 0;
}

/*
 * Main function
 */
int address_gui(GtkWidget *vbox, GtkWidget *hbox)
{
   extern GtkWidget *glob_date_label;
   extern int glob_date_timer_tag;
   GtkWidget *pixmapwid;
   GdkPixmap *pixmap;
   GdkBitmap *mask;
   GtkWidget *vbox1, *vbox2;
   GtkWidget *hbox_temp;
   GtkWidget *vbox_temp1, *vbox_temp2, *vbox_temp3, *hbox_temp4;
   GtkWidget *separator;
   GtkWidget *label;
   GtkWidget *button;
   GtkWidget *frame;
   GtkWidget *table1, *table2, *table3;
   GtkWidget *notebook_tab;
   GSList *group;
   long ivalue, notebook_page;
   const char *svalue;
   char *titles[]={"","",""};

   int i, i1, i2;
   int order[NUM_ADDRESS_ENTRIES+1]={
      0,1,13,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,0
   };

   clist_row_selected=0;

   init();

   get_address_app_info(&address_app_info);

   for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
      sort_l[i].Pcat=address_app_info.category.name[i];
      sort_l[i].cat_num=i;
   }
   qsort(sort_l, NUM_ADDRESS_CAT_ITEMS, sizeof(struct sorted_cats), cat_compare);
#ifdef JPILOT_DEBUG
   for (i=0; i<NUM_ADDRESS_CAT_ITEMS; i++) {
      printf("cat %d [%s]\n", sort_l[i].cat_num, sort_l[i].Pcat);
   }
#endif

   if (address_app_info.category.name[address_category][0]=='\0') {
      address_category=CATEGORY_ALL;
   }

   pane = gtk_hpaned_new();
   get_pref(PREF_ADDRESS_PANE, &ivalue, &svalue);
   gtk_paned_set_position(GTK_PANED(pane), ivalue + 2);

   gtk_box_pack_start(GTK_BOX(hbox), pane, TRUE, TRUE, 5);

   vbox1 = gtk_vbox_new(FALSE, 0);
   vbox2 = gtk_vbox_new(FALSE, 0);
   hbox_temp = gtk_hbox_new(FALSE, 0);
   gtk_paned_pack1(GTK_PANED(pane), vbox1, TRUE, FALSE);
   gtk_paned_pack2(GTK_PANED(pane), vbox2, TRUE, FALSE);

   /* Separator */
   separator = gtk_hseparator_new();
   gtk_box_pack_start(GTK_BOX(vbox1), separator, FALSE, FALSE, 5);

   /* Make the Today is: label */
   glob_date_label = gtk_label_new(" ");
   gtk_box_pack_start(GTK_BOX(vbox1), glob_date_label, FALSE, FALSE, 0);
   timeout_date(NULL);
   glob_date_timer_tag = gtk_timeout_add(CLOCK_TICK, timeout_date, NULL);

   /* Separator */
   separator = gtk_hseparator_new();
   gtk_box_pack_start(GTK_BOX(vbox1), separator, FALSE, FALSE, 5);

   /* Put the category menu up */
   make_category_menu(&category_menu1, address_cat_menu_item1,
		      sort_l, cb_category, TRUE);
   gtk_box_pack_start(GTK_BOX(vbox1), category_menu1, FALSE, FALSE, 0);

   /* Put the address list window up */
   scrolled_window = gtk_scrolled_window_new(NULL, NULL);
   /*gtk_widget_set_usize(GTK_WIDGET(scrolled_window), 150, 0); */
   gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 0);
   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   gtk_box_pack_start(GTK_BOX(vbox1), scrolled_window, TRUE, TRUE, 0);

   clist = gtk_clist_new_with_titles(3, titles);
   clist_hack=FALSE;
   gtk_clist_column_title_passive(GTK_CLIST(clist), ADDRESS_PHONE_COLUMN);
   gtk_clist_column_title_passive(GTK_CLIST(clist), ADDRESS_NOTE_COLUMN);

   if (address_app_info.sortByCompany) {
      gtk_clist_set_column_title(GTK_CLIST(clist), ADDRESS_NAME_COLUMN, _("Company/Name"));
   } else {
      gtk_clist_set_column_title(GTK_CLIST(clist), ADDRESS_NAME_COLUMN, _("Name/Company"));
   }
   gtk_signal_connect(GTK_OBJECT(GTK_CLIST(clist)->column[ADDRESS_NAME_COLUMN].button),
		      "clicked", GTK_SIGNAL_FUNC(cb_resort), 
		      GINT_TO_POINTER(address_app_info.sortByCompany));
   gtk_clist_set_column_title(GTK_CLIST(clist), ADDRESS_PHONE_COLUMN, _("Phone"));
   /* Put pretty pictures in the clist column headings */
   get_pixmaps(vbox, PIXMAP_NOTE, &pixmap, &mask);
   pixmapwid = gtk_pixmap_new(pixmap, mask);
   hack_clist_set_column_title_pixmap(clist, ADDRESS_NOTE_COLUMN, pixmapwid);

   gtk_signal_connect(GTK_OBJECT(clist), "select_row",
		      GTK_SIGNAL_FUNC(cb_clist_selection),
		      text);
   gtk_clist_set_shadow_type(GTK_CLIST(clist), SHADOW);
   gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE);

   gtk_clist_set_column_auto_resize(GTK_CLIST(clist), ADDRESS_NAME_COLUMN, TRUE);
   gtk_clist_set_column_auto_resize(GTK_CLIST(clist), ADDRESS_NOTE_COLUMN, TRUE);
   gtk_clist_set_column_auto_resize(GTK_CLIST(clist), ADDRESS_PHONE_COLUMN, FALSE);

   gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(clist));
   /*gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_RIGHT); */

   hbox_temp = gtk_hbox_new(FALSE, 0);
   gtk_box_pack_start(GTK_BOX(vbox1), hbox_temp, FALSE, FALSE, 0);

   label = gtk_label_new(_("Quick Find"));
   gtk_box_pack_start(GTK_BOX(hbox_temp), label, FALSE, FALSE, 0);

   address_quickfind_entry = gtk_entry_new();
   gtk_signal_connect(GTK_OBJECT(address_quickfind_entry), "key_press_event",
		      GTK_SIGNAL_FUNC(cb_key_pressed_quickfind), NULL);
   gtk_signal_connect(GTK_OBJECT(address_quickfind_entry), "changed",
		      GTK_SIGNAL_FUNC(cb_address_quickfind),
		      NULL);
   gtk_box_pack_start(GTK_BOX(hbox_temp), address_quickfind_entry, TRUE, TRUE, 0);
   gtk_widget_grab_focus(GTK_WIDGET(address_quickfind_entry));

   /* The new entry gui */
   hbox_temp = gtk_hbox_new(FALSE, 0);
   gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);

   /* Delete Button */
   button = gtk_button_new_with_label(_("Delete"));
   gtk_signal_connect(GTK_OBJECT(button), "clicked",
		      GTK_SIGNAL_FUNC(cb_delete_address),
		      GINT_TO_POINTER(DELETE_FLAG));
   gtk_box_pack_start(GTK_BOX(hbox_temp), button, TRUE, TRUE, 0);

   /* Create "Copy" button */
   button = gtk_button_new_with_label(_("Copy"));
   gtk_signal_connect(GTK_OBJECT(button), "clicked",
		      GTK_SIGNAL_FUNC(cb_add_new_record),
		      GINT_TO_POINTER(COPY_FLAG));
   gtk_box_pack_start(GTK_BOX(hbox_temp), button, TRUE, TRUE, 0);

   /* Create "New" button */
   new_record_button = gtk_button_new_with_label(_("New Record"));
   gtk_signal_connect(GTK_OBJECT(new_record_button), "clicked",
		      GTK_SIGNAL_FUNC(cb_address_clear), NULL);
   gtk_box_pack_start(GTK_BOX(hbox_temp), new_record_button, TRUE, TRUE, 0);

   /* Create "Add Record" button */
   add_record_button = gtk_button_new_with_label(_("Add Record"));
   gtk_signal_connect(GTK_OBJECT(add_record_button), "clicked",
		      GTK_SIGNAL_FUNC(cb_add_new_record),
		      GINT_TO_POINTER(NEW_FLAG));
   gtk_box_pack_start(GTK_BOX(hbox_temp), add_record_button, TRUE, TRUE, 0);
   gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(GTK_BIN(add_record_button)->child)),
		       "label_high");

   /* Create "apply changes" button */
   apply_record_button = gtk_button_new_with_label(_("Apply Changes"));
   gtk_signal_connect(GTK_OBJECT(apply_record_button), "clicked",
		      GTK_SIGNAL_FUNC(cb_add_new_record),
		      GINT_TO_POINTER(MODIFY_FLAG));
   gtk_box_pack_start(GTK_BOX(hbox_temp), apply_record_button, TRUE, TRUE, 0);
   gtk_widget_set_name(GTK_WIDGET(GTK_LABEL(GTK_BIN(apply_record_button)->child)),
		       "label_high");

   /*Separator */
   separator = gtk_hseparator_new();
   gtk_box_pack_start(GTK_BOX(vbox2), separator, FALSE, FALSE, 5);


   /*Private check box */
   hbox_temp = gtk_hbox_new(FALSE, 0);
   gtk_box_pack_start(GTK_BOX(vbox2), hbox_temp, FALSE, FALSE, 0);
   private_checkbox = gtk_check_button_new_with_label(_("Private"));
   gtk_box_pack_end(GTK_BOX(hbox_temp), private_checkbox, FALSE, FALSE, 0);

   /*Add the new category menu */
   make_category_menu(&category_menu2, address_cat_menu_item2,
		      sort_l, NULL, FALSE);

   gtk_box_pack_start(GTK_BOX(hbox_temp), category_menu2, TRUE, TRUE, 0);


   /*Add the notebook for new entries */
   notebook = gtk_notebook_new();
   gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
   gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
   gtk_signal_connect(GTK_OBJECT(notebook), "switch-page",
		      GTK_SIGNAL_FUNC(cb_notebook_changed), NULL);

   gtk_box_pack_start(GTK_BOX(vbox2), notebook, TRUE, TRUE, 0);

   /*Page 1 */
   notebook_tab = gtk_label_new(_("Name"));
   vbox_temp1 = gtk_vbox_new(FALSE, 0);
   gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_temp1, notebook_tab);
   /* Notebook tabs have to be shown before the show_all */
   gtk_widget_show(vbox_temp1);
   gtk_widget_show(notebook_tab);

   /*Page 2 */
   notebook_tab = gtk_label_new(_("Address"));
   vbox_temp2 = gtk_vbox_new(FALSE, 0);
   gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_temp2, notebook_tab);
   /* Notebook tabs have to be shown before the show_all */
   gtk_widget_show(vbox_temp2);
   gtk_widget_show(notebook_tab);

   /*Page 3 */
   notebook_tab = gtk_label_new(_("Other"));
   vbox_temp3 = gtk_vbox_new(FALSE, 0);
   gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_temp3, notebook_tab);
   /* Notebook tabs have to be shown before the show_all */
   gtk_widget_show(vbox_temp3);
   gtk_widget_show(notebook_tab);

   /*Page 3 */
   notebook_tab = gtk_label_new(_("All"));
   hbox_temp4 = gtk_vbox_new(FALSE, 0);
   gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox_temp4, notebook_tab);
   /* Notebook tabs have to be shown before the show_all */
   gtk_widget_show(hbox_temp4);
   gtk_widget_show(notebook_tab);

   /*Put a table on every page */
   table1 = gtk_table_new(9, 3, FALSE);
   gtk_box_pack_start(GTK_BOX(vbox_temp1), table1, TRUE, TRUE, 0);
   table2 = gtk_table_new(9, 2, FALSE);
   gtk_box_pack_start(GTK_BOX(vbox_temp2), table2, TRUE, TRUE, 0);
   table3 = gtk_table_new(9, 2, FALSE);
   gtk_box_pack_start(GTK_BOX(vbox_temp3), table3, TRUE, TRUE, 0);

   label = NULL;
   for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
      i2=order[i];
      if (i2>2 && i2<8) {
	 make_phone_menu(i2-3, (i2-3)<<4, i2-3);
      } else {
 	 label = gtk_label_new(address_app_info.labels[i2]);
	 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
      }
      address_text[i2] = gtk_text_new(NULL, NULL);

      gtk_text_set_editable(GTK_TEXT(address_text[i2]), TRUE);
      gtk_text_set_word_wrap(GTK_TEXT(address_text[i2]), TRUE);
      gtk_widget_set_usize(GTK_WIDGET(address_text[i2]), 0, 25);
      /*gtk_box_pack_start(GTK_BOX(hbox_temp), address_text[i2], TRUE, TRUE, 0); */
      /*hbox_temp = gtk_hbox_new(FALSE, 0); */
      if (i<9) {
	 if (i2>2 && i2<8) {
	    gtk_table_attach_defaults(GTK_TABLE(table1), GTK_WIDGET(phone_list_menu[i2-3]),
				      1, 2, i, i+1);
	 } else {
	    gtk_table_attach_defaults(GTK_TABLE(table1), GTK_WIDGET(label),
				      1, 2, i, i+1);
	 }
	 gtk_table_attach_defaults(GTK_TABLE(table1), GTK_WIDGET(address_text[i2]),
				   2, 3, i, i+1);
	 /*gtk_box_pack_start(GTK_BOX(vbox_temp1), hbox_temp, TRUE, TRUE, 0); */
      }
      if (i>8 && i<14) {
	 gtk_table_attach_defaults(GTK_TABLE(table2), GTK_WIDGET(label),
				   0, 1, i-9, i-8);
	 gtk_table_attach_defaults(GTK_TABLE(table2), GTK_WIDGET(address_text[i2]),
				   1, 2, i-9, i-8);
	 /*gtk_box_pack_start(GTK_BOX(vbox_temp2), hbox_temp, TRUE, TRUE, 0); */
      }
      if (i>13 && i<100) {
	 gtk_table_attach_defaults(GTK_TABLE(table3), GTK_WIDGET(label),
				   0, 1, i-14, i-13);
	 gtk_table_attach_defaults(GTK_TABLE(table3), GTK_WIDGET(address_text[i2]),
				   1, 2, i-14, i-13);
      }
   }
   /* Capture the TAB key to change focus with it */
   for (i=0; i<NUM_ADDRESS_ENTRIES; i++) {
      i1=order[i];
      i2=order[i+1];
      if (i2<NUM_ADDRESS_ENTRIES) {
	 gtk_signal_connect(GTK_OBJECT(address_text[i1]), "key_press_event",
			    GTK_SIGNAL_FUNC(cb_key_pressed), address_text[i2]);
      }
   }

   /* Put some radio buttons for selecting which number to display in view */
   group = NULL;
   for (i=0; i<NUM_PHONE_ENTRIES; i++) {
      radio_button[i] = gtk_radio_button_new_with_label(group, _("Show\nIn List"));
      group = gtk_radio_button_group(GTK_RADIO_BUTTON(radio_button[i]));
      gtk_widget_set_usize(GTK_WIDGET(radio_button[i]), 5, 0);
      gtk_table_attach_defaults(GTK_TABLE(table1), GTK_WIDGET(radio_button[i]),
				0, 1, i+4, i+5);
   }

   /*The Quickview page */
   frame = gtk_frame_new(_("Quick View"));
   gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.0);
   gtk_box_pack_start(GTK_BOX(hbox_temp4), frame, TRUE, TRUE, 0);
   /*The text box on the right side */
   hbox_temp = gtk_hbox_new (FALSE, 0);
   gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
   gtk_container_add(GTK_CONTAINER(frame), hbox_temp);

   text = gtk_text_new(NULL, NULL);
   gtk_text_set_editable(GTK_TEXT(text), FALSE);
   gtk_text_set_word_wrap(GTK_TEXT(text), TRUE);
   vscrollbar = gtk_vscrollbar_new(GTK_TEXT(text)->vadj);
   gtk_box_pack_start(GTK_BOX(hbox_temp), text, TRUE, TRUE, 0);
   gtk_box_pack_start(GTK_BOX(hbox_temp), vscrollbar, FALSE, FALSE, 0);


   gtk_widget_show_all(vbox);
   gtk_widget_show_all(hbox);

   gtk_widget_hide(add_record_button);
   gtk_widget_hide(apply_record_button);

   get_pref(PREF_ADDRESS_NOTEBOOK_PAGE, &notebook_page, NULL);

   if ((notebook_page<4) && (notebook_page>-1)) {
      gtk_notebook_set_page(GTK_NOTEBOOK(notebook), notebook_page);
   }

   address_refresh();

   return 0;
}
jpilot-0.99.2/alarms.c0100644000175000001440000010650207421115314013611 0ustar  mouseusers/* alarms.c
 * A module of J-Pilot http://jpilot.org
 * 
 * Copyright (C) 2000-2001 by Judd Montgomery
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * The PalmOS datebook will alarm on private records even when they are hidden
 * and they show up on the screen.  Right, or wrong, who knows.
 * I will do the same.
 */
#include "config.h"
#include <gtk/gtk.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <pi-datebook.h>

#include "utils.h"
#include "log.h"
#include "prefs.h"
#include "i18n.h"
#include "datebook.h"

/* This is how often to check for alarms in seconds */
/* Every call takes CPU time(not much), so you may want it to be greater */
#define ALARM_INTERVAL 1


#define PREV_ALARM_MASK 1
#define NEXT_ALARM_MASK 2

/* #define ALARMS_DEBUG */


/*
 * Throughout the code, event_time is the time of the event
 * alarm_time is the event_time - advance
 * remind_time is the time a window is to be popped up (it may be postponed)
 */

typedef enum {
   ALARM_NONE = 0,
   ALARM_NEW,
   ALARM_MISSED,
   ALARM_POSTPONED
} AlarmType;

struct jp_alarms {
   unsigned int unique_id;
   AlarmType type;
   time_t event_time;
   time_t alarm_advance;
   struct jp_alarms *next;
};

struct alarm_dialog_data {
   unsigned int unique_id;
   time_t remind_time;
   GtkWidget *remind_entry;
   GtkWidget *radio1;
   GtkWidget *radio2;
   int button_hit;
};

static struct jp_alarms *alarm_list=NULL;
static struct jp_alarms *Plast_alarm_list=NULL;
static struct jp_alarms *next_alarm=NULL;

static int glob_skip_all_alarms;
static int total_alarm_windows;

void alarms_add_to_list(unsigned int unique_id,
			AlarmType type,
			time_t alarm_time,
			time_t alarm_advance);
int alarms_find_next(struct tm *date1, struct tm *date2, int soonest_only);

/*
 * Alarm GUI
 */
/*
 * Start of Dialog window code
 */
static void cb_dialog_button(GtkWidget *widget,
			       gpointer   data)
{
   struct alarm_dialog_data *Pdata;
   GtkWidget *w;
   int i;

   for (w=widget, i=10; w && (i>0); w=w->parent, i--) {
      if (GTK_IS_WINDOW(w)) {
	 Pdata = gtk_object_get_data(GTK_OBJECT(w), "alarm");
	 if (Pdata) {
	    Pdata->button_hit = GPOINTER_TO_INT(data);
	 }
	 gtk_widget_destroy(GTK_WIDGET(w));
      }
   }
}

static gboolean cb_destroy_dialog(GtkWidget *widget)
{
   struct alarm_dialog_data *Pdata;
   char *entry;
   time_t ltime;
   time_t advance;
   time_t remind;

   total_alarm_windows--;
#ifdef ALARMS_DEBUG
   printf("total_alarm_windows=%d\n",total_alarm_windows);
#endif
   Pdata = gtk_object_get_data(GTK_OBJECT(widget), "alarm");
   if (!Pdata) {
      return TRUE;
   }
   entry = gtk_entry_get_text(GTK_ENTRY(Pdata->remind_entry));
   if (Pdata->button_hit==DIALOG_SAID_2) {
      remind = atoi(entry);
      jpilot_logf(LOG_DEBUG, "remind entry = [%s]\n", entry);
      set_pref(PREF_REMIND_IN, 0, entry);
      if (GTK_TOGGLE_BUTTON(Pdata->radio1)->active) {
	 set_pref(PREF_REMIND_UNITS, 0, NULL);
	 remind *= 60;
      } else {
	 set_pref(PREF_REMIND_UNITS, 1, NULL);
	 remind *= 3600;
      }
      time(&ltime);
      localtime(&ltime);
      advance = -(ltime + remind - Pdata->remind_time);
      alarms_add_to_list(Pdata->unique_id,
			 ALARM_POSTPONED,
			 Pdata->remind_time,
			 advance);
   }
   free(Pdata);

   return TRUE;
}

int dialog_alarm(char *title, char *frame_text,
		 char *time_str, char *desc_str, char *note_str,
		 unsigned int unique_id,
		 time_t remind_time)
{
   GSList *group;
   GtkWidget *button, *label;
   GtkWidget *hbox1, *vbox1;
   GtkWidget *vbox_temp;
   GtkWidget *frame;
   GtkWidget *alarm_dialog;
   GtkWidget *remind_entry;
   GtkWidget *radio1;
   GtkWidget *radio2;
   struct alarm_dialog_data *Pdata;
   long pref_units;
   const char *pref_entry;

   /* Prevent alarms from going crazy and using all resources */
   if (total_alarm_windows>20) {
      return -1;
   }
   total_alarm_windows++;
#ifdef ALARMS_DEBUG
   printf("total_alarm_windows=%d\n",total_alarm_windows);
#endif
   alarm_dialog = gtk_widget_new(GTK_TYPE_WINDOW,
				 "type", GTK_WINDOW_TOPLEVEL,
				 "title", title,
				 NULL);

   gtk_signal_connect(GTK_OBJECT(alarm_dialog), "destroy",
                      GTK_SIGNAL_FUNC(cb_destroy_dialog), alarm_dialog);


   frame = gtk_frame_new(frame_text);
   gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.0);
   vbox1 = gtk_vbox_new(FALSE, 2);
   hbox1 = gtk_hbox_new(TRUE, 2);

   gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
   gtk_container_set_border_width(GTK_CONTAINER(vbox1), 5);
   gtk_container_set_border_width(GTK_CONTAINER(hbox1), 5);

   gtk_container_add(GTK_CONTAINER(alarm_dialog), frame);
   gtk_container_add(GTK_CONTAINER(frame), vbox1);

   /* Label */
   label = gtk_label_new(time_str);
   gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
   gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
   gtk_box_pack_start(GTK_BOX(vbox1), label, FALSE, FALSE, 2);

   /* Label */
   label = gtk_label_new(desc_str);
   gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
   gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
   gtk_box_pack_start(GTK_BOX(vbox1), label, FALSE, FALSE, 2);

   /* Label */
   label = gtk_label_new(note_str);
   gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
   gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
   gtk_box_pack_start(GTK_BOX(vbox1), label, FALSE, FALSE, 2);

   /* Buttons */
   gtk_box_pack_start(GTK_BOX(vbox1), hbox1, TRUE, TRUE, 2);

   button = gtk_button_new_with_label(_("OK"));
   gtk_signal_connect(GTK_OBJECT(button), "clicked",
		      GTK_SIGNAL_FUNC(cb_dialog_button),
		      GINT_TO_POINTER(DIALOG_SAID_1));
   gtk_box_pack_start(GTK_BOX(hbox1), button, TRUE, TRUE, 1);

   button = gtk_button_new_with_label(_("Remind me"));
   gtk_signal_connect(GTK_OBJECT(button), "clicked",
		      GTK_SIGNAL_FUNC(cb_dialog_button),
		      GINT_TO_POINTER(DIALOG_SAID_2));
   gtk_box_pack_start(GTK_BOX(hbox1), button, TRUE, TRUE, 1);

   remind_entry = gtk_entry_new_with_max_length(4);
   gtk_box_pack_start(GTK_BOX(hbox1), remind_entry, TRUE, TRUE, 1);

   group = NULL;
   radio1 = gtk_radio_button_new_with_label(NULL, _("Minutes"));
   group = gtk_radio_button_group(GTK_RADIO_BUTTON(radio1));
   radio2 = gtk_radio_button_new_with_label(group, _("Hours"));
   group = gtk_radio_button_group(GTK_RADIO_BUTTON(radio2));

   gtk_widget_set_usize(GTK_WIDGET(remind_entry), 5, 0);
   gtk_widget_set_usize(GTK_WIDGET(radio1), 5, 0);
   gtk_widget_set_usize(GTK_WIDGET(radio2), 5, 0);

   Pdata = malloc(sizeof(struct alarm_dialog_data));
   if (Pdata) {
      Pdata->unique_id = unique_id;
      Pdata->remind_time = remind_time;
      /* Set the default button pressed to OK */
      Pdata->button_hit = DIALOG_SAID_1;     
      Pdata->remind_entry=remind_entry;
      Pdata->radio1=radio1;
      Pdata->radio2=radio2;
   }
   gtk_object_set_data(GTK_OBJECT(alarm_dialog), "alarm", Pdata);                    

   get_pref(PREF_REMIND_IN, NULL, &pref_entry);
   gtk_entry_set_text(GTK_ENTRY(remind_entry), pref_entry);

   get_pref(PREF_REMIND_UNITS, &pref_units, NULL);
   if (pref_units) {
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio2), TRUE);
   } else {
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio1), TRUE);
   }

   vbox_temp = gtk_vbox_new(FALSE, 0);
   gtk_box_pack_start(GTK_BOX(hbox1), vbox_temp, TRUE, TRUE, 1);
   gtk_box_pack_start(GTK_BOX(vbox_temp), radio1, TRUE, TRUE, 1);
   gtk_box_pack_start(GTK_BOX(vbox_temp), radio2, TRUE, TRUE, 1);

   gtk_widget_show_all(alarm_dialog);

   return 0;
}
/*
 * End Alarm GUI
 */

#ifdef ALARMS_DEBUG
const char *print_date(const time_t t1)
{
   struct tm *Pnow;
   static char str[100];

   Pnow = localtime(&t1);
   strftime(str, 80, "%B %d, %Y %H:%M:%S", Pnow);
   return str;
}
const char *print_type(AlarmType type)
{
   switch (type) {
    case ALARM_NONE:
      return "ALARM_NONE";
    case ALARM_NEW:
      return "ALARM_NEW";
    case ALARM_MISSED:
      return "ALARM_MISSED";
    case ALARM_POSTPONED:
      return "ALARM_POSTPONED";
    default:
      return "? ALARM_UNKNOWN";
   }
}
#else
inline const char *print_date(const time_t t1)
{
   return "";
}
inline const char *print_type(AlarmType type)
{
   return "";
}
#endif


void alarms_add_to_list(unsigned int unique_id,
			AlarmType type,
			time_t event_time,
			time_t alarm_advance)
{
   struct jp_alarms *temp_alarm;

#ifdef ALARMS_DEBUG
   printf("alarms_add_to_list()\n");
#endif

   temp_alarm = malloc(sizeof(struct jp_alarms));
   if (!temp_alarm) {
      jpilot_logf(LOG_WARN, "alarms_add_to_list: Out of memory\n");
      return;
   }
   temp_alarm->unique_id = unique_id;
   temp_alarm->type = type;
   temp_alarm->event_time = event_time;
   temp_alarm->alarm_advance = alarm_advance;
   temp_alarm->next = NULL;
   if (Plast_alarm_list) {
      Plast_alarm_list->next=temp_alarm;
      Plast_alarm_list=temp_alarm;
   } else {
      alarm_list=Plast_alarm_list=temp_alarm;
   }
   Plast_alarm_list=temp_alarm;
}

void alarms_remove_from_to_list(unsigned int unique_id)
{
   struct jp_alarms *temp_alarm, *prev_alarm, *next_alarm;

#ifdef ALARMS_DEBUG
   printf("remove from list(%d)\n", unique_id);
#endif
   for(prev_alarm=NULL, temp_alarm=alarm_list;
       temp_alarm;
       temp_alarm=next_alarm) {
      if (temp_alarm->unique_id==unique_id) {
	 /* Tail of list? */
	 if (temp_alarm->next==NULL) {
	    Plast_alarm_list=prev_alarm;
	 }
	 /* Last of list? */
	 if (Plast_alarm_list==alarm_list) {
	    Plast_alarm_list=alarm_list=NULL;
	 }
	 if (prev_alarm) {
	    prev_alarm->next=temp_alarm->next;
	 } else {
	    /* Head of list */
	    alarm_list=temp_alarm->next;
	 }
	 free(temp_alarm);
	 return;
      } else {
	 prev_alarm=temp_alarm;
	 next_alarm=temp_alarm->next;
      }
   }
}

void free_alarms_list(int mask)
{
   struct jp_alarms *ta, *ta_next;

   if (mask&PREV_ALARM_MASK) {
      for (ta=alarm_list; ta; ta=ta_next) {
	 ta_next=ta->next;
	 free(ta);
      }
      Plast_alarm_list=alarm_list=NULL;
   }

   if (mask&NEXT_ALARM_MASK) {
      for (ta=next_alarm; ta; ta=ta_next) {
	 ta_next=ta->next;
	 free(ta);
      }
      next_alarm=NULL;
   }
}

void alarms_write_file(void)
{
   FILE *out;
   char line[256];
   int fail, n;
   time_t ltime;
   struct tm *now;

   jpilot_logf(LOG_DEBUG, "alarms_write_file()\n");

   time(&ltime);
   now = localtime(&ltime);

   out=jp_open_home_file("jpilot.alarms.tmp", "w");
   if (!out) {
      jpilot_logf(LOG_WARN, "Could not open jpilot.alarms.tmp file\n");
      return;
   }
   fail=0;
   strncpy(line, 
	   "# This file was generated by jpilot, changes will be lost\n",
	   255);
   n = fwrite(line, strlen(line), 1, out);
   if (n<1) fail=1;

   strncpy(line, 
	   "# This is the last time that jpilot was ran\n",
	   255);
   n = fwrite(line, strlen(line), 1, out);
   if (n<1) fail=1;

   sprintf(line, "UPTODATE %d %d %d %d %d\n",
	   now->tm_year+1900,
	   now->tm_mon+1,
	   now->tm_mday,
	   now->tm_hour,
	   now->tm_min
	   );
   n = fwrite(line, strlen(line), 1, out);
   if (n<1) fail=1;

   fclose(out);

   if (fail) {
      unlink_file("jpilot.alarms.tmp");
   } else {
      rename_file("jpilot.alarms.tmp", "jpilot.alarms");
   }
}

/* This attempts to make the command safe.
 * I'm sure I'm missing things.
 */
void make_command_safe(char *command)
{
   int i, len;
   char c;

   len = strlen(command);
   for (i=0; i<len; i++) {
      c=command[i];
      if (strchr("\r\n|&;()<>", c)) {
	 command[i]=' ';
      }
   }
}

/*
 * Pop up window.
 * Do alarm setting (play sound, or whatever).
 * if user postpones then put in postponed alarm list.
 */
int alarms_do_one(struct Appointment *a,
		  unsigned long unique_id,
		  time_t t_alarm,
		  AlarmType type)
{
   struct tm *Pnow;
   char time_str[255];
   char desc_str[255];
   char note_str[255];
   char pref_time[50];
   char time1_str[50];
   char time2_str[50];
   char date_str[50];
   char command[1024];
   char *reason;
   long ivalue;
   long wants_windows;
   long do_command;
   const char *pref_date;
   const char *pref_command;
   char c1, c2;
   int i, len;

   alarms_write_file();

   switch (type) {
    case ALARM_NONE:
      return 0;
    case ALARM_NEW:
      reason=_("Appointment Reminder");
      break;
    case ALARM_MISSED:
      reason=_("Past Appointment");
      break;
    case ALARM_POSTPONED:
      reason=_("Postponed Appointment");
      break;
    default:
      reason=_("Appointment");
   }
   get_pref(PREF_SHORTDATE, &ivalue, &pref_date);
   get_pref_time_no_secs(pref_time);

   Pnow = localtime(&t_alarm);

   strftime(date_str, 50, pref_date, Pnow);
   strftime(time1_str, 50, pref_time, Pnow);
   strftime(time2_str, 50, pref_time, &(a->end));
   sprintf(time_str, "%s %s-%s\n", date_str, time1_str, time2_str);
   desc_str[0]='\0';
   note_str[0]='\0';
   if (a->description) {
      strncpy(desc_str, a->description, 200);
      desc_str[200]='\0';
   }
   if (a->note) {
      strncpy(note_str, a->note, 200);
      note_str[200]='\0';
   }

   get_pref(PREF_ALARM_COMMAND, &ivalue, &pref_command);
   get_pref(PREF_DO_ALARM_COMMAND, &do_command, NULL);
#ifdef ALARMS_DEBUG
   printf("pref_command = [%s]\n", pref_command);
#endif
   bzero(command, 1024);
   if (do_command) {
      command[0]='\0';
      for (i=0; i<MAX_PREF_VALUE-1; i++) {
	 c1 = pref_command[i];
	 c2 = pref_command[i+1];
	 len = strlen(command);
	 /* expand '%t' */
	 if (c1=='%') {
	    if (c2=='t') {
	       i++;
	       strncat(command, time1_str, 1022-len);
	       continue;
	    }
	    /* expand '%d' */
	    if (c2=='d') {
	       i++;
	       strncat(command, date_str, 1022-len);
	       continue;
	    }
#ifdef ALARM_SHELL_DESC_NOTE
	    /* expand '%D' */
	    if (c2=='D') {
	       i++;
	       strncat(command, desc_str, 1022-len);
	       continue;
	    }
	    if (c2=='N') {
	       i++;
	       strncat(command, note_str, 1022-len);
	       continue;
	    }
#endif
	 }
	 if (len<1020) {
	    command[len++]=c1;
	    command[len]='\0';
	 }
	 if (c1=='\0') {
	    break;
	 }
      }
      command[1022]='\0';

      make_command_safe(command);
      jpilot_logf(LOG_STDOUT|LOG_FILE, "executing command = [%s]\n", command);
      system(command);
   }

   get_pref(PREF_OPEN_ALARM_WINDOWS, &wants_windows, NULL);

   if (wants_windows) {
      return dialog_alarm("J-Pilot Alarm", reason,
			  time_str, desc_str, note_str,
			  unique_id,
			  t_alarm);
   }
   return 0;
}


/*
 * See if next_alarm is due in less than ALARM_INTERVAL/2 secs.
 * If it is, then do_alarm and find_next_alarm.
 */
gint cb_timer_alarms(gpointer data)
{
   struct jp_alarms *temp_alarm, *ta_next;
   AppointmentList *a_list;
   AppointmentList *temp_al;
   static int first=0;
   time_t t, diff;
   time_t t_alarm_time;
   struct tm *Ptm;
   struct tm copy_tm;

   a_list=NULL;

   if (!first) {
      alarms_write_file();
      first=1;
   }

   time(&t);

   for (temp_alarm=alarm_list; temp_alarm; temp_alarm=ta_next) {
      ta_next=temp_alarm->next;
      diff = temp_alarm->event_time - t - temp_alarm->alarm_advance;
      if (temp_alarm->type!=ALARM_MISSED) {
	 if (diff >= ALARM_INTERVAL/2) {
	    continue;
	 }
      }
      if (a_list==NULL) {
	 get_days_appointments2(&a_list, NULL, 0, 0, 1);
      }
#ifdef ALARMS_DEBUG
      printf("unique_id=%d\n", temp_alarm->unique_id);
      printf("type=%s\n", print_type(temp_alarm->type));
      printf("event_time=%s\n", print_date(temp_alarm->event_time));
      printf("alarm_advance=%ld\n", temp_alarm->alarm_advance);
#endif
      for (temp_al = a_list; temp_al; temp_al=temp_al->next) {
	 if (temp_al->ma.unique_id == temp_alarm->unique_id) {
#ifdef ALARMS_DEBUG
	    printf("%s\n", temp_al->ma.a.description);
#endif
	    alarms_do_one(&(temp_al->ma.a),
			  temp_alarm->unique_id,
			  temp_alarm->event_time,
			  ALARM_MISSED);
	    break;
	 }
      }
      /* Be careful, this modifies the list we are parsing and
       removes the current node */
      alarms_remove_from_to_list(temp_al->ma.unique_id);
   }

   if (next_alarm) {
      diff = next_alarm->event_time - t - next_alarm->alarm_advance;
      if (diff <= ALARM_INTERVAL/2) {
	 if (a_list==NULL) {
	    get_days_appointments2(&a_list, NULL, 0, 0, 1);
	 }
	 for (temp_alarm=next_alarm; temp_alarm; temp_alarm=ta_next) {
	    for (temp_al = a_list; temp_al; temp_al=temp_al->next) {
	       if (temp_al->ma.unique_id == temp_alarm->unique_id) {
#ifdef ALARMS_DEBUG
		  printf("** next unique_id=%d\n", temp_alarm->unique_id);
		  printf("** next type=%s\n", print_type(temp_alarm->type));
		  printf("** next event_time=%s\n", print_date(temp_alarm->event_time));
		  printf("** next alarm_advance=%ld\n", temp_alarm->alarm_advance);
		  printf("** next %s\n", temp_al->ma.a.description);
#endif		  
		  alarms_do_one(&(temp_al->ma.a),
				temp_alarm->unique_id,
				temp_alarm->event_time,
				ALARM_NEW);
		  break;
	       }
	    }
	    /* This may not be exactly right */
	    t_alarm_time = temp_alarm->event_time + 1;
#ifdef ALARMS_DEBUG
	    printf("** t_alarm_time-->%s\n", print_date(t_alarm_time));
#endif
	    ta_next=temp_alarm->next;
	    free(temp_alarm);
	    next_alarm = ta_next;
	 }
	 Ptm = localtime(&t_alarm_time);
	 memcpy(&copy_tm, Ptm, sizeof(struct tm));
	 alarms_find_next(&copy_tm, &copy_tm, TRUE);
      }
   }
   if (a_list) {
      free_AppointmentList(&a_list);
   }

   return TRUE;
}

static int find_prev_next(struct Appointment *a,
			  int adv,
			  struct tm *date1,
			  struct tm *date2,
			  struct tm *tm_prev,
			  struct tm *tm_next,
			  int *prev_found,
			  int *next_found)
{
   struct tm t;
   struct tm *Pnow;
   time_t t_temp;
   time_t t1, t2;
   time_t t_begin, t_end;
   time_t t_alarm;
   time_t t_past;
   time_t t_future;
   time_t t_interval;
   int forward, backward;
   int offset;
   int freq;
   int found;
   int count;
   int dow;
   int i;
   int safety_counter;
   int fdom, ndim;
   long fdow;
   int days, begin_days;

#ifdef ALARMS_DEBUG
   printf("fpn: entered find_previous_next\n");
#endif
   *prev_found=*next_found=0;
   forward=backward=1;

   t1=mktime(date1);
   t2=mktime(date2);

   bzero(tm_prev, sizeof(struct tm));
   bzero(tm_next, sizeof(struct tm));

   bzero(&t, sizeof(struct tm));
   t.tm_year=a->begin.tm_year;
   t.tm_mon=a->begin.tm_mon;
   t.tm_mday=a->begin.tm_mday;
   t.tm_hour=a->begin.tm_hour;
   t.tm_min=a->begin.tm_min;
   t.tm_isdst=-1;

   freq = 0;
   switch (a->repeatType) {
    case repeatNone:
#ifdef ALARMS_DEBUG
      printf("fpn: repeatNone\n");
#endif
      t_alarm=mktime(&(a->begin)) - adv;
      if ((t_alarm < t2) && (t_alarm > t1)) {
	 memcpy(tm_prev, &(a->begin), sizeof(struct tm));
	 *prev_found=1;
#ifdef ALARMS_DEBUG
	 printf("fpn: prev_found none\n");
#endif
      } else if (t_alarm > t2) {
	 memcpy(tm_next, &(a->begin), sizeof(struct tm));
	 *next_found=1;
#ifdef ALARMS_DEBUG
	 printf("fpn: next_found none\n");
#endif
      }
      forward=backward=0;
      break;
    case repeatDaily:
#ifdef ALARMS_DEBUG
      printf("fpn: repeatDaily\n");
#endif
      freq = a->repeatFrequency;
      t_interval = a->repeatFrequency * 86400;
      if (t_interval==0) t_interval=1;
      t_alarm = mktime(&t);
      if ((t2 + adv) > t_alarm) {
	 t_past = ((t2 + adv - t_alarm) / t_interval) *t_interval + t_alarm;
	 t_future = (((t2 + adv - t_alarm) / t_interval) + 1) *t_interval + t_alarm;
	 *prev_found=*next_found=1;
      } else {
	 t_future = t_alarm;
	 *next_found=1;
      }
      Pnow = localtime(&t_past);
      memcpy(tm_prev, Pnow, sizeof(struct tm));
      Pnow = localtime(&t_future);
      memcpy(tm_next, Pnow, sizeof(struct tm));
      forward=backward=0;
#ifdef ALARMS_DEBUG
	{
	   char str[100];
	   strftime(str, 80, "%B %d, %Y %H:%M", tm_prev);
	   printf("fpn: daily tm_prev=%s\n", str);
	   strftime(str, 80, "%B %d, %Y %H:%M", tm_next);
	   printf("fpn: daily tm_next=%s\n", str);
	}
#endif
      break;
    case repeatWeekly:
#ifdef ALARMS_DEBUG
      printf("fpn: repeatWeekly\n");
#endif
      freq = a->repeatFrequency;
      t.tm_year=date2->tm_year;
      t.tm_mon=date2->tm_mon;
      t.tm_mday=date2->tm_mday;
      mktime(&t);
      begin_days = dateToDays(&(a->begin));
      days = dateToDays(&t);
#ifdef ALARMS_DEBUG
      printf("fpn: begin_days %d days %d\n", begin_days, days);
      printf("fpn: t.tm_wday %d a->begin.tm_wday %d\n", t.tm_wday, a->begin.tm_wday);
#endif
      get_pref(PREF_FDOW, &fdow, NULL);

      /* Offset is how many weeks we are off of an iteration */
      offset = ((int)((days - t.tm_wday) - (begin_days - a->begin.tm_wday))/7)%freq;
#ifdef ALARMS_DEBUG
      printf("fpn: offset %d\n", offset);
#endif
      if (offset > 0) {
	 sub_days_from_date(&t, offset*7);
      } else {
	 add_days_to_date(&t, offset*-7);
      }
      found=0;
      for (count=0, i=t.tm_wday; i>=0; i--, count++) {
	 if (a->repeatDays[i]) {
	    sub_days_from_date(&t, count);
	    found=1;
#ifdef ALARMS_DEBUG
	      {
		 char str[100];
		 strftime(str, 80, "%B %d, %Y %H:%M", &t);
		 printf("fpn: initial weekly=%s\n", str);
	      }
#endif
	    break;
	 }
      }
      if (!found) {
	 for (count=0, i=t.tm_wday; i<7; i++, count++) {
	    if (a->repeatDays[i]) {
	       add_days_to_date(&t, count);
	       found=1;
	       break;
	    }
	 }
      }
      break;
    case repeatMonthlyByDay:
#ifdef ALARMS_DEBUG
      printf("fpn: repeatMonthlyByDay\n");
#endif
      t.tm_mon=date2->tm_mon;
      t.tm_year=date2->tm_year;
      freq = a->repeatFrequency;
#ifdef ALARMS_DEBUG
      printf("fpn: freq=%d\n", freq);
#endif
      offset = ((t.tm_year - a->begin.tm_year)*12 +
		(t.tm_mon - a->begin.tm_mon))%(a->repeatFrequency);
      /* This will adjust for leap year, and exceeding the end of the month */
      if (((t.tm_year - a->begin.tm_year)*12 + (t.tm_mon - a->begin.tm_mon)) < 0) {
	 add_months_to_date(&t, offset);
      } else {
	 sub_months_from_date(&t, offset);
      }
      get_month_info(t.tm_mon, 1, t.tm_year, &fdom, &ndim);
      t.tm_mday=((a->repeatDay+7-fdom)%7) - ((a->repeatDay)%7) + a->repeatDay + 1;
#ifdef ALARMS_DEBUG
      printf("fpn: %02d/01/%02d, fdom=%d\n", t.tm_mon+1, t.tm_year+1900, fdom);
      printf("fpn: mday = %d\n", t.tm_mday);
#endif
      if (t.tm_mday > ndim) {
	 t.tm_mday -= 7;
      }
#ifdef ALARMS_DEBUG      
	{
	   char str[100];
	   strftime(str, 80, "%B %d, %Y %H:%M", &t);
	   printf("fpn: initial monthly by day=%s\n", str);
	}
#endif
      break;
    case repeatMonthlyByDate:
#ifdef ALARMS_DEBUG
      printf("fpn: repeatMonthlyByDate\n");
#endif
      t.tm_mon=date2->tm_mon;
      t.tm_year=date2->tm_year;
      freq = a->repeatFrequency;
      offset = ((t.tm_year - a->begin.tm_year)*12 +
		(t.tm_mon - a->begin.tm_mon))%(a->repeatFrequency);
      /* This will adjust for leap year, and exceeding the end of the month */
      if (((t.tm_year - a->begin.tm_year)*12 + (t.tm_mon - a->begin.tm_mon)) < 0) {
	 add_months_to_date(&t, offset);
      } else {
	 sub_months_from_date(&t, offset);
      }
      break;
    case repeatYearly:
#ifdef ALARMS_DEBUG
      printf("fpn: repeatYearly\n");
#endif
      t.tm_year=date2->tm_year;
      freq = a->repeatFrequency;
      offset = (t.tm_year - a->begin.tm_year)%(a->repeatFrequency);
#ifdef ALARMS_DEBUG      
      printf("fpn: (%d - %d)%%%d\n", t.tm_year,a->begin.tm_year,a->repeatFrequency);
      printf("fpn: *** years offset = %d\n", offset);
#endif
      /* This will adjust for leap year, and exceeding the end of the month */
      if ((t.tm_year - a->begin.tm_year) < 0) {
	 add_years_to_date(&t, offset);
      } else {
	 sub_years_from_date(&t, offset);
      }
      break;
   }/*switch */

   safety_counter=0;
   while(forward || backward) {
      safety_counter++;
      if (safety_counter > 20) {
	 jpilot_logf(LOG_STDOUT|LOG_FILE, "find_prev_next():infinite loop, breaking\n");
	 if (a->description) {
	    jpilot_logf(LOG_STDOUT|LOG_FILE, "desc=[%s]\n", a->description);
	 }
	 break;
      }
      t_temp = mktime(&t);
#ifdef ALARMS_DEBUG
	{
	   char str[100];
	   strftime(str, 80, "%B %d, %Y %H:%M", &t);
	   printf("fpn: trying with=%s\n", str);
	}
#endif
      /* See that we aren't before then begin date */
      t_begin = mktime(&(a->begin));
      if (t_temp < t_begin - adv) {
#ifdef ALARMS_DEBUG      
	 printf("fpn:1\n");
#endif
	 backward=0;
      }
      /* If the appointment has an end date, see that we are not past it */
      if (!(a->repeatForever)) {
	 t_end = mktime(&(a->repeatEnd));
	 if (t_temp > t_end) {
	    forward=0;
#ifdef ALARMS_DEBUG      
	    printf("fpn: 2\n");
#endif
	 }
      }
      t_temp-=adv;
      if (t_temp >= t2) {
	 memcpy(tm_next, &t, sizeof(t));
	 *next_found=1;
	 forward=0;
#ifdef ALARMS_DEBUG      
	 printf("fpn: next found\n");
#endif
      } else {
	 memcpy(tm_prev, &t, sizeof(t));
	 *prev_found=1;
	 backward=0;
#ifdef ALARMS_DEBUG      
	 printf("fpn: prev_found\n");
#endif
      }
      /* increment or decrement time */
      if (forward) {
	 switch (a->repeatType) {
	  case repeatNone:
	    break;
	  case repeatDaily:
	    break;
	  case repeatWeekly:
	    for (count=0, dow=t.tm_wday; count<14; count++) {
	       add_days_to_date(&t, 1);
#ifdef ALARMS_DEBUG      
	       printf("fpn: weekly forward t.tm_wday=%d, freq=%d\n", t.tm_wday, freq);
#endif
	       dow++;
	       if (dow==7) {
#ifdef ALARMS_DEBUG      
		  printf("fpn: dow==7\n");
#endif
		  add_days_to_date(&t, (freq-1)*7);
		  dow=0;
	       }
	       if (a->repeatDays[dow]) {
#ifdef ALARMS_DEBUG      
		  printf("fpn: repeatDay[dow] dow=%d\n", dow);
#endif
		  break;
	       }
	    }
	    break;
	  case repeatMonthlyByDay:
	    add_months_to_date(&t, freq);
	    get_month_info(t.tm_mon, 1, t.tm_year, &fdom, &ndim);
	    t.tm_mday=((a->repeatDay+7-fdom)%7) - ((a->repeatDay)%7) + a->repeatDay + 1;
	    if (t.tm_mday > ndim-1) {
	       t.tm_mday -= 7;
	    }
	    break;
	  case repeatMonthlyByDate:
	    t.tm_mday=a->begin.tm_mday;
	    add_months_to_date(&t, freq);
	    break;
	  case repeatYearly:
	    t.tm_mday=a->begin.tm_mday;
	    add_years_to_date(&t, freq);
	    break;
	 }/*switch */
	 continue;
      }
      if (backward) {
	 switch (a->repeatType) {
	  case repeatNone:
	    break;
	  case repeatDaily:
	    break;
	  case repeatWeekly:
	    for (count=0, dow=t.tm_wday; count<14; count++) {
	       sub_days_from_date(&t, 1);
#ifdef ALARMS_DEBUG      
	       printf("fpn: weekly backward t.tm_wday=%d, freq=%d\n", t.tm_wday, freq);
#endif
	       dow--;
	       if (dow==-1) {
#ifdef ALARMS_DEBUG      
		  printf("fpn: dow==-1\n");
#endif
		  sub_days_from_date(&t, (freq-1)*7);
		  dow=6;
	       }
	       if (a->repeatDays[dow]) {
#ifdef ALARMS_DEBUG      
		  printf("fpn: repeatDay[dow] dow=%d\n", dow);
#endif
		  break;
	       }
	    }
	    break;
	  case repeatMonthlyByDay:
	    sub_months_from_date(&t, freq);
	    get_month_info(t.tm_mon, 1, t.tm_year, &fdom, &ndim);
	    t.tm_mday=((a->repeatDay+7-fdom)%7) - ((a->repeatDay)%7) + a->repeatDay + 1;
	    if (t.tm_mday > ndim-1) {
	       t.tm_mday -= 7;
	    }
	    break;
	  case repeatMonthlyByDate:
	    t.tm_mday=a->begin.tm_mday;
	    sub_months_from_date(&t, freq);
	    break;
	  case repeatYearly:
	    t.tm_mday=a->begin.tm_mday;
	    sub_years_from_date(&t, freq);
	    break;
	 }/*switch */
	 continue;
      }
   }
   return 0;
}


/*
 * Find the next appointment alarm
 *  if soonest_only then return the next alarm, else return all alarms
 *  that occur between the 2 dates.
 */
int alarms_find_next(struct tm *date1_in, struct tm *date2_in, int soonest_only)
{
   AppointmentList *a_list;
   AppointmentList *temp_al;
   struct jp_alarms *ta;

   time_t adv;
   time_t ltime;
   time_t t1, t2;
   time_t t_alarm;
   time_t t_begin, t_end;
   time_t t_prev;
   time_t t_future;
   time_t t_interval;
   time_t t_soonest;
   struct tm *tm_temp;
   struct tm date1, date2;
   struct tm tm_prev, tm_next;
   int prev_found, next_found;
   int add_a_next;

   jpilot_logf(LOG_DEBUG, "alarms_find_next()\n");

   if (glob_skip_all_alarms) return 0;

   if (!date1_in) {
      time(&ltime);
      tm_temp = localtime(&ltime);
   } else {
      tm_temp=date1_in;
   }
   bzero(&date1, sizeof(date1));
   date1.tm_year=tm_temp->tm_year;
   date1.tm_mon=tm_temp->tm_mon;
   date1.tm_mday=tm_temp->tm_mday;
   date1.tm_hour=tm_temp->tm_hour;
   date1.tm_min=tm_temp->tm_min;
   date1.tm_sec=tm_temp->tm_sec;
   date1.tm_isdst=tm_temp->tm_isdst;

   if (!date2_in) {
      time(&ltime);
      tm_temp = localtime(&ltime);
   } else {
      tm_temp=date2_in;
   }
   bzero(&date2, sizeof(date2));
   date2.tm_year=tm_temp->tm_year;
   date2.tm_mon=tm_temp->tm_mon;
   date2.tm_mday=tm_temp->tm_mday;
   date2.tm_hour=tm_temp->tm_hour;
   date2.tm_min=tm_temp->tm_min;
   date2.tm_sec=tm_temp->tm_sec;
   date2.tm_isdst=tm_temp->tm_isdst;

   t1=mktime(&date1);
   t2=mktime(&date2);

#ifdef ALARMS_DEBUG
     {
	char str[100];
	struct tm *Pnow;
	strftime(str, 80, "%B %d, %Y %H:%M", &date1);
	printf("date1=%s\n", str);
	strftime(str, 80, "%B %d, %Y %H:%M", &date2);
	printf("date2=%s\n", str);
	Pnow = localtime(&t1);
	strftime(str, 80, "%B %d, %Y %H:%M", Pnow);
	printf("^date1=%s\n", str);
     }
#endif

   if (!soonest_only) {
      free_alarms_list(PREV_ALARM_MASK | NEXT_ALARM_MASK);
   } else {
      free_alarms_list(NEXT_ALARM_MASK);
   }

   a_list=NULL;
   get_days_appointments2(&a_list, NULL, 0, 0, 1);

   t_soonest=0;

   for (temp_al=a_list; temp_al; temp_al=temp_al->next) {
      /*
       * No alarm, skip
       */
      if (!temp_al->ma.a.alarm) {
	 continue;
      }
#ifdef ALARMS_DEBUG      
      printf("\n[%s]\n", temp_al->ma.a.description);
#endif
      /*
       * See if the appointment starts before date1
       * and is a non repeating appointment
       */
      if (temp_al->ma.a.repeatType == repeatNone) {
	 t_alarm = mktime(&(temp_al->ma.a.begin));
	 if (t_alarm < t1) {
#ifdef ALARMS_DEBUG      
	    printf("non repeat before t1, t_alarm<t1, %ld<%ld\n",t_alarm,t1);
#endif
	    continue;
	 }
      }

      /* If the appointment has an end date, see that we are not past it */
      if (!(temp_al->ma.a.repeatForever)) {
	 t_end = mktime(&(temp_al->ma.a.repeatEnd));
	 /* We need to add 24 hours to the end date to make it inclusive */
	 t_end += 86400;
	 t_begin = mktime(&(temp_al->ma.a.begin));
	 if (t_end < t2) {
#ifdef ALARMS_DEBUG      
	    printf("past end date\n");
#endif
	    continue;
	 }
      }

#ifdef ALARMS_DEBUG      
      printf("alarm advance %d ", temp_al->ma.a.advance);
#endif
      adv = 0;
      if (temp_al->ma.a.advanceUnits == advMinutes) {
#ifdef ALARMS_DEBUG      
	 printf("minutes\n");
#endif
	 adv = temp_al->ma.a.advance*60;
      }
      if (temp_al->ma.a.advanceUnits == advHours) {
#ifdef ALARMS_DEBUG      
	 printf("hours\n");
#endif
	 adv = temp_al->ma.a.advance*3600;
      }
      if (temp_al->ma.a.advanceUnits == advDays) {
#ifdef ALARMS_DEBUG      
	 printf("days\n");
#endif
	 adv = temp_al->ma.a.advance*86400;
      }

#ifdef ALARMS_DEBUG      
      printf("adv=%ld\n", adv);
#endif

      t_prev=t_future=t_interval=0;
      prev_found=next_found=0;

      find_prev_next(&(temp_al->ma.a),
		     adv,
		     &date1,
		     &date2,
		     &tm_prev,
		     &tm_next,
		     &prev_found,
		     &next_found);
      t_prev=mktime(&tm_prev);
      t_future=mktime(&tm_next);

#ifdef ALARMS_DEBUG      
      printf("adv = %ld\n", adv);
#endif
      /*
       * Skip the alarms if they are before date1, or after date2
       */

      if (prev_found) {
	 if (t_prev - adv < t1) {
#ifdef ALARMS_DEBUG      
	    printf("failed prev is before t1\n");
#endif
	    prev_found=0;
	 }
	 if (t_prev - adv > t2) {
#ifdef ALARMS_DEBUG      
	    printf("failed prev is after t2\n");
#endif
	    continue;
	 }
      }
      if (next_found) {
	 if (!(temp_al->ma.a.repeatForever)) {
	    t_end = mktime(&(temp_al->ma.a.repeatEnd));
	    if (t_future > t_end) {
#ifdef ALARMS_DEBUG      
	       printf("failed future is after t_end\n");
#endif
	       next_found=0;
	    }
	 }
      }
#ifdef ALARMS_DEBUG
      printf("t1=       %s\n", print_date(t1));
      printf("t2=       %s\n", print_date(t2));
      printf("t_prev=   %s\n", prev_found ? print_date(t_prev):"None");
      printf("t_future= %s\n", next_found ? print_date(t_future):"None");
      printf("alarm me= %s\n", next_found ? print_date(t_future):"None");
      printf("desc=[%s]\n", temp_al->ma.a.description);
#endif
      if (!soonest_only) {
	 if (prev_found) {
	    alarms_add_to_list(temp_al->ma.unique_id, ALARM_MISSED, t_prev, adv);
	 }
      }
      if (next_found) {
	 add_a_next=0;
	 if (next_alarm==NULL) {
	    add_a_next=1;
	 } else if 
	   (t_future - adv <= next_alarm->event_time - next_alarm->alarm_advance) {
	      add_a_next=1;
	      if (t_future - adv < next_alarm->event_time - next_alarm->alarm_advance) {
#ifdef ALARMS_DEBUG
		 printf("next alarm=%s\n", print_date(next_alarm->event_time - next_alarm->alarm_advance));
		 printf("freeing next alarms\n");
#endif
		 free_alarms_list(NEXT_ALARM_MASK);
	      }
	   }
	 if (add_a_next) {
#ifdef ALARMS_DEBUG
	    printf("found a new next\n");
#endif
	    ta = malloc(sizeof(struct jp_alarms));
	    if (ta) {
	       ta->next = next_alarm;
	       next_alarm = ta;
	       next_alarm->unique_id = temp_al->ma.unique_id;
	       next_alarm->type = ALARM_NEW;
	       next_alarm->event_time = t_future;
	       next_alarm->alarm_advance = adv;
	    }
	 }
      }
   }
   free_AppointmentList(&a_list);

   return 0;
}

/*
 * At startup see when rc file was written and find all past-due alarms.
 * Add them to the postponed alarm list with 0 minute reminder.
 * Find next alarm and put it in list
 */
int alarms_init(unsigned char skip_past_alarms,
		unsigned char skip_all_alarms)
{
   FILE *in;
   time_t ltime;
   struct tm now, *Pnow;
   struct tm tm1;
   char line[256];
   int found_uptodate;
   int year, mon, day, hour, min, n;

   jpilot_logf(LOG_DEBUG, "alarms_init()\n");

   alarm_list=NULL;
   Plast_alarm_list=NULL;
   next_alarm=NULL;

   total_alarm_windows = 0;
   glob_skip_all_alarms = skip_all_alarms;

   if (skip_past_alarms) {
      alarms_write_file();
   }
   if (skip_all_alarms) {
      alarms_write_file();
      return 0;
   }

   found_uptodate=0;
   in=jp_open_home_file("jpilot.alarms", "r");
   if (!in) {
      jpilot_logf(LOG_WARN, "Could not open jpilot.alarms file\n");
      return -1;
   }

   while (!feof(in)) {
      line[0]='\0';
      fgets(line, 255, in);
      line[255] = '\0';
      if (line[0]=='#') continue;
      if (!strncmp(line, "UPTODATE ", 9)) {
	 n = sscanf(line+9, "%d %d %d %d %d\n", &year, &mon, &day, &hour, &min);
	 if (n==5) {
	    found_uptodate=1;
	 }
	 jpilot_logf(LOG_DEBUG, "UPTODATE %d %d %d %d %d\n", year, mon, day, hour, min);
      }
   }

   time(&ltime);
   Pnow = localtime(&ltime);
   bzero(&now, sizeof(now));
   now.tm_year=Pnow->tm_year;
   now.tm_mon=Pnow->tm_mon;
   now.tm_mday=Pnow->tm_mday;
   now.tm_hour=Pnow->tm_hour;
   now.tm_min=Pnow->tm_min;
   now.tm_isdst=-1;
   mktime(&now);

   if (!found_uptodate) {
      alarms_write_file();
      year = now.tm_year+1900;
      mon = now.tm_mon+1;
      day = now.tm_mday;
      hour = now.tm_hour;
      min = now.tm_min;
   }

   bzero(&tm1, sizeof(tm1));
   tm1.tm_year=year-1900;
   tm1.tm_mon=mon-1;
   tm1.tm_mday=day;
   tm1.tm_hour=hour;
   tm1.tm_min=min;
   tm1.tm_isdst=-1;

   mktime(&tm1);

   alarms_find_next(&tm1, &now, FALSE);

   gtk_timeout_add(ALARM_INTERVAL*1000, cb_timer_alarms, NULL);

   return 0;
}
jpilot-0.99.2/datebook.c0100644000175000001440000005636707430116344014143 0ustar  mouseusers/* datebook.c
 * A module of J-Pilot http://jpilot.org
 *
 * Copyright (C) 1999-2001 by Judd Montgomery
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "config.h"
#include "i18n.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pi-source.h>
#include <pi-socket.h>
#include <pi-datebook.h>
#include <pi-dlp.h>
#include <pi-file.h>
#include <time.h>
/*#include <sys/stat.h> */
/*#include <sys/types.h> */
#include <unistd.h>
#include <utime.h>

#include "datebook.h"
#include "utils.h"
#include "log.h"
#include "prefs.h"
#include "libplugin.h"
#include "password.h"

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif

#define DATEBOOK_EOF 7

int datebook_compare(const void *v1, const void *v2)
{
   AppointmentList **al1, **al2;
   struct Appointment *a1, *a2;

   al1=(AppointmentList **)v1;
   al2=(AppointmentList **)v2;

   a1=&((*al1)->ma.a);
   a2=&((*al2)->ma.a);

   /* Jim Rees pointed out my sorting error */
   /* return ((a1->begin.tm_hour*60 + a1->begin.tm_min) > */
   return ((a1->begin.tm_hour*60 + a1->begin.tm_min) -
	   (a2->begin.tm_hour*60 + a2->begin.tm_min));
}

static int datebook_sort(AppointmentList **al)
{
   AppointmentList *temp_al;
   AppointmentList **sort_al;
   int count, i;

   /* Count the entries in the list */
   for (count=0, temp_al=*al; temp_al; temp_al=temp_al->next, count++) {
      ;
   }

   if (count<2) {
      /* We don't have to sort less than 2 items */
      return 0;
   }

   /* Allocate an array to be qsorted */
   sort_al = calloc(count, sizeof(AppointmentList *));
   if (!sort_al) {
      jpilot_logf(LOG_WARN, "datebook_sort(): Out of Memory\n");
      return 0;
   }

   /* Set our array to be a list of pointers to the nodes in the linked list */
   for (i=0, temp_al=*al; temp_al; temp_al=temp_al->next, i++) {
      sort_al[i] = temp_al;
   }

   /* qsort them */
   qsort(sort_al, count, sizeof(AppointmentList *), datebook_compare);

   /* Put the linked list in the order of the array */
   sort_al[count-1]->next = NULL;
   for (i=count-1; i; i--) {
      sort_al[i-1]->next=sort_al[i];
   }

   *al = sort_al[0];

   free(sort_al);

   return 0;
}

#ifdef USE_DB3
int db3_hack_date(struct Appointment *a, struct tm *today)
{
   int t1, t2;

   if (today==NULL) {
      return 0;
   }
   if (!a->note) {
      return 0;
   }
   if (strlen(a->note) > 8) {
      if ((a->note[0]=='#') && (a->note[1]=='#')) {
	 if (a->note[2]=='f') {
	    /* Check to see if its in the future */
	    t1 = a->begin.tm_mday + a->begin.tm_mon*31 + a->begin.tm_year*372;
	    t2 = today->tm_mday + today->tm_mon*31 + today->tm_year*372;
	    if (t1 > t2) return 0;
	    /* We found some silly hack, so we lie about the date */
	    /*memcpy(&(a->begin), today, sizeof(struct tm));*/
	    /*memcpy(&(a->end), today, sizeof(struct tm));*/
	    a->begin.tm_mday = today->tm_mday;
	    a->begin.tm_mon = today->tm_mon;
	    a->begin.tm_year = today->tm_year;
	    a->begin.tm_wday = today->tm_wday;
	    a->begin.tm_yday = today->tm_yday;
	    a->begin.tm_isdst = today->tm_isdst;
	    a->end.tm_mday = today->tm_mday;
	    a->end.tm_mon = today->tm_mon;
	    a->end.tm_year = today->tm_year;
	    a->end.tm_wday = today->tm_wday;
	    a->end.tm_yday = today->tm_yday;
	    a->end.tm_isdst = today->tm_isdst;
	    /* If the appointment has an end date, and today is past the end
	     * date, because of this hack we would never be able to view
	     * it anymore (or delete it).
	     */
	    if (!(a->repeatForever)) {
	       if (compareTimesToDay(today, &(a->repeatEnd))==1) {
		  /* end date is before start date, illegal appointment */
		  /* make it legal, by only floating upto the end date */
		  memcpy(&(a->begin), &(a->repeatEnd), sizeof(struct tm));
		  memcpy(&(a->end), &(a->repeatEnd), sizeof(struct tm));
	       }
	    }
	 }
      }
   }
   return 0;
}

/* Returns a bitmask
 * 0 if not a floating OR
 * bitmask:
 *  1 if float,
 *  2 if completed float
 *  16 if float has a note
 */
int db3_is_float(struct Appointment *a, int *category)
{
   int len, mask=0;

   *category=0;
   if (!a->note) {
      return 0;
   }
   len = strlen(a->note);
   if (len > 8) {
      if ((a->note[0]=='#') && (a->note[1]=='#')) {
	 *category = a->note[4] - '@';
	 if (len > 10) {
	    mask=mask | DB3_FLOAT_HAS_NOTE;
	 }
	 if (a->note[2]=='f') {
	    mask=mask | DB3_FLOAT;
	    return mask;
	 }
	 if (a->note[2]=='c') {
	    mask=mask | DB3_FLOAT_COMPLETE;
	    return mask;
	 }
      } else if (a->note[0] != '\0') {
	 mask=mask | DB3_FLOAT_HAS_NOTE;
      }
   }
   return mask;
}
#endif

int pc_datebook_write(struct Appointment *a, PCRecType rt, unsigned char attrib)
{
   char record[65536];
   int rec_len;
   buf_rec br;
   long char_set;

   get_pref(PREF_CHAR_SET, &char_set, NULL);
   if (char_set != CHAR_SET_ENGLISH) {
      if (a->description) charset_j2p(a->description, strlen(a->description)+1, char_set);
      if (a->note) charset_j2p(a->note, strlen(a->note)+1, char_set);
   }

   rec_len = pack_Appointment(a, record, 65535);
   if (!rec_len) {
      PRINT_FILE_LINE;
      jpilot_logf(LOG_WARN, "pack_Appointment %s\n", _("error"));
      return -1;
   }
   br.rt=rt;
   br.attrib = attrib;
   br.buf = record;
   br.size = rec_len;

   jp_pc_write("DatebookDB", &br);
   /* *unique_id = br.unique_id;*/

   return 0;
}

void free_AppointmentList(AppointmentList **al)
{
   AppointmentList *temp_al, *temp_al_next;
   for (temp_al = *al; temp_al; temp_al=temp_al_next) {
      free_Appointment(&(temp_al->ma.a));
      temp_al_next = temp_al->next;
      free(temp_al);
   }
   *al = NULL;
}

/*
 * If a copy is made, then it should be freed through free_Appointment
 */
int datebook_copy_appointment(struct Appointment *a1,
			     struct Appointment **a2)
{
   *a2=malloc(sizeof(struct Appointment));
   if (!(*a2)) {
      jpilot_logf(LOG_WARN, "datebook_copy_appointment(): Out of memory\n");
      return -1;
   }
   memcpy(*a2, a1, sizeof(struct Appointment));

   (*a2)->exception = (struct tm *)malloc(a1->exceptions * sizeof(struct tm));
   if (!(*a2)->exception) {
      jpilot_logf(LOG_WARN, "datebook_copy_appointment(): Out of memory 2\n");
      return -1;
   }
   memcpy((*a2)->exception, a1->exception, a1->exceptions * sizeof(struct tm));

   if (a1->description) {
      (*a2)->description=strdup(a1->description);
   }
   if (a1->note) {
      (*a2)->note=strdup(a1->note);
   }

   return 0;
}


/* Year is years since 1900 */
/* Mon is 0-11 */
/* Day is 1-31 */
/* */
int datebook_add_exception(struct Appointment *a, int year, int mon, int day)
{
   struct tm *new_exception, *Ptm;

   if (a->exceptions==0) {
      a->exception=NULL;
   }

   new_exception = malloc((a->exceptions + 1) * sizeof(struct tm));
   if (!new_exception) {
      jpilot_logf(LOG_WARN, "datebook_add_exception(): Out of memory\n");
      return -1;
   }
   memcpy(new_exception, a->exception, (a->exceptions) * sizeof(struct tm));
   free(a->exception);
   a->exceptions++;
   a->exception = new_exception;
   Ptm = &(a->exception[a->exceptions - 1]);
   Ptm->tm_year = year;
   Ptm->tm_mon = mon;
   Ptm->tm_mday = day;
   Ptm->tm_hour = 0;
   Ptm->tm_min = 0;
   Ptm->tm_sec = 0;
   Ptm->tm_isdst = -1;
   mktime(Ptm);
   return 0;
}

int dateToSecs(struct tm *tm1)
{
   time_t t1;
   struct tm *gmt;
   struct tm tm2;
   static time_t adj = -1;

   memcpy(&tm2, tm1, sizeof(struct tm));
   tm2.tm_isdst = 0;
   tm2.tm_hour=0;
   t1 = mktime(&tm2);
   if (-1 == adj) {
      gmt = gmtime(&t1);
      adj = t1 - mktime(gmt);
   }
   return (t1+adj);
}

int dateToDays(struct tm *tm1)
{
   time_t t1;
   struct tm *gmt;
   struct tm tm2;
   static time_t adj = -1;

   memcpy(&tm2, tm1, sizeof(struct tm));
   tm2.tm_isdst = 0;
   tm2.tm_hour=12;
   t1 = mktime(&tm2);
   if (-1 == adj) {
      gmt = gmtime(&t1);
      adj = t1 - mktime(gmt);
   }
   return (t1+adj)/86400; /*There are 86400 secs in a day */
}

/*returns 0 if times equal */
/*returns 1 if time1 is greater (later) */
/*returns 2 if time2 is greater (later) */
/*
int compareTimesToSec(struct tm *tm1, struct tm *tm2)
{
   time_t t1, t2;

   t1 = mktime(tm1);
   t2 = mktime(tm2);
   if (t1 > t2) return 1;
   if (t1 < t2) return 2;
   return 0;
}
*/
/*returns 0 if times equal */
/*returns 1 if time1 is greater (later) */
/*returns 2 if time2 is greater (later) */
int compareTimesToDay(struct tm *tm1, struct tm *tm2)
{
   unsigned int t1, t2;

   t1 = tm1->tm_year*366+tm1->tm_yday;
   t2 = tm2->tm_year*366+tm2->tm_yday;
   if (t1 > t2 ) return 1;
   if (t1 < t2 ) return 2;
   return 0;
}

unsigned int isApptOnDate(struct Appointment *a, struct tm *date)
{
/*   long fdow; */
   unsigned int ret;
   unsigned int r;
   int week1, week2;
   int dow, ndim;
   int i;
   /* days_in_month is adjusted for leap year with the date structure */
   int days_in_month[]={31,28,31,30,31,30,31,31,30,31,30,31
   };
   int exception_days;
   static int days, begin_days;
   static struct tm cached_date;

   /* jpilot_logf(LOG_DEBUG, "isApptOnDate\n"); */

   ret = FALSE;

   if (!date) {
      return FALSE;
   }

   /* To try to speed things up */
   if (memcmp(date, &cached_date, sizeof(struct tm))) {
      memcpy(&cached_date, date, sizeof(struct tm));
   } else {
      days=0;
      begin_days=0;
   }
   /* Leap year */
   if ((date->tm_year%4 == 0) &&
       !(((date->tm_year+1900)%100==0) && ((date->tm_year+1900)%400!=0))
       ) {
      days_in_month[1]++;
   }

   /* See if the appointment starts after date */
   r = compareTimesToDay(&(a->begin), date);
   if (r == 1) {
      return FALSE;
   }
   if (r == 0) {
      ret = TRUE;
   }
   /* If the appointment has an end date, see that we are not past it */
   if (!(a->repeatForever)) {
      r = compareTimesToDay(&(a->repeatEnd), date);
      if (r == 2) {
	 return FALSE;
      }
   }

   switch (a->repeatType) {
    case repeatNone:
      break;
    case repeatDaily:
      /* See if this appt repeats on this day */
      if (!begin_days) {
	 begin_days = dateToDays(&(a->begin));
      }
      if (!days) {
	 days = dateToDays(date);
      }
      ret = (((days - begin_days)%(a->repeatFrequency))==0);
      break;
    case repeatWeekly:
      get_month_info(date->tm_mon, date->tm_mday, date->tm_year, &dow, &ndim);
      /* See if the appointment repeats on this day */
      /*
      if (a->repeatWeekstart > 1) {
	 a->repeatWeekstart = 1;
      }
      if (a->repeatWeekstart < 0) {
	 a->repeatWeekstart = 0;
      }
      */
      /*if (!(a->repeatDays[dow + a->repeatWeekstart])) {*/
      if (!(a->repeatDays[dow])) {
	 ret = FALSE;
	 break;
      }
      /*See if we are in a week that is repeated in */
      if (!begin_days) {
	 begin_days = dateToDays(&(a->begin));
      }
      if (!days) {
	 days = dateToDays(date);
      }
      /* get_pref(PREF_FDOW, &fdow, NULL); */
      /* Note: Palm Bug?  I think the palm does this wrong.
       * I prefer this way of doing it so that you can have appts repeating
       * from Wed->Tue, for example.  The palms way prevents this */
      /* ret = (((int)((days - begin_days - fdow)/7))%(a->repeatFrequency)==0);*/
      /* But, here is the palm way */
      /* ret = (((int)((days-begin_days+a->begin.tm_wday-fdow)/7))
	     %(a->repeatFrequency)==0); */
      /* The above seemed to be wrong for fdow=1 and dow=0 appointment */
      ret = (((int)((days-begin_days+a->begin.tm_wday)/7))
	     %(a->repeatFrequency)==0);
      break;
    case repeatMonthlyByDay:
      /* See if we are in a month that is repeated in */
      ret = (((date->tm_year - a->begin.tm_year)*12 +
       (date->tm_mon - a->begin.tm_mon))%(a->repeatFrequency)==0);
      if (!ret) {
	 break;
      }
      /* If the days of the week match - good */
      /* e.g. Monday or Thur, etc. */
      if (a->repeatDay%7 != date->tm_wday) {
	 ret = FALSE;
	 break;
      }
      /* Are they both in the same week in the month */
      /* e.