#define STICKY_NOTES_C

#include "main.h"

#include "master_front.xpm"

static sticky_base base;

static GtkWidget *main_window = NULL;
static GtkWidget *save_all_item = NULL;

static main_config prefs =
{
 STICKY_MASTER_BUTTON_LOWER, /* button one */
 STICKY_MASTER_BUTTON_RAISE, /* button two */
 TRUE, /* auto_placment */
 STICKY_MASTER_QUIT_ASK, /* ask about saving before quit */
};

#ifdef USE_MOVE_RESIZE_SIGNAL
static int sticky_note_store_move_resize(GtkWidget *from, 
                                         int *x, int *y, int width, int height,
                                         sticky_note *note)
{
 note->x_pos = *x;
 note->y_pos = *y;
 note->x_size = width;
 note->y_size = height;

 printf("x = %d, y = %d, w = %d, h = %d.\n",
        note->x_pos, note->y_pos, note->x_size, note->y_size);

 return (FALSE);
}
#endif

#ifndef NDEBUG
static void tmp_print(GtkWidget *from, sticky_note *note)
{
  int tmp = 0;
  int x_tmp = 0;
  int y_tmp = 0;
  int width_tmp;
  int height_tmp;
  
  gdk_window_get_origin(note->window->window, &x_tmp, &y_tmp);
  printf("o x = %d, y = %d\n", x_tmp, y_tmp);
  
  gdk_window_get_position(note->window->window, &x_tmp, &y_tmp);
  printf("p x = %d, y = %d\n", x_tmp, y_tmp);
  
  gdk_window_get_geometry(note->window->window,
                          &x_tmp, &y_tmp,
                          &width_tmp, &height_tmp, &tmp);
  printf("g x = %d, y = %d\n", x_tmp, y_tmp);
  printf("g w = %d, h = %d\n", width_tmp, height_tmp);
}
#endif

static void get_size_pos(GtkWidget *window,
                         int *x_pos, int *y_pos, int *x_size, int *y_size)
{
 int dummy = 0;

 if (!x_pos) x_pos = &dummy;
 if (!y_pos) y_pos = &dummy;
 if (!x_size) x_size = &dummy;
 if (!y_size) y_size = &dummy;
 
 if (GTK_WIDGET_VISIBLE(window))
 {
  /* NOTE: I really don't know the `gtk' way of doing this
   * but this seems to work */
#if 1
  Window root;
  int tx = 0;
  int ty = 0;
  unsigned int tborder_width = 0;
  unsigned int tdepth = 0;

#ifdef USE_MOVE_RESIZE_SIGNAL
  printf("x = %d, y = %d, w = %d, h = %d.\n",
         *x_pos, *y_pos, *x_size, *y_size);
#endif
  
  gdk_window_get_origin(window->window, x_pos, y_pos);
  
  XGetGeometry(((GdkWindowPrivate *)window->window)->xdisplay,
               ((GdkWindowPrivate *)window->window)->xwindow,
               &root, &tx, &ty, x_size, y_size,
               &tborder_width, &tdepth);
  
  *x_pos -= (tx + 1 + tborder_width);
  *y_pos -= (ty + 1 + tborder_width);

#ifdef USE_MOVE_RESIZE_SIGNAL
  printf("x = %d, y = %d, w = %d, h = %d.\n",
         *x_pos, *y_pos, *x_size, *y_size);
#endif
#else
  unsigned int depth = 0;
  int x_geom = 0;
  int y_geom = 0;
  
  gdk_window_get_origin(window->window, x_pos, y_pos);
  gdk_window_get_geometry(window->window, &x_geom, &y_geom,
                          x_size, y_size, &depth);
  
  *x_pos -= (x_geom + 1);
  *y_pos -= (y_geom + 2);
#endif
 }
}


static void save_note_click(GtkWidget *from, sticky_note *note)
{ /* FIXME: get position/size and dump that with other config stuff... */
 file_io fs;
 const char *chars = gtk_editable_get_chars(GTK_EDITABLE(note->text), 0, -1);
 
 if (note->filename &&
     file_write_open(note->filename, STICKY_FILE_NOTE_VERSION, &fs))
 {
  file_section_beg("options", &fs);

  file_put_bitflag("auto_placment", note->auto_placment, &fs);

  file_put_bitflag("hidden",
                   GTK_WIDGET_VISIBLE(note->window) ? FALSE : TRUE, &fs);
  
  file_put_string("name", note->name, &fs);
  
  file_put_unsigned_int("unique_id", note->unique_id, &fs);
  
  file_put_bitflag("using_right", note->using_right, &fs);
  file_put_bitflag("using_scrollbar",
                   GTK_WIDGET_VISIBLE(note->vscrollbar) ? TRUE : FALSE, &fs);

  get_size_pos(note->window, &note->x_pos, &note->y_pos,
               &note->x_size, &note->y_size);
  
  file_put_int("x_position", note->x_pos, &fs);
  file_put_int("x_size", note->x_size, &fs);
  file_put_int("y_position", note->y_pos, &fs);
  file_put_int("y_size", note->y_size, &fs);
  
  file_section_end(&fs);
  
  file_put_string("text", chars, &fs);
  
  file_write_close(&fs);
  
  gtk_widget_set_sensitive(note->save_menu_item, FALSE);
 }
}

#if 0
/* maybe some sick weirdo will want this on sticky notes... :)
 * option later */
static void text_word_wrap_toggle(GtkWidget *checkbutton, sticky_note *note)
{
 gtk_text_set_word_wrap(GTK_TEXT(data->text),
                        GTK_TOGGLE_BUTTON(checkbutton)->active);
}
#endif

static void cut_note_click(GtkWidget *from, sticky_note *note)
{
 gtk_editable_cut_clipboard(GTK_EDITABLE(note->text), 0L);
}

static void copy_note_click(GtkWidget *from, sticky_note *note)
{
 gtk_editable_copy_clipboard(GTK_EDITABLE(note->text), 0L);
}

static void paste_note_click(GtkWidget *from, sticky_note *note)
{
 gtk_editable_paste_clipboard(GTK_EDITABLE(note->text), 0L);
}

static void hide_note_toggle(GtkWidget *from, sticky_note *note)
{
 if (GTK_WIDGET_VISIBLE(note->window))
 {
  get_size_pos(note->window, &note->x_pos, &note->y_pos,
               &note->x_size, &note->y_size);
  gtk_widget_hide(note->window);
 }
 else
 {
  if (note->auto_placment)
    gtk_widget_set_uposition(note->window, -1, -1);
  else
    gtk_widget_set_uposition(note->window, note->x_pos, note->y_pos);

  if ((note->x_size < 120) || (note->y_size < 120))
    gtk_widget_set_usize(note->window, 120, 120);
  else
    gtk_widget_set_usize(note->window, note->x_size, note->y_size);

  gtk_widget_show(note->window);
 }
 
 gtk_widget_set_sensitive(note->save_menu_item, TRUE);
 gtk_widget_set_sensitive(save_all_item, TRUE);
}

static void erase_note_click(GtkWidget *from, sticky_note *note)
{
 gtk_editable_delete_text(GTK_EDITABLE(note->text), 0, -1);
}

static int internal_write_index_file(sticky_note *note, va_list va)
{
 int *count = va_arg(va, int *);
 file_io *fs = va_arg(va, file_io *);
 char buffer[64];

 sprintf(buffer, "%04d", ++*count);
 
 file_put_unsigned_int(buffer, note->unique_id, fs);
  
 return (TRUE);
}

static void write_index_file(void)
{
 file_io fs;
 char buffer[1024];
 
 sprintf(buffer, "%.1000s/%s", getenv("HOME"), "/.sticky/index");

 if (file_write_open(buffer, STICKY_FILE_MASTER_INDEX_VERSION, &fs))
 {
  int count = 0;
  
  file_put_int("count", base.count, &fs);

  file_section_beg("data", &fs);
  
  sticky_note_do_cronorder(internal_write_index_file, &base, &count, &fs);

  file_section_end(&fs);

  file_write_close(&fs);
  
  assert(count == base.count);
 }
}

static void write_preferences_file(void)
{
 file_io fs;
 char buffer[1024];
 
 sprintf(buffer, "%.1000s/%s", getenv("HOME"), "/.sticky/prefs");

 if (file_write_open(buffer, STICKY_FILE_MASTER_PREFERENCES_VERSION, &fs))
 {
  int x_pos = -1;
  int y_pos = -1;

  file_put_int("action_on_quit", prefs.action_on_quit, &fs);
  file_put_bitflag("auto_placment", prefs.auto_placment, &fs);

  file_put_int("mouse_one_action", prefs.mouse_one_action, &fs);
  file_put_int("mouse_two_action", prefs.mouse_two_action, &fs);

  get_size_pos(main_window, &x_pos, &y_pos, NULL, NULL);
  
  file_put_int("x_position", x_pos, &fs);
  file_put_int("y_position", y_pos, &fs);

  file_write_close(&fs);
 }
}

static void read_preferences_file(void)
{
 file_io fs;
 char buffer[1024];
 
 sprintf(buffer, "%.1000s/%s", getenv("HOME"), "/.sticky/prefs");

 if (file_read_open(buffer, &fs))
 {
  int x_pos = -1;
  int y_pos = -1;

  prefs.action_on_quit = file_get_int("action_on_quit", &fs);
  prefs.auto_placment = file_get_bitflag("auto_placment", &fs);

  prefs.mouse_one_action = file_get_int("mouse_one_action", &fs);
  prefs.mouse_two_action = file_get_int("mouse_two_action", &fs);
  
  if (!prefs.auto_placment)
  {
   x_pos = file_get_int("x_position", &fs);
   y_pos = file_get_int("y_position", &fs);
  }
  
  gtk_widget_set_uposition(main_window, x_pos, y_pos);
  
  file_read_close(&fs);
 }
 else
   gtk_widget_set_uposition(main_window, -1, -1);
}

static void on_finish_event(GtkWidget *from, gpointer nothing)
{
 gtk_grab_remove(from);
 gtk_main_quit();
}

static void destroy_note_click(GtkWidget *from, sticky_note *note)
{
 unlink(note->filename);
 gtk_editable_delete_text(GTK_EDITABLE(note->text), 0, -1);
 gtk_widget_destroy(note->window);
 write_index_file();
}

static void about_note_click(GtkWidget *from, sticky_note *note)
{
 GtkWidget *window = NULL;
 GtkWidget *label = NULL;
 GtkWidget *vbox = NULL;
 GtkWidget *button = NULL;
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

 gtk_window_set_wmclass(GTK_WINDOW(window),
                        "about_notes", "gtk_note");
 gtk_widget_set_uposition(window, -1, -1);
 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
 gtk_window_set_title(GTK_WINDOW(window), "About");
 gtk_signal_connect(GTK_OBJECT(window),
                    "destroy", GTK_SIGNAL_FUNC(on_finish_event), NULL);

 vbox = gtk_vbox_new(FALSE, 5);
 gtk_container_border_width(GTK_CONTAINER(vbox), 5);
 gtk_container_add(GTK_CONTAINER(window), vbox);
 gtk_widget_show(vbox);
 
 label = gtk_label_new("\n"
                       " This program was written by,\n"
              "  James Antill (jamesa@demon.net, james@crazylands.org).\n"
                       " This program is distributed under the GPL \n"
                       "  (GNU General Public License, see www.gnu.org).\n\n");
 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
 gtk_widget_show(label);
 
 button = gtk_button_new_with_label("Close");
 gtk_widget_show(button);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, TRUE, 0);

 gtk_widget_show(window);
 gtk_grab_add(window);
 gtk_main();
}

static int internal_get_new_unique(sticky_note *note, va_list va)
{
 int *unique_id = va_arg(va, int *);
 
 if (*unique_id < note->unique_id)
   return (FALSE);
 else
   if (*unique_id == note->unique_id)
     ++*unique_id;
 
 return (TRUE);
}

static int sticky_get_new_unique(void)
{
 int unique_id = 1;

 sticky_note_do_cronorder(internal_get_new_unique, &base, &unique_id);

 return (unique_id);
}

static void create_finnish_new_event(GtkWidget *from, gpointer real_params)
{
 change_event_params *params = real_params;
 sticky_note *note = g_malloc(sizeof(sticky_note));

 note->unique_id = sticky_get_new_unique();
 note->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(params->name_entry)));
 note->x_size = atoi(gtk_entry_get_text(GTK_ENTRY(params->x_size_entry)));
 note->y_size = atoi(gtk_entry_get_text(GTK_ENTRY(params->y_size_entry)));
 note->x_pos = -1;
 note->y_pos = -1;
 note->auto_placment = FALSE;
 note->using_right = TRUE;
 
 note->filename = NULL; /* so it won't load */
 sticky_note_create(note);

#ifndef HAVE_ASPRINTF
 {
  char filename[1024];
  
  sprintf(filename, "%.1000s/%s/%04u", getenv("HOME"), ".sticky",
          note->unique_id);
  note->filename = g_strdup(filename);
 }
#else
 asprintf(&note->filename, "%.1000s/%s/%04u",
          getenv("HOME"), ".sticky", note->unique_id);
#endif
 
 sticky_note_add(&base, note);
 
 gtk_widget_show(note->window);

 write_index_file();
 save_note_click(NULL, note);
 
 gtk_widget_destroy(from);
}

static void new_main_click(GtkWidget *from, gpointer *nothing)
{
 GtkWidget *window = NULL;
 GtkWidget *vbox = NULL;
 GtkWidget *hbox = NULL;
 GtkWidget *entry = NULL;
 GtkWidget *button = NULL;
 change_event_params destroy_params;
 char buffer[256];
 
 destroy_params.note = NULL;
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

 gtk_window_set_wmclass(GTK_WINDOW(window), "create_note", "gtk_note");
 gtk_widget_set_uposition(window, -1, -1);
 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
 gtk_window_set_title(GTK_WINDOW(window), "Notes list");
 gtk_signal_connect(GTK_OBJECT(window),
                    "destroy", GTK_SIGNAL_FUNC(on_finish_event), NULL);
 
 vbox = gtk_vbox_new(FALSE, 5);
 gtk_container_border_width(GTK_CONTAINER(vbox), 5);
 gtk_container_add(GTK_CONTAINER(window), vbox);
 gtk_widget_show(vbox);

 sprintf(buffer, "Note %d", sticky_get_new_unique());

 MAKE_TEXT_EDITCELL("Note title: ");
 gtk_entry_set_text(GTK_ENTRY(entry), buffer);
 gtk_entry_select_region(GTK_ENTRY(entry), 0, GTK_ENTRY(entry)->text_length);
 gtk_widget_grab_focus(entry);
 destroy_params.name_entry = entry;
 MAKE_TEXT_EDITCELL("Note X size: ");
 gtk_entry_set_text(GTK_ENTRY(entry), "250");
 destroy_params.x_size_entry = entry;
 MAKE_TEXT_EDITCELL("Note Y size: ");
 gtk_entry_set_text(GTK_ENTRY(entry), "200");
 destroy_params.y_size_entry = entry;

 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 button = gtk_button_new_with_label("Ok");
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 gtk_widget_grab_default(button);
 gtk_widget_show(button);
 gtk_signal_connect(GTK_OBJECT(button),
                    "clicked", GTK_SIGNAL_FUNC(create_finnish_new_event),
                    (gpointer) &destroy_params);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 button = gtk_button_new_with_label("Cancel");
 gtk_widget_show(button);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);

 gtk_widget_show(window);
 gtk_grab_add(window);
 gtk_main();
}

static void vert_scrollbar_note_toggle(GtkWidget *from, sticky_note *data)
{   
 if (GTK_WIDGET_VISIBLE(data->vscrollbar))
   gtk_widget_hide(data->vscrollbar);
 else
   gtk_widget_show(data->vscrollbar);
}

static void which_side_scrollbar_note_toggle(GtkWidget *from,
                                             sticky_note *note)
{
 note->using_right = !note->using_right;

 gtk_box_reorder_child(GTK_BOX(note->hbox), note->vscrollbar,
                       note->using_right);
}

static void change_finnish_note_pref_event(GtkWidget *from,
                                           gpointer real_params)
{
 change_event_params *params = real_params;
 sticky_note *note = params->note;
 
 note->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(params->name_entry)));

 note->auto_placment = params->auto_placment;
 
 if (params->using_right != note->using_right)
   which_side_scrollbar_note_toggle(from, note);
 if ((params->using_scrollbar && !GTK_WIDGET_VISIBLE(note->vscrollbar)) ||
     (!params->using_scrollbar && GTK_WIDGET_VISIBLE(note->vscrollbar)))
   vert_scrollbar_note_toggle(from, note);
   
 gtk_window_set_title(GTK_WINDOW(note->window), note->name);

 sticky_note_del(note);
 sticky_note_add(&base, note);
 
 gtk_widget_set_sensitive(note->save_menu_item, TRUE);
 gtk_widget_set_sensitive(save_all_item, TRUE);
 
 gtk_widget_destroy(from);
}

static void change_on_toggle_note_pref_event(GtkWidget *from,
                                             gpointer real_params)
{
 change_event_params *params = real_params;
 params->using_scrollbar = TRUE;
}

static void change_left_toggle_note_pref_event(GtkWidget *from,
                                               gpointer real_params)
{
 change_event_params *params = real_params;
 params->using_right = FALSE;
}

static void change_off_toggle_note_pref_event(GtkWidget *from,
                                              gpointer real_params)
{
 change_event_params *params = real_params;
 params->using_scrollbar = FALSE;
}

static void change_right_toggle_note_pref_event(GtkWidget *from,
                                                gpointer real_params)
{
 change_event_params *params = real_params;
 params->using_right = TRUE;
}

static void change_auto_toggle_note_pref_event(GtkWidget *from,
                                               gpointer real_params)
{
 change_event_params *params = real_params;
 params->auto_placment = TRUE;
}

static void change_saved_toggle_note_pref_event(GtkWidget *from,
                                                gpointer real_params)
{
 change_event_params *params = real_params;
 params->auto_placment = FALSE;
}

static void preferences_note_click(GtkWidget *from, sticky_note *note)
{
 GtkWidget *window = NULL;
 GtkWidget *vbox = NULL;
 GtkWidget *hbox = NULL;
 GtkWidget *entry = NULL;
 GtkWidget *button = NULL;
 change_event_params destroy_params;

 destroy_params.note = note;
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

 gtk_window_set_wmclass(GTK_WINDOW(window), "change_note", "gtk_note");
 gtk_widget_set_uposition(window, -1, -1);
 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
 gtk_window_set_title(GTK_WINDOW(window), "Note Preferences");
 gtk_signal_connect(GTK_OBJECT(window),
                    "destroy", GTK_SIGNAL_FUNC(on_finish_event), NULL);
 
 vbox = gtk_vbox_new(FALSE, 5);
 gtk_container_border_width(GTK_CONTAINER(vbox), 5);
 gtk_container_add(GTK_CONTAINER(window), vbox);
 gtk_widget_show(vbox);

 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 button = gtk_button_new_with_label("Ok");
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 gtk_widget_grab_default(button);
 gtk_widget_show(button);
 gtk_signal_connect(GTK_OBJECT(button),
                    "clicked", GTK_SIGNAL_FUNC(change_finnish_note_pref_event),
                    (gpointer) &destroy_params);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 button = gtk_button_new_with_label("Cancel");
 gtk_widget_show(button);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 
 MAKE_TEXT_EDITCELL("Note title: ");
 gtk_entry_set_text(GTK_ENTRY(entry), note->name);
 gtk_entry_select_region(GTK_ENTRY(entry), 0, GTK_ENTRY(entry)->text_length);
 destroy_params.name_entry = entry;

 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 destroy_params.auto_placment = note->auto_placment;
  
 button = gtk_radio_button_new_with_label(NULL, "Auto placment");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_auto_toggle_note_pref_event),
                    &destroy_params);
 if (destroy_params.auto_placment)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 
 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Saved placment");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_saved_toggle_note_pref_event),
                    &destroy_params);
 if (!destroy_params.auto_placment)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 destroy_params.using_right = note->using_right;
 if (GTK_WIDGET_VISIBLE(note->vscrollbar))
   destroy_params.using_scrollbar = TRUE;
 else
   destroy_params.using_scrollbar = FALSE;
 
 button = gtk_radio_button_new_with_label(NULL, "Scrollbar on");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_on_toggle_note_pref_event),
                    &destroy_params);
 if (destroy_params.using_scrollbar)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Scrollbar off");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_off_toggle_note_pref_event),
                    &destroy_params);
 if (!destroy_params.using_scrollbar)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_end(GTK_BOX(vbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 button = gtk_radio_button_new_with_label(NULL, "Scrollbar left");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_left_toggle_note_pref_event),
                    &destroy_params);
 if (!note->using_right)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 
 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Scrollbar right");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_right_toggle_note_pref_event),
                    &destroy_params);
 if (note->using_right)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 gtk_widget_show(window);
 gtk_grab_add(window);
 gtk_main();
}

static void change_finnish_main_pref_event(GtkWidget *from,
                                           gpointer real_params)
{
 main_config *params = real_params;
 
 prefs.auto_placment = params->auto_placment;
 prefs.action_on_quit = params->action_on_quit;
 prefs.mouse_one_action = params->mouse_one_action;
 prefs.mouse_two_action = params->mouse_two_action;

 write_preferences_file();
 
 gtk_widget_destroy(from);
}

static void change_auto_toggle_main_pref_event(GtkWidget *from,
                                               gpointer real_params)
{
 main_config *params = real_params;
 params->auto_placment = TRUE;
}

static void change_saved_toggle_main_pref_event(GtkWidget *from,
                                                gpointer real_params)
{
 main_config *params = real_params;
 params->auto_placment = FALSE;
}

static void change_mouse_raise_toggle_main_pref_event(GtkWidget *from,
                                                      gpointer real_params)
{
 int *param = real_params;
 
 *param = STICKY_MASTER_BUTTON_RAISE;
}

static void change_mouse_lower_toggle_main_pref_event(GtkWidget *from,
                                                      gpointer real_params)
{
 int *param = real_params;
 
 *param = STICKY_MASTER_BUTTON_LOWER;
}

static void change_mouse_show_toggle_main_pref_event(GtkWidget *from,
                                                     gpointer real_params)
{
 int *param = real_params;
 
 *param = STICKY_MASTER_BUTTON_SHOW;
}

static void change_mouse_hide_toggle_main_pref_event(GtkWidget *from,
                                                      gpointer real_params)
{
 int *param = real_params;
 
 *param = STICKY_MASTER_BUTTON_HIDE;
}

static void change_quit_quit_toggle_main_pref_event(GtkWidget *from,
                                                    gpointer real_params)
{
 main_config *params = real_params;
 params->action_on_quit = STICKY_MASTER_QUIT_QUIT;
}

static void change_quit_save_toggle_main_pref_event(GtkWidget *from,
                                                    gpointer real_params)
{
 main_config *params = real_params;
 params->action_on_quit = STICKY_MASTER_QUIT_SAVE;
}

static void change_quit_ask_toggle_main_pref_event(GtkWidget *from,
                                                    gpointer real_params)
{
 main_config *params = real_params;
 params->action_on_quit = STICKY_MASTER_QUIT_ASK;
}

static void preferences_main_click(GtkWidget *from, gpointer nothing)
{
 GtkWidget *window = NULL;
 GtkWidget *vbox = NULL;
 GtkWidget *hbox = NULL;
 GtkWidget *label = NULL;
 GtkWidget *button = NULL;
 main_config destroy_params;
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

 gtk_window_set_wmclass(GTK_WINDOW(window), "change_main", "gtk_note");
 gtk_widget_set_uposition(window, -1, -1);
 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
 gtk_window_set_title(GTK_WINDOW(window), "Main Preferences");
 gtk_signal_connect(GTK_OBJECT(window),
                    "destroy", GTK_SIGNAL_FUNC(on_finish_event), NULL);
 
 vbox = gtk_vbox_new(FALSE, 5);
 gtk_container_border_width(GTK_CONTAINER(vbox), 5);
 gtk_container_add(GTK_CONTAINER(window), vbox);
 gtk_widget_show(vbox);

 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 button = gtk_button_new_with_label("Ok");
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 gtk_widget_grab_default(button);
 gtk_widget_show(button);
 gtk_signal_connect(GTK_OBJECT(button),
                    "clicked", GTK_SIGNAL_FUNC(change_finnish_main_pref_event),
                    (gpointer) &destroy_params);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 button = gtk_button_new_with_label("Cancel");
 gtk_widget_show(button);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);

 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 destroy_params.auto_placment = prefs.auto_placment;
 
 button = gtk_radio_button_new_with_label(NULL, "Auto placment");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_auto_toggle_main_pref_event),
                    &destroy_params);
 if (destroy_params.auto_placment)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 
 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Saved placment");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_saved_toggle_main_pref_event),
                    &destroy_params);
 if (!destroy_params.auto_placment)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 label = gtk_label_new(" Button one action");
 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
 gtk_widget_show(label);
 
 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 destroy_params.mouse_one_action = prefs.mouse_one_action;

 button = gtk_radio_button_new_with_label(NULL, "Raise");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_mouse_raise_toggle_main_pref_event),
                    &destroy_params.mouse_one_action);
 if (destroy_params.mouse_one_action == STICKY_MASTER_BUTTON_RAISE)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 
 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Lower");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_mouse_lower_toggle_main_pref_event),
                    &destroy_params.mouse_one_action);
 if (destroy_params.mouse_one_action == STICKY_MASTER_BUTTON_LOWER)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Show");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_mouse_show_toggle_main_pref_event),
                    &destroy_params.mouse_one_action);
 if (destroy_params.mouse_one_action == STICKY_MASTER_BUTTON_SHOW)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 
 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Hide");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_mouse_hide_toggle_main_pref_event),
                    &destroy_params.mouse_one_action);
 if (destroy_params.mouse_one_action == STICKY_MASTER_BUTTON_HIDE)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 label = gtk_label_new(" Button two action");
 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
 gtk_widget_show(label);
 
 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 destroy_params.mouse_two_action = prefs.mouse_two_action;

 button = gtk_radio_button_new_with_label(NULL, "Raise");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_mouse_raise_toggle_main_pref_event),
                    &destroy_params.mouse_two_action);
 if (destroy_params.mouse_two_action == STICKY_MASTER_BUTTON_RAISE)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 
 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Lower");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_mouse_lower_toggle_main_pref_event),
                    &destroy_params.mouse_two_action);
 if (destroy_params.mouse_two_action == STICKY_MASTER_BUTTON_LOWER)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Show");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_mouse_show_toggle_main_pref_event),
                    &destroy_params.mouse_two_action);
 if (destroy_params.mouse_two_action == STICKY_MASTER_BUTTON_SHOW)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 
 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Hide");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_mouse_hide_toggle_main_pref_event),
                    &destroy_params.mouse_two_action);
 if (destroy_params.mouse_two_action == STICKY_MASTER_BUTTON_HIDE)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 label = gtk_label_new(" On quit");
 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
 gtk_widget_show(label);

 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 destroy_params.action_on_quit = prefs.action_on_quit;
 
 button = gtk_radio_button_new_with_label(NULL, "Quit");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_quit_quit_toggle_main_pref_event),
                    &destroy_params);
 if (destroy_params.action_on_quit == STICKY_MASTER_QUIT_QUIT)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 
 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Save all");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_quit_save_toggle_main_pref_event),
                    &destroy_params);
 if (destroy_params.action_on_quit == STICKY_MASTER_QUIT_SAVE)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button),
                                                      "Ask");
 gtk_signal_connect(GTK_OBJECT(button), "clicked",
                    GTK_SIGNAL_FUNC(change_quit_ask_toggle_main_pref_event),
                    &destroy_params);
 if (destroy_params.action_on_quit == STICKY_MASTER_QUIT_ASK)
   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);

 gtk_widget_show(window);
 gtk_grab_add(window);
 gtk_main();
}

static void hide_list_click(GtkWidget *gtklist, gpointer other)
{
 GList *tmp = GTK_LIST(gtklist)->selection;

 while (tmp)
 {
  sticky_note *note = gtk_object_get_user_data(GTK_OBJECT(tmp->data));

  assert(note);

  if (GTK_WIDGET_VISIBLE(note->window))
    hide_note_toggle(NULL, note);

  tmp = tmp->next;
 }
}

static void show_list_click(GtkWidget *gtklist, gpointer other)
{
 GList *tmp = GTK_LIST(gtklist)->selection;

 while (tmp)
 {
  sticky_note *note = gtk_object_get_user_data(GTK_OBJECT(tmp->data));

  assert(note);

  if (!GTK_WIDGET_VISIBLE(note->window))
    hide_note_toggle(NULL, note);

  tmp = tmp->next;
 }
}

static int internal_add_sticky_to_list(sticky_note *scan, va_list va)
{
 GtkWidget *gtklist = va_arg(va, GtkWidget *);
 GtkWidget *list_item = gtk_list_item_new();
 GtkWidget *label = gtk_label_new(scan->name);
 
 gtk_container_add(GTK_CONTAINER(list_item), label);
 gtk_widget_show(label);
 
 gtk_container_add(GTK_CONTAINER(gtklist), list_item);
 gtk_widget_show(list_item); 

 gtk_object_set_user_data(GTK_OBJECT(list_item), scan);

 return (TRUE);
}

static void change_list_selection(GtkWidget *from, gpointer other)
{/* could do a raise event here */}

static void internal_list_main_click(GtkWidget *from, sticky_note *data,
                                     int hidden_only)
{
 GtkWidget *window = NULL;
 GtkWidget *gtklist = NULL;
 GtkWidget *vbox = NULL;
 GtkWidget *hbox = NULL;
 GtkWidget *button = NULL;
 GtkWidget *scrolled_window = NULL;
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

 gtk_window_set_wmclass(GTK_WINDOW(window), "list_notes", "gtk_note");
 gtk_widget_set_uposition(window, -1, -1);
 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
 gtk_window_set_title(GTK_WINDOW(window), "Notes list");
 gtk_signal_connect(GTK_OBJECT(window),
                    "destroy", GTK_SIGNAL_FUNC(on_finish_event), NULL);
 
 vbox = gtk_vbox_new(FALSE, 5);
 gtk_container_border_width(GTK_CONTAINER(vbox), 5);
 gtk_container_add(GTK_CONTAINER(window), vbox);
 gtk_widget_show(vbox);           

 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
 gtk_widget_set_usize(scrolled_window, 250, 150);
 gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, FALSE, FALSE, 0);
 gtk_widget_show(scrolled_window);           

 gtklist = gtk_list_new();
 gtk_list_set_selection_mode(GTK_LIST(gtklist), GTK_SELECTION_MULTIPLE);
 gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
 gtk_widget_show(gtklist);
 gtk_signal_connect(GTK_OBJECT(gtklist),
                    "selection_changed",
                    GTK_SIGNAL_FUNC(change_list_selection),
                    NULL);

 if (hidden_only)
   sticky_note_do_inorder_hidden(internal_add_sticky_to_list, &base, gtklist);
 else
   sticky_note_do_inorder(internal_add_sticky_to_list, &base, gtklist);
 
 hbox = gtk_hbox_new(FALSE, 8);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 button = gtk_button_new_with_label("Show");
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 gtk_widget_grab_default(button);
 gtk_widget_show(button);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "clicked", GTK_SIGNAL_FUNC(show_list_click),
                           GTK_OBJECT(gtklist));

 button = gtk_button_new_with_label("Hide");
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "clicked", GTK_SIGNAL_FUNC(hide_list_click),
                           GTK_OBJECT(gtklist));

 button = gtk_button_new_with_label("Close");
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 gtk_widget_show(button);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));

 gtk_widget_show(window);
 gtk_grab_add(window);
 gtk_main();
}

static void list_main_click(GtkWidget *from, sticky_note *data)
{
 internal_list_main_click(from, data, FALSE);
}

static void list_hidden_main_click(GtkWidget *from, sticky_note *data)
{
 internal_list_main_click(from, data, TRUE);
}

#ifndef NDEBUG
static void editable_note_toggle(GtkWidget *from, sticky_note *data)
{ 
 gtk_text_set_editable(GTK_TEXT(data->text),
                       !GTK_EDITABLE(data->text)->editable);
}
#endif

static void changed_text_press(GtkWidget *from, sticky_note *note)
{
 gtk_widget_set_sensitive(save_all_item, TRUE);
 gtk_widget_set_sensitive(note->save_menu_item, TRUE);
}

static int changed_position_size(GtkWidget *window,
                                 int x_pos, int y_pos,
                                 int x_size, int y_size)
{
 int tmp_x_pos = x_pos;
 int tmp_y_pos = y_pos;
 int tmp_x_size = x_size;
 int tmp_y_size = y_size;

 get_size_pos(window, &x_pos, &y_pos, &x_size, &y_size);
 if ((x_pos == tmp_x_pos) &&
     (y_pos == tmp_y_pos) &&
     (x_size == tmp_x_size) &&
     (y_size == tmp_y_size))
   return (FALSE);
 else
   return (TRUE);
}

static int internal_are_unsaved_stickies(sticky_note *note, va_list va)
{
 int *ret = va_arg(va, int *);
 
 if (GTK_WIDGET_IS_SENSITIVE(note->save_menu_item) ||
     changed_position_size(note->window,
                           note->x_pos, note->y_pos,
                           note->x_size, note->y_size))
   *ret = TRUE;
 
 return (!*ret);
}

static int are_unsaved_stickies(void)
{
 int ret = FALSE;
 
 sticky_note_do_inorder(internal_are_unsaved_stickies, &base, &ret);

 return (ret);
}

static int internal_save_all_stickies(sticky_note *note, va_list va)
{
 if (GTK_WIDGET_IS_SENSITIVE(note->save_menu_item) ||
     changed_position_size(note->window,
                           note->x_pos, note->y_pos,
                           note->x_size, note->y_size))
   save_note_click(NULL, note);
 
 return (TRUE);
}

static void save_all_stickies(GtkWidget *from, GtkWidget *other)
{
 if (GTK_WIDGET_IS_SENSITIVE(save_all_item) && are_unsaved_stickies())
   sticky_note_do_inorder(internal_save_all_stickies, &base);

 gtk_widget_set_sensitive(save_all_item, FALSE);
}

static void save_one_finnish_quit_event(GtkWidget *from,
                                        gpointer real_params)
{
 sticky_note *note = real_params;

 if (GTK_WIDGET_IS_SENSITIVE(note->save_menu_item) ||
     changed_position_size(note->window,
                           note->x_pos, note->y_pos,
                           note->x_size, note->y_size))
   save_note_click(NULL, note);
 
 gtk_widget_destroy(from);
}

static void save_all_finnish_quit_event(GtkWidget *from, gpointer nothing)
{
 save_all_stickies(NULL, NULL);
 
 gtk_widget_destroy(from);
}

static void query_save_stickies(GtkWidget *from, sticky_note *note)
{
 GtkWidget *window = NULL;
 GtkWidget *vbox = NULL;
 GtkWidget *hbox = NULL;
 GtkWidget *label = NULL;
 GtkWidget *button = NULL;
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

 gtk_window_set_wmclass(GTK_WINDOW(window), "saved_ask", "gtk_note");
 gtk_widget_set_uposition(window, -1, -1);
 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE);
 gtk_window_set_title(GTK_WINDOW(window), "Query save");
 gtk_signal_connect(GTK_OBJECT(window),
                    "destroy", GTK_SIGNAL_FUNC(on_finish_event), NULL);
 
 vbox = gtk_vbox_new(FALSE, 5);
 gtk_container_border_width(GTK_CONTAINER(vbox), 5);
 gtk_container_add(GTK_CONTAINER(window), vbox);
 gtk_widget_show(vbox);
 
 label = gtk_label_new(" Do you want to save all your unsaved notes ?");
 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
 gtk_widget_show(label);
 
 hbox = gtk_hbox_new(FALSE, 5);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 gtk_widget_show(hbox);

 button = gtk_button_new_with_label("Yes");
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 gtk_widget_grab_default(button);
 gtk_widget_show(button);
 gtk_signal_connect(GTK_OBJECT(button),
                    "clicked", GTK_SIGNAL_FUNC(save_all_finnish_quit_event),
                    NULL);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 button = gtk_button_new_with_label("No");
 gtk_widget_show(button);
 gtk_signal_connect_object(GTK_OBJECT(button),
                           "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);

 if (note) /* done from a note */
 {
  char buffer[90];

  if (strnlen(note->name, 80) == 80)
  {
   sprintf(buffer, "Save: %.80s...", note->name);
   button = gtk_button_new_with_label(buffer);
  }
  else
  {
   sprintf(buffer, "Save: %s", note->name);
   button = gtk_button_new_with_label(buffer);
  }
  
  gtk_widget_show(button);
  gtk_signal_connect(GTK_OBJECT(button),
                     "clicked", GTK_SIGNAL_FUNC(save_one_finnish_quit_event),
                     note);
  gtk_signal_connect_object(GTK_OBJECT(button),
                            "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroy),
                           GTK_OBJECT(window));
  gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0);
 }

 gtk_widget_show(window);
 gtk_grab_add(window);
 gtk_main();
}

static void quit_program_click(GtkWidget *from, sticky_note *note)
{
 if (are_unsaved_stickies())
   switch (prefs.action_on_quit)
   {
    default:
      assert(FALSE);
    break;
    
    case STICKY_MASTER_QUIT_SAVE:
      save_all_stickies(NULL, NULL);
      break;
    case STICKY_MASTER_QUIT_ASK:
      query_save_stickies(from, note);
      break;
      
    case STICKY_MASTER_QUIT_QUIT:
      break;
   }
 
 gtk_main_quit();
}

static void setup_notes_menus(GtkWindow *window, GtkBox *box,
                              sticky_note *note)
{
 GtkWidget *menu_bar = NULL;
 GtkAcceleratorTable *table = gtk_accelerator_table_new();
 GtkWidget *menu = NULL;
 GtkWidget *file_menu = NULL;
 GtkWidget *edit_menu = NULL;
 GtkWidget *note_menu = NULL;
 GtkWidget *help_menu = NULL;
 
 menu_helper file_menu_input[] =
 {
  {"New", GTK_SIGNAL_FUNC(new_main_click), NULL, "<control>-N"},
  MENU_HELPER_SEPARATOR(),
  {"Preferences...", GTK_SIGNAL_FUNC(preferences_note_click), note},
  MENU_HELPER_SEPARATOR(),
  {"Quit", GTK_SIGNAL_FUNC(quit_program_click), note, "<control>-Q"},
  MENU_HELPER_END(),
 };
 menu_helper edit_menu_input[] =
 {
#if 0
  /* not sure how to do this yet ... */
  {"Undo", GTK_SIGNAL_FUNC(undo_note_click), note},
  MENU_HELPER_SEPARATOR(),
#endif
  {"Cut", GTK_SIGNAL_FUNC(cut_note_click), note, "<control>-X"},
  {"Copy", GTK_SIGNAL_FUNC(copy_note_click), note, "<control>-C"},
  MENU_HELPER_SEPARATOR(),
  {"Paste", GTK_SIGNAL_FUNC(paste_note_click), note, "<control>-V"},
  MENU_HELPER_END(),
 };
 menu_helper note_menu_input[] =
 {
#ifndef NDEBUG
  {"tmp", GTK_SIGNAL_FUNC(tmp_print), note},
#endif
  {"Hide", GTK_SIGNAL_FUNC(hide_note_toggle), note},
  {"Erase", GTK_SIGNAL_FUNC(erase_note_click), note, "<alt>-W"},
  MENU_HELPER_SEPARATOR(),
  {"Destroy", GTK_SIGNAL_FUNC(destroy_note_click), note, "<control>-<alt>-W"},
#ifndef NDEBUG
  MENU_HELPER_SEPARATOR(),
  {"Editable", GTK_SIGNAL_FUNC(editable_note_toggle), note},
#endif
  MENU_HELPER_END(),
 };
 menu_helper help_menu_input[] =
 {
  {"About...", GTK_SIGNAL_FUNC(about_note_click), note},
  MENU_HELPER_END(),
 };
 
 file_menu = gtk_menu_item_new_with_label("File");
 gtk_widget_show(file_menu);

 menu = gtk_menu_new();
 note->save_menu_item = gtk_menu_item_new_with_label("Save");
 gtk_widget_set_sensitive(note->save_menu_item, FALSE);
 gtk_widget_install_accelerator(note->save_menu_item, table, "activate",
                                'S', GDK_CONTROL_MASK); 
 gtk_menu_append(GTK_MENU(menu), note->save_menu_item);
 gtk_signal_connect(GTK_OBJECT(note->save_menu_item), "activate",
                    GTK_SIGNAL_FUNC(save_note_click), note);
 gtk_widget_show(note->save_menu_item);
 
 gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_menu),
                           menu_helper_add(menu, &table, file_menu_input)); 
 
 edit_menu = gtk_menu_item_new_with_label("Edit");
 gtk_widget_show(edit_menu);
 gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_menu),
                           menu_helper_add(gtk_menu_new(),
                                           &table, edit_menu_input)); 
 
 note_menu = gtk_menu_item_new_with_label("Note");
 gtk_widget_show(note_menu);
 gtk_menu_item_set_submenu(GTK_MENU_ITEM(note_menu),
                           menu_helper_add(gtk_menu_new(),
                                           &table, note_menu_input)); 

 help_menu = gtk_menu_item_new_with_label("Help");
 gtk_menu_item_right_justify(GTK_MENU_ITEM(help_menu));
 gtk_widget_show(help_menu);
 gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_menu),
                           menu_helper_add(gtk_menu_new(),
                                           NULL, help_menu_input)); 
 gtk_window_add_accelerator_table(window, table);
 
 menu_bar = gtk_menu_bar_new();
 gtk_box_pack_start(GTK_BOX(box), menu_bar, FALSE, FALSE, 0);
 
 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), file_menu);
 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), edit_menu);
 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), note_menu);

 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), help_menu);
 
 gtk_widget_show(menu_bar);
}

static void sticky_note_cleanup(GtkWidget *from, sticky_note *note)
{
 sticky_note_del(note);
 sticky_note_destroy(note);
}

int sticky_note_create(sticky_note *note)
{
 GtkWidget *window = NULL;
 GtkWidget *vbox = NULL;
 GtkWidget *hbox = NULL;
 GtkWidget *vscrollbar = NULL;
 GtkWidget *text = NULL;
 char *text_data = NULL;
 size_t nchars = 0;
 int using_scrollbar = TRUE;
 int hidden = FALSE;
 file_io fs;
 
 if (!(note->window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL)))
   return (FALSE);
 
 if (note->filename)
   if (file_read_open(note->filename, &fs))
   {
    file_section_beg("options", &fs);

    note->auto_placment = file_get_bitflag("auto_placment", &fs);
    hidden = file_get_bitflag("hidden", &fs);
    
    note->name = file_get_malloc("name", NULL, &fs);
    assert(note->name);
    
    note->unique_id = file_get_unsigned_int("unique_id", &fs);
    
    note->using_right = file_get_bitflag("using_right", &fs);
    using_scrollbar = file_get_bitflag("using_scrollbar", &fs);
    
    note->x_pos = file_get_int("x_position", &fs);
    note->x_size = file_get_int("x_size", &fs);
    note->y_pos = file_get_int("y_position", &fs);
    note->y_size = file_get_int("y_size", &fs);
    
    file_section_end(&fs);
    
    if (!(text_data = file_get_malloc("text", &nchars, &fs)))
    {
     free(note->filename);
     free(note);
     
     return (FALSE);
    }
    
    file_read_close(&fs);
   }
   else
   {
    free(note->filename);
    free(note);
    return (FALSE);
   }
 
 gtk_window_set_wmclass(GTK_WINDOW(window), "note", "gtk_note");
 if (note->auto_placment)
   gtk_widget_set_uposition(window, -1, -1);
 else
   gtk_widget_set_uposition(window, note->x_pos, note->y_pos);
 if ((note->x_size < 120) || (note->y_size < 120))
   gtk_widget_set_usize(window, 120, 120);
 else
   gtk_widget_set_usize(window, note->x_size, note->y_size);
 gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, TRUE); 
 gtk_window_set_title(GTK_WINDOW(window), note->name);
 gtk_signal_connect(GTK_OBJECT(window), "destroy",
                    GTK_SIGNAL_FUNC(sticky_note_cleanup), note);
 gtk_signal_connect(GTK_OBJECT(window), "delete_event",
                    GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete), NULL);
#ifdef USE_MOVE_RESIZE_SIGNAL
 gtk_signal_connect(GTK_OBJECT(window), "move_resize",
                    GTK_SIGNAL_FUNC(sticky_note_store_move_resize), note);
#endif
 gtk_container_border_width(GTK_CONTAINER(window), 0);
  
 vbox = gtk_vbox_new(FALSE, 1);
 gtk_container_add(GTK_CONTAINER(window), vbox);
 gtk_container_border_width(GTK_CONTAINER(vbox), 1);
 gtk_widget_show(vbox);

 setup_notes_menus(GTK_WINDOW(window), GTK_BOX(vbox), note);
 
 note->hbox = hbox = gtk_hbox_new(FALSE, 4);
 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
 gtk_widget_show(hbox);
  
 note->text = text = gtk_text_new(NULL, NULL);
 gtk_text_set_editable(GTK_TEXT(text), TRUE);
 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
 gtk_signal_connect(GTK_OBJECT(text), "changed",
                    GTK_SIGNAL_FUNC(changed_text_press), note); 
 gtk_widget_show(text);
 
 vscrollbar = gtk_vscrollbar_new(GTK_TEXT(text)->vadj);
 gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0);
 gtk_widget_show(vscrollbar);
 note->vscrollbar = vscrollbar;

#if 0
 /* FIXME: widget doesn't work with horiz scrollbar yet. */
 hscrollbar = gtk_hscrollbar_new(GTK_TEXT(text)->hadj);
 gtk_box_pack_start(GTK_BOX(vbox), hscrollbar, TRUE, TRUE, 0);
 gtk_widget_show(hscrollbar);
#endif
 
 gtk_widget_realize(text);
 
 if (!note->using_right)
   gtk_box_reorder_child(GTK_BOX(hbox), vscrollbar, 0);
 
 if (!using_scrollbar)
   gtk_widget_hide(note->vscrollbar);

 if (nchars && text_data)
 {
  gtk_text_freeze(GTK_TEXT(note->text));
  
  gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL, text_data, nchars);
  
  gtk_text_thaw(GTK_TEXT(note->text));
 }

 if (hidden)
   gtk_widget_realize(window);
 else
   gtk_widget_show(window);

 return (TRUE);
}

void sticky_note_destroy(sticky_note *note)
{
 assert(note);

 free((void *) note->filename);
 free((void *) note->name);
 free(note);
}

static int internal_hide_all_stickies(sticky_note *note, va_list va)
{
 if (GTK_WIDGET_VISIBLE(note->window))
   gtk_widget_hide(note->window);
 
 return (TRUE);
}

static void hide_all_stickies(GtkWidget *from, GtkWidget *other)
{
 sticky_note_do_inorder(internal_hide_all_stickies, &base);
}

static int internal_show_all_stickies(sticky_note *note, va_list va)
{
 if (!GTK_WIDGET_VISIBLE(note->window))
   gtk_widget_show(note->window);
 
 return (TRUE);
}

static void show_all_stickies(GtkWidget *from, GtkWidget *other)
{
 sticky_note_do_inorder(internal_show_all_stickies, &base);
}

static int internal_raise_all_stickies(sticky_note *note, va_list va)
{
 gdk_window_raise(note->window->window);
 
 return (TRUE);
}

static void raise_all_stickies(GtkWidget *from, GtkWidget *other)
{
 sticky_note_do_inorder(internal_raise_all_stickies, &base);
}

static int internal_lower_all_stickies(sticky_note *note, va_list va)
{
 gdk_window_lower(note->window->window);
 
 return (TRUE);
}

static void lower_all_stickies(GtkWidget *from, GtkWidget *other)
{
 sticky_note_do_inorder(internal_lower_all_stickies, &base);
}

static int main_clicked_event(GtkWidget *from, GdkEventButton *event,
                              GtkWidget *menu)
{
 switch (event->type)
 {
  case GDK_BUTTON_PRESS:
    /* normal */
    break;
    
  case GDK_2BUTTON_PRESS:
    /* double click */
  case GDK_3BUTTON_PRESS:
    /* triple click */
    return (FALSE);
    
  default:
    printf("%d\n", event->type);
    assert(FALSE);
 }

 switch (event->button)
 {
  case 1:
  case 2:
    switch ((event->button == 1) ?
            prefs.mouse_one_action : prefs.mouse_two_action)
    {
     case STICKY_MASTER_BUTTON_RAISE:
       raise_all_stickies(NULL, NULL);
       return (TRUE);
     case STICKY_MASTER_BUTTON_LOWER:
       lower_all_stickies(NULL, NULL);
       return (TRUE);
     case STICKY_MASTER_BUTTON_SHOW:
       show_all_stickies(NULL, NULL);
       return (TRUE);
     case STICKY_MASTER_BUTTON_HIDE:
       hide_all_stickies(NULL, NULL);
       return (TRUE);

     default:
       assert(FALSE);
    }
    return (FALSE);

  case 3: /* menu */
    /* FIXME: hack until I can find out when things have moved/resized */
    if (are_unsaved_stickies())
      gtk_widget_set_sensitive(save_all_item, TRUE);

    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
                   event->button, event->time);
    return (TRUE);

  default:
    /* does X support this ? */
    assert(FALSE);
 }
 
 return (FALSE);
}

static void quit_main_click(GtkWidget *from, GtkWidget *other)
{
 gtk_widget_destroy(main_window);
}

static void init_one_sticky(unsigned int number)
{
 sticky_note *note = malloc(sizeof(sticky_note));
 
#ifndef HAVE_ASPRINTF
 char filename[1024];
 
 sprintf(filename, "%.1000s/%s/%04u", getenv("HOME"), ".sticky", number);
 note->filename = g_strdup(filename);
#else
 asprintf(&note->filename, "%.1000s/%s/%04u",
          getenv("HOME"), ".sticky", number);
#endif

 if (!sticky_note_create(note))
   return;

 sticky_note_add(&base, note);
}

static void init_all_stickies(void)
{
 struct stat buf;
 char buffer[1024];
 
 sprintf(buffer, "%.1000s/%s", getenv("HOME"), "/.sticky/index");

 if (!stat(buffer, &buf) && S_ISREG(buf.st_mode))
 {
  file_io fs;

  if (file_read_open(buffer, &fs))
  {
   int total_count = file_get_int("count", &fs);
   int count = 0;
   
   file_section_beg("data", &fs);
   
   while (count < total_count)
   {
    char buffer[64];
    int unique_id = 0;
    
    sprintf(buffer, "%04d", ++count);

    unique_id = file_get_unsigned_int(buffer, &fs);
    
    init_one_sticky(unique_id);
   }
   assert(count == total_count);

   file_section_end(&fs);
   
   file_read_close(&fs);
  }
 }
 else
 {
  sprintf(buffer, "%.1000s/%s", getenv("HOME"), "/.sticky");
  mkdir(buffer, 0700);
  write_index_file();
 }
}

static void create_main_window(void)
{
 GtkAcceleratorTable *table = gtk_accelerator_table_new();
 GtkWidget *button = NULL;
 GtkWidget *pixmapwid = NULL;
 GdkPixmap *pixmap = NULL;
 GdkBitmap *mask = NULL;
 GtkStyle *style = NULL;
 GtkWidget *menu = NULL;
 
 menu_helper main_menu_input_start[] =
 {
  {"New", GTK_SIGNAL_FUNC(new_main_click), NULL, "<control>-N"},
  MENU_HELPER_SEPARATOR(),
  {"Preferences...", GTK_SIGNAL_FUNC(preferences_main_click), NULL},
  MENU_HELPER_END(),
 };

 menu_helper main_menu_input_end[] =
 {
  {"List all notes", GTK_SIGNAL_FUNC(list_main_click), NULL, "<control>-L"},
  {"List hidden notes", GTK_SIGNAL_FUNC(list_hidden_main_click)},

  {"Raise all notes", GTK_SIGNAL_FUNC(raise_all_stickies), NULL},
  {"Lower all notes", GTK_SIGNAL_FUNC(lower_all_stickies), NULL},
  {"Show all notes", GTK_SIGNAL_FUNC(show_all_stickies), NULL},
  {"Hide all notes", GTK_SIGNAL_FUNC(hide_all_stickies), NULL},  
  MENU_HELPER_SEPARATOR(),
  {"Quit", GTK_SIGNAL_FUNC(quit_main_click), NULL, "<control>-Q"},
  MENU_HELPER_END(),
 };

 if (!(main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL)))
 {
  printf(" Couldn't open main window.\n");
  exit (EXIT_FAILURE);
 }

 gtk_window_set_wmclass(GTK_WINDOW(main_window), "master", "gtk_note");

 read_preferences_file();
 
 gtk_window_set_policy(GTK_WINDOW(main_window), TRUE, TRUE, TRUE);
 gtk_container_border_width(GTK_CONTAINER(main_window), 0);
 gtk_signal_connect(GTK_OBJECT(main_window), "destroy",
                    GTK_SIGNAL_FUNC(quit_program_click), NULL);
 gtk_widget_realize(main_window);
 
 gtk_window_set_title(GTK_WINDOW(main_window), "Main");

 style = gtk_widget_get_style(main_window);
 pixmap = gdk_pixmap_create_from_xpm_d(main_window->window, &mask,
                                       &style->bg[GTK_STATE_NORMAL],
                                       (char **)xpm_data);
 pixmapwid = gtk_pixmap_new(pixmap, mask);

 gtk_widget_show(pixmapwid); 
 
 button = gtk_button_new();
 gtk_container_add(GTK_CONTAINER(button), pixmapwid);
 gtk_container_add(GTK_CONTAINER(main_window), button);

 menu = gtk_menu_new();
 menu_helper_add(menu, &table, main_menu_input_start);
 
 save_all_item = gtk_menu_item_new_with_label("Save all stickies");
 gtk_widget_set_sensitive(save_all_item, FALSE);
 gtk_widget_install_accelerator(save_all_item, table, "activate",
                                'S', GDK_CONTROL_MASK); 
 gtk_menu_append(GTK_MENU(menu), save_all_item);
 gtk_signal_connect(GTK_OBJECT(save_all_item), "activate",
                    GTK_SIGNAL_FUNC(save_all_stickies), NULL);
 gtk_widget_show(save_all_item);

 menu_helper_add(menu, &table, main_menu_input_end);
 
 gtk_signal_connect(GTK_OBJECT(button), "button_press_event",
                    GTK_SIGNAL_FUNC(main_clicked_event),
                    GTK_OBJECT(menu));
 gtk_window_add_accelerator_table(GTK_WINDOW(main_window), table);
 gtk_widget_show(button);

 gtk_widget_show(main_window);
}

void init_rcs(void)
{
 struct stat buf;
 char buffer[1024];

 sprintf(buffer, "%.1000s%s", getenv("HOME"), "/.sticky/gtkrc");

 if (!stat(buffer, &buf) && S_ISREG(buf.st_mode))
   gtk_rc_parse(buffer);
 else
   gtk_rc_parse(STICKY_NOTES_SYSTEM_RC);
}


int main(int argc, char *argv[])
{ 
 gtk_set_locale();
 
 gtk_init(&argc, &argv);

 init_rcs();
 
 create_main_window();

 assert(main_window);
 
 init_all_stickies();
 
 gtk_main();

 exit (EXIT_SUCCESS);
}
