Mercurial > grim > purple-plugin-pack
view autoprofile/gtk_widget.c @ 1028:314cfd774bc4
s/purple.guifications.org/plugins.guifications.org/
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Thu, 06 Aug 2009 12:30:12 -0700 |
parents | 9e2f8ef9e772 |
children | 888daeb5b79d |
line wrap: on
line source
/*--------------------------------------------------------------------------* * AUTOPROFILE * * * * A Purple away message and profile manager that supports dynamic text * * * * AutoProfile is the legal property of its developers. Please refer to * * the COPYRIGHT file distributed with this source distribution. * * * * 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 * *--------------------------------------------------------------------------*/ #include "widget.h" #include "request.h" #include "autoprofile.h" #include "gtkprefs.h" #include "gtkimhtml.h" #define AP_RESPONSE_CHOOSE 98125 static GtkWidget *dialog_box = NULL; static GtkWidget *dialog_box_contents = NULL; static GtkWidget *dialog_box_preview = NULL; static struct widget *dialog_box_widget = NULL; static GStaticMutex preview_mutex = G_STATIC_MUTEX_INIT; static GtkWidget *component_dialog = NULL; static GtkWidget *choose_button = NULL; static GtkWidget *widget_dialog = NULL; static GtkWidget *delete_button = NULL; static GtkWidget *rename_button = NULL; static GtkListStore *tree_list = NULL; static GHashTable *pref_names = NULL; static void component_dialog_show (); void ap_widget_prefs_updated (struct widget *w) { gchar *output; if (dialog_box_preview == NULL) return; if (w != dialog_box_widget) return; // TODO: Investigate how laggy this is, possibly add a timeout output = w->component->generate (w); g_static_mutex_lock (&preview_mutex); gtk_imhtml_clear (GTK_IMHTML(dialog_box_preview)); gtk_imhtml_append_text (GTK_IMHTML(dialog_box_preview), output, GTK_IMHTML_NO_SCROLL); g_static_mutex_unlock (&preview_mutex); free (output); } static void update_widget_list (GtkListStore *ls) { GtkTreeIter iter; GList *widgets, *widgets_start; struct widget *w; GString *s; s = g_string_new (""); gtk_list_store_clear (ls); widgets_start = widgets = ap_widget_get_widgets (); for (widgets = widgets_start; widgets != NULL; widgets = widgets->next) { gtk_list_store_append (ls, &iter); w = (struct widget *) widgets->data; g_string_printf (s, "<b>%s</b>", w->alias); gtk_list_store_set (ls, &iter, 0, s->str, 1, w, -1); } g_list_free (widgets_start); g_string_free (s, TRUE); } static void refresh_cb (GtkWidget *widget, gpointer data) { struct widget *w; w = (struct widget *) data; ap_widget_prefs_updated (w); } /* Widget configuration menu */ static GtkWidget *get_widget_configuration (struct widget *w) { GtkWidget *config, *hbox, *vbox, *sw; GtkWidget *label, *button; GtkWidget *menu; GString *s; gchar *output; config = gtk_vbox_new (FALSE, 0); /* Title/Description */ hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER(hbox), 6); gtk_box_pack_start (GTK_BOX(config), hbox, FALSE, FALSE, 0); s = g_string_new (""); g_string_printf (s, "<b>%s:</b> %s", w->component->name, w->component->description); label = gtk_label_new (""); gtk_label_set_markup (GTK_LABEL(label), s->str); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); g_string_free (s, TRUE); /* Separator */ gtk_box_pack_start (GTK_BOX (config), gtk_hseparator_new (), FALSE, FALSE, 0); /* Preview window */ vbox = gtk_vbox_new (FALSE, 6); gtk_container_set_border_width (GTK_CONTAINER(vbox), 6); gtk_box_pack_start (GTK_BOX(config), vbox, FALSE, FALSE, 0); hbox = gtk_hbox_new (FALSE, 8); gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0); label = gtk_label_new (""); gtk_label_set_markup (GTK_LABEL(label), _("<b>Preview</b>")); gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0); button = gtk_button_new_with_label (_("Refresh")); gtk_box_pack_end (GTK_BOX(hbox), button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (refresh_cb), w); 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(vbox), sw, TRUE, TRUE, 0); dialog_box_preview = gtk_imhtml_new (NULL, NULL); gtk_container_add (GTK_CONTAINER(sw), dialog_box_preview); pidgin_setup_imhtml (dialog_box_preview); output = w->component->generate (w); gtk_imhtml_append_text (GTK_IMHTML(dialog_box_preview), output, GTK_IMHTML_NO_SCROLL); free (output); dialog_box_widget = w; /* Separator */ gtk_box_pack_start (GTK_BOX (config), gtk_hseparator_new (), FALSE, FALSE, 0); /* Configuration stuff */ vbox = gtk_vbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER(vbox), 6); gtk_box_pack_start (GTK_BOX(config), vbox, TRUE, TRUE, 0); label = gtk_label_new (""); gtk_label_set_markup (GTK_LABEL(label), _("<b>Configuration</b>")); gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); if (w->component->pref_menu == NULL || (menu = (w->component->pref_menu) (w)) == NULL) { label = gtk_label_new (_("No options available for this component")); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); } else { gtk_box_pack_start (GTK_BOX (vbox), menu, TRUE, TRUE, 0); } return config; } /* Info message */ static GtkWidget *get_info_message () { GtkWidget *page; GtkWidget *aboutwin; GtkWidget *text; /* Make the box */ page = gtk_vbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (page), 12); /* Window with info */ aboutwin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(aboutwin), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(aboutwin), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX(page), aboutwin, TRUE, TRUE, 0); text = gtk_imhtml_new (NULL, NULL); gtk_container_add (GTK_CONTAINER(aboutwin), text); pidgin_setup_imhtml (text); /* Info text */ gtk_imhtml_append_text (GTK_IMHTML(text), _("<b><u>Basic info</u></b><br>"), GTK_IMHTML_NO_SCROLL); gtk_imhtml_append_text (GTK_IMHTML(text), _("A <b>widget</b> is a little piece/snippet of automatically " "generated text. There are all sorts of widgets; each type has " "different content (i.e. a random quote, text from a blog, the " "song currently playing, etc).<br><br>"), GTK_IMHTML_NO_SCROLL); gtk_imhtml_append_text (GTK_IMHTML(text), _("To use a widget, simply drag it from the list on the left and " "drop it into a profile or status message. <i>It's that easy!</i>" "<br><br>"), GTK_IMHTML_NO_SCROLL); gtk_imhtml_append_text (GTK_IMHTML(text), _("<b>To edit your profile:</b> " "Use the \"Info/profile\" tab in this window.<br>"), GTK_IMHTML_NO_SCROLL); gtk_imhtml_append_text (GTK_IMHTML(text), _("<b>To edit your available/away/status message:</b> " "Use the regular Purple interface built into the bottom of the buddy " "list.<br><br>"), GTK_IMHTML_NO_SCROLL); gtk_imhtml_append_text (GTK_IMHTML(text), _("<b><u>Advanced Tips</u></b><br>"), GTK_IMHTML_NO_SCROLL); gtk_imhtml_append_text (GTK_IMHTML(text), _("You can insert a widget into a profile or status by typing its name. " "To do this, just type \"[widget-name]\" wherever you want to " "place a widget (names of widgets are listed on the left). <br><br>" "<b>You type:</b> The song I am playing now is [iTunesInfo].<br>" "<b>AutoProfile result:</b> The song I am playing now is " "The Beatles - Yellow Submarine.<br><br>"), GTK_IMHTML_NO_SCROLL); return page; } /* Dialog window actions */ static void widget_popup_rename_cb ( struct widget *w, const char *new_text) { GtkTreeIter iter; GValue val; struct widget *cur_widget; GString *s; gtk_tree_model_get_iter_first (GTK_TREE_MODEL(tree_list), &iter); while (TRUE) { val.g_type = 0; gtk_tree_model_get_value (GTK_TREE_MODEL(tree_list), &iter, 1, &val); cur_widget = g_value_get_pointer(&val); if (cur_widget == w) break; if (!gtk_tree_model_iter_next (GTK_TREE_MODEL(tree_list), &iter)) { purple_notify_error (NULL, NULL, N_("Unable to change name"), N_("The specified widget no longer exists.")); return; } } if (ap_widget_rename (w, new_text)) { s = g_string_new (""); g_string_printf (s, "<b>%s</b>", w->alias); // Set value in ls gtk_list_store_set (tree_list, &iter, 0, s->str, 1, w, -1); g_string_free (s, TRUE); } else { purple_notify_error (NULL, NULL, N_("Unable to change name"), N_("The widget name you have specified is already in use.")); } } static void delete_cb (GtkWidget *button, GtkTreeSelection *sel) { GtkTreeModel *model; GtkTreeIter iter; GValue val; struct widget *w; gtk_tree_selection_get_selected (sel, &model, &iter); val.g_type = 0; gtk_tree_model_get_value (model, &iter, 1, &val); w = g_value_get_pointer(&val); ap_widget_delete (w); gtk_list_store_remove (GTK_LIST_STORE(model), &iter); } static void rename_cb (GtkWidget *button, GtkTreeSelection *sel) { GtkTreeModel *model; GtkTreeIter iter; GValue val; struct widget *w; gtk_tree_selection_get_selected (sel, &model, &iter); val.g_type = 0; gtk_tree_model_get_value (model, &iter, 1, &val); w = g_value_get_pointer(&val); purple_request_input(NULL, _("Rename Widget"), NULL, _("Enter a new name for this widget."), w->alias, FALSE, FALSE, NULL, _("Rename"), G_CALLBACK(widget_popup_rename_cb), _("Cancel"), NULL, NULL, NULL, NULL, w); } static void add_cb (GtkWidget *button, GtkTreeSelection *sel) { component_dialog_show (); } void ap_widget_gtk_start () { pref_names = g_hash_table_new (g_str_hash, g_str_equal); } void ap_widget_gtk_finish () { done_with_widget_list (); g_hash_table_destroy (pref_names); pref_names = NULL; } static void widget_sel_cb (GtkTreeSelection *sel, GtkTreeModel *model) { GtkTreeIter iter; struct widget *w; GValue val; gtk_widget_destroy (dialog_box_contents); if (!gtk_tree_selection_get_selected (sel, &model, &iter)) { gtk_widget_set_sensitive(rename_button, FALSE); gtk_widget_set_sensitive(delete_button, FALSE); dialog_box_contents = get_info_message (); dialog_box_preview = NULL; dialog_box_widget = NULL; } else { gtk_widget_set_sensitive(rename_button, TRUE); gtk_widget_set_sensitive(delete_button, TRUE); val.g_type = 0; gtk_tree_model_get_value (GTK_TREE_MODEL(tree_list), &iter, 1, &val); w = g_value_get_pointer(&val); dialog_box_contents = get_widget_configuration (w); } gtk_box_pack_start (GTK_BOX(dialog_box), dialog_box_contents, TRUE, TRUE, 0); gtk_widget_show_all (dialog_box); } GtkWidget *ap_widget_get_config_page () { GtkTreeSelection *sel; GtkWidget *vbox; GtkWidget *add_button; /* Arrange main parts of window */ dialog_box = gtk_hbox_new (FALSE, 0); vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX(dialog_box), vbox, FALSE, FALSE, 0); get_widget_list (vbox, &sel); g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (widget_sel_cb), NULL); add_button = gtk_button_new_with_label (_("New Widget")); g_signal_connect (G_OBJECT(add_button), "clicked", G_CALLBACK(add_cb), sel); gtk_box_pack_start (GTK_BOX(vbox), add_button, FALSE, FALSE, 0); rename_button = gtk_button_new_with_label (_("Rename")); gtk_widget_set_sensitive(rename_button, FALSE); g_signal_connect (G_OBJECT(rename_button), "clicked", G_CALLBACK(rename_cb), sel); gtk_box_pack_start (GTK_BOX(vbox), rename_button, FALSE, FALSE, 0); delete_button = gtk_button_new_with_label (_("Delete")); gtk_widget_set_sensitive(delete_button, FALSE); g_signal_connect (G_OBJECT(delete_button), "clicked", G_CALLBACK(delete_cb), sel); gtk_box_pack_start (GTK_BOX(vbox), delete_button, FALSE, FALSE, 0); dialog_box_contents = get_info_message (); gtk_box_pack_start (GTK_BOX(dialog_box), dialog_box_contents, TRUE, TRUE, 0); return dialog_box; } /* DND */ static void drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx, GtkSelectionData *data, guint info, guint time, gpointer user_data) { GtkListStore *ls = (GtkListStore *) user_data; if (ls == NULL) return; if (data->target == gdk_atom_intern ("STRING", FALSE)) { GtkTreeRowReference *ref; GtkTreePath *source_row; GtkTreeIter iter; GString *s; struct widget *w; GValue val = {0}; ref = g_object_get_data (G_OBJECT(ctx), "gtk-tree-view-source-row"); source_row = gtk_tree_row_reference_get_path (ref); if (source_row == NULL) return; gtk_tree_model_get_iter(GTK_TREE_MODEL(ls), &iter, source_row); gtk_tree_model_get_value(GTK_TREE_MODEL(ls), &iter, 1, &val); w = g_value_get_pointer (&val); s = g_string_new (""); g_string_printf (s, "[%s]", w->alias); gtk_selection_data_set (data, gdk_atom_intern ("STRING", FALSE), 8, (guchar *)s->str, strlen(s->str)+1); g_string_free (s, TRUE); gtk_tree_path_free (source_row); } } void done_with_widget_list () { if (tree_list) { g_object_unref (tree_list); tree_list = NULL; } widget_dialog = NULL; delete_button = NULL; dialog_box = NULL; dialog_box_contents = NULL; dialog_box_preview = NULL; dialog_box_widget = NULL; if (component_dialog != NULL) { gtk_widget_destroy (component_dialog); component_dialog = NULL; choose_button = NULL; } } GtkWidget *get_widget_list (GtkWidget *box, GtkTreeSelection **sel) { GtkWidget *sw; GtkWidget *event_view; GtkCellRenderer *rend; GtkTreeViewColumn *col; GtkTargetEntry gte [] = {{"STRING", 0, GTK_IMHTML_DRAG_STRING}}; if (tree_list == NULL) { tree_list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(tree_list), 0, GTK_SORT_ASCENDING); update_widget_list (tree_list); g_object_ref (G_OBJECT(tree_list)); } /* List of widgets */ 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(box), sw, TRUE, TRUE, 0); event_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_list)); *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view)); rend = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes (_("Widget"), rend, "markup", 0, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (event_view), col); gtk_tree_view_column_set_sort_column_id (col, 0); gtk_container_add (GTK_CONTAINER(sw), event_view); /* Drag and Drop */ gtk_tree_view_enable_model_drag_source( GTK_TREE_VIEW(event_view), GDK_BUTTON1_MASK, gte, 1, GDK_ACTION_COPY); g_signal_connect(G_OBJECT(event_view), "drag-data-get", G_CALLBACK(drag_data_get_cb), tree_list); return event_view; } /********************************************************* Component selection window **********************************************************/ static void add_component (struct component *c) { struct widget *w; GtkTreeIter iter; GString *s; w = ap_widget_create (c); if (w == NULL) return; s = g_string_new (""); gtk_list_store_append (tree_list, &iter); g_string_printf (s, "<b>%s</b>", w->alias); gtk_list_store_set (tree_list, &iter, 0, s->str, 1, w, -1); g_string_free (s, TRUE); } static void component_row_activate_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer null) { GtkTreeSelection *sel; GtkTreeIter iter; struct component *c; GtkTreeModel *model; sel = gtk_tree_view_get_selection (view); if (gtk_tree_selection_get_selected (sel, &model, &iter)) { gtk_tree_model_get (model, &iter, 1, &c, -1); add_component (c); } gtk_widget_destroy (component_dialog); component_dialog = NULL; choose_button = NULL; } static void component_response_cb(GtkWidget *d, int response, GtkTreeSelection *sel) { GtkTreeModel *model; GtkTreeIter iter; GValue val; struct component *c; switch (response) { case AP_RESPONSE_CHOOSE: gtk_tree_selection_get_selected (sel, &model, &iter); val.g_type = 0; gtk_tree_model_get_value (model, &iter, 1, &val); c = g_value_get_pointer(&val); add_component (c); case GTK_RESPONSE_CLOSE: case GTK_RESPONSE_CANCEL: case GTK_RESPONSE_DELETE_EVENT: gtk_widget_destroy(d); component_dialog = NULL; choose_button = NULL; break; } } static void component_sel_cb (GtkTreeSelection *sel, GtkTreeModel *model) { GtkTreeIter iter; if (!gtk_tree_selection_get_selected (sel, &model, &iter)) { gtk_widget_set_sensitive(choose_button, FALSE); } else { gtk_widget_set_sensitive(choose_button, TRUE); } } static void update_component_list (GtkListStore *ls) { GtkTreeIter iter; GList *components; struct component *c; GString *s; gchar *name, *description; gtk_list_store_clear (ls); s = g_string_new (""); for (components = ap_component_get_components (); components != NULL; components = components->next) { gtk_list_store_append (ls, &iter); c = (struct component *) components->data; name = g_markup_escape_text (c->name, -1); description = g_markup_escape_text (c->description, -1); g_string_printf (s, "<b>%s</b>\n%s", name, description); gtk_list_store_set (ls, &iter, 0, s->str, 1, c, -1); free (name); free (description); } g_string_free (s, TRUE); } static void component_dialog_show () { GtkWidget *sw; GtkWidget *event_view; GtkListStore *ls; GtkCellRenderer *rendt; GtkTreeViewColumn *col; GtkTreeSelection *sel; if (component_dialog != NULL) { gtk_window_present(GTK_WINDOW(component_dialog)); return; } component_dialog = gtk_dialog_new_with_buttons (_("Select a widget type"), NULL, GTK_DIALOG_NO_SEPARATOR, NULL); choose_button = gtk_dialog_add_button (GTK_DIALOG(component_dialog), _("Create widget"), AP_RESPONSE_CHOOSE); gtk_dialog_add_button (GTK_DIALOG(component_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); gtk_widget_set_sensitive (choose_button, FALSE); sw = gtk_scrolled_window_new (NULL,NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX(GTK_DIALOG(component_dialog)->vbox), sw, TRUE, TRUE, 0); ls = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(ls), 0, GTK_SORT_ASCENDING); update_component_list (ls); event_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(ls)); g_signal_connect(G_OBJECT(event_view), "row-activated", G_CALLBACK(component_row_activate_cb), event_view); sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view)); rendt = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes (_("Widget type"), rendt, "markup", 0, NULL); #if GTK_CHECK_VERSION(2,6,0) gtk_tree_view_column_set_expand (col, TRUE); g_object_set(rendt, "ellipsize", PANGO_ELLIPSIZE_END, NULL); #endif gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col); gtk_tree_view_column_set_sort_column_id (col, 0); g_object_unref (G_OBJECT(ls)); gtk_container_add (GTK_CONTAINER(sw), event_view); g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (component_sel_cb), NULL); g_signal_connect(G_OBJECT(component_dialog), "response", G_CALLBACK(component_response_cb), sel); gtk_window_set_default_size(GTK_WINDOW(component_dialog), 550, 430); gtk_widget_show_all(component_dialog); } /* Preferences stuff */ static void pref_callback (const char *name, PurplePrefType type, gconstpointer val, gpointer data) { struct widget *w = (struct widget *) data; ap_widget_prefs_updated (w); } static const gchar *get_const_pref (struct widget *w, const char *key) { gchar *pref, *result; // This is here to prevent memory leaks pref = ap_prefs_get_pref_name (w, key); if (pref_names == NULL) { pref_names = g_hash_table_new (g_str_hash, g_str_equal); } result = g_hash_table_lookup (pref_names, pref); if (!result) { g_hash_table_insert (pref_names, pref, pref); return pref; } else { free (pref); return result; } } GtkWidget *ap_prefs_checkbox (struct widget *w, const char *title, const char *key, GtkWidget *page) { GtkWidget *result; const gchar *pref; pref = get_const_pref (w, key); result = pidgin_prefs_checkbox (title, pref, page); purple_prefs_connect_callback (ap_get_plugin_handle (), pref, pref_callback, w); return result; } GtkWidget *ap_prefs_dropdown_from_list (struct widget *w, GtkWidget *page, const gchar *title, PurplePrefType type, const char *key, GList *menuitems) { GtkWidget *result; const gchar *pref; pref = get_const_pref (w, key); result = pidgin_prefs_dropdown_from_list ( page, title, type, pref, menuitems); purple_prefs_connect_callback (ap_get_plugin_handle (), pref, pref_callback, w); return result; } GtkWidget *ap_prefs_labeled_entry (struct widget *w, GtkWidget *page, const gchar *title, const char *key, GtkSizeGroup *sg) { GtkWidget *result; const gchar *pref; pref = get_const_pref (w, key); result = pidgin_prefs_labeled_entry (page, title, pref, sg); purple_prefs_connect_callback (ap_get_plugin_handle (), pref, pref_callback, w); return result; } GtkWidget *ap_prefs_labeled_spin_button (struct widget *w, GtkWidget *page, const gchar *title, const char *key, int min, int max, GtkSizeGroup *sg) { GtkWidget *result; const gchar *pref; pref = get_const_pref (w, key); result = pidgin_prefs_labeled_spin_button (page, title, pref, min, max, sg); purple_prefs_connect_callback (ap_get_plugin_handle (), pref, pref_callback, w); return result; }