#include "config.h"

#include "stdio.h"
#include "string.h"

#include "gnome.h"
#include "glade/glade.h"
#include "gconf/gconf-client.h"

#include "goat.h"
#include "goatx.h"
#include "ipmsg.h"

#include "goat-callbacks.h"

static void entry_activate(GtkWidget *widget, GtkWidget *dialog)

     /* Return the ACCEPT response when the dialog entry
	is activated */
{
  gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
}

static gchar *request_dialog(Goat *goat, const gchar *title, const gchar *message,
			     const gchar *default_text)

     /* Creates a dialog that requests a string */
{
  GtkWidget *dialog, *label, *entry;
  int button;
  gchar *response;

  dialog = gtk_dialog_new_with_buttons(title, 
				       GTK_WINDOW(goat->window),
				       GTK_DIALOG_MODAL,
				       GTK_STOCK_CANCEL,
				       GTK_RESPONSE_REJECT,
				       GTK_STOCK_OK,
				       GTK_RESPONSE_ACCEPT,
				       NULL);

  label = gtk_label_new(message);
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 5);

  entry = gtk_entry_new();
  if (default_text) gtk_entry_set_text(GTK_ENTRY(entry), default_text);
  gtk_widget_show(entry);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, TRUE, TRUE, 20);
  g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(entry_activate),
		   dialog);

  button = gtk_dialog_run(GTK_DIALOG(dialog));

  if (button == GTK_RESPONSE_ACCEPT)
    response =  g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
  else
    response = NULL;

  gtk_widget_destroy(dialog);

  return response;
}

static void update_text(Goat *goat)

     /* Update the note text in the GConf database */
{
  GtkTextBuffer *buffer;
  GtkTextIter start, end;
  gchar *text;

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(goat->text_view));
  gtk_text_buffer_get_start_iter(buffer, &start);
  gtk_text_buffer_get_end_iter(buffer, &end);
  text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);

  goat_set_string(goat, "text", text);
  g_free(text);
}

static void update_geom(Goat *goat)

     /* Update the position and size of the note in GConf */
{
  goat_set_int(goat, "x", goat->geometry->x);
  goat_set_int(goat, "y", goat->geometry->y);

  goat_set_int(goat, "height", goat->geometry->height);
  goat_set_int(goat, "width", goat->geometry->width);
}

static void pipe_command(Goat *goat, const gchar *command)

     /* Pipes the output of the command into the note as text */
{
  gchar *buffer=NULL;
  gint exit;

  g_spawn_command_line_sync(command, &buffer, NULL, &exit, NULL);

  goat_insert_text(goat, buffer);
  g_free(buffer);

  update_text(goat);
}

static void insert_file(Goat *goat, const gchar *filename)

     /* Insert a file into the note */
{
  gchar buffer[20];
  FILE *file;

  file = fopen(filename, "r");

  if(file) {
    while(fgets(buffer, 20, file))
      goat_insert_text(goat, buffer);
  }

  update_text(goat);
}

static void pos_menu(GtkMenu *menu, int *x, int *y, gboolean *push_in, Goat *goat)

     /* Position the menu over the note when it is popped up using the
	keyboard */
{
  *x = goat->geometry->x;
  *y = goat->geometry->y;
  *push_in = TRUE;
}

gint goat_menu_button_cb(GtkWidget *w, GdkEventButton *event, 
			 Goat *goat)
{
    /* Right button = menu */

  if (event->button == 3) {
    gtk_menu_popup(GTK_MENU(goat->menu), NULL, NULL, 
		   NULL, NULL, event->button, event->time);

    /* Don't pass the button press to other callbacks that might pop
       up another menu */

    return TRUE;
  }
  else
    return FALSE;
}

void goat_menu_popup_cb(GtkWidget *w, Goat *goat)

     /* Popup the menu */
{
  g_signal_stop_emission_by_name (w, "popup-menu");

  gtk_menu_popup(GTK_MENU(goat->menu), NULL, NULL, 
		 (GtkMenuPositionFunc)pos_menu, goat,
		 3, 0);
}

gint goat_text_focus_out_cb(GtkWidget *w, GdkEventFocus *event, Goat *goat)

     /* When the pointer leaves the note we update the text and
	position in GConf */
{
  update_text(goat);
  update_geom(goat);

  return TRUE;
}

gint goat_resize_pressed_cb(GtkWidget *w, GdkEventButton *event, 
			    Goat *goat)
     
     /* Button pressed on the resize icon */
{
  if(event->button == 1) {

    /* Left button to resize */

    if (!goat_get_bool(goat, "shaded"))
      gtk_window_begin_resize_drag(GTK_WINDOW(goat->window),
     				   GDK_WINDOW_EDGE_NORTH_WEST,
				   event->button,
				   event->x_root,
				   event->y_root,
				   event->time);
  }
  else if (event->button == 3)
    gtk_menu_popup(GTK_MENU(goat->menu), NULL, NULL,
		   NULL, NULL, 
		   event->button, event->time);
    
  return FALSE;
}

gint goat_topbar_pressed_cb(GtkWidget *w, GdkEventButton *event, 
			    Goat *goat)

     /* Button pressed on the title bar */
{
  if (event->button == 1) {

    /* double click to shade */

    if (event->type == GDK_2BUTTON_PRESS) {
      goat_set_bool(goat, "shaded", !goat_get_bool(goat, "shaded"));
      return FALSE;
    }

    /* single click move drag */

    goat_raise(goat);
    goat_set_bool(goat, "lowered", FALSE);

    gtk_window_begin_move_drag(GTK_WINDOW(goat->window),
			       event->button,
			       event->x_root,
			       event->y_root,
			       event->time);
    return FALSE;
  }
  else if (event->button == 2) {

    /* middle button - set title... */

      goat_set_title_cb(w, goat);

  }
  else if (event->button == 3) {

    /* Right button = menu */

    gtk_menu_popup(GTK_MENU(goat->menu), NULL, NULL,
		   NULL, NULL, 
		   event->button, event->time);
  }

  return FALSE;
}

gint goat_configure_cb(GtkWidget *w, GdkEventConfigure *event,
		       Goat *goat)

     /* Callback when size / position of the note changes. Don't
	record size when shaded as this will mess things up 
	when the note is unshaded */
{
  goat->geometry->x = event->x;
  goat->geometry->y = event->y;
  
  if (!goat_get_bool(goat, "shaded")) {
    goat->geometry->width = event->width;
    goat->geometry->height = event->height;
  }

  return FALSE;
}

gint goat_cross_pressed_cb(GtkWidget *w, GdkEventButton *event, 
			   Goat *goat)

     /* Button pressed on the cross icon */
{
  gboolean delete_hides;

  /* left button deletes/hides the goat */

  if(event->button == 1) {
    delete_hides = gconf_client_get_bool
      (goat->gconf_client, "/apps/goats/settings/delete_hides",
       NULL);
    if(!delete_hides) goat_delete_cb(w, goat);
    else goat_set_bool(goat, "hidden", TRUE);
  }
  else if (event->button == 3) 
    gtk_menu_popup(GTK_MENU(goat->menu), NULL, NULL, 
		   NULL, NULL, 
		   event->button, event->time);

  return FALSE;
}

static GList *uri_list_extract_uris(const gchar *uri_list)
{
  /* Note that this is mostly very stolen from nautilius-icon-dnd.c, who 
     in turn stole it from old libgnome/gnome-mime.c */

  const gchar *p, *q;
  gchar *retval;
  GList *result = NULL;
  
  g_return_val_if_fail (uri_list != NULL, NULL);
  
  p = uri_list;
  
  /* We don't actually try to validate the URI according to RFC
   * 2396, or even check for allowed characters - we just ignore
   * comments and trim whitespace off the ends.  We also
   * allow LF delimination as well as the specified CRLF.
   */
  while (p != NULL) {
    if (*p != '#') {
      while (g_ascii_isspace (*p))
	p++;
      
      q = p;
      while ((*q != '\0')
	     && (*q != '\n')
	     && (*q != '\r'))
	q++;
      
      if (q > p) {
	q--;
	while (q > p
	       && g_ascii_isspace (*q))
	  q--;
	
	retval = g_malloc (q - p + 2);
	strncpy (retval, p, q - p + 1);
	retval[q - p + 1] = '\0';
	
	result = g_list_prepend (result, retval);
      }
    }
    p = strchr (p, '\n');
    if (p != NULL)
      p++;
  }
  
  return g_list_reverse (result);
}

static gchar *uri_get_filename(const gchar *uri)

     /* Get the filename corresponding to a
	file:// URI */
{
  gchar *filename, *protocol;
  int pos = 0;

  for (pos = 0; pos<strlen(uri); pos++) {
    if (uri[pos] == ':') break;
  }

  protocol = g_strndup(uri, pos);
  if (strcmp(protocol, "file") != 0) {
    g_free(protocol);
    return NULL; 
  }

  filename = g_strdup(&uri[pos + 3]);
  
  g_free(protocol);
  return filename;
}

void goat_drag_received_cb(GtkWidget *w, GdkDragContext *context,
			   gint x, gint y, GtkSelectionData *data,
			   guint info, guint time, Goat *goat)

     /* Drag / drop received callback */
{
  GList *list;
  gchar *filename;

  gtk_drag_finish (context, TRUE, FALSE, time);

  list = uri_list_extract_uris (data->data);

  while (list != NULL) {
      filename = uri_get_filename((gchar *)list->data);
      insert_file(goat, filename);
      g_free (filename);
      list = list->next;
  }

  g_list_foreach(list, (GFunc)g_free, NULL);
  g_list_free(list);
}

void goat_window_mapped_cb(GtkWidget *w, Goat *goat)

     /* When note is first mapped move it to 
	the correct initial position */
{
  int ws;

  if(goat_get_bool(goat, "sticky")) 
    gtk_window_stick(GTK_WINDOW(goat->window));
  else {
    ws = goat_get_int(goat, "workspace");
    goat_set_workspace(goat, ws);
  }  

  if (goat->geometry->x != -1) {
    goat_set_position(goat, goat->geometry->x,
		      goat->geometry->y); 
  }
  else {
    gtk_window_get_position(GTK_WINDOW(goat->window),
			    &goat->geometry->x,
			    &goat->geometry->y);
    goat_set_int(goat, "x", goat->geometry->x);
    goat_set_int(goat, "y", goat->geometry->y);
  }

  if(goat_get_bool(goat, "lowered"))
    gdk_window_lower(goat->window->window);
}

void goat_text_mapped_cb(GtkWidget *w, Goat *goat)

     /* Set the cursor for the text area */
{
  GdkCursor *cursor;

  cursor = gdk_cursor_new(GDK_XTERM);
  gdk_window_set_cursor(goat->text_view->window, cursor);
  gdk_cursor_destroy(cursor);
}

void goat_e0_mapped_cb(GtkWidget *e0, Goat *goat)

     /* Set the cursor for the resize button */
{
  GdkCursor *cursor;
 
  cursor = gdk_cursor_new(GDK_TOP_LEFT_CORNER);
  gdk_window_set_cursor(e0->window, cursor);
  gdk_cursor_destroy(cursor);
}

void goat_e1_mapped_cb(GtkWidget *e1, Goat *goat)

     /* Set the cursor for the title bar */
{
  GdkCursor *cursor;
 
  cursor = gdk_cursor_new(GDK_FLEUR);
  gdk_window_set_cursor(e1->window, cursor);
  gdk_cursor_destroy(cursor);
}

void goat_e2_mapped_cb(GtkWidget *e2, Goat *goat)

     /* Set the cursor for the delete button */
{
  GdkCursor *cursor;

  cursor = gdk_cursor_new(GDK_PIRATE);
  gdk_window_set_cursor(e2->window, cursor);
  gdk_cursor_destroy(cursor);
}

void goat_window_realized_cb(GtkWidget *w, Goat *goat)

     /* Set the window type so that the Window manager doesn't
	decorate it */
{
  goat_set_window_type(goat);
}

static void insert_selected_file(GtkWidget *w, GtkWidget *file_selector) 

     /* Callback to insert a selected file in the note */
{
  const gchar *filename;
  Goat *goat;

  filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_selector));

  goat = g_object_get_data(G_OBJECT(file_selector), "goat");

  insert_file(goat, filename);
}

static void save_to_file(GtkWidget *w, GtkWidget *file_selector)

     /* Callback to save the text to the selected file */
{
  FILE *file;
  const gchar *filename;
  gchar *text;
  Goat *goat;

  filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_selector));

  goat = g_object_get_data(G_OBJECT(file_selector), "goat");

  file = fopen (filename, "w");

  if (!file) goats_error("save_to_file", _("Couldn't open file for writing"));
  else {
    text = goat_get_string(goat, "text");
    fprintf(file, text);
    g_free(text);
    fclose(file);
  }
}

static void choose_file(Goat *goat, const char *title, void *handler)

     /* Show a dialog to choose a file then call the specified
	handler */
{
  GtkWidget *file_selector;

  /* Create the selector */
  
  file_selector = gtk_file_selection_new(title);
  g_object_set_data(G_OBJECT(file_selector), "goat", goat);

  g_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_selector)->ok_button),
		    "clicked",
		    G_CALLBACK (handler), file_selector);
  
  /* Ensure that the dialog box is destroyed when the user clicks a button. */
  
  g_signal_connect_swapped (GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button),
			    "clicked",
			    G_CALLBACK (gtk_widget_destroy), 
			    (gpointer) file_selector); 
  
  g_signal_connect_swapped (GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->cancel_button),
			    "clicked",
			    G_CALLBACK (gtk_widget_destroy),
			    (gpointer) file_selector); 
  
  /* Display that dialog */
  
  gtk_widget_show (file_selector);
}

void goat_insert_file_cb(GtkWidget *w, Goat *goat)

     /* Choose a file to insert into the note */
{
  choose_file(goat, _("Choose a text file to insert"), (void *)insert_selected_file);
}

void goat_text_save_cb(GtkWidget *w, Goat *goat)

     /* Choose a file to save the text to */
{
  choose_file(goat, _("Save text to file"), (void *)save_to_file);
}

void goat_text_cut_cb(GtkWidget *w, Goat *goat)
{
  GtkClipboard *cb;
  GtkTextBuffer *buf;

  cb = gtk_clipboard_get(GDK_NONE);
  buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(goat->text_view));

  gtk_text_buffer_cut_clipboard(buf, cb, TRUE);

  update_text(goat);
}

void goat_text_copy_cb(GtkWidget *w, Goat *goat)
{
  GtkClipboard *cb;
  GtkTextBuffer *buf;

  cb = gtk_clipboard_get(GDK_NONE);
  buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(goat->text_view));

  gtk_text_buffer_copy_clipboard(buf, cb);
}

void goat_text_paste_cb(GtkWidget *w, Goat *goat)
{
  GtkClipboard *cb;
  GtkTextBuffer *buf;

  cb = gtk_clipboard_get(GDK_NONE);
  buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(goat->text_view));

  gtk_text_buffer_paste_clipboard(buf, cb, NULL, TRUE);

  update_text(goat);
}

void goat_properties_autosize_cb(GtkWidget *w, Goat *goat)

     /* Set the size of the note to just contain the text */
{
  GtkRequisition text_size, title_size;
  GtkWrapMode mode;

  gtk_widget_size_request(goat->eventbox[1], &title_size);

  gtk_widget_set_size_request(goat->text_view, -1, -1);
  gtk_widget_size_request(goat->text_view, &text_size);

  if (title_size.width + 40 > text_size.width) text_size.width = title_size.width + 40;

  goat_set_size(goat, text_size.width, text_size.height + goat_get_topbar_height(goat));

  gtk_widget_set_size_request(goat->text_view, 50, 50);
}

void goat_text_print_cb(GtkWidget *w, Goat *goat)
{
  goat_print(goat);
}

void goat_text_mail_cb(GtkWidget *w, Goat *goat)

     /* Send the text as an email. Uses gnome_url_show
	which needs setting up in GConf */
{
  gchar *url, *body, *address, *subject;
  GtkTextBuffer *buffer;
  GtkTextIter start, end;

  address = request_dialog(goat, _("Mail goat"), 
			   _("Email address to send to"), NULL);

  if (!address) return;

  subject = goat_get_string(goat, "title");

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(goat->text_view));
  gtk_text_buffer_get_start_iter(buffer, &start);
  gtk_text_buffer_get_end_iter(buffer, &end);
  body = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);

  if (!subject) subject = g_strdup(_("No subject"));
  if (!body) body = g_strdup(_("No body"));

  url = g_strdup_printf("mailto:%s?subject=%s&body=%s", address, subject, body);

  if (!gnome_url_show(url, NULL)) goats_warning("goat_text_mail_cb", 
						_("Couldn't start mail program")); 
  g_free(url);
  g_free(body);
  g_free(address);
  g_free(subject);
}

void goat_text_ipmessage_cb(GtkWidget *w, Goat *goat)

     /* Send the text as an IP Message */
{
  gchar *address;
  IPMsgPacket  *msg_packet;
  GtkTextBuffer *buffer;
  GtkTextIter start, end;

  address = request_dialog(goat, _("Send IP message"), 
			   _("IP address to send note to"), NULL);
  
  if (!address) return;

  msg_packet = (IPMsgPacket *)g_malloc(sizeof(IPMsgPacket));
  if (! msg_packet)
    return;

  /* hostname */
  msg_packet->hostname = g_strdup(getenv("HOSTNAME"));
  if (! msg_packet->hostname)
    return;

  /* username */
  msg_packet->username = g_strdup(getenv("USER"));
  if (! msg_packet->username)
    return;

  /* addtional part (message body) */
  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(goat->text_view));
  gtk_text_buffer_get_start_iter(buffer, &start);
  gtk_text_buffer_get_end_iter(buffer, &end);
  msg_packet->additional_part = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);;

  /* version number */
  msg_packet->version = 1;

  /* packet number */
  msg_packet->packet_id = time(NULL);

  /* command number */
  msg_packet->command = IPMSG_SENDMSG;

  /* destination address */
  msg_packet->destination_ip = address;

  /* send message */
  ipmsg_send_msg(msg_packet);

  g_free(msg_packet->username);
  g_free(msg_packet->hostname);
  g_free(msg_packet->additional_part);
  g_free(msg_packet->destination_ip);
  g_free(msg_packet);
}

void goat_text_clear_cb(GtkWidget *w, Goat *goat)

     /* Clear all text from the note */
{
  gboolean confirm;

  if (gconf_client_get_bool(goat->gconf_client, "/apps/goats/settings/paranoid", NULL))
    confirm = goats_confirm(_("Clear all text from the note?"), GTK_STOCK_CLEAR);
  else
    confirm = TRUE;

  if (confirm) goat_set_string(goat, "text", "");
}

void goat_set_title_cb(GtkWidget *w, Goat *goat)

     /* Create a dialog to change the note title */
{
  const gchar *current_title;
  gchar *title;

  current_title = gtk_label_get_text(GTK_LABEL(goat->title));
  title = request_dialog(goat, _("Change title"), 
			 _("Enter new title for the note"),
			 current_title);
  
  if (title) goat_set_string(goat, "title", title);
  g_free(title);
}

static void alarm_apply(Goat *goat)

     /* Set the alarm to the state selected on the dialog */
{
  gboolean active, repeat=TRUE;
  GtkWidget *w;
  time_t alarm_time;
  gint32 repeat_day=0, repeat_week=0, 
    repeat_month=0, repeat_year=0;


  w = glade_xml_get_widget(goat->alarm_xml, 
			   "alarm_on_checkbutton");
  goat_set_bool(goat, "alarm_active",
		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)));

  w = glade_xml_get_widget(goat->alarm_xml, "alarm_date");
  goat_set_int(goat, "alarm_time", 
	       gnome_date_edit_get_time(GNOME_DATE_EDIT(w)));

  w = glade_xml_get_widget(goat->alarm_xml, "repeat_menu");
  goat_set_int(goat, "alarm_repeat",
	       gtk_option_menu_get_history(GTK_OPTION_MENU(w)));

  w = glade_xml_get_widget(goat->alarm_xml, "notify_menu");
  switch(gtk_option_menu_get_history(GTK_OPTION_MENU(w))) {
  case 0:
    goat_set_bool(goat, "alarm_use_message", TRUE);
    break;
  case 1:
    goat_set_bool(goat, "alarm_use_message", FALSE);
    break;
  default:
    goats_error("alarm_apply", _("Bad menu history"));
    break;
  }

  w = glade_xml_get_widget(goat->alarm_xml, "message_entry");
  goat_set_string(goat, "alarm_message", 
		  gtk_entry_get_text(GTK_ENTRY(w)));

  w = glade_xml_get_widget(goat->alarm_xml, 
			   "sound_checkbutton");
  goat_set_bool(goat, "alarm_use_sound",
		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)));

  w = glade_xml_get_widget(goat->alarm_xml, "sound_file_entry");
  goat_set_string(goat, "alarm_sound_filename", 
		  gtk_entry_get_text(GTK_ENTRY(gnome_file_entry_gtk_entry(GNOME_FILE_ENTRY(w)))));

}

static void alarm_cancel(Goat *goat)

     /* Hide the alarm dialog without making changes to the
	note's alarm */
{
  GtkWidget *w;

  w = glade_xml_get_widget(goat->alarm_xml, "set_alarm");
  gtk_widget_hide(w);
}

void goat_set_alarm_cb(GtkWidget *widget, Goat *goat)

     /* Show the set alarm dialog */
{
  gint response;
  GtkWidget *dialog;

  if (!goat->alarm_inited) goat_alarm_init(goat);

  goat_create_alarm_dialog(goat);
}

gint goat_alarm_dialog_delete_cb(GtkWidget *widget, GdkEvent *event,
				 Goat *goat)
{
  gtk_widget_hide(widget);

  return TRUE;
}

void goat_alarm_dialog_ok_cb(GtkWidget *widget, Goat *goat)
{
  alarm_apply(goat);
  alarm_cancel(goat);
}

void goat_alarm_dialog_help_cb(GtkWidget *widget, Goat *goat)

     /* Show help on the alarm dialog */
{
  GError *err;

  err = NULL;
  gnome_help_display ("goats", "goats-set-alarm",
                      &err);

  if (err) {
    goats_error("show_help_cb", err->message);
    g_error_free(err);
  }
}

void goat_alarm_dialog_cancel_cb(GtkWidget *widget, Goat *goat)
{
  alarm_cancel(goat);
}

void goat_insert_time_cb(GtkWidget *w, Goat *goat)

     /* Insert the current time into the note */
{
  time_t current_time;
  gchar *current_time_string, *text;
  gint offset;
   
  current_time = time(NULL);

  /* make sure we grab the right bit of the date string */

  current_time_string = ctime(&current_time);
  offset = strlen(current_time_string) - 14;
  text = current_time_string + offset*sizeof(char);

  goat_insert_text(goat, text);
  update_text(goat);
}

void goat_insert_date_cb(GtkWidget *w, Goat *goat)

     /* Insert the current date into the alarm dialog */
{
  gchar *command;
 
  command = gconf_client_get_string(goat->gconf_client, 
				    "/apps/goats/settings/date", NULL);
  pipe_command(goat, command);
  g_free(command);
}
	       

void goat_insert_calendar_cb(GtkWidget *w, Goat *goat)

     /* Insert a calendar for the current month into the note */
{
  gchar *command;
 
  command = gconf_client_get_string(goat->gconf_client, 
				    "/apps/goats/settings/cal", NULL);
  pipe_command(goat, command);
  g_free(command);
}

void goat_insert_fortune_cb(GtkWidget *w, Goat *goat)

     /* Insert a random fortune into the note using
	the 'fortune' program */
{
  gchar *command;
 
  command = gconf_client_get_string(goat->gconf_client, 
				    "/apps/goats/settings/fortune", NULL);
  pipe_command(goat, command);
  g_free(command);
}

void goat_properties_font_cb(GtkWidget *w, Goat *goat)

     /* Change the font for the note */
{
  GtkWidget *fsd;
  gchar *name;
  gint whichbutton;

  name = goat_get_string(goat, "font_name");
  fsd = gtk_font_selection_dialog_new(_("Choose a font"));
  gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(fsd),
					  name);
  g_free(name);
  gtk_widget_show(fsd);

  whichbutton = gtk_dialog_run (GTK_DIALOG(fsd));
  switch (whichbutton) {
  case GTK_RESPONSE_OK:
    name = gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(fsd));
    goat_set_string(goat, "font_name", name);
    g_free(name);
    break;
  default:
    break;
  }
  gtk_widget_destroy(fsd);
}

void goat_sticky_cb(GtkWidget *w, Goat *goat)

     /* Toggle 'stickiness' on/off */
{
  gboolean sticky;

  sticky = goat_get_bool(goat, "sticky");;
  goat_set_bool(goat, "sticky", !sticky);
}

void goat_shade_cb(GtkWidget *w, Goat *goat)

     /* Toggle shaded on/off */
{
  gboolean shaded;

  shaded = goat_get_bool(goat, "shaded");
  goat_set_bool(goat, "shaded", !shaded);
}

static GdkColor *get_color(GdkColor *initial)

     /* Show a dialog to choose a new color */
{
  GtkWidget *csd;
  GtkColorSelection *cs;
  gint whichbutton;
  GdkColor *color;

  csd = gtk_color_selection_dialog_new(_("Choose a colour"));
  cs = GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(csd)->colorsel);
  gtk_color_selection_set_current_color(cs, initial);
  gtk_widget_show(csd);

  whichbutton = gtk_dialog_run (GTK_DIALOG(csd));
  switch (whichbutton) {
  case GTK_RESPONSE_OK:
    color = gdk_color_copy(initial);
    gtk_color_selection_get_current_color(cs, color);
    break;
  default:
    color = NULL;
    break;
  }
  gtk_widget_destroy(csd);

  return color;
}

void goat_properties_bg_cb(GtkWidget *w, Goat *goat)

     /* Change the note (background) colour */
{
  GdkColor *new_bg;

  new_bg = get_color(goat->bg);
  if(new_bg != NULL) {
    goat_set_gdk_color(goat, "bg", new_bg);
    gdk_color_free(new_bg);
  }
}

void goat_properties_fg_cb(GtkWidget *widget, Goat *goat)

     /* Change the text (foreground) colour */
{
  GdkColor *new_fg;

  new_fg = get_color(goat->fg);
  if(new_fg != NULL) {
    goat_set_gdk_color(goat, "fg", new_fg);
    gdk_color_free(new_fg);
  }
}

void goat_create_new_cb(GtkWidget *w, Goat *goat)

     /* Create a new note */
{
  gchar *name, *key;

  name = g_strdup_printf("new_goat_%i", g_random_int());
  key = g_strdup_printf("/apps/goats/herd/%s/alive", name);

  gconf_client_set_bool(goat->gconf_client, key,
                        TRUE, NULL);
  g_free(key);
  g_free(name);
}

void goat_raise_cb(GtkWidget *w, Goat *goat)

     /* Raise the note above other windows */
{
  gdk_window_raise(goat->window->window);
  goat_set_bool(goat, "lowered", FALSE);
}

void goat_lower_cb(GtkWidget *w, Goat *goat)

     /* Lower the note below other windows */
{
  gdk_window_lower(goat->window->window);
  goat_set_bool(goat, "lowered", TRUE);
}

void goat_hide_cb(GtkWidget *w, Goat *goat)

     /* Make the note disappear without deleting it */
{
  goat_set_bool(goat, "hidden", TRUE);
}

void goat_delete_cb(GtkWidget *w, Goat *goat)

     /* Delete the goat permanently */
{
  gboolean confirm;

  if (gconf_client_get_bool(goat->gconf_client, "/apps/goats/settings/paranoid", NULL))
    confirm = goats_confirm(_("Delete this note?"), GTK_STOCK_DELETE);
  else
    confirm = TRUE;

  if (confirm) goat_delete(goat);
}

void alarm_repeat(Goat *goat)

     /* Reset the alarm to repeat at the requested interval */
{
/* I am sure there is a More Beautiful way to do
   this, but isn't there always? */
  GDate *current_date;
  time_t new_time, alarm_time;
  struct tm *breakdown;
  struct tm *new_breakdown;

  alarm_time = goat_get_int(goat, "alarm_time");
  new_time = 0;
  new_breakdown = g_malloc(sizeof(struct tm));

  current_date = g_date_new();
  g_date_set_time(current_date,alarm_time);  

  breakdown = localtime(&alarm_time); 

  switch (goat_get_int(goat, "alarm_repeat")) {
  case 1:
    g_date_add_days(current_date, 1);
    break;
  case 2:
    g_date_add_days(current_date, 7);
    break;
  case 3:
    g_date_add_months(current_date, 1);
    break;
  case 4:
    g_date_add_years(current_date, 1);
    break;
  default:
    goats_error("alarm_repeat", _("Bad menu history"));
  }

  g_date_to_struct_tm(current_date,new_breakdown);

  new_breakdown->tm_sec = breakdown->tm_sec;
  new_breakdown->tm_min = breakdown->tm_min;
  new_breakdown->tm_hour = breakdown->tm_hour; 

  new_time = mktime(new_breakdown);
  g_free(new_breakdown);  
  g_free(current_date); 

  goat_set_int(goat, "alarm_time", new_time);
  goat_set_bool(goat, "alarm_active", TRUE);
}

void goat_alarm_triggered_cb(Goat *goat)

     /* Callback for when the alarm goes off */
{
  GtkWidget *message_box;
  gchar *sound, *message;

  goat_set_bool(goat, "alarm_active", FALSE);

  if(goat_get_bool(goat, "alarm_use_sound")) {
    sound = goat_get_string(goat, "alarm_sound_filename");
    gnome_sound_play(sound);
    g_free(sound);
  }
  if(goat_get_bool(goat, "alarm_use_message")) {
    gdk_window_raise(goat->window->window); 
    message = goat_get_string(goat, "alarm_message");
    goats_message(message);
    g_free(message);
  }
  else {
    goat_popup(goat);
  }

  if (goat_get_int(goat, "alarm_repeat") != 0)
    alarm_repeat(goat);
}

void goat_alarm_active_toggled_cb(GtkWidget *toggle, Goat *goat)
{
  gboolean active, sound, message;
  GtkWidget *w;
  GladeXML *xml;
  gint history;

  xml = goat->alarm_xml;
  active = gtk_toggle_button_get_active
    (GTK_TOGGLE_BUTTON(toggle));

  w = glade_xml_get_widget(xml,"remind_label");
  gtk_widget_set_sensitive(w, active);

  w = glade_xml_get_widget(xml,"sound_checkbutton");
  sound = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
  gtk_widget_set_sensitive(w, active);

  w = glade_xml_get_widget(xml, "sound_file_entry");
  gtk_widget_set_sensitive(w, active && sound);

  w = glade_xml_get_widget(xml,"notify_menu");
  history = gtk_option_menu_get_history(GTK_OPTION_MENU(w));
  if (history == 0) 
    message = TRUE;
  else
    message = FALSE;

  gtk_widget_set_sensitive(w, active);

  w = glade_xml_get_widget(xml, "message_entry");
  gtk_widget_set_sensitive(w, active && message);

  w = glade_xml_get_widget(xml,"repeat_menu");
  gtk_widget_set_sensitive(w, active);

  w = glade_xml_get_widget(xml,"alarm_date");
  gtk_widget_set_sensitive(w, active);
}

void goat_alarm_sound_toggled_cb(GtkWidget *toggle, Goat *goat)
{
  gboolean active, sound;
  GtkWidget *w;
  GladeXML *xml;

  xml = goat->alarm_xml;
  sound = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle));

  w = glade_xml_get_widget(xml, "alarm_on_checkbutton");
  active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));

  w = glade_xml_get_widget(xml, "sound_file_entry");
  gtk_widget_set_sensitive(w, active && sound);
}

void goat_alarm_menu_changed_cb(GtkWidget *menu, Goat *goat)
{
  GtkWidget *w;
  gboolean active, message;
  GladeXML *xml;

  xml = goat->alarm_xml;
  w = glade_xml_get_widget(xml, "alarm_on_checkbutton");
  active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));

  w = glade_xml_get_widget(xml, "notify_menu");
  switch (gtk_option_menu_get_history(GTK_OPTION_MENU(w))) {
  case 0:
    message = TRUE;
    break;
  case 1:
    message = FALSE;
    break;
  default:
    goats_error("goat_alarm_menu_changed_cb",
		_("Bad menu history"));
    break;
  }

  w = glade_xml_get_widget(xml, "message_entry");
  gtk_widget_set_sensitive(w, message && active);
}

void goat_settings_changed_cb(GConfClient *client, 
			      guint id,
			      GConfEntry *entry, 
			      Goat *goat)
{
  if (strcmp(entry->key, "/apps/goats/settings/delete_hides") == 0)
    goat_set_delete_hides(goat, gconf_value_get_bool(entry->value));
}

void goat_properties_changed_cb(GConfClient *client, 
				guint id,
				GConfEntry *entry, 
				Goat *goat)


     /* Callback for whenever a property of the note
	is changed in GConf */
{
  const gchar *text, *property;
  gint pos, length;
  
  length = strlen(entry->key);
  for (pos = length; pos>=0; pos--) 
    if (entry->key[pos] == '/') break;
  if (pos > 0) property = entry->key + (pos + 1) * sizeof(char);
  else return;

  if (strcmp(property, "hidden") == 0) {
    goat_set_hidden(goat, gconf_value_get_bool(entry->value));
  }
  if (strcmp(property, "shaded") == 0) {
    goat_set_shaded(goat, gconf_value_get_bool(entry->value));
  }
  if (strcmp(property, "sticky") == 0) {
    goat_set_sticky(goat, gconf_value_get_bool(entry->value));
  }
  if (strcmp(property, "font_name") == 0) {
    text = gconf_value_get_string(entry->value);
    goat_set_font(goat, text);
  }
  if (strcmp(property, "title") == 0) {
    text = gconf_value_get_string(entry->value);
    goat_set_title(goat, text);
  }
  if (strcmp(property, "alarm_active") == 0) {
    goat_set_alarm_active(goat, gconf_value_get_bool(entry->value));
  }
  if (strcmp(property, "alarm_time") == 0) {
    goat_set_alarm_time(goat, gconf_value_get_int(entry->value));
  }
  if (strcmp(property, "fg_r") == 0) {
    goat_set_fg(goat, goat_get_gdk_color(goat, "fg"));
  }
  if (strcmp(property, "bg_r") == 0) {
    goat_set_bg(goat, goat_get_gdk_color(goat, "bg"));
  }
  if (strcmp(property, "text") == 0) {
    text = gconf_value_get_string(entry->value);
    goat_set_text(goat, text);
  }
  if (strcmp(property, "alive") == 0) {
    if (!gconf_value_get_bool(entry->value))
      goat_delete(goat);
  }
  if (strcmp(property, "raised") == 0) {
    if (gconf_value_get_bool(entry->value))
      goat_raise(goat);
    goat_set_bool(goat, "raised", FALSE);
  }
  if (strcmp(property, "lowered") == 0) {
    if (gconf_value_get_bool(entry->value))
      goat_lower(goat);
    goat_set_bool(goat, "lowered", FALSE);
  }
  if (strcmp(property, "popup") == 0) {
    if (gconf_value_get_bool(entry->value))
      goat_popup(goat);
    goat_set_bool(goat, "popup", FALSE);
  }
  if (strcmp(property, "x") == 0) {
    goat_set_position(goat, gconf_value_get_int(entry->value), goat->geometry->y);
  }
  if (strcmp(property, "y") == 0) {
    goat_set_position(goat, goat->geometry->x, gconf_value_get_int(entry->value));
  }
  if (strcmp(property, "height") == 0) {
    goat_set_size(goat, goat->geometry->width, gconf_value_get_int(entry->value));
  }
  if (strcmp(property, "width") == 0) {
    goat_set_size(goat, gconf_value_get_int(entry->value), goat->geometry->height);
  }
  if (strcmp(property, "workspace") == 0) {
    goat_set_workspace(goat, gconf_value_get_int(entry->value));
  }
}
