Logo Search packages:      
Sourcecode: guifications version File versions

gf_theme_editor.c

/*
 * Guifications - The end all, be all, toaster popup plugin
 * Copyright (C) 2003-2004 Gary Kramlich
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#ifdef HAVE_CONFIG_H
# include "../gf_config.h"
#endif

#include <glib.h>
#include <gtk/gtk.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>

#define GAIM_PLUGINS

#include <debug.h>
#include <gaim.h>
#include <notify.h>
#include <plugin.h>
#include <request.h>
#include <signals.h>
#include <util.h>
#include <gtkplugin.h>

#include "gf_event.h"
#include "gf_file.h"
#include "gf_internal.h"
#include "gf_item.h"
#include "gf_item_icon.h"
#include "gf_item_image.h"
#include "gf_item_offset.h"
#include "gf_item_text.h"
#include "gf_menu.h"
#include "gf_notification.h"
#include "gf_preferences.h"
#include "gf_theme.h"
#include "gf_theme_editor.h"
#include "gf_theme_info.h"
#include "gf_theme_ops.h"

/* Okay, heres the run down of how all of this works.
 *
 * The theme is loaded and then converted into a GtkTreeStore.  The store holds, the
 * title of what to display in the tree view, as well as what page to display when it's
 * selected, as well as the object itself.
 *
 * The getters and setters are added as data to the widget at creation time.  This way
 * when we hit the callback, we grab the item from the store, and pop it into the
 * getter or setter function, update accordingly, and go about our business.
 *
 */

enum {
      GFTE_STORE_TITLE = 0,
      GFTE_STORE_PAGE,
      GFTE_STORE_OBJECT,
      GFTE_STORE_TOTAL
};

enum {
      GFTE_PAGE_THEME = 0,
      GFTE_PAGE_INFO,
      GFTE_PAGE_OPS,
      GFTE_PAGE_NOTIFICATION,
      GFTE_PAGE_ICON,
      GFTE_PAGE_IMAGE,
      GFTE_PAGE_TEXT,
      GFTE_PAGE_TOTAL
};

enum {
      GFTE_FLAGS_OBJECT = 0,
      GFTE_FLAGS_SUB_OBJECT,
      GFTE_FLAGS_H_OFFSET,
      GFTE_FLAGS_V_OFFSET,
      GFTE_FLAGS_TOTAL
};

enum {
      GFTE_MODIFIED_CLOSE = 0,
      GFTE_MODIFIED_NEW,
      GFTE_MODIFIED_OPEN,
      GFTE_MODIFIED_TOTAL
};

enum {
      GFTE_BUTTON_FILE = 0,
      GFTE_BUTTON_FONT,
      GFTE_BUTTON_COLOR,
      GFTE_BUTTON_TOTAL
};

struct GfThemeEditorInfo {
      GtkWidget *name;
      GtkWidget *version;
      GtkWidget *summary;
      GtkWidget *description;
      GtkWidget *author;
      GtkWidget *website;
};

struct GfThemeEditorOps {
      GtkWidget *time_format;
      GtkWidget *date_format;
      GtkWidget *warning;
      GtkWidget *ellipsis;
};

struct GfThemeEditorNotification {
      GtkWidget *use_gtk;
      GtkWidget *filename;
      GtkWidget *button;
      GtkWidget *width;
      GtkWidget *height;
};

struct GfThemeEditorItem {
      GtkWidget *position;
      GtkWidget *h_offset;
      GtkWidget *h_offset_p;
      GtkWidget *v_offset;
      GtkWidget *v_offset_p;
};

struct GfThemeEditorIcon {
      struct GfThemeEditorItem item;
      GtkWidget *type;
      GtkWidget *size;
};

struct GfThemeEditorImage {
      struct GfThemeEditorItem item;
      GtkWidget *filename;
      GtkWidget *button;
};

struct GfThemeEditorText {
      struct GfThemeEditorItem item;
      GtkWidget *format;
      GtkWidget *width;
      GtkWidget *clipping;
      GtkWidget *font;
      GtkWidget *color;
};

struct GfThemeEditor {
      GfTheme *theme;
      GfNotification *master;

      gchar *filename;
      gchar *path;
      gboolean changed;

      /* toolbar stuff */
      GtkTooltips *tooltips;
      GtkWidget *tool_notification;
      GtkWidget *tool_item;
      GtkWidget *tool_copy;
      GtkWidget *tool_delete;
      GtkWidget *tool_up;
      GtkWidget *tool_down;

      /* main stuff */
      GtkWidget *window;
      GtkWidget *vbox;
      GtkWidget *hbox;
      GtkWidget *tree;
      GtkWidget *note;
      GtkTreeStore *store;

      /* everything else */
      struct GfThemeEditorInfo info;
      struct GfThemeEditorOps ops;
      struct GfThemeEditorNotification notification;
      struct GfThemeEditorIcon icon;
      struct GfThemeEditorImage image;
      struct GfThemeEditorText text;
};

struct GfteModified {
      GtkWidget *window;
      gint type;
      gchar *filename;
};

struct GfteDelete {
      GtkWidget *window;
};

struct GfteNewNotification {
      GtkWidget *window;
      GtkWidget *type;
};

struct GfteNewItem {
      GtkWidget *window;
      GtkWidget *type;
};

typedef void (*GfteSetFunc)(gpointer obj, gconstpointer val);
typedef gconstpointer (*GfteGetFunc)(gpointer obj);

/*******************************************************************************
 * Prototypes ICK!
 ******************************************************************************/
static void gfte_selection_changed_cb(GtkTreeSelection *sel, gpointer data);

/*******************************************************************************
 * Globals
 ******************************************************************************/
GaimPlugin *plugin_handle = NULL;
gpointer image_dialog = NULL;
GtkWidget *opt_dialog = NULL;
struct GfThemeEditor editor;
struct GfteModified modified;
struct GfteDelete del_obj;
struct GfteNewNotification new_notification;
struct GfteNewItem new_item;

/*******************************************************************************
 * Toolbar button stuff
 ******************************************************************************/
static void
gfte_toolbar_buttons_update(gboolean item, gboolean copy, gboolean delete,
                                          gboolean up, gboolean down)
{
      gtk_widget_set_sensitive(editor.tool_item, item);
      gtk_widget_set_sensitive(editor.tool_copy, copy);
      gtk_widget_set_sensitive(editor.tool_delete, delete);
      gtk_widget_set_sensitive(editor.tool_up, up);
      gtk_widget_set_sensitive(editor.tool_down, down);
}

/*******************************************************************************
 * Main stuff thats used all over the place
 ******************************************************************************/
static void
gfte_remove_temp() {
      gchar *name;

      if(!editor.path)
            return;

      name = g_path_get_basename(editor.path);
      if(name && name[0] == '.')
            gf_file_remove_dir(editor.path);
      g_free(name);
}

static void
gfte_save_theme() {
      gchar *path, *oldpath, *dir;
      gboolean loaded;

      loaded = gf_theme_is_loaded(editor.filename);

      if(loaded) {
            GfTheme *theme = gf_theme_find_theme_by_filename(editor.filename);

            if(theme)
                  gf_theme_unload(theme);
      }

      gf_theme_save_to_file(editor.theme, editor.filename);

      dir = gf_theme_strip_name(editor.theme);
      oldpath = g_path_get_dirname(editor.path);
      /* ok, because windows is retarded, and everyone can edit everything, we get to take
       * the editor's path, remove the last part of it, and then attach the new dir name to
       * it.
       */
      path = g_build_filename(oldpath, dir, NULL);
      g_free(oldpath);
      g_free(dir);

      /* move the dir */
      g_rename(editor.path, path);

      /* update the editor's path */
      g_free(editor.path);
      editor.path = path;

    /* update the editor's file */
      g_free(editor.filename);
      editor.filename = g_build_filename(editor.path, "theme.xml", NULL);

      /* reprobe the theme and tell the prefs to refresh it.. */
      gf_theme_probe(editor.filename);
      if(loaded) {
            gf_theme_load(editor.filename);
            gf_themes_save_loaded();
      }
      gf_preferences_refresh_themes_list();

      editor.changed = FALSE;
}

static void
gfte_store_add(GtkTreeStore *store, GtkTreeIter *child, GtkTreeIter *parent,
                     const gchar *title, gint page, gpointer data)
{
      gtk_tree_store_append(store, child, parent);
      gtk_tree_store_set(store, child,
                                 GFTE_STORE_TITLE, title,
                                 GFTE_STORE_PAGE, page,
                                 GFTE_STORE_OBJECT, data,
                                 -1);
}

static GtkTreeStore *
gfte_store_update() {
      GfThemeInfo *info;
      GfThemeOptions *ops;
      GfEvent *event;
      GfNotification *notification;
      GfItem *item;
      GtkTreeStore *store;
      GtkTreeIter parent, child, iter;
      GList *n, *i;

      store = gtk_tree_store_new(GFTE_STORE_TOTAL, G_TYPE_STRING, G_TYPE_INT,
                                             G_TYPE_POINTER);

      /* theme */
      gfte_store_add(store, &parent, NULL,
                           _("Theme"), GFTE_PAGE_THEME, editor.theme);

      /* info */
      info = gf_theme_get_theme_info(editor.theme);
      gfte_store_add(store, &child, &parent,
                           _("Info"), GFTE_PAGE_INFO, info);

      /* options */
      ops = gf_theme_get_theme_options(editor.theme);
      gfte_store_add(store, &child, &parent,
                           _("Options"), GFTE_PAGE_OPS, ops);

      /* notifications */
      for(n = gf_theme_get_notifications(editor.theme); n; n = n->next) {
            notification = GF_NOTIFICATION(n->data);
            event = gf_event_find_for_notification(gf_notification_get_type(notification));
            gfte_store_add(store, &child, &parent,
                                 gf_event_get_name(event),
                                 GFTE_PAGE_NOTIFICATION, notification);

            /* items */
            for(i = gf_notification_get_items(notification); i; i = i->next) {
                  GfItemType type;
                  gint page = -1;

                  item = GF_ITEM(i->data);

                  type = gf_item_get_type(item);
                  switch(type) {
                        case GF_ITEM_TYPE_ICON:
                              page = GFTE_PAGE_ICON;
                              break;
                        case GF_ITEM_TYPE_IMAGE:
                              page = GFTE_PAGE_IMAGE;
                              break;
                        case GF_ITEM_TYPE_TEXT:
                              page = GFTE_PAGE_TEXT;
                              break;
                        case GF_ITEM_TYPE_UNKNOWN:
                        default:
                              break;
                  }

                  if(page == -1)
                        continue;

                  gfte_store_add(store, &iter, &child,
                                       gf_item_type_to_string(type, TRUE),
                                       page, item);
            }
      }

      return store;
}

/* This was yanked from gtk 2.4.x, it's a wrapper to support this function on gtk 2.0 */
static void
gfte_tree_view_expand_to_path(GtkTreeView *tree, GtkTreePath *path) {
#if GTK_CHECK_VERSION(2,2,0)
      gtk_tree_view_expand_to_path(tree, path);
#else
      gint i, depth;
      gint *indices;
      GtkTreePath *tmp;

      g_return_if_fail(GTK_IS_TREE_VIEW(tree));
      g_return_if_fail(path != NULL);

      depth = gtk_tree_path_get_depth(path);
      indices = gtk_tree_path_get_indices(path);

      tmp = gtk_tree_path_new();
      g_return_if_fail(tmp != NULL);

      for(i = 0; i < depth; i++) {
        gtk_tree_path_append_index(tmp, indices[i]);
        gtk_tree_view_expand_row(tree, tmp, FALSE);
      }

      gtk_tree_path_free(tmp);
#endif /* GTK 2.0 */
}

static void
gfte_store_select_iter(GtkTreeIter *iter) {
      GtkTreeModel *model;
      GtkTreeSelection *sel;
      GtkTreePath *path;

      model = gtk_tree_view_get_model(GTK_TREE_VIEW(editor.tree));
      path = gtk_tree_model_get_path(model, iter);
      gfte_tree_view_expand_to_path(GTK_TREE_VIEW(editor.tree), path);
      gtk_tree_path_free(path);

      sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(editor.tree));
      gtk_tree_selection_select_iter(sel, iter);
}

static gchar *
gfte_make_temp_dir() {
      gchar *path, *dir;

      dir = g_strdup_printf(".%x", g_random_int());
      path = g_build_filename(gaim_user_dir(), "guifications", "themes", dir, NULL);
      g_free(dir);

      g_mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR);

      return path;
}

void
gfte_setup(const gchar *filename) {
      GfTheme *old = editor.theme;

      if(filename) {
            GfNotification *notification;
            GList *l;

            editor.theme = gf_theme_new_from_file(filename);

            for(l = gf_theme_get_notifications(editor.theme); l; l = l->next) {
                  notification = GF_NOTIFICATION(l->data);

                  if(!g_ascii_strcasecmp(GF_NOTIFICATION_MASTER,
                                                   gf_notification_get_type(notification))) {
                        editor.master = notification;
                        break;
                  }
            }
      } else {
            editor.theme = gf_theme_new();
            gf_theme_set_theme_info(editor.theme, gf_theme_info_new());
            gf_theme_set_theme_options(editor.theme, gf_theme_options_new());

            editor.master = gf_notification_new(editor.theme);
            gf_notification_set_type(editor.master, GF_NOTIFICATION_MASTER);
            gf_theme_add_notification(editor.theme, editor.master);
      }

      if(editor.theme) {
            if(old)
                  gf_theme_unload(old);
            old = NULL;
      } else {
            editor.theme = old;
            return;
      }

      gfte_remove_temp();

      if(editor.filename)
            g_free(editor.filename);

      if(filename) {
            editor.filename = g_strdup(filename);
      } else {
            gchar *path = NULL;

            path = gfte_make_temp_dir();
            editor.filename = g_build_filename(path, "theme.xml", NULL);
            g_free(path);
      }

      if(editor.path)
            g_free(editor.path);
      editor.path = g_path_get_dirname(editor.filename);

      if(editor.store) {
            gtk_tree_view_set_model(GTK_TREE_VIEW(editor.tree), NULL);
            g_object_unref(G_OBJECT(editor.store));
      }
      editor.store = gfte_store_update();

      if(editor.window) {
            GtkTreeIter iter;

            gtk_tree_view_set_model(GTK_TREE_VIEW(editor.tree),
                                                GTK_TREE_MODEL(editor.store));
            gtk_tree_view_expand_all(GTK_TREE_VIEW(editor.tree));

            gtk_tree_model_get_iter_first(GTK_TREE_MODEL(editor.store), &iter);
            gfte_store_select_iter(&iter);
      }

      editor.changed = FALSE;
}

static void
gfte_dialog_cleanup() {
      if(del_obj.window) {
            gtk_widget_destroy(del_obj.window);
            del_obj.window = NULL;
      }

      if(new_notification.window) {
            gtk_widget_destroy(new_notification.window);
            new_notification.window = NULL;
      }

      if(new_item.window) {
            gtk_widget_destroy(new_item.window);
            new_item.window = NULL;
      }

      if(modified.window) {
            gtk_widget_destroy(modified.window);
            modified.window = NULL;

            if(modified.filename)
                  g_free(modified.filename);
            modified.filename = NULL;
      }

      if(image_dialog) {
            gaim_request_close(GAIM_REQUEST_FILE, image_dialog);
            image_dialog = NULL;
      }

      if(opt_dialog) {
            gtk_widget_destroy(opt_dialog);
            opt_dialog = NULL;
      }
}

static void
gfte_cleanup() {
      gfte_dialog_cleanup();

      editor.window = NULL;

      if(editor.theme)
            gf_theme_unload(editor.theme);
      editor.theme = NULL;

      if(editor.filename)
            g_free(editor.filename);
      editor.filename = NULL;

      if(editor.path) {
            gchar *dir = NULL;

            dir = g_path_get_basename(editor.path);
            if(dir && dir[0] == '.') {
                  gf_file_remove_dir(editor.path);
                  g_free(dir);
            }

            g_free(editor.path);
      }
      editor.path = NULL;

      if(editor.store)
            g_object_unref(G_OBJECT(editor.store));
      editor.store = NULL;

      if(editor.tooltips)
            g_object_unref(G_OBJECT(editor.tooltips));
      editor.tooltips = NULL;
}

/*******************************************************************************
 * new/delete helpers
 ******************************************************************************/
static gpointer
gfte_store_get_row(GtkTreeIter *iter, gint *page, gchar **title) {
      GtkTreeModel *model;
      GtkTreeSelection *sel;
      gpointer object;

      sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(editor.tree));
      if(!gtk_tree_selection_get_selected(sel, &model, iter))
            return NULL;

      gtk_tree_model_get(model, iter,
                                 GFTE_STORE_OBJECT, &object,
                                 GFTE_STORE_PAGE, page,
                                 GFTE_STORE_TITLE, title,
                                 -1);

      return object;
}

/*******************************************************************************
 * modified dialog stuff
 ******************************************************************************/
static gboolean
gfte_modified_deleted_cb(GtkWidget *w, GdkEvent *e, gpointer data) {
      modified.window = NULL;

      if(modified.filename)
            g_free(modified.filename);
      modified.filename = NULL;

      return FALSE;
}

static void
gfte_modified_cancel_cb(GtkButton *button, gpointer data) {
      gtk_widget_destroy(modified.window);
      modified.window = NULL;

      if(modified.filename)
            g_free(modified.filename);
      modified.filename = NULL;
}

static void
gfte_modified_no_cb(GtkButton *button, gpointer data) {
      gtk_widget_destroy(modified.window);
      modified.window = NULL;

      gfte_remove_temp();

      switch(modified.type) {
            case GFTE_MODIFIED_CLOSE:
                  gtk_widget_destroy(editor.window);
                  gfte_cleanup();

                  break;
            case GFTE_MODIFIED_NEW:
                  gfte_setup(NULL);

                  break;
            case GFTE_MODIFIED_OPEN:
                  if(modified.filename) {
                        gfte_setup(modified.filename);
                        g_free(modified.filename);
                        modified.filename = NULL;
                  }

                  break;
            case GFTE_MODIFIED_TOTAL:
            default:
                  break;
      }
}

static void
gfte_modified_yes_cb(GtkButton *button, gpointer data) {
      gtk_widget_destroy(modified.window);
      modified.window = NULL;

      gfte_save_theme();

      switch(modified.type) {
            case GFTE_MODIFIED_CLOSE:
                  gtk_widget_destroy(editor.window);
                  gfte_cleanup();

                  break;
            case GFTE_MODIFIED_NEW:
                  gfte_setup(NULL);

                  break;
            case GFTE_MODIFIED_OPEN:
                  if(modified.filename) {
                        gfte_setup(modified.filename);
                        g_free(modified.filename);
                        modified.filename = NULL;
                  }

                  break;
            case GFTE_MODIFIED_TOTAL:
            default:
                  break;
      }
}

static void
gfte_modified_show(gint type, const gchar *filename) {
      GtkWidget *vbox, *hbox, *widget;
      gchar *label = NULL;

      if(modified.window) {
            gtk_widget_show(modified.window);
            return;
      }

      if(type == GFTE_MODIFIED_CLOSE)
            label = g_strdup(_("Would you like to save before closing?"));
      else if(type == GFTE_MODIFIED_NEW)
            label = g_strdup(_("Would you like to save before creating a new theme?"));
      else if(type == GFTE_MODIFIED_OPEN)
            label = g_strdup_printf(_("Would you like to save before opening %s?"),
                                                filename);
      else
            return;

      gfte_dialog_cleanup();

      modified.type = type;
      modified.filename = g_strdup(filename);

      modified.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW(modified.window), _("Confirm"));
      gtk_window_set_resizable(GTK_WINDOW(modified.window), FALSE);
      gtk_container_set_border_width(GTK_CONTAINER(modified.window), 12);
      g_signal_connect(G_OBJECT(modified.window), "delete-event",
                               G_CALLBACK(gfte_modified_deleted_cb), NULL);

      vbox = gtk_vbox_new(FALSE, 4);
      gtk_container_add(GTK_CONTAINER(modified.window), vbox);

      widget = gtk_label_new(label);
      g_free(label);
      gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);
      gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);

      widget = gtk_hseparator_new();
      gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);

      hbox = gtk_hbox_new(FALSE, 4);
      gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_YES);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_modified_yes_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_NO);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_modified_no_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_modified_cancel_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      gtk_widget_show_all(modified.window);
}

/*******************************************************************************
 * Delete Item Stuff
 ******************************************************************************/
static gboolean
gfte_delete_deleted_cb(GtkWidget *w, GdkEvent *e, gpointer data) {
      del_obj.window = NULL;
      return FALSE;
}

static void
gfte_delete_no_cb(GtkButton *button, gpointer data) {
      gtk_widget_destroy(del_obj.window);
      del_obj.window = NULL;
}

static void
gfte_delete_yes_cb(GtkButton *button, gpointer data) {
      GtkTreeIter iter;
      gpointer obj = NULL;
      gchar *title = NULL;
      gint page;

      obj = gfte_store_get_row(&iter, &page, &title);

      if (title)
            g_free(title);

      if(!obj) {
            gtk_widget_destroy(del_obj.window);
            del_obj.window = NULL;
            return;
      }

      if(page == GFTE_PAGE_NOTIFICATION) {
            GfTheme *theme = gf_notification_get_theme(GF_NOTIFICATION(obj));

            gf_theme_remove_notification(theme, GF_NOTIFICATION(obj));
            gf_notification_destroy(GF_NOTIFICATION(obj));
            gtk_tree_store_remove(editor.store, &iter);
      } else if(page == GFTE_PAGE_ICON ||
                    page == GFTE_PAGE_IMAGE ||
                    page == GFTE_PAGE_TEXT)
      {
            GfNotification *notification = gf_item_get_notification(GF_ITEM(obj));

            gf_notification_remove_item(notification, GF_ITEM(obj));
            gf_item_destroy(GF_ITEM(obj));
            gtk_tree_store_remove(editor.store, &iter);

      }

      gtk_widget_destroy(del_obj.window);
      del_obj.window = NULL;

      gfte_toolbar_buttons_update(FALSE, FALSE, FALSE, FALSE, FALSE);

      editor.changed = TRUE;
}

static void
gfte_delete_show(GtkButton *button, gpointer data) {
      GtkWidget *vbox, *hbox, *widget;
      GtkTreeIter iter;
      gchar *item, *title, *label;
      gint page;

      if(del_obj.window) {
            gtk_widget_show(del_obj.window);
            return;
      }

      gfte_dialog_cleanup();

      gfte_store_get_row(&iter, &page, &item);

      if(page == GFTE_PAGE_NOTIFICATION) {
            label = g_strdup_printf(_("Are you sure you want to delete this %s notification?"),
                        item);
            title = g_strdup(_("Confirm delete notification"));
            g_free(item);
      } else if(page == GFTE_PAGE_ICON ||
                  page == GFTE_PAGE_IMAGE ||
                  page == GFTE_PAGE_TEXT)
      {
            label = g_strdup_printf(_("Are you sure you want to delete this %s item?"),
                        item);
            title = g_strdup(_("Confirm delete item"));
            g_free(item);
      } else {
            g_free(item);
            gtk_widget_destroy(del_obj.window);
            del_obj.window = NULL;
            return;
      }

      del_obj.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW(del_obj.window), title);
      g_free(title);
      gtk_window_set_resizable(GTK_WINDOW(del_obj.window), FALSE);
      gtk_container_set_border_width(GTK_CONTAINER(del_obj.window), 12);
      g_signal_connect(G_OBJECT(del_obj.window), "delete-event",
                               G_CALLBACK(gfte_delete_deleted_cb), NULL);

      vbox = gtk_vbox_new(FALSE, 4);
      gtk_container_add(GTK_CONTAINER(del_obj.window), vbox);

      widget = gtk_label_new(label);
      gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);

      /* separator */
      widget = gtk_hseparator_new();
      gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);

      /* buttons */
      hbox = gtk_hbox_new(FALSE, 4);
      gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_YES);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_delete_yes_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_NO);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_delete_no_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      gtk_widget_show_all(del_obj.window);
}

/*******************************************************************************
 * New Notification Stuff
 ******************************************************************************/
static gboolean
gfte_new_notification_deleted_cb(GtkWidget *w, GdkEvent *e, gpointer data) {
      new_notification.window = NULL;
      return FALSE;
}

static void
gfte_new_notification_cancel_cb(GtkButton *button, gpointer data) {
      gtk_widget_destroy(new_notification.window);
      new_notification.window = NULL;
}

static void
gfte_new_notification_ok_cb(GtkButton *button, gpointer data) {
      GfNotification *notification;
      GtkTreeIter parent, child;
      gpointer object;
      const gchar *n_type;
      gint hist;

      gtk_tree_model_get_iter_first(GTK_TREE_MODEL(editor.store), &parent);

      gtk_tree_model_get(GTK_TREE_MODEL(editor.store), &parent,
                                 GFTE_STORE_OBJECT, &object,
                                 -1);

      if(object == NULL) {
            gaim_debug_misc("guifications", "ouch, I don't know where to put this, aborting\n");
            if(new_notification.window)
                  gtk_widget_destroy(new_notification.window);
            new_notification.window = NULL;
            return;
      }

      if(!editor.master) {
            notification = gf_notification_new(GF_THEME(object));
      } else {
            notification = gf_notification_copy(editor.master);
      }

      /* find the notification type, this really needs to get cleaned up but works for now
       */
      hist = gtk_option_menu_get_history(GTK_OPTION_MENU(new_notification.type));
      n_type = gf_events_get_nth_notification(hist);

      /* create the notification */
      gf_notification_set_type(notification, n_type);

      /* add it to the theme */
      gf_theme_add_notification(GF_THEME(object), notification);

      /* add it to the store */
      gfte_store_add(editor.store, &child, &parent,
                           gf_events_get_nth_name(hist),
                           GFTE_PAGE_NOTIFICATION,
                           notification);
      gfte_store_select_iter(&child);

      if(editor.master) {
            GtkTreeIter item;
            GList *l;
            gboolean expand = FALSE;

            for(l = gf_notification_get_items(notification); l; l = l->next) {
                  gint type = gf_item_get_type(GF_ITEM(l->data));

                  gfte_store_add(editor.store, &item, &child,
                                       gf_item_type_to_string(type, TRUE),
                                       type + GFTE_PAGE_ICON,
                                       l->data);

                  if(!expand)
                        expand = TRUE;
            }

            if(expand) {
                  GtkTreePath *path;

                  path = gtk_tree_model_get_path(GTK_TREE_MODEL(editor.store), &child);
                  gtk_tree_view_expand_row(GTK_TREE_VIEW(editor.tree), path, TRUE);
                  gtk_tree_path_free(path);
            }           
      }

      if(new_notification.window)
            gtk_widget_destroy(new_notification.window);
      new_notification.window = NULL;

      editor.changed = TRUE;
}

static void
gfte_new_notification_show(GtkButton *button, gpointer data) {
      GtkWidget *vbox, *hbox, *widget, *menu;

      if(new_notification.window) {
            gtk_widget_show(new_notification.window);
            return;
      }

      gfte_dialog_cleanup();

      new_notification.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW(new_notification.window), _("New Notification"));
      gtk_window_set_resizable(GTK_WINDOW(new_notification.window), FALSE);
      gtk_container_set_border_width(GTK_CONTAINER(new_notification.window), 12);
      g_signal_connect(G_OBJECT(new_notification.window), "delete-event",
                               G_CALLBACK(gfte_new_notification_deleted_cb), NULL);

      vbox = gtk_vbox_new(FALSE, 4);
      gtk_container_add(GTK_CONTAINER(new_notification.window), vbox);

      /* option menu */
      hbox = gtk_hbox_new(FALSE, 4);
      gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

      widget = gtk_label_new(_("New notification type:"));
      gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      menu = gf_menu_build(gf_menu_event);
      new_notification.type = gtk_option_menu_new();
      gtk_option_menu_set_menu(GTK_OPTION_MENU(new_notification.type), menu);
      gtk_box_pack_start(GTK_BOX(hbox), new_notification.type, TRUE, TRUE, 0);

      /* separator */
      widget = gtk_hseparator_new();
      gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);

      /* buttons */
      hbox = gtk_hbox_new(FALSE, 4);
      gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_OK);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_new_notification_ok_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_new_notification_cancel_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      gtk_widget_show_all(new_notification.window);
}

/*******************************************************************************
 * New Item Stuff
 ******************************************************************************/
static gboolean
gfte_new_item_deleted_cb(GtkWidget *w, GdkEvent *e, gpointer data) {
      new_item.window = NULL;
      return FALSE;
}

static void
gfte_new_item_cancel_cb(GtkButton *button, gpointer data) {
      gtk_widget_destroy(new_item.window);
      new_item.window = NULL;
}

static void
gfte_new_item_ok_cb(GtkButton *button, gpointer data) {
      GfItem *item;
      GfItemType type;
      GfItemOffset *offset;
      GtkTreeIter parent, child;
      gpointer object;
      gchar *title = NULL;
      gint page;

      object = gfte_store_get_row(&parent, &page, &title);

      /* we have to grab this early in case the selected node is an item, and we select the
       * notification.  If we don't grab this, the selection changes kills all the dialogs
       * making this and invalid widget.
       */
      type = gtk_option_menu_get_history(GTK_OPTION_MENU(new_item.type));

      if(page == GFTE_PAGE_ICON || page == GFTE_PAGE_IMAGE ||
         page == GFTE_PAGE_TEXT)
      {
            GtkTreeSelection *sel;

            gtk_tree_model_iter_parent(GTK_TREE_MODEL(editor.store), &child, &parent);

            if(title)
                  g_free(title);

            sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(editor.tree));
            gtk_tree_selection_select_iter(sel, &child);

            object = gfte_store_get_row(&parent, &page, &title);
      }

      if(title)
            g_free(title);

      if(!object) {
            gaim_debug_misc("guifications",
                                    "ouch, I don't know where to put this, aborting\n");

            if(new_item.window)
                  gtk_widget_destroy(new_item.window);

            new_item.window = NULL;
            return;
      }

      item = gf_item_new(GF_NOTIFICATION(object));
      gf_item_set_type(item, type);

      if(type == GF_ITEM_TYPE_ICON) {
            GfItemIcon *icon = gf_item_icon_new(item);
            gf_item_set_item_icon(item, icon);
      } else if(type == GF_ITEM_TYPE_IMAGE) {
            GfItemImage *image = gf_item_image_new(item);
            gf_item_set_item_image(item, image);
      } else if(type == GF_ITEM_TYPE_TEXT) {
            GfItemText *text = gf_item_text_new(item);
            gf_item_set_item_text(item, text);
      }

      /* create the offsets for the item */
      offset = gf_item_offset_new(item);
      gf_item_set_horz_offset(item, offset);
      offset = gf_item_offset_new(item);
      gf_item_set_vert_offset(item, offset);

      /* add the item to the notification */
      gf_notification_add_item(GF_NOTIFICATION(object), item);

      /* throw it in the tree */
      gfte_store_add(editor.store, &child, &parent,
                           gf_item_type_to_string(type, TRUE),
                           /* gross dirty hack.. clean me up later */
                           type + GFTE_PAGE_ICON,
                           item);
      gfte_store_select_iter(&child);

      if(new_item.window)
            gtk_widget_destroy(new_item.window);
      new_item.window = NULL;

      editor.changed = TRUE;
}

static void
gfte_new_item_show(GtkButton *button, gpointer data) {
      GtkWidget *vbox, *hbox, *widget, *menu;

      if(new_item.window) {
            gtk_widget_show(new_item.window);
            return;
      }

      gfte_dialog_cleanup();

      new_item.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW(new_item.window), _("New Item"));
      gtk_window_set_resizable(GTK_WINDOW(new_item.window), FALSE);
      gtk_widget_set_size_request(new_item.window, 250, -1);
      gtk_container_set_border_width(GTK_CONTAINER(new_item.window), 12);
      g_signal_connect(G_OBJECT(new_item.window), "delete-event",
                               G_CALLBACK(gfte_new_item_deleted_cb), NULL);

      vbox = gtk_vbox_new(FALSE, 4);
      gtk_container_add(GTK_CONTAINER(new_item.window), vbox);

      /* option menu */
      hbox = gtk_hbox_new(FALSE, 4);
      gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

      widget = gtk_label_new(_("New item type:"));
      gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      menu = gf_menu_build(gf_menu_item_type);
      new_item.type = gtk_option_menu_new();
      gtk_option_menu_set_menu(GTK_OPTION_MENU(new_item.type), menu);
      gtk_box_pack_start(GTK_BOX(hbox), new_item.type, TRUE, TRUE, 0);

      /* separator */
      widget = gtk_hseparator_new();
      gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);

      /* buttons */
      hbox = gtk_hbox_new(FALSE, 4);
      gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_OK);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_new_item_ok_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
      g_signal_connect(G_OBJECT(widget), "clicked",
                               G_CALLBACK(gfte_new_item_cancel_cb), NULL);
      gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      gtk_widget_show_all(new_item.window);
}

/*******************************************************************************
 * Duplicate stuff
 ******************************************************************************/
static void
gfte_duplicate_object(GtkButton *button, gpointer data) {
      GtkTreeIter parent, child, sibling;
      gchar *title;
      gint page;
      gpointer object;

      object = gfte_store_get_row(&sibling, &page, &title);

      gtk_tree_model_iter_parent(GTK_TREE_MODEL(editor.store), &parent, &sibling);

      if(page == GFTE_PAGE_NOTIFICATION) {
            GfNotification *notification;
            GtkTreeIter item;
            GList *l;
            gboolean expand = FALSE;

            notification = gf_notification_copy(GF_NOTIFICATION(object));

            gfte_store_add(editor.store, &child, &parent, title, page,
                                 notification);

            for(l = gf_notification_get_items(notification); l; l = l->next) {
                  gint type = gf_item_get_type(GF_ITEM(l->data));

                  gfte_store_add(editor.store, &item, &child,
                                       gf_item_type_to_string(type, TRUE),
                                       type + GFTE_PAGE_ICON,
                                       l->data);

                  if(!expand)
                        expand = TRUE;
            }

            gf_theme_add_notification(editor.theme, notification);

            if(expand) {
                  GtkTreePath *path;

                  path = gtk_tree_model_get_path(GTK_TREE_MODEL(editor.store), &child);
                  gtk_tree_view_expand_row(GTK_TREE_VIEW(editor.tree), path, TRUE);
                  gtk_tree_path_free(path);
            }
      } else if(page == GFTE_PAGE_ICON || page == GFTE_PAGE_IMAGE ||
                    page == GFTE_PAGE_TEXT)
      {
            GfItem *item;
            item = gf_item_copy(GF_ITEM(object));

            gtk_tree_store_append(editor.store, &child, &parent);
            gtk_tree_store_set(editor.store, &child,
                                       GFTE_STORE_OBJECT, item,
                                       GFTE_STORE_PAGE, page,
                                       GFTE_STORE_TITLE, title,
                                       -1);

            gf_notification_add_item(gf_item_get_notification(GF_ITEM(object)),
                                                 item);
      } else {
            return;
      }

      if(title)
            g_free(title);

      gfte_store_select_iter(&child);

      editor.changed = TRUE;
}

/*******************************************************************************
 * Moving items
 ******************************************************************************/
static gboolean
gfte_is_older_notification(GfNotification *notification) {
      GfTheme *theme;
      GList *l;

      theme = gf_notification_get_theme(notification);
      if(!theme)
            return FALSE;

      l = gf_theme_get_notifications(theme);
      if(l->data == notification)
            return FALSE;
      else
            return TRUE;
}

static gboolean
gfte_is_younger_notification(GfNotification *notification) {
      GfTheme *theme;
      GList *l;

      theme = gf_notification_get_theme(notification);
      if(!theme)
            return FALSE;

      /* get the last notification */
      for(l = gf_theme_get_notifications(theme); l->next; l = l->next);

      if(l->data == notification)
            return FALSE;
      else
            return TRUE;
}

static gboolean
gfte_is_older_item(GfItem *item) {
      GfNotification *notification;
      GList *l;

      notification = gf_item_get_notification(item);
      if(!notification)
            return FALSE;

      l = gf_notification_get_items(notification);
      if(l->data == item)
            return FALSE;
      else
            return TRUE;
}

static gboolean
gfte_is_younger_item(GfItem *item) {
      GfNotification *notification;
      GList *l;

      notification = gf_item_get_notification(item);
      if(!notification)
            return FALSE;

      /* get the last item */
      for(l = gf_notification_get_items(notification); l->next; l = l->next);

      if(l->data == item)
            return FALSE;
      else
            return TRUE;
}

static void
gfte_store_swap(GtkTreeIter *a, GtkTreeIter *b) {
      GtkTreeSelection *sel;
      gchar *a_title, *b_title;
      gint a_page, b_page;
      gpointer a_obj, b_obj;
#if !GTK_CHECK_VERSION(2,2,0)
      gchar *title;
      gint page;
      gpointer obj;
#endif /* !GTK_CHECK_VERSION(2,2,0) */

      /* get 'em */
      gtk_tree_model_get(GTK_TREE_MODEL(editor.store), a,
                                 GFTE_STORE_OBJECT, &a_obj,
                                 GFTE_STORE_PAGE, &a_page,
                                 GFTE_STORE_TITLE, &a_title,
                                 -1);
      gtk_tree_model_get(GTK_TREE_MODEL(editor.store), b,
                                 GFTE_STORE_OBJECT, &b_obj,
                                 GFTE_STORE_PAGE, &b_page,
                                 GFTE_STORE_TITLE, &b_title,
                                 -1);

      if(a_page == GFTE_PAGE_NOTIFICATION)
            gf_notifications_swap(GF_NOTIFICATION(a_obj), GF_NOTIFICATION(b_obj));
      else {
            gf_items_swap(GF_ITEM(a_obj), GF_ITEM(b_obj));
      }

#if GTK_CHECK_VERSION(2,2,0)
      gtk_tree_store_swap(editor.store, a, b);
#else
      /* swap 'em */
      title = a_title;
      page = a_page;
      obj = a_obj;

      a_title = b_title;
      a_page = b_page;
      a_obj = b_obj;

      b_title = title;
      b_page = page;
      b_obj = obj;

      /* set 'em */
      gtk_tree_store_set(editor.store, a,
                                 GFTE_STORE_OBJECT, a_obj,
                                 GFTE_STORE_PAGE, a_page,
                                 GFTE_STORE_TITLE, a_title,
                                 -1);
      gtk_tree_store_set(editor.store, b,
                                 GFTE_STORE_OBJECT, b_obj,
                                 GFTE_STORE_PAGE, b_page,
                                 GFTE_STORE_TITLE, b_title,
                                 -1);

#endif /* GTK_CHECK_VERSION(2,2,0) */

      /* clean up */
      g_free(a_title);
      g_free(b_title);

      sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(editor.tree));
      gfte_selection_changed_cb(sel, NULL);
}

static void
gfte_move_up(GtkButton *button, gpointer data) {
      GtkTreeIter child, sibling;
      GtkTreePath *path;
      gpointer object;
      gchar *title;
      gint page;

      object = gfte_store_get_row(&child, &page, &title);

      if(title)
            g_free(title);

      path = gtk_tree_model_get_path(GTK_TREE_MODEL(editor.store), &child);
      if(!path)
            return;

      if(!gtk_tree_path_prev(path)) {
            gtk_tree_path_free(path);
            return;
      }

      if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(editor.store), &sibling, path)) {
            gtk_tree_path_free(path);
            return;
      }

      gfte_store_swap(&child, &sibling);
      gtk_tree_path_free(path);
}

static void
gfte_move_down(GtkButton *button, gpointer data) {
      GtkTreeIter child, sibling;
      GtkTreePath *path;
      gpointer object;
      gchar *title;
      gint page;

      object = gfte_store_get_row(&child, &page, &title);

      if(title)
            g_free(title);

      path = gtk_tree_model_get_path(GTK_TREE_MODEL(editor.store), &child);
      if(!path)
            return;

      /* we just assume this works since it returns void I guess.. */
      gtk_tree_path_next(path);

      if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(editor.store), &sibling, path)) {
            gtk_tree_path_free(path);
            return;
      }

      gfte_store_swap(&child, &sibling);
      gtk_tree_path_free(path);
}

/*******************************************************************************
 * getter/setter helpers
 ******************************************************************************/
static gpointer
gfte_store_get_object() {
      GtkTreeIter iter;
      GtkTreeModel *model;
      GtkTreeSelection *sel;
      gpointer object;

    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(editor.tree));
      gtk_tree_selection_get_selected(sel, &model, &iter);

      gtk_tree_model_get(model, &iter,
                                 GFTE_STORE_OBJECT, &object,
                                 -1);

      return object;
}

/*******************************************************************************
 * Setters
 ******************************************************************************/
static void
gfte_set_value(GtkWidget *widget, gint page, gpointer object, gconstpointer value) {
      GfteSetFunc setter = g_object_get_data(G_OBJECT(widget), "setter");
      gint flags = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "flags"));

      switch(page) {
            case GFTE_PAGE_THEME:
                  break;
            case GFTE_PAGE_INFO:
                  setter(GF_THEME_INFO(object), value);
                  break;
            case GFTE_PAGE_OPS:
                  setter(GF_THEME_OPTIONS(object), value);
                  break;
            case GFTE_PAGE_NOTIFICATION:
                  setter(GF_NOTIFICATION(object), value);
                  break;
            case GFTE_PAGE_ICON:
                  switch(flags) {
                        case GFTE_FLAGS_OBJECT:
                              setter(GF_ITEM(object), value);
                              break;
                        case GFTE_FLAGS_SUB_OBJECT:
                              setter(gf_item_get_item_icon(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_H_OFFSET:
                              setter(gf_item_get_horz_offset(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_V_OFFSET:
                              setter(gf_item_get_vert_offset(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_TOTAL:
                        default:
                              break;
                  }

                  break;
            case GFTE_PAGE_IMAGE:
                  switch(flags) {
                        case GFTE_FLAGS_OBJECT:
                              setter(GF_ITEM(object), value);
                              break;
                        case GFTE_FLAGS_SUB_OBJECT:
                              setter(gf_item_get_item_image(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_H_OFFSET:
                              setter(gf_item_get_horz_offset(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_V_OFFSET:
                              setter(gf_item_get_vert_offset(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_TOTAL:
                        default:
                              break;
                  }

                  break;
            case GFTE_PAGE_TEXT:
                  switch(flags) {
                        case GFTE_FLAGS_OBJECT:
                              setter(GF_ITEM(object), value);
                              break;
                        case GFTE_FLAGS_SUB_OBJECT:
                              setter(gf_item_get_item_text(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_H_OFFSET:
                              setter(gf_item_get_horz_offset(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_V_OFFSET:
                              setter(gf_item_get_vert_offset(GF_ITEM(object)), value);
                              break;
                        case GFTE_FLAGS_TOTAL:
                        default:
                              break;
                  }

                  break;
            case GFTE_PAGE_TOTAL:
            default:
                  break;
      }
}

static void
gfte_entry_changed_cb(GtkWidget *widget, gpointer data) {
      gpointer object = gfte_store_get_object();
      gpointer value = (gpointer)gtk_entry_get_text(GTK_ENTRY(widget));
      gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(editor.note));

      editor.changed = TRUE;

      gfte_set_value(widget, page, object, value);
}

static void
gfte_check_toggled_cb(GtkWidget *widget, gpointer data) {
      gpointer object = gfte_store_get_object();
      gpointer pvalue;
      gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(editor.note));
      gboolean value;

      value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
      pvalue = GINT_TO_POINTER(value);

      editor.changed = TRUE;

      gfte_set_value(widget, page, object, pvalue);

      /* adjust the sensitivity according to the use gtk setting */
      if(widget == editor.notification.use_gtk) {
            gtk_widget_set_sensitive(editor.notification.button, !value);
            gtk_widget_set_sensitive(editor.notification.width, value);
            gtk_widget_set_sensitive(editor.notification.height, value);
      }
}

static void
gfte_spin_changed_cb(GtkWidget *widget, GtkScrollType arg1, gpointer user_data)
{
      gpointer object = gfte_store_get_object();
      gpointer value = GINT_TO_POINTER(gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
      gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(editor.note));

      editor.changed = TRUE;

      gfte_set_value(widget, page, object, value);
}

static void
gfte_option_menu_changed_cb(GtkWidget *widget, gpointer user_data) {
      gpointer object = gfte_store_get_object();
      gpointer value = GINT_TO_POINTER(gtk_option_menu_get_history(GTK_OPTION_MENU(widget)));
      gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(editor.note));

      editor.changed = TRUE;

      gfte_set_value(widget, page, object, value);
}

/*******************************************************************************
 * Getters
 ******************************************************************************/
static gpointer
gfte_get_value(GtkWidget *widget, gint page, gpointer object) {
      GfteGetFunc getter = g_object_get_data(G_OBJECT(widget), "getter");
      /* The flags decide what item we use in our call to the getter function.  This could
       * be GfTheme, GfThemeInfo, GfThemeOptions, GfNotification, GfItem, GfItemIcon,
       * GfItemImage, GfItemOffset, or GfItemText.  Everything besides the item gets called
       * as is.  Thats why we check it only for the item pages.  Since the object we need
       * to use for the getter could be GfItem, GfItemIcon, GfItemImage, GfItemOffset, or
       * GfItemText.  Once we know what it is, we can call it correctly.
       */
      gint flags = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "flags"));

      switch(page) {
            case GFTE_PAGE_THEME:
                  return (gpointer)getter(GF_THEME(object));
                  break;
            case GFTE_PAGE_INFO:
                  return (gpointer)getter(GF_THEME_INFO(object));
                  break;
            case GFTE_PAGE_OPS:
                  return (gpointer)getter(GF_THEME_OPTIONS(object));
                  break;
            case GFTE_PAGE_NOTIFICATION:
                  return (gpointer)getter(GF_NOTIFICATION(object));
                  break;
            case GFTE_PAGE_ICON:
                  switch(flags) {
                        case GFTE_FLAGS_OBJECT:
                              return (gpointer)getter(GF_ITEM(object));
                              break;
                        case GFTE_FLAGS_SUB_OBJECT:
                              return (gpointer)getter(gf_item_get_item_icon(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_H_OFFSET:
                              return (gpointer)getter(gf_item_get_horz_offset(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_V_OFFSET:
                              return (gpointer)getter(gf_item_get_vert_offset(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_TOTAL:
                        default:
                              return NULL;
                  }

                  break;
            case GFTE_PAGE_IMAGE:
                  switch(flags) {
                        case GFTE_FLAGS_OBJECT:
                              return (gpointer)getter(GF_ITEM(object));
                              break;
                        case GFTE_FLAGS_SUB_OBJECT:
                              return (gpointer)getter(gf_item_get_item_image(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_H_OFFSET:
                              return (gpointer)getter(gf_item_get_horz_offset(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_V_OFFSET:
                              return (gpointer)getter(gf_item_get_vert_offset(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_TOTAL:
                        default:
                              return NULL;
                  }

                  break;
            case GFTE_PAGE_TEXT:
                  switch(flags) {
                        case GFTE_FLAGS_OBJECT:
                              return (gpointer)getter(GF_ITEM(object));
                              break;
                        case GFTE_FLAGS_SUB_OBJECT:
                              return (gpointer)getter(gf_item_get_item_text(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_H_OFFSET:
                              return (gpointer)getter(gf_item_get_horz_offset(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_V_OFFSET:
                              return (gpointer)getter(gf_item_get_vert_offset(GF_ITEM(object)));
                              break;
                        case GFTE_FLAGS_TOTAL:
                        default:
                              return NULL;
                  }

                  break;
            case GFTE_PAGE_TOTAL:
            default:
                  return NULL;
                  break;
      }
}

/*******************************************************************************
 * Update Widgets
 ******************************************************************************/
static void
gfte_update_entry(GtkWidget *entry, gint page, gpointer object) {
      gchar *value = (gchar *)gfte_get_value(entry, page, object);

      g_signal_handlers_block_by_func(G_OBJECT(entry),
                                                      gfte_entry_changed_cb, NULL);

      if(value)
            gtk_entry_set_text(GTK_ENTRY(entry), value);
      else
            gtk_entry_set_text(GTK_ENTRY(entry), "");

      g_signal_handlers_unblock_by_func(G_OBJECT(entry),
                                                        gfte_entry_changed_cb, NULL);
}

static void
gfte_update_option_menu(GtkWidget *opt_menu, gint page, gpointer object) {
      gint value = GPOINTER_TO_INT(gfte_get_value(opt_menu, page, object));

      g_signal_handlers_block_by_func(G_OBJECT(opt_menu),
                                                      gfte_option_menu_changed_cb, NULL);

      gtk_option_menu_set_history(GTK_OPTION_MENU(opt_menu), value);

      g_signal_handlers_unblock_by_func(G_OBJECT(opt_menu),
                                                        gfte_option_menu_changed_cb, NULL);
}

static void
gfte_update_spin_button(GtkWidget *spin, gint page, gpointer object) {
      gint value = GPOINTER_TO_INT(gfte_get_value(spin, page, object));

      g_signal_handlers_block_by_func(G_OBJECT(spin),
                                                      gfte_spin_changed_cb, NULL);

      gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value);

      g_signal_handlers_unblock_by_func(G_OBJECT(spin),
                                                        gfte_spin_changed_cb, NULL);
}

static void
gfte_update_check(GtkWidget *check, gint page, gpointer object) {
      gboolean value = GPOINTER_TO_INT(gfte_get_value(check, page, object));

      g_signal_handlers_block_by_func(G_OBJECT(check),
                                                      gfte_check_toggled_cb, NULL);

      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), value);

      g_signal_handlers_unblock_by_func(G_OBJECT(check),
                                                        gfte_check_toggled_cb, NULL);

      /* adjust the sensitivity according to the use gtk setting */
      if(check == editor.notification.use_gtk) {
            gtk_widget_set_sensitive(editor.notification.button, !value);
            gtk_widget_set_sensitive(editor.notification.width, value);
            gtk_widget_set_sensitive(editor.notification.height, value);
      }
}

/*******************************************************************************
 * Button stuff (requires getters and setters thats why it's here...)
 ******************************************************************************/
static void
gfte_dialog_file_ok_cb(gpointer data, const gchar *filename) {
      GtkWidget *button;
      gpointer object;
      gchar *relative = NULL, *target = NULL;
      gint page;

      if(!filename) {
            image_dialog = NULL;
            return;
      }

      button = GTK_WIDGET(data);
      object = gfte_store_get_object();
      page = gtk_notebook_get_current_page(GTK_NOTEBOOK(editor.note));

      relative = g_path_get_basename(filename);
      target = g_build_filename(editor.path, relative, NULL);

      if(!gf_file_copy_file(filename, target)) {
            g_free(target);
            g_free(relative);
            return;
      }
      g_free(target);

      gfte_set_value(button, page, object, relative);
      g_free(relative);

      if(page == GFTE_PAGE_NOTIFICATION)
            gfte_update_entry(editor.notification.filename, page, object);
      else if(page == GFTE_PAGE_IMAGE)
            gfte_update_entry(editor.image.filename, page, object);
}

static void
gfte_dialog_file_cancel_cb(gpointer data, const gchar *filename) {
      image_dialog = NULL;
}

static void
gfte_dialog_font_ok_cb(GtkButton *b, gpointer data) {
      GtkWidget *button;
      gpointer object;
      gchar *font;
      gint page;

      button = GTK_WIDGET(data);
      object = gfte_store_get_object();
      page = gtk_notebook_get_current_page(GTK_NOTEBOOK(editor.note));

      font = gtk_font_selection_dialog_get_font_name(
                                                      GTK_FONT_SELECTION_DIALOG(opt_dialog));
      gfte_set_value(button, page, object, font);

      if(font)
            g_free(font);

      gtk_widget_destroy(opt_dialog);
      opt_dialog = NULL;
}

static void
gfte_dialog_font_cancel_cb(GtkButton *b, gpointer data) {
      gtk_widget_destroy(opt_dialog);
      opt_dialog = NULL;
}

static void
gfte_dialog_color_ok_cb(GtkButton *b, gpointer data) {
      GtkWidget *button;
      GdkColor gcolor;
      gpointer object;
      gchar ccolor[14];
      gint page;

      button = GTK_WIDGET(data);
      object = gfte_store_get_object();
      page = gtk_notebook_get_current_page(GTK_NOTEBOOK(editor.note));

      gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(
                                          GTK_COLOR_SELECTION_DIALOG(opt_dialog)->colorsel),
                                          &gcolor);

      g_snprintf(ccolor, sizeof(ccolor), "#%04x%04x%04x",
                     gcolor.red, gcolor.green, gcolor.blue);

      gfte_set_value(button, page, object, ccolor);

      gtk_widget_destroy(opt_dialog);
      opt_dialog = NULL;
}

static void
gfte_dialog_color_cancel_cb(GtkButton *b, gpointer data) {
      gtk_widget_destroy(opt_dialog);
      opt_dialog = NULL;
}

static void
gfte_button_clicked_cb(GtkWidget *button, gpointer data) {
      gpointer object, value;
      gint page, type;

      gfte_dialog_cleanup();

      type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "type"));

      object = gfte_store_get_object();
      page = gtk_notebook_get_current_page(GTK_NOTEBOOK(editor.note));
      value = gfte_get_value(button, page, object);

      if(type == GFTE_BUTTON_FILE) {
            image_dialog = gaim_request_file(plugin_handle, _("Open"), "", FALSE,
                                                             G_CALLBACK(gfte_dialog_file_ok_cb),
                                                             G_CALLBACK(gfte_dialog_file_cancel_cb),
                                                             (gpointer)button);
      } else if(type == GFTE_BUTTON_FONT) {
            opt_dialog = gtk_font_selection_dialog_new(_("Select font"));

            gtk_font_selection_dialog_set_font_name(
                                                      GTK_FONT_SELECTION_DIALOG(opt_dialog),
                                                      (value) ? (gchar *)value : "Arial 12");
            gtk_font_selection_dialog_set_preview_text(
                                                      GTK_FONT_SELECTION_DIALOG(opt_dialog),
                                                      _("Guifications"));
            g_signal_connect(G_OBJECT(GTK_FONT_SELECTION_DIALOG(opt_dialog)->ok_button),
                                     "clicked", G_CALLBACK(gfte_dialog_font_ok_cb),
                                     (gpointer)button);
            g_signal_connect(G_OBJECT(GTK_FONT_SELECTION_DIALOG(opt_dialog)->cancel_button),
                                     "clicked", G_CALLBACK(gfte_dialog_font_cancel_cb),
                                     (gpointer)button);

            gtk_widget_show_all(opt_dialog);
      } else if(type == GFTE_BUTTON_COLOR) {
            PangoColor pcolor;
            GdkColor gcolor;

            if(value) {
                  pango_color_parse(&pcolor, (gchar *)value);
                  gcolor.red = pcolor.red;
                  gcolor.green = pcolor.green;
                  gcolor.blue = pcolor.blue;
            } else {
                  gcolor.red = gcolor.green = gcolor.blue = 0;
            }

            opt_dialog = gtk_color_selection_dialog_new(_("Select color"));

            gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(
                                          GTK_COLOR_SELECTION_DIALOG(opt_dialog)->colorsel),
                                          &gcolor);
            g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(opt_dialog)->ok_button),
                                     "clicked", G_CALLBACK(gfte_dialog_color_ok_cb),
                                     (gpointer)button);
            g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(opt_dialog)->cancel_button),
                                     "clicked", G_CALLBACK(gfte_dialog_color_cancel_cb),
                                     (gpointer)button);

            gtk_widget_show_all(opt_dialog);
      }
}

/*******************************************************************************
 * Update pages
 ******************************************************************************/
static void
gfte_update_item(struct GfThemeEditorItem *te_item, gint page, GfItem *item) {
      gfte_update_option_menu(te_item->position, page, item);
      gfte_update_spin_button(te_item->h_offset, page, item);
      gfte_update_check(te_item->h_offset_p, page, item);
      gfte_update_spin_button(te_item->v_offset, page, item);
      gfte_update_check(te_item->v_offset_p, page, item);
}

static void
gfte_update_info_page() {
      GfThemeInfo *info = (GfThemeInfo *)gfte_store_get_object();

      gfte_update_entry(editor.info.name, GFTE_PAGE_INFO, info);
      gfte_update_entry(editor.info.version, GFTE_PAGE_INFO, info);
      gfte_update_entry(editor.info.summary, GFTE_PAGE_INFO, info);
      gfte_update_entry(editor.info.description, GFTE_PAGE_INFO, info);
      gfte_update_entry(editor.info.author, GFTE_PAGE_INFO, info);
      gfte_update_entry(editor.info.website, GFTE_PAGE_INFO, info);
}

static void
gfte_update_ops_page() {
      GfThemeOptions *ops = (GfThemeOptions *)gfte_store_get_object();

      gfte_update_entry(editor.ops.date_format, GFTE_PAGE_OPS, ops);
      gfte_update_entry(editor.ops.time_format, GFTE_PAGE_OPS, ops);
      gfte_update_entry(editor.ops.warning, GFTE_PAGE_OPS, ops);
      gfte_update_entry(editor.ops.ellipsis, GFTE_PAGE_OPS, ops);
}

static void
gfte_update_notification_page() {
      GfNotification *notification = GF_NOTIFICATION(gfte_store_get_object());

      gfte_update_check(editor.notification.use_gtk, GFTE_PAGE_NOTIFICATION,
                                notification);
      gfte_update_entry(editor.notification.filename, GFTE_PAGE_NOTIFICATION,
                                notification);
      gfte_update_spin_button(editor.notification.width, GFTE_PAGE_NOTIFICATION,
                                          notification);
      gfte_update_spin_button(editor.notification.height, GFTE_PAGE_NOTIFICATION,
                                          notification);
}

static void
gfte_update_icon_page() {
      GfItem *item = GF_ITEM(gfte_store_get_object());

      gfte_update_item(&editor.icon.item, GFTE_PAGE_ICON, item);

      gfte_update_option_menu(editor.icon.type, GFTE_PAGE_ICON, item);
      gfte_update_option_menu(editor.icon.size, GFTE_PAGE_ICON, item);
}

static void
gfte_update_image_page() {
      GfItem *item = GF_ITEM(gfte_store_get_object());

      gfte_update_item(&editor.image.item, GFTE_PAGE_IMAGE, item);

      gfte_update_entry(editor.image.filename, GFTE_PAGE_IMAGE, item);
}

static void
gfte_update_text_page() {
      GfItem *item = GF_ITEM(gfte_store_get_object());

      gfte_update_item(&editor.text.item, GFTE_PAGE_TEXT, item);

      gfte_update_entry(editor.text.format, GFTE_PAGE_TEXT, item);
      gfte_update_spin_button(editor.text.width, GFTE_PAGE_TEXT, item);
      gfte_update_option_menu(editor.text.clipping, GFTE_PAGE_TEXT, item);
}

/*******************************************************************************
 * Callbacks
 ******************************************************************************/
static void
gfte_selection_changed_cb(GtkTreeSelection *sel, gpointer data) {
      GtkTreeModel *model;
      GtkTreeIter iter;
      gpointer object;
      gint page;

      gfte_dialog_cleanup();

      if(!gtk_tree_selection_get_selected(sel, &model, &iter)) {
            gtk_notebook_set_current_page(GTK_NOTEBOOK(editor.note),
                                                        GFTE_PAGE_THEME);
            return;
      }

      gtk_tree_model_get(model, &iter,
                                 GFTE_STORE_PAGE, &page,
                                 GFTE_STORE_OBJECT, &object,
                                 -1);
      gtk_notebook_set_current_page(GTK_NOTEBOOK(editor.note), page);

      switch(page) {
            case GFTE_PAGE_THEME:
                  gfte_toolbar_buttons_update(FALSE, FALSE, FALSE, FALSE, FALSE);
                  break;
            case GFTE_PAGE_INFO:
                  gfte_toolbar_buttons_update(FALSE, FALSE, FALSE, FALSE, FALSE);
                  gfte_update_info_page();
                  break;
            case GFTE_PAGE_OPS:
                  gfte_toolbar_buttons_update(FALSE, FALSE, FALSE, FALSE, FALSE);
                  gfte_update_ops_page();
                  break;
            case GFTE_PAGE_NOTIFICATION: {
                  GfNotification *notification = GF_NOTIFICATION(object);
                  gboolean master;

                  master = (g_ascii_strcasecmp(GF_NOTIFICATION_MASTER,
                                                             gf_notification_get_type(notification)));

                  gfte_toolbar_buttons_update(TRUE, master, master,
                                                            gfte_is_older_notification(object),
                                                            gfte_is_younger_notification(object));
                  gfte_update_notification_page();
                  break;
            }
            case GFTE_PAGE_ICON:
                  gfte_toolbar_buttons_update(TRUE, TRUE, TRUE,
                                                            gfte_is_older_item(object),
                                                            gfte_is_younger_item(object));
                  gfte_update_icon_page();
                  break;
            case GFTE_PAGE_IMAGE:
                  gfte_toolbar_buttons_update(TRUE, TRUE, TRUE,
                                                            gfte_is_older_item(object),
                                                            gfte_is_younger_item(object));
                  gfte_update_image_page();
                  break;
            case GFTE_PAGE_TEXT:
                  gfte_toolbar_buttons_update(TRUE, TRUE, TRUE,
                                                            gfte_is_older_item(object),
                                                            gfte_is_younger_item(object));
                  gfte_update_text_page();
                  break;
            case GFTE_PAGE_TOTAL:
            default:
                  break;
      }
}

static void
gfte_new_theme_cb(GtkButton *button, gpointer data) {
      if(editor.changed)
            gfte_modified_show(GFTE_MODIFIED_NEW, NULL);
      else
            gfte_setup(NULL);
}

static void
gfte_save_theme_cb(GtkButton *button, gpointer data) {
      gfte_save_theme();
}

static void
gfte_help(GtkButton *button, gpointer data) {
      gaim_notify_uri(NULL, GF_WEBSITE "/themes/howto.php");
}

/*******************************************************************************
 * Helpers
 ******************************************************************************/
static void
gfte_build_tree() {
      GtkWidget *sw;
      GtkCellRenderer *renderer;
      GtkTreeViewColumn *col;
      GtkTreeSelection *sel;

      sw = gtk_scrolled_window_new(NULL, NULL);
      gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
                                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
      gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
      gtk_box_pack_start(GTK_BOX(editor.hbox), sw, FALSE, FALSE, 0);

      editor.tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(editor.store));
      sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(editor.tree));
      g_signal_connect_after(G_OBJECT(sel), "changed",
                                       G_CALLBACK(gfte_selection_changed_cb), NULL);
      gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(editor.tree), FALSE);
      gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(editor.tree), FALSE);
      gtk_tree_view_expand_all(GTK_TREE_VIEW(editor.tree));
      gtk_container_add(GTK_CONTAINER(sw), editor.tree);

      renderer = gtk_cell_renderer_text_new();
      col = gtk_tree_view_column_new_with_attributes(NULL, renderer,
                                                                           "text", GFTE_STORE_TITLE,
                                                                           NULL);
      gtk_tree_view_append_column(GTK_TREE_VIEW(editor.tree), col);
}

static GtkWidget *
gfte_make_label(const gchar *text, GtkSizeGroup *sg) {
      GtkWidget *label;

      label = gtk_label_new_with_mnemonic(text);
      gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

      if(sg)
            gtk_size_group_add_widget(sg, label);

      return label;
}

static GtkWidget *
gfte_add_label(GtkWidget *widget, const gchar *label, GtkSizeGroup *sg) {
      GtkWidget *hbox;

      hbox = gtk_hbox_new(FALSE, 4);

      gtk_box_pack_start(GTK_BOX(hbox), gfte_make_label(label, sg),
                                 FALSE, FALSE, 0);
      gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);

      return hbox;
}

static GtkWidget *
gfte_add_entry(GtkWidget *parent, GtkSizeGroup *sg, gint flags,
                     const gchar *label, gpointer getter, gpointer setter)
{
      GtkWidget *entry, *hbox;

      entry = gtk_entry_new();
      g_object_set_data(G_OBJECT(entry), "getter", getter);
      g_object_set_data(G_OBJECT(entry), "setter", setter);
      g_object_set_data(G_OBJECT(entry), "flags", GINT_TO_POINTER(flags));
      g_signal_connect(G_OBJECT(entry), "changed",
                               G_CALLBACK(gfte_entry_changed_cb), NULL);

      hbox = gfte_add_label(entry, label, sg);

      gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);

      return entry;
}

static GtkWidget *
gfte_add_check(GtkWidget *parent, gint flags, const gchar *label,
                     gpointer getter, gpointer setter)
{
      GtkWidget *check;

      check = gtk_check_button_new_with_mnemonic(label);
      g_object_set_data(G_OBJECT(check), "getter", getter);
      g_object_set_data(G_OBJECT(check), "setter", setter);
      g_object_set_data(G_OBJECT(check), "flags", GINT_TO_POINTER(flags));
      g_signal_connect(G_OBJECT(check), "toggled",
                               G_CALLBACK(gfte_check_toggled_cb), NULL);

      gtk_box_pack_start(GTK_BOX(parent), check, FALSE, FALSE, 0);

      return check;
}

static GtkWidget *
gfte_add_spin_button(GtkWidget *parent, GtkSizeGroup *sg, gint flags,
                               const gchar *label, gint min, gint max,
                               gpointer getter, gpointer setter)
{
      GtkWidget *spin, *hbox;

      spin = gtk_spin_button_new_with_range(min, max, 1);
      gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spin), TRUE);
      g_object_set_data(G_OBJECT(spin), "getter", getter);
      g_object_set_data(G_OBJECT(spin), "setter", setter);
      g_object_set_data(G_OBJECT(spin), "flags", GINT_TO_POINTER(flags));
      g_signal_connect(G_OBJECT(spin), "value-changed",
                               G_CALLBACK(gfte_spin_changed_cb), NULL);

      hbox = gfte_add_label(spin, label, sg);

      gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);

      return spin;
}

static GtkWidget *
gfte_add_option_menu(GtkWidget *parent, GtkSizeGroup *sg, gint flags,
                               const gchar *label, GfMenuItemBuilder builder,
                               gpointer getter, gpointer setter)
{
      GtkWidget *option_menu, *hbox;

      option_menu = gtk_option_menu_new();
      gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu),
                                           gf_menu_build(builder));
      g_object_set_data(G_OBJECT(option_menu), "getter", getter);
      g_object_set_data(G_OBJECT(option_menu), "setter", setter);
      g_object_set_data(G_OBJECT(option_menu), "flags", GINT_TO_POINTER(flags));
      g_signal_connect(G_OBJECT(option_menu), "changed",
                               G_CALLBACK(gfte_option_menu_changed_cb), NULL);

      hbox = gfte_add_label(option_menu, label, sg);

      gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);

      return option_menu;
}

static GtkWidget *
gfte_add_button(GtkWidget *parent, gint flags, gint type, const gchar *stock_id,
                        gpointer getter, gpointer setter)
{
      GtkWidget *button;

      button = gtk_button_new_from_stock(stock_id);
      g_object_set_data(G_OBJECT(button), "getter", getter);
      g_object_set_data(G_OBJECT(button), "setter", setter);
      g_object_set_data(G_OBJECT(button), "flags", GINT_TO_POINTER(flags));
      g_object_set_data(G_OBJECT(button), "type", GINT_TO_POINTER(type));
      g_signal_connect(G_OBJECT(button), "clicked",
                               G_CALLBACK(gfte_button_clicked_cb), NULL);

      if(parent)
            gtk_box_pack_start(GTK_BOX(parent), button, FALSE, FALSE, 0);

      return button;
}

/*******************************************************************************
 * Pages
 ******************************************************************************/
static GtkWidget *
gfte_make_theme_page() {
      GtkWidget *page;

      page = gtk_vbox_new(FALSE, 0);

      return page;
}

static GtkWidget *
gfte_make_info_page() {
      GtkWidget *page;
      GtkSizeGroup *sg;

      sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

      page = gtk_vbox_new(FALSE, 4);
      gtk_container_set_border_width(GTK_CONTAINER(page), 12);

      editor.info.name =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Name:"),
                                                   gf_theme_info_get_name,
                                                   gf_theme_info_set_name);
      editor.info.version =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Version:"),
                                                   gf_theme_info_get_version,
                                                   gf_theme_info_set_version);
      editor.info.summary =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Summary:"),
                                                   gf_theme_info_get_summary,
                                                   gf_theme_info_set_summary);
      editor.info.description =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Description:"),
                                                   gf_theme_info_get_description,
                                                   gf_theme_info_set_description);
      editor.info.author =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Author:"),
                                                   gf_theme_info_get_author,
                                                   gf_theme_info_set_author);
      editor.info.website =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Website:"),
                                                   gf_theme_info_get_website,
                                                   gf_theme_info_set_website);

      return page;
}

static GtkWidget *
gfte_make_ops_page() {
      GtkWidget *page;
      GtkSizeGroup *sg;

      sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

      page = gtk_vbox_new(FALSE, 4);
      gtk_container_set_border_width(GTK_CONTAINER(page), 12);

      editor.ops.date_format =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Date Format:"),
                                                   gf_theme_options_get_date_format,
                                                   gf_theme_options_set_date_format);
      editor.ops.time_format =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Time Format:"),
                                                   gf_theme_options_get_time_format,
                                                   gf_theme_options_set_time_format);
      editor.ops.warning =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Warning:"),
                                                   gf_theme_options_get_warning,
                                                   gf_theme_options_set_warning);
      editor.ops.ellipsis =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Ellipsis:"),
                                                   gf_theme_options_get_ellipsis,
                                                   gf_theme_options_set_ellipsis);

      return page;
}

static GtkWidget *
gfte_make_notification_page() {
      GtkWidget *page;
      GtkSizeGroup *sg;

      sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

      page = gtk_vbox_new(FALSE, 4);
      gtk_container_set_border_width(GTK_CONTAINER(page), 12);

      editor.notification.use_gtk =
                              gfte_add_check(page, GFTE_FLAGS_OBJECT,
                                                   _("Use Gtk theme background"),
                                                   gf_notification_get_use_gtk,
                                                   gf_notification_set_use_gtk);

      editor.notification.filename =
                              gfte_add_entry(page, sg, GFTE_FLAGS_OBJECT, _("Background:"),
                                                   gf_notification_get_background,
                                                   gf_notification_set_background);
      gtk_widget_set_sensitive(editor.notification.filename, FALSE);

      editor.notification.button =
                              gfte_add_button(editor.notification.filename->parent,
                                                      GFTE_FLAGS_OBJECT, GFTE_BUTTON_FILE,
                                                      GTK_STOCK_OPEN,
                                                      gf_notification_get_background,
                                                      gf_notification_set_background);

      editor.notification.width =
                              gfte_add_spin_button(page, sg, GFTE_FLAGS_OBJECT, _("Width:"),
                                                             GF_NOTIFICATION_MIN, GF_NOTIFICATION_MAX,
                                                             gf_notification_get_width,
                                                             gf_notification_set_width);

      editor.notification.height =
                              gfte_add_spin_button(page, sg, GFTE_FLAGS_OBJECT, _("Height:"),
                                                             GF_NOTIFICATION_MIN, GF_NOTIFICATION_MAX,
                                                             gf_notification_get_height,
                                                             gf_notification_set_height);

      return page;
}

static void
gfte_make_item_widgets(GtkWidget *parent, GtkSizeGroup *sg,
                                 struct GfThemeEditorItem *item)
{
      item->position = gfte_add_option_menu(parent, sg, GFTE_FLAGS_OBJECT,
                                                              _("_Position:"),
                                                              gf_menu_item_position,
                                                              gf_item_get_position,
                                                              gf_item_set_position);

      item->h_offset = gfte_add_spin_button(parent, sg, GFTE_FLAGS_H_OFFSET,
                                                              _("_Horizontal Offset:"), -1024, 1023,
                                                              gf_item_offset_get_value,
                                                              gf_item_offset_set_value);

      item->h_offset_p = gfte_add_check(item->h_offset->parent,
                                                        GFTE_FLAGS_H_OFFSET, _("Percentage"),
                                                        gf_item_offset_get_is_percentage,
                                                        gf_item_offset_set_is_percentage);

      item->v_offset = gfte_add_spin_button(parent, sg, GFTE_FLAGS_V_OFFSET,
                                                              _("_Vertical Offset:"), -1024, 1023,
                                                              gf_item_offset_get_value,
                                                              gf_item_offset_set_value);

      item->v_offset_p = gfte_add_check(item->v_offset->parent,
                                                        GFTE_FLAGS_V_OFFSET, _("Percentage"),
                                                        gf_item_offset_get_is_percentage,
                                                        gf_item_offset_set_is_percentage);

}

static GtkWidget *
gfte_make_icon_page() {
      GtkWidget *page;
      GtkSizeGroup *sg;

      sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

      page = gtk_vbox_new(FALSE, 4);
      gtk_container_set_border_width(GTK_CONTAINER(page), 12);

      gfte_make_item_widgets(page, sg, &editor.icon.item);

      editor.icon.type = gfte_add_option_menu(page, sg, GFTE_FLAGS_SUB_OBJECT,
                                                                  _("_Type:"), gf_menu_item_icon_type,
                                                                  gf_item_icon_get_type,
                                                                  gf_item_icon_set_type);

      editor.icon.size = gfte_add_option_menu(page, sg, GFTE_FLAGS_SUB_OBJECT,
                                                                  _("_Size:"), gf_menu_item_icon_size,
                                                                  gf_item_icon_get_size,
                                                                  gf_item_icon_set_size);

      return page;
}

static GtkWidget *
gfte_make_image_page() {
      GtkWidget *page;
      GtkSizeGroup *sg;

      sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

      page = gtk_vbox_new(FALSE, 4);
      gtk_container_set_border_width(GTK_CONTAINER(page), 12);

      gfte_make_item_widgets(page, sg, &editor.image.item);

      editor.image.filename = gfte_add_entry(page, sg, GFTE_FLAGS_SUB_OBJECT,
                                                               _("Image:"),
                                                               gf_item_image_get_image,
                                                               gf_item_image_set_image);
      gtk_widget_set_sensitive(editor.image.filename, FALSE);

      editor.image.button = gfte_add_button(editor.image.filename->parent,
                                                              GFTE_FLAGS_SUB_OBJECT, GFTE_BUTTON_FILE,
                                                              GTK_STOCK_OPEN,
                                                              gf_item_image_get_image,
                                                              gf_item_image_set_image);

      return page;
}

static GtkWidget *
gfte_make_text_page() {
      GtkWidget *page, *hbox;
      GtkSizeGroup *sg;

      sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

      page = gtk_vbox_new(FALSE, 4);
      gtk_container_set_border_width(GTK_CONTAINER(page), 12);

      gfte_make_item_widgets(page, sg, &editor.text.item);

      editor.text.format = gfte_add_entry(page, sg, GFTE_FLAGS_SUB_OBJECT,
                                                            _("Format:"),
                                                            gf_item_text_get_format,
                                                            gf_item_text_set_format);

      editor.text.width = gfte_add_spin_button(page, sg, GFTE_FLAGS_SUB_OBJECT,
                                                                   _("Width:"), 0, 1023,
                                                                   gf_item_text_get_width,
                                                                   gf_item_text_set_width);

      editor.text.clipping = gfte_add_option_menu(page, sg, GFTE_FLAGS_SUB_OBJECT,
                                                                        _("Clipping:"),
                                                                        gf_menu_item_text_clipping,
                                                                        gf_item_text_get_clipping,
                                                                        gf_item_text_set_clipping);

      editor.text.font = gfte_add_button(NULL, GFTE_FLAGS_SUB_OBJECT,
                                                         GFTE_BUTTON_FONT, GTK_STOCK_SELECT_FONT,
                                                         gf_item_text_get_font,
                                                         gf_item_text_set_font);
      hbox = gfte_add_label(editor.text.font, NULL, sg);
      gtk_box_pack_start(GTK_BOX(page), hbox, FALSE, FALSE, 0);

      editor.text.color = gfte_add_button(NULL, GFTE_FLAGS_SUB_OBJECT,
                                                            GFTE_BUTTON_COLOR, GTK_STOCK_SELECT_COLOR,
                                                            gf_item_text_get_color,
                                                            gf_item_text_set_color);
      hbox = gfte_add_label(editor.text.color, NULL, sg);
      gtk_box_pack_start(GTK_BOX(page), hbox, FALSE, FALSE, 0);

      return page;
}

/*******************************************************************************
 * Main stuff
 ******************************************************************************/
static GtkWidget *
gfte_toolbar_button_new(GtkWidget *parent, const gchar *stock_id,
                                    const gchar *tooltip, GCallback cb, gpointer data)
{
      GtkWidget *button, *image;

      button = gtk_button_new();
      gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
      if(cb)
            g_signal_connect(G_OBJECT(button), "clicked", cb, data);

      gtk_tooltips_set_tip(editor.tooltips, button, tooltip, NULL);

      image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_SMALL_TOOLBAR);
      gtk_container_add(GTK_CONTAINER(button), image);

      gtk_box_pack_start(GTK_BOX(parent), button, FALSE, FALSE, 0);

      return button;
}

static void
gfte_toolbar_separator_new(GtkWidget *parent) {
      GtkWidget *sep;

      sep = gtk_vseparator_new();
      gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
}

static void
gfte_build_toolbar() {
      GtkWidget *frame, *hbox, *help;

      frame = gtk_frame_new(NULL);
      gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
      gtk_box_pack_start(GTK_BOX(editor.vbox), frame, FALSE, FALSE, 0);

      hbox = gtk_hbox_new(FALSE, 0);
      gtk_container_add(GTK_CONTAINER(frame), hbox);

      gfte_toolbar_button_new(hbox, GTK_STOCK_NEW, _("New theme"),
                                          G_CALLBACK(gfte_new_theme_cb), NULL);
      gfte_toolbar_button_new(hbox, GTK_STOCK_SAVE, _("Save theme"),
                                          G_CALLBACK(gfte_save_theme_cb), NULL);

      gfte_toolbar_separator_new(hbox);
      editor.tool_notification =
                        gfte_toolbar_button_new(hbox, GTK_STOCK_EXECUTE,
                                                            _("New notification"),
                                                            G_CALLBACK(gfte_new_notification_show),
                                                            NULL);
      editor.tool_item =
                        gfte_toolbar_button_new(hbox, GTK_STOCK_PROPERTIES,
                                                            _("New item"),
                                                            G_CALLBACK(gfte_new_item_show), NULL);
      editor.tool_copy =
                        gfte_toolbar_button_new(hbox, GTK_STOCK_COPY,
                                                            _("Duplicate"),
                                                            G_CALLBACK(gfte_duplicate_object),
                                                            NULL);
      editor.tool_delete =
                        gfte_toolbar_button_new(hbox, GTK_STOCK_DELETE,
                                                            _("Delete"),
                                                            G_CALLBACK(gfte_delete_show), NULL);

      gfte_toolbar_separator_new(hbox);

      editor.tool_up = 
                        gfte_toolbar_button_new(hbox, GTK_STOCK_GO_UP, _("Move up"),
                                                            G_CALLBACK(gfte_move_up), NULL);
      editor.tool_down =
                        gfte_toolbar_button_new(hbox, GTK_STOCK_GO_DOWN, _("Move down"),
                                                            G_CALLBACK(gfte_move_down), NULL);

      gfte_toolbar_separator_new(hbox);

      help = gfte_toolbar_button_new(hbox, GTK_STOCK_HELP, _("Help"),
                                                   G_CALLBACK(gfte_help), NULL);

      gfte_toolbar_buttons_update(FALSE, FALSE, FALSE, FALSE, FALSE);
}

static void
gfte_build_notebook() {
      editor.note = gtk_notebook_new();
      gtk_notebook_set_show_tabs(GTK_NOTEBOOK(editor.note), FALSE);
      gtk_box_pack_start(GTK_BOX(editor.hbox), editor.note, TRUE, TRUE, 4);

      gtk_notebook_insert_page(GTK_NOTEBOOK(editor.note),
                                           gfte_make_theme_page(),
                                           NULL, GFTE_PAGE_THEME);
      gtk_notebook_insert_page(GTK_NOTEBOOK(editor.note),
                                           gfte_make_info_page(),
                                           NULL, GFTE_PAGE_INFO);
      gtk_notebook_insert_page(GTK_NOTEBOOK(editor.note),
                                           gfte_make_ops_page(),
                                           NULL, GFTE_PAGE_OPS);
      gtk_notebook_insert_page(GTK_NOTEBOOK(editor.note),
                                           gfte_make_notification_page(),
                                           NULL, GFTE_PAGE_NOTIFICATION);
      gtk_notebook_insert_page(GTK_NOTEBOOK(editor.note),
                                           gfte_make_icon_page(),
                                           NULL, GFTE_PAGE_ICON);
      gtk_notebook_insert_page(GTK_NOTEBOOK(editor.note),
                                           gfte_make_image_page(),
                                           NULL, GFTE_PAGE_IMAGE);
      gtk_notebook_insert_page(GTK_NOTEBOOK(editor.note),
                                           gfte_make_text_page(),
                                           NULL, GFTE_PAGE_TEXT);
}

static gboolean
gfte_window_destroyed_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
      if(editor.changed) {
            gfte_modified_show(GFTE_MODIFIED_CLOSE, NULL);
            return TRUE;
      } else {
            gfte_cleanup();
            return FALSE;
      }
}

static void
gfte_build_window() {
      editor.tooltips = gtk_tooltips_new();
      g_object_ref(G_OBJECT(editor.tooltips));
      gtk_object_sink(GTK_OBJECT(editor.tooltips));

      editor.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title(GTK_WINDOW(editor.window), _("Guifications Theme Editor"));
      g_signal_connect(G_OBJECT(editor.window), "delete-event",
                               G_CALLBACK(gfte_window_destroyed_cb), NULL);

      editor.vbox = gtk_vbox_new(FALSE, 4);
      gtk_container_add(GTK_CONTAINER(editor.window), editor.vbox);

      gfte_build_toolbar();

      editor.hbox = gtk_hbox_new(FALSE, 4);
      gtk_box_pack_start(GTK_BOX(editor.vbox), editor.hbox, TRUE, TRUE, 0);

      gfte_build_tree();
      gfte_build_notebook();
}

/*******************************************************************************
 * Basic helpers
 ******************************************************************************/
void
gfte_show() {
      if(editor.window) {
            gtk_window_present(GTK_WINDOW(editor.window));
            return;
      }

      gfte_build_window();
      gtk_widget_show_all(editor.window);
}

/*******************************************************************************
 * Exports
 ******************************************************************************/
void
gf_theme_editor_init(GaimPlugin *plugin) {
      plugin_handle = plugin;
}

void
gf_theme_editor_uninit() {
      if(editor.window)
            gtk_widget_destroy(editor.window);

      gfte_cleanup();
      editor.window = NULL;
}

void
gf_theme_editor_show(const gchar *filename) {
      /* if filename is NULL a new theme is being created */
      if(!filename) {
            gfte_setup(NULL);
            gfte_show();
            return;
      }

      /* theme editor isn't showing so we create it */
      if(!editor.window) {
            gfte_setup(filename);
            gfte_show();
            return;
      }

      /* theme editor is showing, so we check the file names, if they match, we just show
       * the editor.  If they don't, we check if the theme is modified.  If it is, then we
       * show the modified dialog, and save it if the user wants it saved, and then open
       * the other theme.
       */
      if(editor.filename) {
            if(!g_ascii_strcasecmp(editor.filename, filename)) {
                  gfte_show();
            } else {
                  if(editor.changed)
                        gfte_modified_show(GFTE_MODIFIED_OPEN, filename);
                  else
                        gfte_setup(filename);
            }
      }

      return;
}

Generated by  Doxygen 1.6.0   Back to index