grim/purple-plugin-pack
--leaks
org.guifications.plugins
2009-04-15, darkrain42
/*--------------------------------------------------------------------------* * 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 * *--------------------------------------------------------------------------*/ #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) { 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, g_static_mutex_unlock (&preview_mutex); static void update_widget_list (GtkListStore *ls) { GList *widgets, *widgets_start; 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, g_list_free (widgets_start); static void refresh_cb (GtkWidget *widget, gpointer data) { 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; config = gtk_vbox_new (FALSE, 0); 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); 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); gtk_box_pack_start (GTK_BOX (config), gtk_hseparator_new (), 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_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); gtk_box_pack_start (GTK_BOX (config), gtk_hseparator_new (), /* 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); gtk_box_pack_start (GTK_BOX (vbox), menu, TRUE, TRUE, 0); static GtkWidget *get_info_message () { page = gtk_vbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (page), 12); 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_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); 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_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); /* Dialog window actions */ static void widget_popup_rename_cb ( struct widget *w, const char *new_text) { struct widget *cur_widget; gtk_tree_model_get_iter_first (GTK_TREE_MODEL(tree_list), &iter); 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.")); if (ap_widget_rename (w, new_text)) { g_string_printf (s, "<b>%s</b>", w->alias); gtk_list_store_set (tree_list, &iter, 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) gtk_tree_selection_get_selected (sel, &model, &iter); gtk_tree_model_get_value (model, &iter, 1, &val); w = g_value_get_pointer(&val); gtk_list_store_remove (GTK_LIST_STORE(model), &iter); static void rename_cb (GtkWidget *button, GtkTreeSelection *sel) gtk_tree_selection_get_selected (sel, &model, &iter); 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, _("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); static void widget_sel_cb (GtkTreeSelection *sel, GtkTreeModel *model) { 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; gtk_widget_set_sensitive(rename_button, TRUE); gtk_widget_set_sensitive(delete_button, TRUE); 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, gtk_widget_show_all (dialog_box); GtkWidget *ap_widget_get_config_page () /* 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), 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, drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx, GtkSelectionData *data, guint info, guint time, GtkListStore *ls = (GtkListStore *) user_data; if (data->target == gdk_atom_intern ("STRING", FALSE)) { GtkTreeRowReference *ref; 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, w = g_value_get_pointer (&val); 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); gtk_tree_path_free (source_row); void done_with_widget_list () { g_object_unref (tree_list); dialog_box_contents = NULL; dialog_box_preview = NULL; dialog_box_widget = NULL; if (component_dialog != NULL) { gtk_widget_destroy (component_dialog); GtkWidget *get_widget_list (GtkWidget *box, GtkTreeSelection **sel) GtkTargetEntry gte [] = {{"STRING", 0, GTK_IMHTML_DRAG_STRING}}; 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), update_widget_list (tree_list); g_object_ref (G_OBJECT(tree_list)); 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_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, 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); gtk_tree_view_enable_model_drag_source( GTK_TREE_VIEW(event_view), GDK_BUTTON1_MASK, gte, g_signal_connect(G_OBJECT(event_view), "drag-data-get", G_CALLBACK(drag_data_get_cb), tree_list); /********************************************************* Component selection window **********************************************************/ static void add_component (struct component *c) { w = ap_widget_create (c); gtk_list_store_append (tree_list, &iter); g_string_printf (s, "<b>%s</b>", w->alias); gtk_list_store_set (tree_list, &iter, static void component_row_activate_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer null) 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); gtk_widget_destroy (component_dialog); static void component_response_cb(GtkWidget *d, int response, gtk_tree_selection_get_selected (sel, &model, &iter); gtk_tree_model_get_value (model, &iter, 1, &val); c = g_value_get_pointer(&val); case GTK_RESPONSE_CANCEL: case GTK_RESPONSE_DELETE_EVENT: static void component_sel_cb (GtkTreeSelection *sel, GtkTreeModel *model) { if (!gtk_tree_selection_get_selected (sel, &model, &iter)) { gtk_widget_set_sensitive(choose_button, FALSE); gtk_widget_set_sensitive(choose_button, TRUE); static void update_component_list (GtkListStore *ls) { gchar *name, *description; gtk_list_store_clear (ls); for (components = ap_component_get_components (); 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, static void component_dialog_show () if (component_dialog != NULL) { gtk_window_present(GTK_WINDOW(component_dialog)); component_dialog = gtk_dialog_new_with_buttons (_("Select a widget type"), 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_box_pack_start (GTK_BOX(GTK_DIALOG(component_dialog)->vbox), sw, ls = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(ls), 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"), #if GTK_CHECK_VERSION(2,6,0) gtk_tree_view_column_set_expand (col, TRUE); g_object_set(rendt, "ellipsize", PANGO_ELLIPSIZE_END, NULL); 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); 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) { // 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); g_hash_table_insert (pref_names, pref, pref); GtkWidget *ap_prefs_checkbox (struct widget *w, const char *title, const char *key, GtkWidget *page) pref = get_const_pref (w, key); result = pidgin_prefs_checkbox (title, pref, page); purple_prefs_connect_callback (ap_get_plugin_handle (), pref, GtkWidget *ap_prefs_dropdown_from_list (struct widget *w, GtkWidget *page, const gchar *title, PurplePrefType type, const char *key, GList *menuitems) 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, GtkWidget *ap_prefs_labeled_entry (struct widget *w, GtkWidget *page, const gchar *title, const char *key, GtkSizeGroup *sg) { pref = get_const_pref (w, key); result = pidgin_prefs_labeled_entry (page, title, pref, sg); purple_prefs_connect_callback (ap_get_plugin_handle (), pref, GtkWidget *ap_prefs_labeled_spin_button (struct widget *w, GtkWidget *page, const gchar *title, const char *key, int min, int max, GtkSizeGroup *sg) pref = get_const_pref (w, key); result = pidgin_prefs_labeled_spin_button (page, title, pref, purple_prefs_connect_callback (ap_get_plugin_handle (), pref,