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 * *--------------------------------------------------------------------------*/ #include "gtksavedstatuses.h" /*--------------------------------------------------------------------------* *--------------------------------------------------------------------------*/ static GtkWidget *get_info_page () { page = gtk_vbox_new (FALSE, 5); gtk_container_set_border_width (GTK_CONTAINER (page), 5); labeltext = g_strdup_printf ( _("<span weight=\"bold\" size=\"larger\">AutoProfile %s</span>"), label = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL(label), labeltext); gtk_label_set_line_wrap (GTK_LABEL(label), TRUE); gtk_misc_set_alignment (GTK_MISC(label), 0.5, 0); gtk_box_pack_start (GTK_BOX(page), label, FALSE, FALSE, 0); aboutwin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(aboutwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 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), _("Use the <b>Autoprofile</b> portion of the <b>Tools</b> " "buddy list</b> to configure the actual content that will go in your " "status messages and profiles and set options.<br><br>"), gtk_imhtml_append_text (GTK_IMHTML(text), _("<u>DOCUMENTATION / HELP</u><br>"), GTK_IMHTML_NO_SCROLL); gtk_imhtml_append_text (GTK_IMHTML(text), _("Complete documentation can be found at:<br> <a href=" "\"http://hkn.eecs.berkeley.edu/~casey/autoprofile/documentation.php\">" "hkn.eecs.berkeley.edu/~casey/autoprofile/documentation.php</a><br>"), gtk_imhtml_append_text (GTK_IMHTML(text), _("<br><u>ABOUT</u><br>"), GTK_IMHTML_NO_SCROLL); "<font size=\"3\"><b>", _("Developers"), ":</b></font><br>" " Casey Ho (Lead Developer)<br>" " Mitchell Harwell<br>", NULL); gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL); "<font size=\"3\"><b>", _("Contributors/Patchers"), ":</b></font><br>" " Mark Painter<br>", NULL); gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL); "<font size=\"3\"><b>", _("Website"), ":</b></font><br>" " <a href=\"http://autoprofile.sourceforge.net\">" "autoprofile.sourceforge.net<br>", NULL); gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL); /*---------------------------------------------------------------------------- *--------------------------------------------------------------------------*/ /* PRIMARILY RIPPED FROM GAIM GTKACCOUNT.C */ } PidginAccountAddUserData; GtkTreeViewColumn *screenname_col; static void add_account_to_liststore(PurpleAccount *, gpointer); static void set_account(GtkListStore *, GtkTreeIter *, PurpleAccount *); static gboolean is_profile_settable (PurpleAccount *a) { const gchar *id = purple_account_get_protocol_id (a); if (!strcmp (id, "prpl-yahoo") || !strcmp (id, "prpl-msn") || !strcmp (id, "prpl-jabber")) { drag_data_get_cb(GtkWidget *widget, GdkDragContext *ctx, GtkSelectionData *data, guint info, guint time, if (data->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE)) { GtkTreeRowReference *ref; PurpleAccount *account = NULL; 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(dialog->model), &iter, source_row); gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter, dialog->drag_iter = iter; account = g_value_get_pointer(&val); gtk_selection_data_set(data, gdk_atom_intern("PURPLE_ACCOUNT", FALSE), 8, (void *)&account, sizeof(account)); gtk_tree_path_free(source_row); move_account_after(GtkListStore *store, GtkTreeIter *iter, gtk_tree_model_get(GTK_TREE_MODEL(store), iter, COLUMN_DATA, &account, -1); gtk_list_store_insert_after(store, &new_iter, position); set_account(store, &new_iter, account); gtk_list_store_remove(store, iter); move_account_before(GtkListStore *store, GtkTreeIter *iter, gtk_tree_model_get(GTK_TREE_MODEL(store), iter, COLUMN_DATA, &account, -1); gtk_list_store_insert_before(store, &new_iter, position); set_account(store, &new_iter, account); gtk_list_store_remove(store, iter); drag_data_received_cb(GtkWidget *widget, GdkDragContext *ctx, guint x, guint y, GtkSelectionData *sd, guint info, guint t, AccountsWindow *dialog) if (sd->target == gdk_atom_intern("PURPLE_ACCOUNT", FALSE) && sd->data) { GtkTreePath *path = NULL; GtkTreeViewDropPosition position; memcpy(&a, sd->data, sizeof(a)); if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path); gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter, account = g_value_get_pointer(&val); case GTK_TREE_VIEW_DROP_AFTER: case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: move_account_after(dialog->model, &dialog->drag_iter, &iter); dest_index = g_list_index(purple_accounts_get_all(), account) + 1; case GTK_TREE_VIEW_DROP_BEFORE: case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: dest_index = g_list_index(purple_accounts_get_all(), account); move_account_before(dialog->model, &dialog->drag_iter, &iter); purple_accounts_reorder(a, dest_index); enabled_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) AccountsWindow *dialog = (AccountsWindow *)data; GtkTreeModel *model = GTK_TREE_MODEL(dialog->model); gtk_tree_model_get_iter_from_string(model, &iter, path_str); gtk_tree_model_get(model, &iter, COLUMN_ENABLED, &enabled, /* Change profile settings */ ap_account_enable_profile (account, !enabled); set_account (dialog->model, &iter, account); add_columns(GtkWidget *treeview, AccountsWindow *dialog) GtkCellRenderer *renderer; GtkTreeViewColumn *column; column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, _("Screen Name")); gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); gtk_tree_view_column_set_resizable(column, TRUE); renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(column, renderer, FALSE); gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", COLUMN_ICON); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "text", COLUMN_SCREENNAME); dialog->screenname_col = column; renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(enabled_cb), dialog); gtk_tree_view_column_new_with_attributes(_("AutoProfile sets user info"), renderer, "active", COLUMN_ENABLED, NULL); gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); gtk_tree_view_column_set_resizable(column, TRUE); column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, _("Protocol")); gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); gtk_tree_view_column_set_resizable(column, TRUE); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "text", COLUMN_PROTOCOL); set_account(GtkListStore *store, GtkTreeIter *iter, PurpleAccount *account) pixbuf = pidgin_create_prpl_icon(account, 0.5); scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); if (purple_account_is_disconnected(account)) gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); gtk_list_store_set(store, iter, COLUMN_SCREENNAME, purple_account_get_username(account), COLUMN_ENABLED, ap_account_has_profile_enabled(account), COLUMN_PROTOCOL, purple_account_get_protocol_name(account), if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); if (scale != NULL) g_object_unref(G_OBJECT(scale)); add_account_to_liststore(PurpleAccount *account, gpointer user_data) AccountsWindow *dialog = (AccountsWindow *) user_data; if (dialog == NULL) return; if (!is_profile_settable (account)) return; gtk_list_store_append(dialog->model, &iter); set_account(dialog->model, &iter, account); populate_accounts_list(AccountsWindow *dialog) gtk_list_store_clear(dialog->model); for (l = purple_accounts_get_all(); l != NULL; l = l->next) add_account_to_liststore((PurpleAccount *)l->data, dialog); #if !GTK_CHECK_VERSION(2,2,0) get_selected_helper(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) *((gboolean *)user_data) = TRUE; account_selected_cb(GtkTreeSelection *sel, AccountsWindow *dialog) gboolean selected = FALSE; #if GTK_CHECK_VERSION(2,2,0) selected = (gtk_tree_selection_count_selected_rows(sel) > 0); gtk_tree_selection_selected_foreach(sel, get_selected_helper, &selected); create_accounts_list(AccountsWindow *dialog) GtkTargetEntry gte[] = {{"PURPLE_ACCOUNT", GTK_TARGET_SAME_APP, 0}}; /* Create the scrolled window. */ sw = gtk_scrolled_window_new(0, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); /* Create the list model. */ dialog->model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER, /* And now the actual treeview */ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); dialog->treeview = treeview; gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(account_selected_cb), dialog); gtk_container_add(GTK_CONTAINER(sw), treeview); gtk_widget_show(treeview); add_columns(treeview, dialog); populate_accounts_list(dialog); /* Setup DND. I wanna be an orc! */ gtk_tree_view_enable_model_drag_source( GTK_TREE_VIEW(treeview), GDK_BUTTON1_MASK, gte, gtk_tree_view_enable_model_drag_dest( GTK_TREE_VIEW(treeview), gte, 1, GDK_ACTION_COPY | GDK_ACTION_MOVE); g_signal_connect(G_OBJECT(treeview), "drag-data-received", G_CALLBACK(drag_data_received_cb), dialog); g_signal_connect(G_OBJECT(treeview), "drag-data-get", G_CALLBACK(drag_data_get_cb), dialog); static void account_page_delete_cb (GtkObject *object, gpointer data) GtkWidget *get_account_page () { AccountsWindow *accounts_window; page = gtk_vbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (page), 12); accounts_window = g_new0(AccountsWindow, 1); /* Setup the scrolled window that will contain the list of accounts. */ sw = create_accounts_list(accounts_window); gtk_box_pack_start(GTK_BOX(page), sw, TRUE, TRUE, 0); _("Accounts that do not support user-specified profiles are not shown")); gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (page), "destroy", G_CALLBACK (account_page_delete_cb), accounts_window); /*---------------------------------------------------------------------------- *--------------------------------------------------------------------------*/ void ap_gtk_prefs_add_summary_option (GtkWidget *widget) { pidgin_prefs_dropdown (widget, "Show AutoProfile summary window", "/plugins/gtk/autoprofile/show_summary", "Always", "always", "When away", "away", "Never", "never", NULL); set_idle_away(PurpleSavedStatus *status) purple_prefs_set_int("/core/savedstatus/idleaway", purple_savedstatus_get_creation_time(status)); static GtkWidget *get_behavior_page () { GtkWidget *frame, *vbox, *hbox; GtkWidget *button, *select, *menu; page = gtk_vbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (page), 12); /*---------- Update frequency ----------*/ frame = pidgin_make_frame (page, _("Update frequency")); vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); pidgin_prefs_labeled_spin_button (vbox, _("Minimum number of seconds between updates"), "/plugins/gtk/autoprofile/delay_update", label = gtk_label_new (""); markup = g_markup_printf_escaped ("<span style=\"italic\">%s</span>", _("WARNING: Using values below 60 seconds may increase the frequency\n" "of rate limiting errors")); gtk_label_set_markup (GTK_LABEL (label), markup); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); /*----------- Auto-away stuff ------------*/ frame = pidgin_make_frame(page, _("Auto-away")); button = pidgin_prefs_checkbox(_("Change status when idle"), "/plugins/gtk/autoprofile/away_when_idle", frame); sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); select = pidgin_prefs_labeled_spin_button(frame, _("Minutes before changing status:"), "/core/away/mins_before_away", g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pidgin_toggle_sensitive), select); hbox = gtk_hbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), hbox); label = gtk_label_new_with_mnemonic(_("Change status to:")); gtk_size_group_add_widget(sg, label); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pidgin_toggle_sensitive), label); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); /* TODO: Show something useful if we don't have any saved statuses. */ menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away)); gtk_box_pack_start(GTK_BOX(frame), menu, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pidgin_toggle_sensitive), menu); gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu); if (!purple_prefs_get_bool("/plugins/gtk/autoprofile/away_when_idle")) { gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(select), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(label), FALSE); /*---------------------------------------------------------------------------- *--------------------------------------------------------------------------*/ /* Update string arguments */ static gboolean update_behavior_string (GtkWidget *widget, GdkEventFocus *evt, ap_debug ("preferences", "behavior string preference modified"); if (!strcmp (data, "text_trigger")) { purple_prefs_set_string ("/plugins/gtk/autoprofile/autorespond/trigger", gtk_entry_get_text (GTK_ENTRY (widget))); } else if (!strcmp (data, "text_respond")) { purple_prefs_set_string ("/plugins/gtk/autoprofile/autorespond/text", gtk_entry_get_text (GTK_ENTRY (widget))); ap_debug_error ("preferences", "invalid data argument to string update"); /* Update value returned from spinner for auto-respond delay */ static gboolean update_delay_respond (GtkWidget *widget, GdkEventFocus *evt, purple_prefs_set_int ("/plugins/gtk/autoprofile/delay_respond", gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget))); static GtkWidget *get_autoreply_page () { GtkWidget *label, *checkbox, *spinner, *entry; GtkWidget *frame, *vbox, *large_vbox, *hbox; page = gtk_vbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (page), 12); frame = pidgin_make_frame(page, _("General")); dd = pidgin_prefs_dropdown(frame, _("Auto-reply:"), PURPLE_PREF_STRING, "/plugins/gtk/autoprofile/autorespond/auto_reply", _("When both away and idle"), "awayidle", sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); gtk_size_group_add_widget(sg, dd); gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5); /*---------- Auto-responses ----------*/ frame = pidgin_make_frame (page, _("Dynamic auto-responses")); vbox = gtk_vbox_new (FALSE, 5); gtk_container_add (GTK_CONTAINER (frame), vbox); /* Auto-response activated */ checkbox = pidgin_prefs_checkbox ( _("Allow users to request more auto-responses"), "/plugins/gtk/autoprofile/autorespond/enable", vbox); large_vbox = gtk_vbox_new (FALSE, 5); gtk_box_pack_start (GTK_BOX (vbox), large_vbox, FALSE, FALSE, 0); /* Auto-response delay */ hbox = gtk_hbox_new (FALSE, 5); gtk_box_pack_start (GTK_BOX (large_vbox), hbox, FALSE, FALSE, 0); label = gtk_label_new (_("Delay")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); spinner = gtk_spin_button_new_with_range (1, G_MAXINT, 1); gtk_box_pack_start (GTK_BOX (hbox), spinner, TRUE, TRUE, 0); label = gtk_label_new (_("seconds between auto-responses")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinner), purple_prefs_get_int ( "/plugins/gtk/autoprofile/autorespond/delay")); g_signal_connect (G_OBJECT (spinner), "value-changed", G_CALLBACK (update_delay_respond), NULL); /* Auto-response message string */ label = gtk_label_new (_("Message sent with first autoresponse:")); gtk_box_pack_start (GTK_BOX (large_vbox), label, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (large_vbox), entry, FALSE, FALSE, 0); gtk_entry_set_max_length (GTK_ENTRY (entry), 100); gtk_entry_set_text (GTK_ENTRY (entry), purple_prefs_get_string ( "/plugins/gtk/autoprofile/autorespond/text")); g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (update_behavior_string), "text_respond"); label = gtk_label_new (_("Request trigger message:")); gtk_box_pack_start (GTK_BOX (large_vbox), label, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (large_vbox), entry, FALSE, FALSE, 0); gtk_entry_set_max_length (GTK_ENTRY (entry), 50); gtk_entry_set_text (GTK_ENTRY (entry), purple_prefs_get_string ( "/plugins/gtk/autoprofile/autorespond/trigger")); g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (update_behavior_string), "text_trigger"); /* Sensitivity signals */ g_signal_connect(G_OBJECT(checkbox), "clicked", G_CALLBACK(pidgin_toggle_sensitive), large_vbox); if (!purple_prefs_get_bool ("/plugins/gtk/autoprofile/autorespond/enable")) { gtk_widget_set_sensitive (large_vbox, FALSE); gtk_widget_set_sensitive (large_vbox, TRUE); /*---------------------------------------------------------------------------- *--------------------------------------------------------------------------*/ static GtkWidget *get_config_frame (PurplePlugin *plugin) GtkWidget *info = get_info_page (); gtk_widget_set_size_request (info, 350, 400); static void dialog_cb (GtkDialog *dialog, gint arg1, gpointer user_data) gtk_widget_destroy ((GtkWidget *)dialog); void ap_preferences_display () GtkWidget *dialog, *notebook; notebook = gtk_notebook_new (); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), get_behavior_page (), gtk_label_new (_("General"))); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), get_account_page (), gtk_label_new (_("User info/profiles"))); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), get_autoreply_page (), gtk_label_new (_("Auto-reply"))); g_object_set (notebook, "homogeneous", TRUE, NULL); dialog = gtk_dialog_new_with_buttons(PIDGIN_ALERT_TITLE, NULL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), notebook); gtk_window_set_default_size (GTK_WINDOW(dialog), 400, 400); gtk_widget_show_all (dialog); g_signal_connect (G_OBJECT(dialog), "response", G_CALLBACK(dialog_cb), dialog); /*--------------- Generate the preference widget once ----------------*/ PidginPluginUiInfo ui_info =