org.guifications.plugins.buddytools: 8afe303ac7111f529055302aedc98a4d69d75b7d

rlaager at guifications.org rlaager at guifications.org
Mon Oct 22 00:20:24 CDT 2007


-----------------------------------------------------------------
Revision: 8afe303ac7111f529055302aedc98a4d69d75b7d
Ancestor: b66ba42e24d7869faf8642f6ca7b1f9294cc4957
Author: rlaager at guifications.org
Date: 2006-05-16T03:32:30
Branch: org.guifications.plugins.buddytools
Tag: v0.3

Added files:
        gtktimezonetest.c
Modified files:
        Changelog Makefile README buddyedit.c buddylang.c
        buddynotes.c buddytimezone.c

ChangeLog: 

[buddytools @ 6]
Importing Martijn's changes, which resulted in version 0.3:

version 0.3
        * Now have editing for all node types, but Chat is a bit lame so far
        * Have made gtkwidget to will be successor to huge list box for
          timezone, now just need to add it somehow.
        * Groups can now have default timezones and languages.
        * You can now select Disabled as well as Default, if you don't want
          to display and spellcheck anything ever.
        * You can now optionally use the system timezone code.


-----------------------------------------------------------------
This revision's diffstat output:
 Changelog         |   10 +
 Makefile          |   20 ++-
 README            |   12 -
 buddyedit.c       |  160 +++++++++++++++++++------
 buddylang.c       |  137 ++++++++++++++++-----
 buddynotes.c      |   41 ++++--
 buddytimezone.c   |  201 ++++++++++++++++++++++++-------
 gtktimezonetest.c |  343 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 784 insertions(+), 140 deletions(-)
-------------- next part --------------
============================================================
--- gtktimezonetest.c	b4cd9377736de4250aa51fd3d8c443be0868bdea
+++ gtktimezonetest.c	b4cd9377736de4250aa51fd3d8c443be0868bdea
@@ -0,0 +1,343 @@
+/*************************************************************************
+ * GTK Timezone test program
+ * by Martijn van Oosterhout <kleptog at svana.org> (C) April 2006
+ * Licenced under the GNU General Public Licence version 2.
+ *
+ * A test program to play with different ways that user could select from
+ * the huge list of timezones. Eventually things tested here should migrate
+ * to the module itself.
+ *************************************************************************/
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include <ctype.h>
+#include "recurse.h"
+
+#define PACKAGE "Hello World"
+#define VERSION "0.1"
+
+#define DISABLED_STRING "<Disabled>"
+#define DEFAULT_STRING "<Default>"
+#define MORE_STRING "More..."
+/*
+ * Terminate the main loop.
+ */
+static void
+on_destroy(GtkWidget * widget, gpointer data)
+{
+    gtk_main_quit();
+}
+
+enum
+{
+    STRING_COLUMN,
+    N_COLUMNS
+};
+
+struct nodestate
+{
+#ifdef USE_COMBOBOX
+    GtkTreeIter iter;
+#else
+    GtkWidget *submenu;
+#endif
+    gchar *string;
+};
+
+struct state
+{
+#ifdef USE_COMBOBOX
+    GtkTreeStore *store;
+    GtkTreeIter *extra;
+#else
+    GtkWidget *base;
+    GtkWidget *extra;
+#endif
+    int currdepth;
+    struct nodestate stack[4];
+};
+
+static inline const char *
+menuitem_get_label(GtkMenuItem * menuitem)
+{
+    return gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(menuitem))));
+}
+
+static GtkWidget *
+menu_get_first_menuitem(GtkMenu * menu)
+{
+    GList *list = gtk_container_get_children(GTK_CONTAINER(menu));
+    GtkWidget *selection = GTK_WIDGET(g_list_nth_data(list, 0));
+    g_list_free(list);
+    return selection;
+}
+
+int
+menu_select_cb(GtkMenuItem * menuitem, GtkWidget * menu)
+{
+    const char *label = menuitem_get_label(menuitem);
+
+//      printf( "menuitem = %s(%p), menu = %s(%p)\n", G_OBJECT_TYPE_NAME(menuitem), menuitem, G_OBJECT_TYPE_NAME(menu), menu );
+
+    if(label[0] == '<')
+    {
+        GtkWidget *selection;
+        gchar *selstring;
+
+        if(strcmp(label, DEFAULT_STRING) == 0)
+            selstring = "";
+        else if(strcmp(label, DISABLED_STRING) == 0)
+            selstring = "none";
+
+        selection = menu_get_first_menuitem(GTK_MENU(menu));
+        gtk_widget_hide(selection);
+    }
+    else
+    {
+        char *str = g_strdup(label);
+
+        GtkWidget *parent;
+
+        for (;;)
+        {
+            GtkMenuItem *parentitem;
+            const char *label2;
+            char *temp;
+
+            parent = gtk_widget_get_parent(GTK_WIDGET(menuitem));
+//                      printf( "parent = %s(%p)\n", G_OBJECT_TYPE_NAME(parent), parent);
+            if(menu == parent)
+                break;
+
+            parentitem = GTK_MENU_ITEM(gtk_menu_get_attach_widget(GTK_MENU(parent)));
+//                      printf( "parentitem = %s(%p)\n", G_OBJECT_TYPE_NAME(parentitem), parentitem);
+            label2 = menuitem_get_label(parentitem);
+            if(strcmp(label2, MORE_STRING) != 0)
+            {
+                temp = g_strconcat(label2, "/", str, NULL);
+                g_free(str);
+                str = temp;
+            }
+
+            menuitem = parentitem;
+        }
+        {
+            GtkLabel *label;
+
+            GtkWidget *selection = menu_get_first_menuitem(GTK_MENU(menu));
+            GtkOptionMenu *optionmenu = GTK_OPTION_MENU(gtk_menu_get_attach_widget(GTK_MENU(menu)));
+
+            label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(selection)));
+
+            gtk_label_set_text(label, str);
+            gtk_widget_show(GTK_WIDGET(selection));
+            gtk_option_menu_set_history(optionmenu, 0);
+            g_free(str);
+        }
+    }
+    return 0;
+}
+
+int
+make_menu_cb(char *path, struct state *state)
+{
+    int i, j;
+
+    char **elements;
+
+    /* Here we ignore strings not beginning with uppercase, since they are auxilliary files, not timezones */
+    if(!isupper(path[0]))
+        return 0;
+
+    elements = g_strsplit(path, "/", 4);
+
+    for (i = 0; i < state->currdepth && state->stack[i].string; i++)
+    {
+        if(strcmp(elements[i], state->stack[i].string) != 0)
+            break;
+    }
+    /* i is now the index of the first non-matching element, so free the rest */
+    for (j = i; j < state->currdepth; j++)
+        g_free(state->stack[j].string);
+    state->currdepth = i;
+
+    while (elements[i])
+    {
+#ifdef USE_COMBOBOX
+        GtkTreeIter *parent = (i == 0) ? NULL : &state->stack[i - 1].iter;
+#else
+        GtkWidget *parent = (i == 0) ? state->base : state->stack[i - 1].submenu;
+        GtkWidget *menuitem;
+#endif
+
+        if(i == 0 && elements[1] == NULL)
+            parent = state->extra;
+
+#ifdef USE_COMBOBOX
+        gtk_tree_store_append(state->store, &state->stack[i].iter, parent);
+        gtk_tree_store_set(state->store, &state->stack[i].iter, STRING_COLUMN, elements[i], -1);
+        state->stack[i].string = g_strdup(elements[i]);
+        state->currdepth++;
+#else
+        menuitem = gtk_menu_item_new_with_label(elements[i]);
+        gtk_menu_append(parent, menuitem);
+
+        if(elements[i + 1] != NULL)     /* Has submenu */
+        {
+            state->stack[i].submenu = gtk_menu_new();
+            gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), state->stack[i].submenu);
+
+            state->currdepth++;
+            state->stack[i].string = g_strdup(elements[i]);
+        }
+        else
+            g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb),
+                             state->base);
+
+#endif
+
+        i++;
+    }
+    g_strfreev(elements);
+    return 0;
+}
+
+GtkWidget *
+make_menu2(char *selected)
+{
+    int i;
+    struct state state;
+
+#ifdef USE_COMBOBOX
+    GtkTreeStore *store = gtk_tree_store_new(N_COLUMNS, /* Total number of columns */
+                                             G_TYPE_STRING);    /* Timezone              */
+    GtkWidget *tree;
+
+    GtkCellRenderer *renderer;
+    GtkTreeIter iter1, iter2;
+
+    gtk_tree_store_append(store, &iter1, NULL); /* Acquire an iterator */
+    gtk_tree_store_set(store, &iter1, STRING_COLUMN, DISABLED_STRING, -1);
+    gtk_tree_store_append(store, &iter1, NULL); /* Acquire an iterator */
+    gtk_tree_store_set(store, &iter1, STRING_COLUMN, DEFAULT_STRING, -1);
+    gtk_tree_store_append(store, &iter2, &iter1);
+    gtk_tree_store_set(store, &iter1, STRING_COLUMN, MORE_STRING, -1);
+
+    state.store = store;
+    state.extra = &iter1;
+#else
+
+    GtkWidget *menu;
+    GtkWidget *optionmenu, *menuitem, *selection;
+
+    menu = gtk_menu_new();
+    menuitem = gtk_menu_item_new_with_label(selected);
+    gtk_menu_append(menu, menuitem);
+    selection = menuitem;
+
+    menuitem = gtk_menu_item_new_with_label(DISABLED_STRING);
+    g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb), menu);
+    gtk_menu_append(menu, menuitem);
+    menuitem = gtk_menu_item_new_with_label(DEFAULT_STRING);
+    g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_select_cb), menu);
+    gtk_menu_append(menu, menuitem);
+    menuitem = gtk_menu_item_new_with_label(MORE_STRING);
+    gtk_menu_append(menu, menuitem);
+
+    state.base = menu;
+    state.extra = gtk_menu_new();
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), state.extra);
+#endif
+    state.currdepth = 0;
+
+    recurse_directory("/usr/share/zoneinfo", (DirRecurseMatch)make_menu_cb, &state);
+
+    for (i = 0; i < state.currdepth; i++)
+        g_free(state.stack[i].string);
+
+#ifdef USE_COMBOBOX
+    tree = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
+
+    renderer = gtk_cell_renderer_text_new();
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(tree), renderer, TRUE);
+    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(tree), renderer, "text", 0, NULL);
+
+    gtk_widget_show_all(tree);
+    return tree;
+#else
+    optionmenu = gtk_option_menu_new();
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu);
+    gtk_widget_show_all(optionmenu);
+
+    if(strcmp(selected, "") == 0)
+    {
+        gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 2);
+        gtk_widget_hide(selection);
+    }
+    else if(strcmp(selected, "none") == 0)
+    {
+        gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 1);
+        gtk_widget_hide(selection);
+    }
+    else
+    {
+        gtk_option_menu_set_history(GTK_OPTION_MENU(optionmenu), 0);
+    }
+
+    return optionmenu;
+#endif
+
+}
+
+int
+main(int argc, char *argv[])
+{
+    GtkWidget *window;
+    GtkWidget *label;
+    GtkWidget *menu;
+    GtkWidget *frame;
+
+    gtk_init(&argc, &argv);
+
+    /* create the main, top level, window */
+    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+    /* give the window a 20px wide border */
+    gtk_container_set_border_width(GTK_CONTAINER(window), 20);
+
+    /* give it the title */
+    gtk_window_set_title(GTK_WINDOW(window), PACKAGE " " VERSION);
+
+    /* open it a bit wider so that both the label and title show up */
+    gtk_window_set_default_size(GTK_WINDOW(window), 200, 50);
+
+    /* Connect the destroy event of the window with our on_destroy function
+     * When the window is about to be destroyed we get a notificaiton and
+     * stop the main GTK loop
+     */
+    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(on_destroy), NULL);
+
+    /* Create the "Hello, World" label  */
+    label = gtk_label_new("Select a timezone:");
+    gtk_widget_show(label);
+
+    frame = gtk_vbox_new(FALSE, 0);
+    gtk_widget_show(frame);
+
+    /* and insert it into the main window  */
+    gtk_container_add(GTK_CONTAINER(window), frame);
+    gtk_container_add(GTK_CONTAINER(frame), label);
+
+    menu = make_menu2("none");
+
+    gtk_container_add(GTK_CONTAINER(frame), menu);
+    gtk_widget_show(menu);
+
+    /* make sure that everything, window and label, are visible */
+    gtk_widget_show(window);
+
+    /* start the main loop */
+    gtk_main();
+
+    return 0;
+}
============================================================
--- Changelog	9f2160abbabad35b9b4dea3c4e50ef8cafba9c8a
+++ Changelog	3017b5bfdfebffb5a1c7fbff217ad9d55bacf716
@@ -1,7 +1,17 @@
+version 0.3
+	* Now have editing for all node types, but Chat is a bit lame so far
+	* Have made gtkwidget to will be successor to huge list box for
+	  timezone, now just need to add it somehow.
+	* Groups can now have default timezones and languages.
+	* You can now select Disabled as well as Default, if you don't want
+	  to display and spellcheck anything ever.
+	* You can now optionally use the system timezone code.
+
 version 0.2 (4 Apr 2006)
 	* Patches and improvements by rlaager. It should now compile and run
 	  with Gaim 2.0.0 (can't test it myself though).
 	* Several suggestions on IRC regard naming of modules and signals.
+	* Changed signals to names including the plugin name.
 
 version 0.1 (4 Apr 2006)
 	* Initial release with all 4 modules in good working order.
============================================================
--- Makefile	c70fe7e43af1f857afd6abe781d14e19c8a8e287
+++ Makefile	37237d7cdca5294b73ecce004eee3f63d0a3654e
@@ -1,22 +1,36 @@
 #*************************************************************************
 #* Buddy Edit Makefile
 #* by Martijn van Oosterhout <kleptog at svana.org> (C) April 2006
 #* Licenced under the GNU General Public Licence version 2.
 #*************************************************************************/
 
-VERSION=0.2
+VERSION=0.3
+# yes or no
+PRIVATE_TZLIB=yes
 
 CFLAGS=-O2 -g -Wall `pkg-config --cflags glib-2.0 gaim` -DPLUGIN_VERSION=$(VERSION)
 LDFLAGS=`pkg-config --libs glib-2.0 gaim`
 CC=gcc
 
-all: buddyedit.so buddytimezone.so buddynotes.so buddylang.so timetest recursetest
+ifeq ($(PRIVATE_TZLIB),yes)
+CFLAGS += -DPRIVATE_TZLIB
+PRIVATE_TZLIB_OBJS=localtime.o
+endif
 
+all: buddyedit.so buddytimezone.so buddynotes.so buddylang.so timetest recursetest gtktimezonetest
+
 recursetest: recurse.o recursetest.o
 
 timetest: timetest.o localtime.o
 
-buddytimezone.so: buddytimezone.o localtime.o recurse.o
+gtktimezonetest.o: gtktimezonetest.c
+	gcc -c $(CFLAGS) `pkg-config --cflags gtk+-2.0` -o $@ $<
+
+gtktimezonetest: gtktimezonetest.o recurse.o
+	gcc $(LDFLAGS) -lgtk-x11-2.0 -lgdk-x11-2.0 -o $@ $^
+
+
+buddytimezone.so: buddytimezone.o $(PRIVATE_TZLIB_OBJS) recurse.o
 	gcc -shared $(LDFLAGS) -o $@ $^
 
 buddyedit.so: buddyedit.o
============================================================
--- README	9083fe1abb209a725929bd45a1e688b26bc89f71
+++ README	f2b19c8eb481b9e4dced9cc28830e9fead319f42
@@ -1,4 +1,4 @@
-This package contains GAIM modules created by 
+This package contains Gaim modules created by 
 
 Martijn van Oosterhout <kleptog at svana.org>
 http://svana.org/kleptog/
@@ -17,23 +17,23 @@
  *************************************************************************
  * Buddy Edit Module
  *
- * A GAIM plugin that adds an edit to to buddies allowing you to change
+ * A Gaim plugin that adds an edit to to buddies allowing you to change
  * various details you can't normally change. It also provides a mechanism
  * for subsequent plugins to add themselves to that dialog.
  *************************************************************************
  * Buddy Language Module
  *
- * A GAIM plugin that allows you to configure the language of the spelling
+ * A Gaim plugin that allows you to configure the language of the spelling
  * control on the conversation screen on a per-contact basis.
  *************************************************************************
  * Buddy Notes Module
  *
- * A GAIM plugin the allows you to add notes to contacts which will be
+ * A Gaim plugin the allows you to add notes to contacts which will be
  * displayed in the conversation screen as well as the hover tooltip.
  *************************************************************************
  * Buddy Timezone Module
  *
- * A GAIM plugin that allows you to configure a timezone on a per-contact
+ * A Gaim plugin that allows you to configure a timezone on a per-contact
  * basis so it can display the localtime of your contact when a conversation
  * starts. Convenient if you deal with contacts from many parts of the
  * world.
@@ -45,4 +45,4 @@
 gtkspell-dev and gtk2-dev.
 
 This will install the module in ~/.gaim/plugins and they will be available
+next time you start Gaim.
-next time you start GAIM.
============================================================
--- buddyedit.c	822797b0ced08cae4460dec5bbdbdf0c545192fc
+++ buddyedit.c	965519f1590863759cc359410786aca7f55d48f6
@@ -1,9 +1,9 @@
 /*************************************************************************
  * Buddy Edit Module
  * by Martijn van Oosterhout <kleptog at svana.org> (C) April 2006
  * Licenced under the GNU General Public Licence version 2.
  *
- * A GAIM plugin that adds an edit to to buddies allowing you to change
+ * A Gaim plugin that adds an edit to to buddies allowing you to change
  * various details you can't normally change. It also provides a mechanism
  * for subsequent plugins to add themselves to that dialog.
  *************************************************************************/
@@ -13,7 +13,6 @@
 
 #include <glib.h>
 #include <string.h>
-#include <ctype.h>
 
 #include "notify.h"
 #include "plugin.h"
@@ -31,30 +30,57 @@ buddyedit_editcomplete_cb(GaimBlistNode 
     gboolean buddy_destroy = FALSE;
 
     /* Account detail stuff */
-    if(GAIM_BLIST_NODE_IS_BUDDY(data))
+    switch (data->type)
     {
-        GaimBuddy *buddy = (GaimBuddy *) data;
-        GaimAccount *account = gaim_request_fields_get_account(fields, "account");
-        const char *name = gaim_request_fields_get_string(fields, "name");
-        const char *alias = gaim_request_fields_get_string(fields, "alias");
+        case GAIM_BLIST_BUDDY_NODE:
+        {
+            GaimBuddy *buddy = (GaimBuddy *) data;
+            GaimAccount *account = gaim_request_fields_get_account(fields, "account");
+            const char *name = gaim_request_fields_get_string(fields, "name");
+            const char *alias = gaim_request_fields_get_string(fields, "alias");
 
-        /* If any details changes, create the buddy */
-        if((account != buddy->account) || strcmp(name, buddy->name))
-        {
-            GHashTable *tmp;
-            GaimBuddy *newbuddy = gaim_buddy_new(account, name, alias);
-            gaim_blist_add_buddy(newbuddy, NULL, NULL, data);   /* Copy it to correct location */
+            /* If any details changes, create the buddy */
+            if((account != buddy->account) || strcmp(name, buddy->name))
+            {
+                GHashTable *tmp;
+                GaimBuddy *newbuddy = gaim_buddy_new(account, name, alias);
+                gaim_blist_add_buddy(newbuddy, NULL, NULL, data);       /* Copy it to correct location */
 
-            /* Now this is ugly, but we want to copy the settings and avoid issues with memory management */
-            tmp = ((GaimBlistNode *) buddy)->settings;
-            ((GaimBlistNode *) buddy)->settings = ((GaimBlistNode *) newbuddy)->settings;
-            ((GaimBlistNode *) newbuddy)->settings = tmp;
+                /* Now this is ugly, but we want to copy the settings and avoid issues with memory management */
+                tmp = ((GaimBlistNode *) buddy)->settings;
+                ((GaimBlistNode *) buddy)->settings = ((GaimBlistNode *) newbuddy)->settings;
+                ((GaimBlistNode *) newbuddy)->settings = tmp;
 
-            buddy_destroy = TRUE;
-            data = (GaimBlistNode *) newbuddy;
+                buddy_destroy = TRUE;
+                data = (GaimBlistNode *) newbuddy;
+            }
+            else
+                gaim_blist_alias_buddy(buddy, alias);
+            break;
         }
-        else
-            gaim_blist_alias_buddy(buddy, alias);
+        case GAIM_BLIST_CONTACT_NODE:
+        {
+            GaimContact *contact = (GaimContact *) data;
+            const char *alias = gaim_request_fields_get_string(fields, "alias");
+            gaim_contact_set_alias(contact, alias);
+            break;
+        }
+        case GAIM_BLIST_GROUP_NODE:
+        {
+            GaimGroup *group = (GaimGroup *) data;
+            const char *alias = gaim_request_fields_get_string(fields, "alias");
+            gaim_blist_rename_group(group, alias);
+            break;
+        }
+        case GAIM_BLIST_CHAT_NODE:
+        {
+            GaimChat *chat = (GaimChat *) data;
+            const char *alias = gaim_request_fields_get_string(fields, "alias");
+            gaim_blist_alias_chat(chat, alias);
+            break;
+        }
+        case GAIM_BLIST_OTHER_NODE:
+            break;
     }
 
     gaim_signal_emit(gaim_blist_get_handle(), PLUGIN "-submit-fields", fields, data);
@@ -73,31 +99,76 @@ buddy_edit_cb(GaimBlistNode * node, gpoi
     GaimRequestFields *fields;
     GaimRequestField *field;
     GaimRequestFieldGroup *group;
+    char *request_title = NULL;
 
     fields = gaim_request_fields_new();
 
-    /* First group: Account details */
-    if(GAIM_BLIST_NODE_IS_BUDDY(node))
+    switch (node->type)
     {
-        GaimBuddy *buddy = (GaimBuddy *) node;
-        group = gaim_request_field_group_new("Buddy Details");
-        gaim_request_fields_add_group(fields, group);
+        case GAIM_BLIST_BUDDY_NODE:
+        {
+            GaimBuddy *buddy = (GaimBuddy *) node;
+            group = gaim_request_field_group_new("Buddy Details");
+            gaim_request_fields_add_group(fields, group);
 
-        field = gaim_request_field_account_new("account", "Account", buddy->account);
-        gaim_request_field_account_set_show_all(field, TRUE);
-        gaim_request_field_group_add_field(group, field);
+            field = gaim_request_field_account_new("account", "Account", buddy->account);
+            gaim_request_field_account_set_show_all(field, TRUE);
+            gaim_request_field_group_add_field(group, field);
 
-        field = gaim_request_field_string_new("name", "Name", buddy->name, FALSE);
-        gaim_request_field_group_add_field(group, field);
+            field = gaim_request_field_string_new("name", "Name", buddy->name, FALSE);
+            gaim_request_field_group_add_field(group, field);
 
-        field = gaim_request_field_string_new("alias", "Alias", buddy->alias, FALSE);
-        gaim_request_field_group_add_field(group, field);
+            field = gaim_request_field_string_new("alias", "Alias", buddy->alias, FALSE);
+            gaim_request_field_group_add_field(group, field);
+
+            request_title = "Edit Buddy";
+            break;
+        }
+        case GAIM_BLIST_CONTACT_NODE:
+        {
+            GaimContact *contact = (GaimContact *) node;
+            group = gaim_request_field_group_new("Contact Details");
+            gaim_request_fields_add_group(fields, group);
+
+            field = gaim_request_field_string_new("alias", "Alias", contact->alias, FALSE);
+            gaim_request_field_group_add_field(group, field);
+
+            request_title = "Edit Contact";
+            break;
+        }
+        case GAIM_BLIST_GROUP_NODE:
+        {
+            GaimGroup *grp = (GaimGroup *) node;
+            group = gaim_request_field_group_new("Group Details");
+            gaim_request_fields_add_group(fields, group);
+
+            field = gaim_request_field_string_new("alias", "Name", grp->name, FALSE);
+            gaim_request_field_group_add_field(group, field);
+
+            request_title = "Edit Group";
+            break;
+        }
+        case GAIM_BLIST_CHAT_NODE:
+        {
+            GaimChat *chat = (GaimChat *) node;
+            group = gaim_request_field_group_new("Chat Details");
+            gaim_request_fields_add_group(fields, group);
+
+            field = gaim_request_field_string_new("alias", "Alias", chat->alias, FALSE);
+            gaim_request_field_group_add_field(group, field);
+
+            request_title = "Edit Chat";
+            break;
+        }
+        default:
+            request_title = "Edit";
+            break;
     }
 
     gaim_signal_emit(gaim_blist_get_handle(), PLUGIN "-create-fields", fields, node);
 
     gaim_request_fields(plugin_self,
-                        "Edit Contact", NULL, NULL,
+                        request_title, NULL, NULL,
                         fields, "OK", G_CALLBACK(buddyedit_editcomplete_cb), "Cancel", NULL, node);
 }
 
@@ -110,9 +181,20 @@ buddy_menu_cb(GaimBlistNode * node, GLis
     GaimMenuAction *action;
 #endif
 
-    if(!GAIM_BLIST_NODE_IS_BUDDY(node) && !GAIM_BLIST_NODE_IS_CONTACT(node))
-        return;
+    switch (node->type)
+    {
+            /* These are the types we handle */
+        case GAIM_BLIST_BUDDY_NODE:
+        case GAIM_BLIST_CONTACT_NODE:
+        case GAIM_BLIST_GROUP_NODE:
+        case GAIM_BLIST_CHAT_NODE:
+            break;
 
+        case GAIM_BLIST_OTHER_NODE:
+        default:
+            return;
+    }
+
 #if GAIM_MAJOR_VERSION < 2
     action = gaim_blist_node_action_new("Edit...", buddy_edit_cb, NULL);
 #else
@@ -129,12 +211,12 @@ plugin_load(GaimPlugin * plugin)
 
     void *blist_handle = gaim_blist_get_handle();
 
-    gaim_signal_register(blist_handle, PLUGIN "-create-fields",       /* Called when about to create dialog */
+    gaim_signal_register(blist_handle, PLUGIN "-create-fields", /* Called when about to create dialog */
                          gaim_marshal_VOID__POINTER_POINTER, NULL, 2,   /* (FieldList*,BlistNode*) */
                          gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_TYPE_POINTER),  /* FieldList */
                          gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_SUBTYPE_BLIST));
 
-    gaim_signal_register(blist_handle, PLUGIN "-submit-fields",       /* Called when dialog submitted */
+    gaim_signal_register(blist_handle, PLUGIN "-submit-fields", /* Called when dialog submitted */
                          gaim_marshal_VOID__POINTER_POINTER, NULL, 2,   /* (FieldList*,BlistNode*) */
                          gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_TYPE_POINTER),  /* FieldList */
                          gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_SUBTYPE_BLIST));
@@ -162,7 +244,7 @@ static GaimPluginInfo info = {
 
     "Enable editing of buddy properties",
     "A plugin that adds an edit to to buddies allowing you to change various details you can't normally change. "
-    "It also provides a mechanism for subsequent plugins to add themselves to that dialog. ",
+        "It also provides a mechanism for subsequent plugins to add themselves to that dialog. ",
     "Martijn van Oosterhout <kleptog at svana.org>",
     "http://svana.org/kleptog/gaim/",
 
============================================================
--- buddylang.c	7e56091484b862cecac5e2969f01aa6188d2a854
+++ buddylang.c	52c01bd74c194d9d08a70d53cc580608a3bbbb15
@@ -1,17 +1,18 @@
 /*************************************************************************
  * Buddy Language Module
  * by Martijn van Oosterhout <kleptog at svana.org> (C) April 2006
  * Licenced under the GNU General Public Licence version 2.
  *
- * A GAIM plugin that allows you to configure the language of the spelling
+ * A Gaim plugin that allows you to configure the language of the spelling
  * control on the conversation screen on a per-contact basis.
  *************************************************************************/
 
 #define GAIM_PLUGINS
 #define PLUGIN "core-kleptog-buddylang"
+#define SETTING_NAME "language"
+#define CONTROL_NAME PLUGIN "-" SETTING_NAME
 
 #include <glib.h>
-#include <ctype.h>
 #include <string.h>
 
 #include "notify.h"
@@ -33,23 +34,57 @@ static GaimPlugin *plugin_self;
 #define LANGUAGE_FLAG ((void*)1)
 #define DISABLED_FLAG ((void*)2)
 
+/* Resolve specifies what the return value should mean:
+ *
+ * If TRUE, it's for display, we want to know the *effect* thus hiding the
+ * "none" value and going to going level to find the default
+ *
+ * If false, we only want what the user enter, thus the string "none" if
+ * that's what it is
+ */
 static const char *
-buddy_get_language(GaimBlistNode * node)
+buddy_get_language(GaimBlistNode * node, gboolean resolve)
 {
-    GaimContact *contact = NULL;
+    GaimBlistNode *datanode = NULL;
+    const char *language;
+
     switch (node->type)
     {
         case GAIM_BLIST_BUDDY_NODE:
-            contact = gaim_buddy_get_contact((GaimBuddy *) node);
+            datanode = (GaimBlistNode *) gaim_buddy_get_contact((GaimBuddy *) node);
             break;
         case GAIM_BLIST_CONTACT_NODE:
-            contact = (GaimContact *) node;
+            datanode = node;
             break;
+        case GAIM_BLIST_GROUP_NODE:
+            datanode = node;
+            break;
         default:
             return NULL;
     }
 
-    return gaim_blist_node_get_string((GaimBlistNode *) contact, "language");
+    language = gaim_blist_node_get_string(datanode, SETTING_NAME);
+
+    if(!resolve)
+        return language;
+
+    if(language && strcmp(language, "none") == 0)
+        return NULL;
+
+    if(language)
+        return language;
+
+    if(datanode->type == GAIM_BLIST_CONTACT_NODE)
+    {
+        /* There is no gaim_blist_contact_get_group(), though there probably should be */
+        datanode = datanode->parent;
+        language = gaim_blist_node_get_string(datanode, SETTING_NAME);
+    }
+
+    if(language && strcmp(language, "none") == 0)
+        return NULL;
+
+    return language;
 }
 
 static void
@@ -76,7 +111,7 @@ buddylang_createconv_cb(GaimConversation
     if(!buddy)
         return;
 
-    language = buddy_get_language((GaimBlistNode *) buddy);
+    language = buddy_get_language((GaimBlistNode *) buddy, TRUE);
 
     if(!language)
         return;
@@ -87,8 +122,8 @@ buddylang_createconv_cb(GaimConversation
     if(!gtkspell)
         return;
 
-    if( strcmp( language, "none" ) )
-    	gtkspell_detach( gtkspell );
+    if(strcmp(language, "none"))
+        gtkspell_detach(gtkspell);
     else if(!gtkspell_set_language(gtkspell, language, &error) && error)
     {
         gaim_debug_warning(PLUGIN, "Failed to configure GtkSpell for language %s: %s\n",
@@ -103,50 +138,81 @@ buddylang_submitfields_cb(GaimRequestFie
 static void
 buddylang_submitfields_cb(GaimRequestFields * fields, GaimBlistNode * data)
 {
-    GaimContact *contact;
+    GaimBlistNode *node;
     GaimRequestField *list;
     const GList *sellist;
     void *seldata = NULL;
 
-    gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddylang_submitfields_cb(%p,%p)\n", fields,
-               data);
-    if(GAIM_BLIST_NODE_IS_BUDDY(data))
-        contact = gaim_buddy_get_contact((GaimBuddy *) data);
-    else                        /* Contact */
-        contact = (GaimContact *) data;
+    gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddylang_submitfields_cb(%p,%p)\n", fields, data);
 
-    list = gaim_request_fields_get_field(fields, "language");
+    switch (data->type)
+    {
+        case GAIM_BLIST_BUDDY_NODE:
+            node = (GaimBlistNode *) gaim_buddy_get_contact((GaimBuddy *) data);
+            break;
+        case GAIM_BLIST_CONTACT_NODE:
+        case GAIM_BLIST_GROUP_NODE:
+            /* code handles either case */
+            node = data;
+            break;
+        case GAIM_BLIST_CHAT_NODE:
+        case GAIM_BLIST_OTHER_NODE:
+        default:
+            /* Not applicable */
+            return;
+    }
+
+    list = gaim_request_fields_get_field(fields, CONTROL_NAME);
     sellist = gaim_request_field_list_get_selected(list);
-    if( sellist )
-    	seldata = gaim_request_field_list_get_data(list, sellist->data);
+    if(sellist)
+        seldata = gaim_request_field_list_get_data(list, sellist->data);
 
     /* Otherwise, it's fixed value and this means deletion */
     if(seldata == LANGUAGE_FLAG)
-        gaim_blist_node_set_string((GaimBlistNode *) contact, "language", sellist->data);
-    else if( seldata == DISABLED_FLAG)
-    	gaim_blist_node_set_string((GaimBlistNode *) contact, "language", "none");
+        gaim_blist_node_set_string(node, SETTING_NAME, sellist->data);
+    else if(seldata == DISABLED_FLAG)
+        gaim_blist_node_set_string(node, SETTING_NAME, "none");
     else
-        gaim_blist_node_remove_setting((GaimBlistNode *) contact, "language");
+        gaim_blist_node_remove_setting(node, SETTING_NAME);
 }
 
 /* Node is either a contact or a buddy */
 static void
 buddylang_createfields_cb(GaimRequestFields * fields, GaimBlistNode * data)
 {
-    gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddylang_createfields_cb(%p,%p)\n", fields,
-               data);
+    gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddylang_createfields_cb(%p,%p)\n", fields, data);
     GaimRequestField *field;
     GaimRequestFieldGroup *group;
     const char *language;
+    gboolean is_default;
 
     struct AspellConfig *aspellconfig;
     struct AspellDictInfoList *dictinfolist;
     struct AspellDictInfoEnumeration *dictinfoenumeration;
 
+    switch (data->type)
+    {
+        case GAIM_BLIST_BUDDY_NODE:
+        case GAIM_BLIST_CONTACT_NODE:
+            is_default = FALSE;
+            break;
+        case GAIM_BLIST_GROUP_NODE:
+            is_default = TRUE;
+            break;
+        case GAIM_BLIST_CHAT_NODE:
+        case GAIM_BLIST_OTHER_NODE:
+        default:
+            /* Not applicable */
+            return;
+    }
+
     group = gaim_request_field_group_new(NULL);
     gaim_request_fields_add_group(fields, group);
 
-    field = gaim_request_field_list_new("language", "Spellcheck language");
+    field =
+        gaim_request_field_list_new(CONTROL_NAME,
+                                    is_default ? "Default spellcheck language for group" :
+                                    "Spellcheck language");
     gaim_request_field_list_set_multi_select(field, FALSE);
     gaim_request_field_list_add(field, "<Default>", "");
     gaim_request_field_list_add(field, "<Disabled>", DISABLED_FLAG);
@@ -161,8 +227,7 @@ buddylang_createfields_cb(GaimRequestFie
     dictinfolist = get_aspell_dict_info_list(aspellconfig);
     if(!dictinfolist)
     {
-        gaim_debug(GAIM_DEBUG_INFO, PLUGIN,
-                   "get_aspell_dict_info_list() returned NULL\n");
+        gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "get_aspell_dict_info_list() returned NULL\n");
         return;
     }
     gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "dictinfo size = %d\n",
@@ -170,8 +235,7 @@ buddylang_createfields_cb(GaimRequestFie
     dictinfoenumeration = aspell_dict_info_list_elements(dictinfolist);
     if(!dictinfoenumeration)
     {
-        gaim_debug(GAIM_DEBUG_INFO, PLUGIN,
-                   "aspell_dict_info_list_elements() returned NULL\n");
+        gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "aspell_dict_info_list_elements() returned NULL\n");
         return;
     }
 
@@ -190,9 +254,16 @@ buddylang_createfields_cb(GaimRequestFie
     delete_aspell_dict_info_enumeration(dictinfoenumeration);
     delete_aspell_config(aspellconfig);
 
-    language = buddy_get_language(data);
+    language = buddy_get_language(data, FALSE);
     if(language)
-        gaim_request_field_list_add_selected(field, language);
+    {
+        if(strcmp(language, "none") == 0)
+            gaim_request_field_list_add_selected(field, "<Disabled>");
+        else
+            gaim_request_field_list_add_selected(field, language);
+    }
+    else
+        gaim_request_field_list_add_selected(field, "<Default>");
 
     gaim_request_field_group_add_field(group, field);
 }
============================================================
--- buddynotes.c	939548f8e60de653e2dc6307d5d9ede84943ba91
+++ buddynotes.c	209b7b50a3d09a7646bd4f5c73296e9da8714a38
@@ -1,17 +1,18 @@
 /*************************************************************************
  * Buddy Notes Module
  * by Martijn van Oosterhout <kleptog at svana.org> (C) April 2006
  * Licenced under the GNU General Public Licence version 2.
  *
- * A GAIM plugin the allows you to add notes to contacts which will be
+ * A Gaim plugin the allows you to add notes to contacts which will be
  * displayed in the conversation screen as well as the hover tooltip.
  *************************************************************************/
 
 #define GAIM_PLUGINS
 #define PLUGIN "core-kleptog-buddynotes"
+#define SETTING_NAME "notes"
+#define CONTROL_NAME PLUGIN "-" SETTING_NAME
 
 #include <glib.h>
-#include <ctype.h>
 
 #include "notify.h"
 #include "plugin.h"
@@ -46,7 +47,7 @@ buddy_get_notes(GaimBlistNode * node)
             return NULL;
     }
 
-    return gaim_blist_node_get_string((GaimBlistNode *) contact, "notes");
+    return gaim_blist_node_get_string((GaimBlistNode *) contact, SETTING_NAME);
 }
 
 static void
@@ -107,18 +108,26 @@ buddynotes_submitfields_cb(GaimRequestFi
 
     /* buddynotes stuff */
     gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddynotes_submitfields_cb(%p,%p)\n", fields, data);
-    if(GAIM_BLIST_NODE_IS_BUDDY(data))
-        contact = gaim_buddy_get_contact((GaimBuddy *) data);
-    else                        /* Contact */
-        contact = (GaimContact *) data;
+    switch (data->type)
+    {
+        case GAIM_BLIST_BUDDY_NODE:
+            contact = gaim_buddy_get_contact((GaimBuddy *) data);
+            break;
+        case GAIM_BLIST_CONTACT_NODE:
+            contact = (GaimContact *) data;
+            break;
+        default:
+            /* Not applicable */
+            return;
+    }
 
-    notes = gaim_request_fields_get_string(fields, "notes");
+    notes = gaim_request_fields_get_string(fields, CONTROL_NAME);
 
     /* Otherwise, it's fixed value and this means deletion */
     if(notes && notes[0])
-        gaim_blist_node_set_string((GaimBlistNode *) contact, "notes", notes);
+        gaim_blist_node_set_string((GaimBlistNode *) contact, SETTING_NAME, notes);
     else
-        gaim_blist_node_remove_setting((GaimBlistNode *) contact, "notes");
+        gaim_blist_node_remove_setting((GaimBlistNode *) contact, SETTING_NAME);
 }
 
 /* Node is either a contact or a buddy */
@@ -130,12 +139,22 @@ buddynotes_createfields_cb(GaimRequestFi
     GaimRequestFieldGroup *group;
     const char *notes;
 
+    switch (data->type)
+    {
+        case GAIM_BLIST_BUDDY_NODE:
+        case GAIM_BLIST_CONTACT_NODE:
+            /* Continue, code works for either */
+            break;
+        default:
+            /* Not applicable */
+            return;
+    }
     group = gaim_request_field_group_new(NULL);
     gaim_request_fields_add_group(fields, group);
 
     notes = buddy_get_notes(data);
 
-    field = gaim_request_field_string_new("notes", "Notes:", notes, FALSE);
+    field = gaim_request_field_string_new(CONTROL_NAME, "Notes", notes, FALSE);
 
     gaim_request_field_group_add_field(group, field);
 }
============================================================
--- buddytimezone.c	ae311d68c1b30b21e391c6e6b27736c0c64a31df
+++ buddytimezone.c	954fa97d481ecf78ffa2a54f0df11568355ab282
@@ -1,9 +1,9 @@
 /*************************************************************************
  * Buddy Timezone Module
  * by Martijn van Oosterhout <kleptog at svana.org> (C) April 2006
  * Licenced under the GNU General Public Licence version 2.
  *
- * A GAIM plugin that allows you to configure a timezone on a per-contact
+ * A Gaim plugin that allows you to configure a timezone on a per-contact
  * basis so it can display the localtime of your contact when a conversation
  * starts. Convenient if you deal with contacts from many parts of the
  * world.
@@ -11,10 +11,12 @@
 
 #define GAIM_PLUGINS
 #define PLUGIN "core-kleptog-buddytimezone"
+#define SETTING_NAME "timezone"
+#define CONTROL_NAME PLUGIN "-" SETTING_NAME
 
 #include <glib.h>
+#include <errno.h>
 #include <ctype.h>
-#include <errno.h>
 #include <string.h>
 
 #include "notify.h"
@@ -34,28 +36,65 @@ void *gaim_gtk_blist_get_handle();
 //#include "gtkblist.h"   /* gaim_gtk_blist_get_handle */  Requires gtk-dev
 
 #define TIMEZONE_FLAG  ((void*)1)
+#define DISABLED_FLAG ((void*)2)
 
 static GaimPlugin *plugin_self;
 
+/* Resolve specifies what the return value should mean:
+ *
+ * If TRUE, it's for display, we want to know the *effect* thus hiding the
+ * "none" value and going to going level to find the default
+ *
+ * If false, we only want what the user enter, thus the string "none" if
+ * that's what it is
+ */
 static const char *
-buddy_get_timezone(GaimBlistNode * node)
+buddy_get_timezone(GaimBlistNode * node, gboolean resolve)
 {
-    GaimContact *contact = NULL;
+    GaimBlistNode *datanode = NULL;
+    const char *timezone;
+
     switch (node->type)
     {
         case GAIM_BLIST_BUDDY_NODE:
-            contact = gaim_buddy_get_contact((GaimBuddy *) node);
+            datanode = (GaimBlistNode *) gaim_buddy_get_contact((GaimBuddy *) node);
             break;
         case GAIM_BLIST_CONTACT_NODE:
-            contact = (GaimContact *) node;
+            datanode = node;
             break;
+        case GAIM_BLIST_GROUP_NODE:
+            datanode = node;
+            break;
         default:
             return NULL;
     }
 
-    return gaim_blist_node_get_string((GaimBlistNode *) contact, "timezone");
+    timezone = gaim_blist_node_get_string(datanode, SETTING_NAME);
+
+    if(!resolve)
+        return timezone;
+
+    /* The effect of "none" is to stop recursion */
+    if(timezone && strcmp(timezone, "none") == 0)
+        return NULL;
+
+    if(timezone)
+        return timezone;
+
+    if(datanode->type == GAIM_BLIST_CONTACT_NODE)
+    {
+        /* There is no gaim_blist_contact_get_group(), though there probably should be */
+        datanode = datanode->parent;
+        timezone = gaim_blist_node_get_string(datanode, SETTING_NAME);
+    }
+
+    if(timezone && strcmp(timezone, "none") == 0)
+        return NULL;
+
+    return timezone;
 }
 
+#ifdef PRIVATE_TZLIB
 static int
 timezone_get_time(const char *timezone, struct tm *tm)
 {
@@ -70,7 +109,30 @@ timezone_get_time(const char *timezone, 
     }
     return 0;
 }
+#else
+static int
+timezone_get_time(const char *timezone, struct tm *tm_in)
+{
+    time_t now;
+    struct tm *tm;
+    const gchar *old_tz;
 
+    /* Store the current TZ value. */
+    old_tz = g_getenv("TZ");
+    g_setenv("TZ", timezone, TRUE);
+    now = time(NULL);
+    tm = localtime(&now);
+    memcpy(tm_in, tm, sizeof(*tm));
+
+    /* Reset the old TZ value. */
+    if(old_tz == NULL)
+        g_unsetenv("TZ");
+    else
+        g_setenv("TZ", old_tz, TRUE);
+    return 0;
+}
+#endif
+
 static void
 timezone_createconv_cb(GaimConversation * conv, void *data)
 {
@@ -93,7 +155,7 @@ timezone_createconv_cb(GaimConversation 
     if(!buddy)
         return;
 
-    timezone = buddy_get_timezone((GaimBlistNode *) buddy);
+    timezone = buddy_get_timezone((GaimBlistNode *) buddy, TRUE);
 
     if(!timezone)
         return;
@@ -102,8 +164,15 @@ timezone_createconv_cb(GaimConversation 
 
     if(ret == 0)
     {
-        char *str = g_strdup_printf("Remote Local Time: %d:%02d", tm.tm_hour, tm.tm_min);
+#if GAIM_MAJOR_VERSION > 1
+        char *text = gaim_date_format_short(tm);
+#else
+        char text[64];
+        strftime(text, sizeof(text), "%X", &tm);
+#endif
 
+        char *str = g_strdup_printf("Remote Local Time: %s", text);
+
         gaim_conversation_write(conv, PLUGIN, str, GAIM_MESSAGE_SYSTEM, time(NULL));
 
         g_free(str);
@@ -111,8 +180,7 @@ timezone_createconv_cb(GaimConversation 
 }
 
 #if GAIM_MAJOR_VERSION > 1
-static void
-buddytimezone_tooltip_cb(GaimBlistNode * node, char **text, gboolean full, void *data);
+static void buddytimezone_tooltip_cb(GaimBlistNode * node, char **text, gboolean full, void *data);
 #else
 static void
 buddytimezone_tooltip_cb(GaimBlistNode * node, char **text, void *data)
@@ -124,12 +192,12 @@ buddytimezone_tooltip_cb(GaimBlistNode *
     int ret;
 
 #if GAIM_MAJOR_VERSION > 1
-    if (!full)
+    if(!full)
         return;
 #endif
-            
+
     gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "type %d\n", node->type);
-    timezone = buddy_get_timezone(node);
+    timezone = buddy_get_timezone(node, TRUE);
     if(!timezone)
         return;
 
@@ -144,32 +212,45 @@ static void
 }
 
 static void
-buddyedit_submitfields_cb(GaimRequestFields * fields, GaimBlistNode * data)
+buddytimezone_submitfields_cb(GaimRequestFields * fields, GaimBlistNode * data)
 {
-    GaimContact *contact;
+    GaimBlistNode *node;
     GaimRequestField *list;
     const GList *sellist;
-    int is_timezone;
+    void *seldata = NULL;
 
     /* timezone stuff */
-    gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddyedit_submitfields_cb(%p,%p)\n", fields,
-               data);
-    if(GAIM_BLIST_NODE_IS_BUDDY(data))
-        contact = gaim_buddy_get_contact((GaimBuddy *) data);
-    else                        /* Contact */
-        contact = (GaimContact *) data;
+    gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddytimezone_submitfields_cb(%p,%p)\n", fields, data);
 
-    list = gaim_request_fields_get_field(fields, "timezone");
+    switch (data->type)
+    {
+        case GAIM_BLIST_BUDDY_NODE:
+            node = (GaimBlistNode *) gaim_buddy_get_contact((GaimBuddy *) data);
+            break;
+        case GAIM_BLIST_CONTACT_NODE:
+        case GAIM_BLIST_GROUP_NODE:
+            /* code handles either case */
+            node = data;
+            break;
+        case GAIM_BLIST_CHAT_NODE:
+        case GAIM_BLIST_OTHER_NODE:
+        default:
+            /* Not applicable */
+            return;
+    }
+
+    list = gaim_request_fields_get_field(fields, CONTROL_NAME);
     sellist = gaim_request_field_list_get_selected(list);
-    /* Timezones have a data value of NULL, in which case we use the item text */
-    is_timezone = (sellist
-                   && gaim_request_field_list_get_data(list, sellist->data) == TIMEZONE_FLAG);
+    if(sellist)
+        seldata = gaim_request_field_list_get_data(list, sellist->data);
 
     /* Otherwise, it's fixed value and this means deletion */
-    if(is_timezone)
-        gaim_blist_node_set_string((GaimBlistNode *) contact, "timezone", sellist->data);
+    if(seldata == TIMEZONE_FLAG)
+        gaim_blist_node_set_string(node, SETTING_NAME, sellist->data);
+    else if(seldata == DISABLED_FLAG)
+        gaim_blist_node_set_string(node, SETTING_NAME, "none");
     else
-        gaim_blist_node_remove_setting((GaimBlistNode *) contact, "timezone");
+        gaim_blist_node_remove_setting(node, SETTING_NAME);
 }
 
 static int
@@ -181,28 +262,54 @@ buddy_add_timezone_cb(char *filename, vo
     return 0;
 }
 
-/* Node is either a contact or a buddy */
 static void
-buddyedit_createfields_cb(GaimRequestFields * fields, GaimBlistNode * data)
+buddytimezone_createfields_cb(GaimRequestFields * fields, GaimBlistNode * data)
 {
-    gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddyedit_createfields_cb(%p,%p)\n", fields,
-               data);
+    gaim_debug(GAIM_DEBUG_INFO, PLUGIN, "buddytimezone_createfields_cb(%p,%p)\n", fields, data);
     GaimRequestField *field;
     GaimRequestFieldGroup *group;
     const char *timezone;
+    gboolean is_default;
 
+    switch (data->type)
+    {
+        case GAIM_BLIST_BUDDY_NODE:
+        case GAIM_BLIST_CONTACT_NODE:
+            is_default = FALSE;
+            break;
+        case GAIM_BLIST_GROUP_NODE:
+            is_default = TRUE;
+            break;
+        case GAIM_BLIST_CHAT_NODE:
+        case GAIM_BLIST_OTHER_NODE:
+        default:
+            /* Not applicable */
+            return;
+    }
+
     group = gaim_request_field_group_new(NULL);
     gaim_request_fields_add_group(fields, group);
 
-    field = gaim_request_field_list_new("timezone", "Timezone of contact (type to select)");
+    field =
+        gaim_request_field_list_new(CONTROL_NAME,
+                                    is_default ? "Default timezone for group" :
+                                    "Timezone of contact (type to select)");
     gaim_request_field_list_set_multi_select(field, FALSE);
-    gaim_request_field_list_add(field, "<Disabled>", "");
+    gaim_request_field_list_add(field, "<Default>", "");
+    gaim_request_field_list_add(field, "<Disabled>", DISABLED_FLAG);
 
     recurse_directory("/usr/share/zoneinfo/", buddy_add_timezone_cb, field);
 
-    timezone = buddy_get_timezone(data);
+    timezone = buddy_get_timezone(data, FALSE);
     if(timezone)
-        gaim_request_field_list_add_selected(field, timezone);
+    {
+        if(strcmp(timezone, "none") == 0)
+            gaim_request_field_list_add_selected(field, "<Disabled>");
+        else
+            gaim_request_field_list_add_selected(field, timezone);
+    }
+    else
+        gaim_request_field_list_add_selected(field, "<Default>");
 
     gaim_request_field_group_add_field(group, field);
 }
@@ -210,24 +317,23 @@ plugin_load(GaimPlugin * plugin)
 static gboolean
 plugin_load(GaimPlugin * plugin)
 {
-
-    const char *zoneinfo_dir;
-
     plugin_self = plugin;
 
     gaim_signal_connect(gaim_blist_get_handle(), "core-kleptog-buddyedit-create-fields", plugin,
-                        GAIM_CALLBACK(buddyedit_createfields_cb), NULL);
+                        GAIM_CALLBACK(buddytimezone_createfields_cb), NULL);
     gaim_signal_connect(gaim_blist_get_handle(), "core-kleptog-buddyedit-submit-fields", plugin,
-                        GAIM_CALLBACK(buddyedit_submitfields_cb), NULL);
+                        GAIM_CALLBACK(buddytimezone_submitfields_cb), NULL);
     gaim_signal_connect(gaim_gtk_blist_get_handle(), "drawing-tooltip", plugin,
                         GAIM_CALLBACK(buddytimezone_tooltip_cb), NULL);
     gaim_signal_connect(gaim_conversations_get_handle(), "conversation-created", plugin,
                         GAIM_CALLBACK(timezone_createconv_cb), NULL);
 
-    zoneinfo_dir = gaim_prefs_get_string("/plugins/timezone/zoneinfo_dir");
+#ifdef PRIVATE_TZLIB
+    const char *zoneinfo_dir = gaim_prefs_get_string("/plugins/timezone/zoneinfo_dir");
     if(tz_init(zoneinfo_dir) < 0)
         gaim_debug_error(PLUGIN, "Problem opening zoneinfo dir (%s): %s\n", zoneinfo_dir,
                          strerror(errno));
+#endif
 
     return TRUE;
 }
@@ -247,10 +353,9 @@ static GaimPluginInfo info = {
     G_STRINGIFY(PLUGIN_VERSION),
 
     "Quickly see the local time of a buddy",
-    "A GAIM plugin that allows you to configure a timezone on a per-contact "
-    "basis so it can display the localtime of your contact when a conversation "
-    "starts. Convenient if you deal with contacts from many parts of the "
-    "world.",
+    "A Gaim plugin that allows you to configure a timezone on a per-contact "
+        "basis so it can display the localtime of your contact when a conversation "
+        "starts. Convenient if you deal with contacts from many parts of the " "world.",
     "Martijn van Oosterhout <kleptog at svana.org>",
     "http://svana.org/kleptog/gaim/",
 


More information about the Plugins-commits mailing list