/* goat.c */

#include <config.h> 
#include <gnome.h>
#include <glade/glade.h>

#include <gconf/gconf-client.h>
#include <libgnomeprint/gnome-print.h>
#include <libgnomeprint/gnome-print-job.h>
#include <libgnomeprintui/gnome-print-dialog.h>
#include <libgnomeprintui/gnome-print-job-preview.h>

#include "goat.h"
#include "goat-callbacks.h"

/* here's some defaults */

#define MIN_SIZE 70
#define MAX_SIZE 1000

static void update_alarm_timeout(Goat *goat)
{
  guint elapsed;
  gint current_time, alarm_time, timeout_id;

  timeout_id = goat_get_int(goat, "timeout_id");
  alarm_time = goat_get_int(goat, "alarm_time");

  if (timeout_id != 0) {
    g_source_remove(timeout_id);
    goat_set_int(goat, "timeout_id", 0);
  }

  if (goat_get_bool(goat, "alarm_active")) {
    current_time = time(NULL);
    if (current_time < alarm_time)
      elapsed = alarm_time - current_time;
    else
      elapsed = 0;
 
    goat_set_int(goat, "timeout_id",
		 g_timeout_add(1000*elapsed,
			       (GSourceFunc)goat_alarm_triggered_cb,
			       goat));
  }
}

static GdkColor *get_gdk_color(Goat *goat, const gchar *path)

     /* FIXME make this look like gconf_client funcs? */
{
  GdkColor color;
  GdkColormap *colormap;
  gchar *key;

  colormap = gdk_colormap_get_system();

  key = g_strdup_printf("%s_r", path);
  color.red = gconf_client_get_int(goat->gconf_client, 
				    key, NULL);
  g_free(key);
  key = g_strdup_printf("%s_g", path);
  color.green = gconf_client_get_int(goat->gconf_client, 
				      key, NULL);
  g_free(key);
  key = g_strdup_printf("%s_b", path);
  color.blue = gconf_client_get_int(goat->gconf_client, 
				     key, NULL);
  g_free(key);

  gdk_colormap_alloc_color(colormap, &color, FALSE, TRUE);

  return gdk_color_copy(&color);
}

static void set_gdk_color(Goat *goat, const gchar *path,
			  const GdkColor *color)
{
  gchar *key;

  key = g_strdup_printf("%s_r", path);
  gconf_client_set_int(goat->gconf_client, 
		       key, color->red, NULL);
  g_free(key);
  key = g_strdup_printf("%s_g", path);
  gconf_client_set_int(goat->gconf_client, 
		       key, color->green, NULL);
  g_free(key);
  key = g_strdup_printf("%s_b", path);
  gconf_client_set_int(goat->gconf_client, 
		       key, color->blue, NULL);
  g_free(key);
}

static void gconf_client_init(Goat *goat)
{
  goat->gconf_client=gconf_client_get_default();
  g_assert(goat->gconf_client != NULL);
  gconf_client_add_dir(goat->gconf_client,
		       "/apps/goats", 
		       GCONF_CLIENT_PRELOAD_NONE, NULL);

  goat->gconf_path=g_strdup_printf("%s%s",
				   "/apps/goats/herd/",
				   goat->name);
}

static void window_init(Goat *goat)
{
  GtkWidget *hbox, *cross, *resize, *scrollwin, *w;
  gchar *fname, *title, *goat_title, *text;
  GtkTextBuffer *buffer;
  GtkTextIter start;
  GtkTextMark *mark;
  GtkTargetList *targets;

  /* create the main window */

  goat->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (goat->window), "Goat");
  gtk_window_set_resizable(GTK_WINDOW(goat->window), TRUE);
  gtk_window_set_icon(GTK_WINDOW(goat->window), goat->icon);
  gtk_window_set_role(GTK_WINDOW(goat->window), goat->name);
  gtk_window_set_decorated(GTK_WINDOW(goat->window), FALSE);
  gtk_window_set_type_hint(GTK_WINDOW(goat->window), 
			   GDK_WINDOW_TYPE_HINT_TOOLBAR);
  gtk_window_set_default_size(GTK_WINDOW(goat->window),
			      goat->geometry->width,
			      goat->geometry->height);

  goat_title = goat_get_string(goat, "title");
  if (!goat_title) goat_title = g_strdup(_("(no title)"));
  title = g_strdup_printf("Goat: %s", goat_title);
  gtk_window_set_title(GTK_WINDOW(goat->window), title);
  g_free(title);
  g_free(goat_title);

  gtk_widget_set_events(goat->window, 
                        GDK_BUTTON_PRESS_MASK |
                        GDK_BUTTON_RELEASE_MASK |
                        GDK_POINTER_MOTION_MASK |
                        GDK_POINTER_MOTION_HINT_MASK |
			GDK_CONFIGURE);

  goat->vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (goat->window), goat->vbox);

  hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (goat->vbox), hbox, FALSE, FALSE, 0);

  if (gconf_client_get_bool(goat->gconf_client,
			    "/apps/goats/settings/delete_hides",
			    NULL))
    fname = g_strdup_printf("%s%s",GOATS_DIR,"hide.png");
  else
    fname = g_strdup_printf("%s%s",GOATS_DIR,"cross.png");

  goat->cross = gtk_image_new_from_file(fname);
  g_free(fname);

  fname = g_strdup_printf("%s%s",GOATS_DIR,"resize.png");
  resize = gtk_image_new_from_file(fname);
  g_free(fname);

  goat->eventbox[0] = gtk_event_box_new();
  gtk_box_pack_start (GTK_BOX (hbox), goat->eventbox[0], FALSE, FALSE, 0);
  gtk_container_add(GTK_CONTAINER(goat->eventbox[0]), resize);

  goat->eventbox[1] = gtk_event_box_new();
  gtk_box_pack_start (GTK_BOX (hbox), goat->eventbox[1], TRUE, TRUE, 0);
  goat->title = gtk_label_new(NULL);
  gtk_label_set_justify(GTK_LABEL(goat->title), GTK_JUSTIFY_LEFT);
  gtk_container_add(GTK_CONTAINER(goat->eventbox[1]), goat->title);

  goat->eventbox[2] = gtk_event_box_new();
  gtk_box_pack_start (GTK_BOX (hbox), goat->eventbox[2], FALSE, FALSE, 0);
  gtk_container_add(GTK_CONTAINER(goat->eventbox[2]), goat->cross);

  scrollwin = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
				 GTK_POLICY_NEVER,
				 GTK_POLICY_NEVER);

  goat->text_view = gtk_text_view_new();
  gtk_text_view_set_editable(GTK_TEXT_VIEW(goat->text_view), 
			     TRUE);
  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(goat->text_view), 
			      GTK_WRAP_WORD);

  targets = gtk_drag_dest_get_target_list(goat->text_view);
  gtk_target_list_add(targets, gdk_atom_intern("text/uri-list", FALSE),
		      0, 0);
  gtk_drag_dest_set_target_list(goat->text_view, targets);

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

  gtk_container_add(GTK_CONTAINER(scrollwin), 
		    goat->text_view);

  gtk_box_pack_start (GTK_BOX (goat->vbox), scrollwin, 
		      TRUE, TRUE, 0);

  goat->fg = NULL;
  goat_set_fg(goat, goat_get_gdk_color(goat, "fg"));
  goat->bg = NULL;
  goat->bg_topbar = NULL;
  goat_set_bg(goat, goat_get_gdk_color(goat, "bg"));
  
  text = goat_get_string(goat, "font_name");
  goat_set_font(goat, text);
  g_free(text);

  text = goat_get_string(goat, "text");
  goat_set_text(goat, text);
  g_free(text);

  text = goat_get_string(goat, "title");
  goat_set_title(goat, text);
  g_free(text);

}

static void menu_init(Goat *goat)
{
  GtkAccelGroup *accel_group;

  goat->menu_xml = glade_xml_new(goat->xml_file, "menu", NULL);
  /*  glade_xml_signal_autoconnect(goat->menu_xml); */

  goat->menu = glade_xml_get_widget(goat->menu_xml, "menu");
  glade_xml_signal_connect_data(goat->menu_xml, "goat_create_new_cb", 
				(GtkSignalFunc)goat_create_new_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_save_cb",
				(GtkSignalFunc)goat_text_save_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_cut_cb", 
				(GtkSignalFunc)goat_text_cut_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_copy_cb", 
				(GtkSignalFunc)goat_text_copy_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_paste_cb", 
				(GtkSignalFunc)goat_text_paste_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_print_cb", 
				(GtkSignalFunc)goat_text_print_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_mail_cb", 
				(GtkSignalFunc)goat_text_mail_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_ipmessage_cb", 
				(GtkSignalFunc)goat_text_ipmessage_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_clear_cb", 
				(GtkSignalFunc)goat_text_clear_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_set_title_cb", 
				(GtkSignalFunc)goat_set_title_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_set_alarm_cb", 
				(GtkSignalFunc)goat_set_alarm_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_insert_file_cb", 
				(GtkSignalFunc)goat_insert_file_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_insert_time_cb", 
				(GtkSignalFunc)goat_insert_time_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_insert_date_cb", 
				(GtkSignalFunc)goat_insert_date_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_insert_calendar_cb", 
				(GtkSignalFunc)goat_insert_calendar_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_insert_fortune_cb", 
				(GtkSignalFunc)goat_insert_fortune_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_text_fitsize_cb", 
				(GtkSignalFunc)goat_properties_autosize_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_change_colour_cb", 
				(GtkSignalFunc)goat_properties_bg_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_change_fg_cb", 
				(GtkSignalFunc)goat_properties_fg_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_change_font_cb", 
				(GtkSignalFunc)goat_properties_font_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_raise_cb", 
				(GtkSignalFunc)goat_raise_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_lower_cb", 
				(GtkSignalFunc)goat_lower_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_change_sticky_cb", 
				(GtkSignalFunc)goat_sticky_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_shade_cb", 
				(GtkSignalFunc)goat_shade_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_hide_cb", 
				(GtkSignalFunc)goat_hide_cb, goat);
  glade_xml_signal_connect_data(goat->menu_xml, "goat_delete_cb", 
				(GtkSignalFunc)goat_delete_cb, goat);

  accel_group = gtk_menu_get_accel_group(GTK_MENU(goat->menu));
  gtk_window_add_accel_group(GTK_WINDOW(goat->window), accel_group);
}

static void connect_signals(Goat *goat)
{
  GtkWidget *w;

  /* Gconf signals when properties are modified */

  goat->gconf_settings_notify_id = 
    gconf_client_notify_add(goat->gconf_client,
			    "/apps/goats/settings",
			    (GConfClientNotifyFunc)goat_settings_changed_cb, 
			    goat, NULL, NULL);
  goat->gconf_properties_notify_id = 
    gconf_client_notify_add(goat->gconf_client,
			    goat->gconf_path,
			    (GConfClientNotifyFunc)goat_properties_changed_cb, 
			    goat, NULL, NULL);

  /* GUI signals for operations on the note */

  /* Main window */

  g_signal_connect (G_OBJECT (goat->window),
		    "configure_event",
		    G_CALLBACK (goat_configure_cb),
		    goat);
  g_signal_connect_after (G_OBJECT (goat->window),
			  "map",
			  G_CALLBACK (goat_window_mapped_cb),
			  goat);
  g_signal_connect_after (G_OBJECT (goat->window),
			  "realize",
			  G_CALLBACK (goat_window_realized_cb),
			  goat);

  /* Text box */

  g_signal_connect (G_OBJECT (goat->text_view),
		    "button_press_event",
		    G_CALLBACK (goat_menu_button_cb),
		    goat);
  g_signal_connect_after(G_OBJECT (goat->text_view),
		    "focus-out-event",
		    G_CALLBACK (goat_text_focus_out_cb),
		    goat);
  g_signal_connect (G_OBJECT(goat->text_view),
		    "popup-menu",
		    G_CALLBACK(goat_menu_popup_cb),
		    goat);
  g_signal_connect (G_OBJECT (goat->text_view),
		    "drag-data-received",
		    G_CALLBACK (goat_drag_received_cb),
		    goat);
  g_signal_connect_after (G_OBJECT (goat->text_view),
			  "realize",
			  G_CALLBACK (goat_text_mapped_cb),
			  goat);

  /* Topbar widgets */

  g_signal_connect (G_OBJECT (goat->eventbox[0]),
		    "button_press_event",
		    G_CALLBACK (goat_resize_pressed_cb),
		    goat);
  g_signal_connect (G_OBJECT (goat->eventbox[1]),
		    "button_press_event",
		    G_CALLBACK (goat_topbar_pressed_cb),
		    goat);
  g_signal_connect (G_OBJECT (goat->eventbox[2]),
		    "button_press_event",
  		    G_CALLBACK (goat_cross_pressed_cb),
		    goat);
  g_signal_connect_after (G_OBJECT (goat->eventbox[0]),
			  "realize",
			  G_CALLBACK (goat_e0_mapped_cb),
			  goat);
  g_signal_connect_after (G_OBJECT (goat->eventbox[1]),
			  "realize",
			  G_CALLBACK (goat_e1_mapped_cb),
			  goat);
  g_signal_connect_after (G_OBJECT (goat->eventbox[2]),
			  "realize",
			  G_CALLBACK (goat_e2_mapped_cb),
			  goat);
}

static void connect_alarm_signals(Goat *goat)

  /* alarm signals */
{
  GtkWidget *w;

  w = glade_xml_get_widget(goat->alarm_xml, "set_alarm");
  g_signal_connect(G_OBJECT(w), "delete_event", 
		   G_CALLBACK(goat_alarm_dialog_delete_cb),
		   goat);
  w = glade_xml_get_widget(goat->alarm_xml, "ok");
  g_signal_connect(G_OBJECT(w), "clicked",
		   G_CALLBACK(goat_alarm_dialog_ok_cb),
		   goat);
  w = glade_xml_get_widget(goat->alarm_xml, "cancel");
  g_signal_connect(G_OBJECT(w), "clicked",
		   G_CALLBACK(goat_alarm_dialog_cancel_cb),
		   goat);
  w = glade_xml_get_widget(goat->alarm_xml, "help");
  g_signal_connect(G_OBJECT(w), "clicked",
		   G_CALLBACK(goat_alarm_dialog_help_cb),
		   goat);
  w = glade_xml_get_widget(goat->alarm_xml, 
			   "alarm_on_checkbutton");
  g_signal_connect(G_OBJECT(w), "toggled",
		   G_CALLBACK(goat_alarm_active_toggled_cb),
		   goat);

  w = glade_xml_get_widget(goat->alarm_xml, "sound_checkbutton");
  g_signal_connect(G_OBJECT(w), "toggled",
		   G_CALLBACK(goat_alarm_sound_toggled_cb),
		   goat);

  w = gtk_option_menu_get_menu(GTK_OPTION_MENU(glade_xml_get_widget(goat->alarm_xml, "notify_menu")));
  g_signal_connect(G_OBJECT(w), "selection_done",
		   G_CALLBACK(goat_alarm_menu_changed_cb),
		   goat);
}

static void set_default_properties(Goat *goat)
{
  gint bg_index;
  gchar *key, *text;
  gboolean sticky;

  goat_set_bool(goat, "hidden", FALSE);
  goat_set_bool(goat, "shaded", FALSE);
  sticky = gconf_client_get_bool(goat->gconf_client,
				 "/apps/goats/defaults/sticky",
				 NULL);

  goat_set_bool(goat, "sticky", sticky);		
  goat_set_bool(goat, "lowered" , FALSE); 
  text = gconf_client_get_string(goat->gconf_client,
				 "/apps/goats/defaults/font_name",
				 NULL);
  goat_set_string(goat, "font_name", text);
  g_free(text);

  goat_set_bool(goat, "alarm_active", FALSE);
  goat_set_int(goat, "alarm_time", time(NULL));
  goat_set_int(goat, "alarm_repeat", 0);
  goat_set_bool(goat, "alarm_use_sound", 
		gconf_client_get_bool(goat->gconf_client,
				      "/apps/goats/defaults/use_alarm_sound",
				      NULL));
  text = gconf_client_get_string(goat->gconf_client,
				 "/apps/goats/defaults/alarm_sound_filename",
				 NULL);
  goat_set_string(goat, "alarm_sound_filename", text);
  g_free(text);
  goat_set_bool(goat, "alarm_use_message", TRUE);
  goat_set_string(goat, "alarm_message", _("Alarm Triggered!"));

  goat->fg = NULL;
  goat_set_gdk_color(goat, "fg", 
		     get_gdk_color(goat, "/apps/goats/defaults/fg"));
  goat->bg = NULL;
  goat->bg_topbar = NULL;
  bg_index = gconf_client_get_int(goat->gconf_client, 
				  "/apps/goats/defaults/bg", 
				  NULL);
  if (bg_index == 3) bg_index = rand()%3;
  key = g_strdup_printf("/apps/goats/defaults/bg%i", bg_index+1);
  goat_set_gdk_color(goat, "bg", get_gdk_color(goat, key));
  g_free(key);

  goat_set_string(goat, "title", NULL);
  goat_set_string(goat, "text", NULL);

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

  if (!sticky) goat_set_int(goat, "workspace", goat_get_current_workspace());
}

Goat *goat_new(const gchar *name, gboolean is_new)
{
  Goat *goat;
  gchar *fname;
  int ws;

  goat = (Goat *)g_malloc(sizeof(Goat));
  goat->geometry = (GoatGeometry *)g_malloc(sizeof(GoatGeometry));

  goat->name = g_strdup(name);
  goat->dragging = FALSE;
  goat->resizing = FALSE;
  goat->alarm_inited = FALSE;
  goat->xml_file = g_strdup_printf("%s%s", GOATS_DIR, 
				   "goats.glade2");
  fname = g_strdup_printf("%sgoats_focus.png", GOATS_DIR);
  goat->icon = gdk_pixbuf_new_from_file(fname, NULL);
  g_free(fname);

  goat->geometry->dx = 0;
  goat->geometry->dy = 0;
  
  gconf_client_init(goat);

  goat_set_int(goat, "timeout_id", 0);

  if (is_new) {
    goat->geometry->width = gconf_client_get_int(goat->gconf_client,
						 "/apps/goats/defaults/width", NULL);
    goat->geometry->height = gconf_client_get_int(goat->gconf_client,
						  "/apps/goats/defaults/height", NULL);
    goat->geometry->x = -1;
    goat->geometry->y = -1;

    set_default_properties(goat);
  }
  else {
    goat->geometry->height = goat_get_int(goat, "height");
    goat->geometry->width = goat_get_int(goat, "width");
    goat->geometry->x = goat_get_int(goat, "x");
    goat->geometry->y = goat_get_int(goat, "y");
  }

  window_init(goat);
  menu_init(goat);

  connect_signals(goat);
  update_alarm_timeout(goat);

  goat_set_hidden(goat, goat_get_bool(goat, "hidden"));

  return goat;
}

void goat_alarm_init(Goat *goat)
{
  if (goat->alarm_inited) return;
  
  goat->alarm_xml = glade_xml_new(goat->xml_file, 
				  "set_alarm", NULL); 

  connect_alarm_signals(goat);
   
  goat->alarm_inited = TRUE;
}

/* Actions on the goat */

static void unset_dir(const gchar *key, GConfClient *client)
{
  GSList *subdirs, *keys;
  GConfEntry *entry;
  int i;

  subdirs = gconf_client_all_dirs(client, key, NULL);
  g_slist_foreach(subdirs, (GFunc)unset_dir, client);
  g_slist_foreach(subdirs, (GFunc)g_free, NULL);
  g_slist_free(subdirs);

  keys = gconf_client_all_entries(client, key, NULL);
  for (i=0; i<g_slist_length(keys); i++) {
    entry = g_slist_nth_data(keys, i);
    gconf_client_unset(client, entry->key, NULL);
    gconf_entry_free(entry);
  }
  g_slist_free(keys);
}

void goat_delete(Goat *goat)

     /* Delete the goat */
{
  /* gconf stuff */

  gconf_client_notify_remove(goat->gconf_client, 
			     goat->gconf_properties_notify_id);
  gconf_client_notify_remove(goat->gconf_client, 
			     goat->gconf_settings_notify_id);
  unset_dir(goat->gconf_path, goat->gconf_client);
  g_free(goat->gconf_path);
  g_object_unref(goat->gconf_client);

  /* GTK stuff */

  gtk_widget_destroy(goat->menu);
  g_object_unref(G_OBJECT(goat->menu_xml));
  gtk_widget_destroy(GTK_WIDGET(goat->window));
  gdk_color_free(goat->fg);
  gdk_color_free(goat->bg);
  gdk_color_free(goat->bg_topbar);
  if (goat->alarm_inited)
    g_object_unref(G_OBJECT(goat->alarm_xml));
  g_free(goat->xml_file);

  /* Misc */

  g_free(goat->name);
  g_free(goat->geometry);

  g_free(goat);
}

void goat_raise(Goat *goat)
{
  gdk_window_raise(goat->window->window);
}

void goat_lower(Goat *goat)
{
  gdk_window_lower(goat->window->window);
}

void goat_insert_text(Goat *goat, const gchar *text)

     /* Insert the given text into the goat at the current
	cursor position */
{
  gint pos, length;

  if (text == NULL) return;

  length = strlen(text);

  gtk_text_buffer_insert_at_cursor
    (GTK_TEXT_BUFFER(gtk_text_view_get_buffer(GTK_TEXT_VIEW(goat->text_view))),
     text, length);
}

gchar *get_line(const char *text, GnomeFont *font, double max_width, int *next_pos)

     /* Get next line from text, ending either at next newline or when
	the width of the line (in given font) is more than max_width.
	Return how many chars to skip in text to get the start of the 
	following line, or -1 for end of text */
{
  int i;
  double width;
  gchar *line;

  if (!text) {
    *next_pos = -1;
    return NULL;
  }

  for (i=0; i<strlen(text); i++) {
    line = g_strndup(text, i);
    if (gnome_font_get_width_utf8(font, line) > max_width) break;
    if (text[i] == '\n') break;
    g_free(line);
  }

  if (i == strlen(text)) {
    /* reached end of string */
    *next_pos = -1;
    line = g_strdup(text);
  }
  else if (text[i] == '\n') *next_pos = i+1; /* skip newline */
  else *next_pos = i;

  return line;
}

static void do_page_border(GnomePrintContext *ctx, gdouble start_x, gdouble start_y,
			   gdouble end_x, gdouble end_y, gdouble max_width, 
			   gdouble titlebox_height,
			   GnomeFont *title_font, const gchar *title_text)
{
  int pos, next_pos, lines;
  gchar *line;
  gdouble line_width, title_height;

  title_height = gnome_font_get_ascender (title_font) -
    gnome_font_get_descender (title_font);

  /* do topbar */
 
  gnome_print_setrgbcolor(ctx, 0.9, 0.9, 0.9); /* grey for topbar */
  gnome_print_rect_filled(ctx, start_x, start_y - titlebox_height,
    			  end_x - start_x, titlebox_height);
  gnome_print_setrgbcolor(ctx, 0, 0, 0);

  /* do title text */

  if (title_text) {
    gnome_print_setfont(ctx, title_font);
    pos = 0;
    lines = 0;
    do {
      line = get_line(&title_text[pos], title_font, max_width, &next_pos);
      line_width = gnome_font_get_width_utf8(title_font, line);
      gnome_print_moveto(ctx, (start_x + end_x - line_width) / 2, 
			 start_y - title_height/2 - gnome_font_get_ascender(title_font) - title_height*lines);
      if (line) gnome_print_show(ctx, line);
      lines++;
      g_free(line);
      pos = pos + next_pos;
    } while (next_pos != -1);
  }

  /* do frame */

  gnome_print_setlinewidth(ctx, 1.0);
  gnome_print_moveto(ctx, start_x, start_y);
  gnome_print_lineto(ctx, end_x, start_y);
  gnome_print_lineto(ctx, end_x, end_y);
  gnome_print_lineto(ctx, start_x, end_y);
  gnome_print_lineto(ctx, start_x, start_y);
  gnome_print_stroke(ctx);
  gnome_print_moveto(ctx, start_x, start_y - titlebox_height);
  gnome_print_lineto(ctx, end_x, start_y - titlebox_height);
  gnome_print_stroke(ctx);

}

static void do_print(Goat *goat, GnomePrintContext *ctx, gdouble start_x, gdouble end_x,
		     gdouble start_y, gdouble end_y)

     /* FIXME check memory */
{
  GnomeFont *title_font, *body_font;
  gdouble title_height, title_width, body_height, 
    line_width, titlebox_height, title_text_width,
    border_width, body_text_width, max_width;
  int i, lines, pages, pos, next_pos, title_nlines, page;
  gchar *title_text, *body_text, *line, 
    *title_font_name, *body_font_name, *page_name;

  border_width = 0.5 * 72 / 2.54; /* This should be 0.5 cm */
  max_width = end_x - start_x - 2 * border_width;
 
  body_font_name = goat_get_string(goat, "font_name");
  body_font = gnome_font_find_closest_from_full_name(body_font_name);
  body_height = gnome_font_get_ascender (body_font) - 
    gnome_font_get_descender (body_font);
  g_free(body_font_name);

  title_font_name = gconf_client_get_string(goat->gconf_client,
					    "/desktop/gnome/interface/font_name", NULL);
  title_font = gnome_font_find_closest_from_full_name(title_font_name);
  title_height = gnome_font_get_ascender (title_font) -
    gnome_font_get_descender (title_font);
  g_free(title_font_name);

  body_text = goat_get_string(goat, "text");
 
  title_text = goat_get_string(goat, "title"); 
  title_text_width = gnome_font_get_width_utf8(title_font, title_text);
  title_nlines = 1 + title_text_width / max_width;
  titlebox_height = (1 + title_nlines) * title_height;

  page=1;
  page_name = g_strdup_printf("%i", page);
  gnome_print_beginpage (ctx, page_name);
  g_free(page_name);

  /* Do body text */

  gnome_print_setrgbcolor(ctx, 0, 0, 0); /* black for lines & text */
  gnome_print_setfont(ctx, body_font);

  lines = 0;
  pos = 0;
  next_pos = 0;
  line_width = 0;
  if (title_text_width < max_width) body_text_width = title_text_width;
  else body_text_width = max_width;

  do {
    line = get_line(&body_text[pos], body_font, max_width, &next_pos);
    gnome_print_moveto(ctx, start_x + border_width, 
		       start_y - border_width - titlebox_height - gnome_font_get_ascender(body_font) - body_height*lines);
    line_width = gnome_font_get_width_utf8(body_font, line);
    if (line_width > body_text_width) {
      body_text_width = line_width;
    }

    if (line)
      gnome_print_show(ctx, line);
    lines++;
    if (start_y - border_width*2 - titlebox_height - body_height*lines < end_y) {
      page++;
      do_page_border(ctx, start_x, start_y, 
		     start_x + body_text_width + 2*border_width, 
		     start_y - border_width*2 - titlebox_height - body_height * lines, 
		     max_width, titlebox_height, title_font, title_text);
      gnome_print_showpage(ctx);
      page_name = g_strdup_printf("%i", page);
      gnome_print_beginpage(ctx, page_name);
      g_free(page_name);
      lines = 0;
      gnome_print_setfont(ctx, body_font);
    }

    g_free(line);
    pos = pos + next_pos;
  } while (next_pos != -1);

  do_page_border(ctx, start_x, start_y, 
		 start_x + body_text_width + 2*border_width,
		 start_y - border_width*2 - titlebox_height - body_height * lines, 
		 max_width, titlebox_height, title_font, title_text);
  gnome_print_showpage(ctx);

  g_free(body_text);
  g_free(title_text);
}

void goat_print(Goat *goat)

     /* Print the note - FIXME check memory */
{
  GnomePrintJob *pjob;
  GnomePrintConfig *pconfig;
  GnomePrintContext *pctx;
  const GnomePrintUnit *unit;
  GtkWidget *dialog, *preview;
  gint response, presponse;
  gdouble margin_t, margin_b, margin_l, margin_r, height, width;

  pjob = gnome_print_job_new(gnome_print_config_default());
  dialog = gnome_print_dialog_new(pjob, "Print note", 
				  GNOME_PRINT_DIALOG_COPIES);
  pctx = gnome_print_job_get_context(pjob);

  gtk_widget_show (dialog);

  response = gtk_dialog_run (GTK_DIALOG (dialog));
  
  pconfig = gnome_print_job_get_config(pjob);
  
  gnome_print_job_get_page_size_from_config(pconfig, &width, &height);
  
  if (gnome_print_config_get_length (pconfig, GNOME_PRINT_KEY_PAGE_MARGIN_LEFT, 
				     &margin_l, &unit)) 
    gnome_print_convert_distance (&margin_l, unit, GNOME_PRINT_PS_UNIT);

  if (gnome_print_config_get_length (pconfig, GNOME_PRINT_KEY_PAGE_MARGIN_RIGHT, 
				     &margin_r, &unit))
    gnome_print_convert_distance (&margin_r, unit, GNOME_PRINT_PS_UNIT);
  
  if (gnome_print_config_get_length (pconfig, GNOME_PRINT_KEY_PAGE_MARGIN_TOP, 
				     &margin_t, &unit)) 
    gnome_print_convert_distance (&margin_t, unit, GNOME_PRINT_PS_UNIT);
  if (gnome_print_config_get_length (pconfig, GNOME_PRINT_KEY_PAGE_MARGIN_BOTTOM, 
				     &margin_b, &unit)) 
    gnome_print_convert_distance (&margin_b, unit, GNOME_PRINT_PS_UNIT);
   
  if (response == GNOME_PRINT_DIALOG_RESPONSE_PREVIEW) {
    do_print(goat, pctx, margin_r, width - margin_r, 
	     height - margin_t, margin_b);
    gnome_print_job_close (pjob);
    preview = gnome_print_job_preview_new(pjob, _("Print Preview"));
    gtk_widget_show(preview);
  }
  
  if (response == GNOME_PRINT_DIALOG_RESPONSE_PRINT) {
    do_print(goat, pctx, margin_r, width - margin_r, 
	     height - margin_t, margin_t);
    gnome_print_job_close (pjob);
    gnome_print_job_print(pjob);
  }
  
  gtk_widget_destroy(dialog);
}

void goat_popup(Goat *goat)

     /* Pop up the goat according to user preferences */
{
  goat_set_bool(goat, "hidden", FALSE);
  goat_set_bool(goat, "shaded", FALSE);
  gtk_window_present(GTK_WINDOW(goat->window));
}

/* Get/set methods */

gint goat_get_topbar_height(Goat *goat)
{
  GtkRequisition req;

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

  if (req.height > 20)
    return req.height;
  else
    return 20;
}

void goat_set_hidden(Goat *goat, gboolean hidden)

     /* Sets whether the goat is visible or not */
{
  if (hidden) {
    gtk_widget_hide(goat->window);
  }
  else {
    gtk_widget_show_all(goat->window);
    if (goat_get_bool(goat, "shaded")) {
      gtk_widget_hide(goat->text_view);
      gtk_window_resize(GTK_WINDOW(goat->window), 
			goat->geometry->width, 
			goat_get_topbar_height(goat));
    }
  }
}

void goat_set_shaded(Goat *goat, gboolean shaded)

     /* Sets if the goat is shaded (only title bar visible) */
{
  GtkWidget *w;
  
  if(!goat_get_bool(goat, "hidden")) {
    if (shaded) {
      gtk_widget_hide(goat->text_view);
      gtk_window_resize(GTK_WINDOW(goat->window), 
			goat->geometry->width, 
			goat_get_topbar_height(goat));
    }
    else {
      gtk_window_resize(GTK_WINDOW(goat->window), 
			goat->geometry->width, 
			goat->geometry->height);
      gtk_widget_show(goat->text_view);
    }
  }

  w = glade_xml_get_widget(goat->menu_xml, "shade");

  if (shaded)
    gtk_label_set_text(GTK_LABEL(GTK_BIN(w)->child), _("Unshade"));
  else 
    gtk_label_set_text(GTK_LABEL(GTK_BIN(w)->child), _("Shade"));
}

void goat_set_size(Goat *goat, gint width, gint height)

     /* Sets the size of the goat, if valid */
{
  if (height < MIN_SIZE) height = MIN_SIZE; 
  if (width < MIN_SIZE) width = MIN_SIZE;
  if (height > MAX_SIZE) height = MAX_SIZE; 
  if (width > MAX_SIZE) width = MAX_SIZE;

  goat->geometry->height = height;
  goat->geometry->width = width;

  gtk_window_resize(GTK_WINDOW(goat->window), width, height);
}

void goat_set_sticky(Goat *goat, gboolean sticky)

     /* Sets if the goat is sticky (appears on all desktops) */
{
  GtkWidget *w;

  if (sticky)
    gtk_window_stick(GTK_WINDOW(goat->window));
  else 
    gtk_window_unstick(GTK_WINDOW(goat->window));

  w = glade_xml_get_widget(goat->menu_xml, "sticky");
  if (w == NULL) return;

  if (sticky)
    gtk_label_set_text(GTK_LABEL(GTK_BIN(w)->child), _("Unstick"));
  else 
    gtk_label_set_text(GTK_LABEL(GTK_BIN(w)->child), _("Stick"));
}

void goat_set_floating(Goat *goat, gboolean floating)

     /* Sets if the goat is floating (apears above other
	windows). This doesn't work at the moment */
{
  GtkWidget *w;
  GQuark detail;
  gint num;

  /*  w = goat->properties_menu[FLOATING_INDEX].widget; */

/*  num = g_signal_handlers_block_matched (G_OBJECT(w),
					 G_SIGNAL_MATCH_FUNC,
					 0, detail,
					 NULL,
					 goat_change_floating_cb,
					 NULL);

  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), 
				 floating); 

  num = g_signal_handlers_unblock_matched (G_OBJECT(w),
					   G_SIGNAL_MATCH_FUNC,
					   0, detail,
					   NULL,
					   goat_change_floating_cb,
					   NULL);
/*   if (floating) */
/*     gnome_win_hints_set_layer(goat->window,GDK_WINDOW_STATE_FLOATING); */
/*   else  */
/*     gnome_win_hints_set_layer(goat->window,GDK_WINDOW_STATE_NORMAL); */
}

void goat_set_title(Goat *goat, const gchar *title)

     /* Sets the title of the goat (appears in title bar) */
{
  gtk_label_set_text(GTK_LABEL(goat->title), title);
}

void goat_set_text(Goat *goat, const gchar *text)
{
  GtkTextIter start, end;
  GtkTextBuffer *buffer;

  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(goat->text_view));

  /* clear the current text */

  gtk_text_buffer_get_start_iter(buffer, &start);
  gtk_text_buffer_get_end_iter(buffer, &end);
  gtk_text_buffer_delete(buffer, &start, &end);

  /* insert the new text */

  goat_insert_text(goat, text);
}

void goat_set_delete_hides(Goat *goat, gboolean delete_hides)

     /* Set if the 'delete' button deletes or hides the goat */
{
  gchar *fname;

  if (delete_hides) 
    fname = g_strdup_printf("%s%s",GOATS_DIR,"hide.png");
  else 
    fname = g_strdup_printf("%s%s",GOATS_DIR,"cross.png");

  gtk_image_set_from_file(GTK_IMAGE(goat->cross), fname);
  g_free(fname);
}

void goat_set_bg(Goat *goat, GdkColor *color)

     /* set the background colour. color must not be freed */
{
  GdkColor *bg_topbar;
  GdkColormap *colormap;
  gboolean success;

  colormap = gdk_colormap_get_system();
  bg_topbar = gdk_color_copy(color);

  if (color->red - 35*256 > 0)
    bg_topbar->red = color->red - 35*256;
  else bg_topbar->red = 0;
  if (color->blue - 35*256 > 0)
    bg_topbar->blue = color->blue - 35*256;
  else bg_topbar->blue = 0;
  if (color->green - 35*256 > 0)
    bg_topbar->green = color->green - 35*256;
  else bg_topbar->green = 0;

  success = gdk_colormap_alloc_color(colormap, bg_topbar,
				     FALSE, TRUE);
  if (!success) goats_error("goat_set_bg",
			    _("Couldn't allocate colour"));

  gtk_widget_modify_base(goat->text_view, GTK_STATE_NORMAL,
			 color);
  gtk_widget_modify_bg(goat->eventbox[0], GTK_STATE_NORMAL,
		       bg_topbar);
  gtk_widget_modify_bg(goat->eventbox[1], GTK_STATE_NORMAL,
		       bg_topbar);
  gtk_widget_modify_bg(goat->eventbox[2], GTK_STATE_NORMAL,
		       bg_topbar);

  if (goat->bg != NULL) gdk_color_free(goat->bg);
  if (goat->bg_topbar != NULL) gdk_color_free(goat->bg_topbar);
  goat->bg = color;
  goat->bg_topbar = bg_topbar;
}

void goat_set_fg(Goat *goat, GdkColor *color)

     /* Set foreground colour. color must not be freed */
{
  gtk_widget_modify_text(goat->text_view, GTK_STATE_NORMAL,
			 color);
  gtk_widget_modify_fg(goat->title, GTK_STATE_NORMAL,
		       color);

  if (goat->fg != NULL) gdk_color_free(goat->fg);
  goat->fg = color;
}

void goat_set_font(Goat *goat, const gchar *font_name)
     
  /* FIXME should the top bar have the same font 
     as the text area? FIXME don't use goat_set_string
     FIXME should we free pango font desc?*/
{
  PangoFontDescription *pfd;

  if (!font_name) return;

  pfd = pango_font_description_from_string(font_name);

  gtk_widget_modify_font(goat->text_view, pfd);
  pango_font_description_free(pfd);
}

void goat_set_position(Goat *goat, gint x, gint y)
{
  gdk_window_move(goat->window->window, x, y);
  gdk_flush();
  goat->geometry->x = x;
  goat->geometry->y = y;
}

void goat_set_alarm_time(Goat *goat, gulong alarm_time)
{
  update_alarm_timeout(goat);
}

void goat_set_alarm_active(Goat *goat, gboolean active)
{
  update_alarm_timeout(goat);
}

void goat_create_alarm_dialog(Goat *goat)
{
  GtkWidget *w;
  gboolean use_sound, use_message, active;
  gint repeat, alarm_time;
  gchar *message, *sound;

  use_sound = goat_get_bool(goat, "alarm_use_sound");
  use_message = goat_get_bool(goat, "alarm_use_message");
  active = goat_get_bool(goat, "alarm_active");
  sound = goat_get_string(goat, "alarm_sound_filename");
  message = goat_get_string(goat, "alarm_message");
  repeat = goat_get_int(goat, "alarm_repeat");
  alarm_time = goat_get_int(goat, "alarm_time");

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

  w = glade_xml_get_widget(goat->alarm_xml, "remind_label");
  gtk_widget_set_sensitive(w, active);
  w = glade_xml_get_widget(goat->alarm_xml, "alarm_date");
  gtk_widget_set_sensitive(w, active);
  gnome_date_edit_set_time(GNOME_DATE_EDIT(w),
			   alarm_time);

  w = glade_xml_get_widget(goat->alarm_xml, "notify_menu");
  gtk_widget_set_sensitive(w, active);
  if (use_message) gtk_option_menu_set_history(GTK_OPTION_MENU(w), 0);
  else gtk_option_menu_set_history(GTK_OPTION_MENU(w), 1);
  w = glade_xml_get_widget(goat->alarm_xml, "message_entry");
  gtk_widget_set_sensitive(w, active && use_message);
  if (message != NULL)
    gtk_entry_set_text(GTK_ENTRY(w), message);
  w = glade_xml_get_widget(goat->alarm_xml, "sound_checkbutton");
  gtk_widget_set_sensitive(w, active);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), use_sound);

  w = glade_xml_get_widget(goat->alarm_xml, "sound_file_entry");
  gtk_widget_set_sensitive(w, active && use_sound);
  if (sound != NULL)
    gtk_entry_set_text
      (GTK_ENTRY(gnome_file_entry_gtk_entry(GNOME_FILE_ENTRY(w))),
       sound);
  gnome_file_entry_set_default_path(GNOME_FILE_ENTRY(w),
				    SOUNDDIR);
  w = glade_xml_get_widget(goat->alarm_xml, "repeat_menu");
  gtk_widget_set_sensitive(w, active);
  gtk_option_menu_set_history(GTK_OPTION_MENU(w), repeat);

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

  gtk_window_set_transient_for(GTK_WINDOW(w), GTK_WINDOW(goat->window));
  gtk_window_present(GTK_WINDOW(w));

  g_free(message);
  g_free(sound);
}

/* Gconf wrappers for properties of the goat */

gboolean goat_get_bool(Goat *goat, const gchar *name)

     /* get a true/false property called name */
{
  gchar *key;
  gboolean value;

  key = g_strdup_printf("%s/%s", goat->gconf_path, name);
  value = gconf_client_get_bool(goat->gconf_client, key,
				NULL);
  g_free(key);
  return value;
}

void goat_set_bool(Goat *goat, const gchar *name, gboolean value)

     /* set true/false property called name to value */
{
  gchar *key;

  key = g_strdup_printf("%s/%s", goat->gconf_path, name);
  gconf_client_set_bool(goat->gconf_client, key,
			value, NULL);
  g_free(key);
}

gchar *goat_get_string(Goat *goat, const gchar *name)

    /* get a string property called name */
{
  gchar *key, *value;

  key = g_strdup_printf("%s/%s", goat->gconf_path, name);
  value = gconf_client_get_string(goat->gconf_client, key,
				  NULL);
  g_free(key);
  return value;
}

void goat_set_string(Goat *goat, const gchar *name, const gchar *value)

     /* set string property called name to value */
{
  gchar *key;

  if (value == NULL) return;

  key = g_strdup_printf("%s/%s", goat->gconf_path, name);
  gconf_client_set_string(goat->gconf_client, key,
			  value, NULL);
  g_free(key);
}

gint goat_get_int(Goat *goat, const gchar *name)

    /* get a string property called name */
{
  gchar *key;
  gint value;

  key = g_strdup_printf("%s/%s", goat->gconf_path, name);
  value = gconf_client_get_int(goat->gconf_client, key,
			       NULL);
  g_free(key);
  return value;
}

void goat_set_int(Goat *goat, const gchar *name, gint value)

     /* set string property called name to value */
{
  gchar *key;

  key = g_strdup_printf("%s/%s", goat->gconf_path, name);
  gconf_client_set_int(goat->gconf_client, key,
		       value, NULL);
  g_free(key);
}

void goat_set_gdk_color(Goat *goat, gchar *name,
			GdkColor *color)
{
  gchar *path;

  path = g_strdup_printf("%s/%s", goat->gconf_path, name);
  set_gdk_color(goat, path, color);
  g_free(path);
}

GdkColor *goat_get_gdk_color(Goat *goat, gchar *name)
{
  GdkColor *color;
  gchar *path;

  path = g_strdup_printf("%s/%s", goat->gconf_path, name);
  color = get_gdk_color(goat, path);
  g_free(path);
	
  return color;
}
