pidgin/purple-plugin-pack
Clone
Instead of trying to fix this plugin up with the existing code, I've decided
to give it a complete rewrite. The intent is that by being as generic as
possible I'll be able to make this plugin work for Finch, Pidgin, and any
other libpurple app that decides to implement sound. We might end up with
some cleaner code out of the process too. :)
--- a/smartear/Makefile.am Tue Jul 03 04:45:10 2007 -0400
+++ b/smartear/Makefile.am Thu Jul 05 05:46:17 2007 -0400
@@ -1,34 +1,26 @@
smarteardir = $(PIDGIN_LIBDIR)
smartear_la_LDFLAGS = -module -avoid-version
smartear_LTLIBRARIES = smartear.la
+smartear_la_SOURCES = smartear.c
+smartear_la_LIBADD = $(PURPLE_LIBS) - -DLIBDIR=\"$(PIDGIN_LIBDIR)\" \
- -DDATADIR=\"$(PIDGIN_DATADIR)\" \
- -DPIXMAPSDIR=\"$(PIDGIN_PIXMAPSDIR)\" \
+ -DLIBDIR=\"$(PURPLE_LIBDIR)\" \ + -DDATADIR=\"$(PURPLE_DATADIR)\" \ + -DPIXMAPSDIR=\"$(PURPLE_PIXMAPSDIR)\" \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/smartear/old-smartear-interface.c Thu Jul 05 05:46:17 2007 -0400
@@ -0,0 +1,546 @@
+# include "../pp_config.h" +#define GLADE_HOOKUP_OBJECT(component,widget,name) \ + g_object_set_data_full (G_OBJECT (component), name, \ + gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref) +#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \ + g_object_set_data (G_OBJECT (component), name, widget) + GtkWidget *config_vbox; + GtkObject *delay_spin_adj; + GtkWidget *scrolledwindow2; + GtkWidget *hbuttonbox1; + config_vbox = gtk_vbox_new (FALSE, 0); + gtk_widget_set_size_request (config_vbox, -1, 640); + frame1 = gtk_frame_new (NULL); + gtk_widget_show (frame1); + gtk_box_pack_start (GTK_BOX (config_vbox), frame1, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame1), 5); + table1 = gtk_table_new (3, 2, FALSE); + gtk_widget_show (table1); + gtk_container_add (GTK_CONTAINER (frame1), table1); + gtk_container_set_border_width (GTK_CONTAINER (table1), 5); + gtk_table_set_row_spacings (GTK_TABLE (table1), 10); + gtk_table_set_col_spacings (GTK_TABLE (table1), 10); + label9 = gtk_label_new ("Time delay between playing sounds for a particular buddy:"); + gtk_widget_show (label9); + gtk_table_attach (GTK_TABLE (table1), label9, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label9), 0, 0.5); + delay_spin_adj = gtk_adjustment_new (60, 0, 1000000, 1, 10, 10); + delay_spin = gtk_spin_button_new (GTK_ADJUSTMENT (delay_spin_adj), 1, 0); + gtk_widget_show (delay_spin); + gtk_table_attach (GTK_TABLE (table1), delay_spin, 1, 2, 0, 1, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (delay_spin), GTK_UPDATE_IF_VALID); + focus_but = gtk_check_button_new_with_mnemonic ("Don't play sounds for the conversation that has focus."); + gtk_widget_show (focus_but); + gtk_table_attach (GTK_TABLE (table1), focus_but, 0, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + timer_but = gtk_check_button_new_with_mnemonic ("Also play sounds if you don't respond to a particular IM within a delay period."); + gtk_widget_show (timer_but); + gtk_table_attach (GTK_TABLE (table1), timer_but, 0, 2, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + label8 = gtk_label_new ("Options"); + gtk_widget_show (label8); + gtk_frame_set_label_widget (GTK_FRAME (frame1), label8); + frame2 = gtk_frame_new (NULL); + gtk_widget_show (frame2); + gtk_box_pack_start (GTK_BOX (config_vbox), frame2, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame2), 5); + table2 = gtk_table_new (2, 3, FALSE); + gtk_widget_show (table2); + gtk_container_add (GTK_CONTAINER (frame2), table2); + gtk_container_set_border_width (GTK_CONTAINER (table2), 5); + gtk_table_set_row_spacings (GTK_TABLE (table2), 10); + gtk_table_set_col_spacings (GTK_TABLE (table2), 10); + delete_but = gtk_button_new_from_stock ("gtk-delete"); + gtk_widget_show (delete_but); + gtk_table_attach (GTK_TABLE (table2), delete_but, 2, 3, 1, 2, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + GTK_WIDGET_SET_FLAGS (delete_but, GTK_CAN_DEFAULT); + new_but = gtk_button_new_from_stock ("gtk-new"); + gtk_widget_show (new_but); + gtk_table_attach (GTK_TABLE (table2), new_but, 0, 1, 1, 2, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + GTK_WIDGET_SET_FLAGS (new_but, GTK_CAN_DEFAULT); + scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow2); + gtk_table_attach (GTK_TABLE (table2), scrolledwindow2, 0, 3, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + treeview = gtk_tree_view_new (); + gtk_widget_show (treeview); + gtk_container_add (GTK_CONTAINER (scrolledwindow2), treeview); + edit_but = gtk_button_new_from_stock ("gtk-properties"); + gtk_widget_show (edit_but); + gtk_table_attach (GTK_TABLE (table2), edit_but, 1, 2, 1, 2, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + label7 = gtk_label_new ("Entries"); + gtk_widget_show (label7); + gtk_frame_set_label_widget (GTK_FRAME (frame2), label7); + hbuttonbox1 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox1); + gtk_box_pack_start (GTK_BOX (config_vbox), hbuttonbox1, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox1), 5); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_SPREAD); + revert_but = gtk_button_new_from_stock ("gtk-revert-to-saved"); + gtk_widget_show (revert_but); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), revert_but); + GTK_WIDGET_SET_FLAGS (revert_but, GTK_CAN_DEFAULT); + save_but = gtk_button_new_from_stock ("gtk-save"); + gtk_widget_show (save_but); + gtk_container_add (GTK_CONTAINER (hbuttonbox1), save_but); + GTK_WIDGET_SET_FLAGS (save_but, GTK_CAN_DEFAULT); + g_signal_connect ((gpointer) config_vbox, "destroy", + G_CALLBACK (on_config_destroy), + g_signal_connect ((gpointer) delay_spin, "changed", + G_CALLBACK (on_delay_changed), + g_signal_connect ((gpointer) focus_but, "toggled", + G_CALLBACK (on_focus_toggled), + g_signal_connect ((gpointer) timer_but, "toggled", + G_CALLBACK (on_timer_toggled), + g_signal_connect ((gpointer) delete_but, "clicked", + G_CALLBACK (on_delete_clicked), + g_signal_connect ((gpointer) new_but, "clicked", + G_CALLBACK (on_new_clicked), + g_signal_connect ((gpointer) edit_but, "clicked", + G_CALLBACK (on_edit_clicked), + g_signal_connect ((gpointer) revert_but, "clicked", + G_CALLBACK (on_revert_clicked), + g_signal_connect ((gpointer) save_but, "clicked", + G_CALLBACK (on_save_clicked), + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, config_vbox, "config_vbox"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, frame1, "frame1"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, table1, "table1"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, label9, "label9"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, delay_spin, "delay_spin"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, focus_but, "focus_but"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, timer_but, "timer_but"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, label8, "label8"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, frame2, "frame2"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, table2, "table2"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, delete_but, "delete_but"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, new_but, "new_but"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, scrolledwindow2, "scrolledwindow2"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, treeview, "treeview"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, edit_but, "edit_but"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, label7, "label7"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, hbuttonbox1, "hbuttonbox1"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, revert_but, "revert_but"); + GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, save_but, "save_but"); +create_file_browse (void) + GtkWidget *file_browse; + GtkWidget *cancel_button1; + file_browse = gtk_file_selection_new ("Select Sound"); + gtk_container_set_border_width (GTK_CONTAINER (file_browse), 10); + gtk_window_set_destroy_with_parent (GTK_WINDOW (file_browse), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (file_browse), GDK_WINDOW_TYPE_HINT_DIALOG); + ok_button1 = GTK_FILE_SELECTION (file_browse)->ok_button; + gtk_widget_show (ok_button1); + GTK_WIDGET_SET_FLAGS (ok_button1, GTK_CAN_DEFAULT); + cancel_button1 = GTK_FILE_SELECTION (file_browse)->cancel_button; + gtk_widget_show (cancel_button1); + GTK_WIDGET_SET_FLAGS (cancel_button1, GTK_CAN_DEFAULT); + g_signal_connect ((gpointer) file_browse, "destroy", + G_CALLBACK (on_file_browse_destroy), + g_signal_connect ((gpointer) ok_button1, "clicked", + G_CALLBACK (on_browse_ok_clicked), + g_signal_connect_swapped ((gpointer) ok_button1, "clicked", + G_CALLBACK (gtk_widget_destroy), + GTK_OBJECT (file_browse)); + g_signal_connect_swapped ((gpointer) cancel_button1, "clicked", + G_CALLBACK (gtk_widget_destroy), + GTK_OBJECT (file_browse)); + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (file_browse, file_browse, "file_browse"); + GLADE_HOOKUP_OBJECT_NO_REF (file_browse, ok_button1, "ok_button1"); + GLADE_HOOKUP_OBJECT_NO_REF (file_browse, cancel_button1, "cancel_button1"); + GtkWidget *type_option; + GtkWidget *hbuttonbox2; + GtkWidget *applysave_but; + GtkWidget *unaway_sound_entry; + GtkWidget *unidle_sound_entry; + GtkWidget *signon_sound_entry; + GtkWidget *unaway_test_but; + GtkWidget *unidle_test_but; + GtkWidget *signon_test_but; + GtkWidget *unaway_browse_but; + GtkWidget *unidle_browse_but; + GtkWidget *signon_browse_but; + GtkWidget *im_browse_but; + GtkWidget *im_sound_entry; + GtkWidget *im_test_but; + edit_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_set_size_request (edit_win, 600, 300); + gtk_window_set_title (GTK_WINDOW (edit_win), "Edit Entry"); + gtk_window_set_default_size (GTK_WINDOW (edit_win), 600, 300); + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox1); + gtk_container_add (GTK_CONTAINER (edit_win), vbox1); + hbox1 = gtk_hbox_new (FALSE, 5); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, TRUE, 10); + gtk_container_set_border_width (GTK_CONTAINER (hbox1), 5); + label29 = gtk_label_new ("Name:"); + gtk_widget_show (label29); + gtk_box_pack_start (GTK_BOX (hbox1), label29, FALSE, FALSE, 0); + gtk_misc_set_alignment (GTK_MISC (label29), 1, 0.5); + name_entry = gtk_entry_new (); + gtk_widget_show (name_entry); + gtk_box_pack_start (GTK_BOX (hbox1), name_entry, TRUE, TRUE, 0); + label30 = gtk_label_new ("Type:"); + gtk_widget_show (label30); + gtk_box_pack_start (GTK_BOX (hbox1), label30, FALSE, TRUE, 0); + gtk_misc_set_alignment (GTK_MISC (label30), 1, 0.5); + type_option = gtk_option_menu_new (); + gtk_widget_show (type_option); + gtk_box_pack_start (GTK_BOX (hbox1), type_option, FALSE, FALSE, 0); + menu1 = gtk_menu_new (); + item_buddy = gtk_menu_item_new_with_mnemonic ("Buddy"); + gtk_widget_show (item_buddy); + gtk_container_add (GTK_CONTAINER (menu1), item_buddy); + item_group = gtk_menu_item_new_with_mnemonic ("Group"); + gtk_widget_show (item_group); + gtk_container_add (GTK_CONTAINER (menu1), item_group); + gtk_option_menu_set_menu (GTK_OPTION_MENU (type_option), menu1); + hbuttonbox2 = gtk_hbutton_box_new (); + gtk_widget_show (hbuttonbox2); + gtk_box_pack_end (GTK_BOX (vbox1), hbuttonbox2, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox2), 5); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox2), GTK_BUTTONBOX_SPREAD); + applysave_but = gtk_button_new_with_mnemonic ("Apply and Save"); + gtk_widget_show (applysave_but); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), applysave_but); + GTK_WIDGET_SET_FLAGS (applysave_but, GTK_CAN_DEFAULT); + apply_but = gtk_button_new_from_stock ("gtk-apply"); + gtk_widget_show (apply_but); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), apply_but); + GTK_WIDGET_SET_FLAGS (apply_but, GTK_CAN_DEFAULT); + cancel_but = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (cancel_but); + gtk_container_add (GTK_CONTAINER (hbuttonbox2), cancel_but); + GTK_WIDGET_SET_FLAGS (cancel_but, GTK_CAN_DEFAULT); + frame3 = gtk_frame_new (NULL); + gtk_widget_show (frame3); + gtk_box_pack_start (GTK_BOX (vbox1), frame3, FALSE, FALSE, 0); + gtk_widget_set_size_request (frame3, -1, 200); + gtk_container_set_border_width (GTK_CONTAINER (frame3), 5); + table5 = gtk_table_new (4, 5, FALSE); + gtk_widget_show (table5); + gtk_container_add (GTK_CONTAINER (frame3), table5); + gtk_widget_set_size_request (table5, 600, 400); + gtk_container_set_border_width (GTK_CONTAINER (table5), 5); + gtk_table_set_row_spacings (GTK_TABLE (table5), 10); + gtk_table_set_col_spacings (GTK_TABLE (table5), 10); + label31 = gtk_label_new ("Play On IM:"); + gtk_widget_show (label31); + gtk_table_attach (GTK_TABLE (table5), label31, 0, 1, 0, 1, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label31), 0, 0.5); + label34 = gtk_label_new ("Play On Unaway:"); + gtk_widget_show (label34); + gtk_table_attach (GTK_TABLE (table5), label34, 0, 1, 3, 4, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label34), 0, 0.5); + label33 = gtk_label_new ("Play On Unidle:"); + gtk_widget_show (label33); + gtk_table_attach (GTK_TABLE (table5), label33, 0, 1, 2, 3, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label33), 0, 0.5); + label32 = gtk_label_new ("Play On Signon:"); + gtk_widget_show (label32); + gtk_table_attach (GTK_TABLE (table5), label32, 0, 1, 1, 2, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label32), 0, 0.5); + unaway_sound_entry = gtk_entry_new (); + gtk_widget_show (unaway_sound_entry); + gtk_table_attach (GTK_TABLE (table5), unaway_sound_entry, 1, 3, 3, 4, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + unidle_sound_entry = gtk_entry_new (); + gtk_widget_show (unidle_sound_entry); + gtk_table_attach (GTK_TABLE (table5), unidle_sound_entry, 1, 3, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + signon_sound_entry = gtk_entry_new (); + gtk_widget_show (signon_sound_entry); + gtk_table_attach (GTK_TABLE (table5), signon_sound_entry, 1, 3, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + unaway_test_but = gtk_button_new_with_mnemonic ("Test"); + gtk_widget_show (unaway_test_but); + gtk_table_attach (GTK_TABLE (table5), unaway_test_but, 4, 5, 3, 4, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + unidle_test_but = gtk_button_new_with_mnemonic ("Test"); + gtk_widget_show (unidle_test_but); + gtk_table_attach (GTK_TABLE (table5), unidle_test_but, 4, 5, 2, 3, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + signon_test_but = gtk_button_new_with_mnemonic ("Test"); + gtk_widget_show (signon_test_but); + gtk_table_attach (GTK_TABLE (table5), signon_test_but, 4, 5, 1, 2, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + unaway_browse_but = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (unaway_browse_but); + gtk_table_attach (GTK_TABLE (table5), unaway_browse_but, 3, 4, 3, 4, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + unidle_browse_but = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (unidle_browse_but); + gtk_table_attach (GTK_TABLE (table5), unidle_browse_but, 3, 4, 2, 3, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + signon_browse_but = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (signon_browse_but); + gtk_table_attach (GTK_TABLE (table5), signon_browse_but, 3, 4, 1, 2, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + im_browse_but = gtk_button_new_from_stock ("gtk-open"); + gtk_widget_show (im_browse_but); + gtk_table_attach (GTK_TABLE (table5), im_browse_but, 3, 4, 0, 1, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + im_sound_entry = gtk_entry_new (); + gtk_widget_show (im_sound_entry); + gtk_table_attach (GTK_TABLE (table5), im_sound_entry, 1, 3, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + im_test_but = gtk_button_new_with_mnemonic ("Test"); + gtk_widget_show (im_test_but); + gtk_table_attach (GTK_TABLE (table5), im_test_but, 4, 5, 0, 1, + (GtkAttachOptions) (0), + (GtkAttachOptions) (0), 0, 0); + label24 = gtk_label_new ("Sound Events"); + gtk_widget_show (label24); + gtk_frame_set_label_widget (GTK_FRAME (frame3), label24); + g_signal_connect ((gpointer) edit_win, "destroy", + G_CALLBACK (on_edit_win_destroy), + g_signal_connect ((gpointer) applysave_but, "clicked", + G_CALLBACK (on_apply_clicked), + g_signal_connect ((gpointer) applysave_but, "clicked", + G_CALLBACK (on_save_clicked), + g_signal_connect_swapped ((gpointer) applysave_but, "clicked", + G_CALLBACK (gtk_widget_destroy), + GTK_OBJECT (edit_win)); + g_signal_connect ((gpointer) apply_but, "clicked", + G_CALLBACK (on_apply_clicked), + g_signal_connect_swapped ((gpointer) apply_but, "clicked", + G_CALLBACK (gtk_widget_destroy), + GTK_OBJECT (edit_win)); + g_signal_connect_swapped ((gpointer) cancel_but, "clicked", + G_CALLBACK (gtk_widget_destroy), + GTK_OBJECT (edit_win)); + g_signal_connect ((gpointer) unaway_test_but, "clicked", + G_CALLBACK (on_unaway_test_clicked), + g_signal_connect ((gpointer) unidle_test_but, "clicked", + G_CALLBACK (on_unidle_test_clicked), + g_signal_connect ((gpointer) signon_test_but, "clicked", + G_CALLBACK (on_signon_test_clicked), + g_signal_connect ((gpointer) unaway_browse_but, "clicked", + G_CALLBACK (on_unaway_browse_clicked), + g_signal_connect ((gpointer) unidle_browse_but, "clicked", + G_CALLBACK (on_unidle_browse_clicked), + g_signal_connect ((gpointer) signon_browse_but, "clicked", + G_CALLBACK (on_signon_browse_clicked), + g_signal_connect ((gpointer) im_browse_but, "clicked", + G_CALLBACK (on_im_browse_clicked), + g_signal_connect ((gpointer) im_test_but, "clicked", + G_CALLBACK (on_im_test_clicked), + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (edit_win, edit_win, "edit_win"); + GLADE_HOOKUP_OBJECT (edit_win, vbox1, "vbox1"); + GLADE_HOOKUP_OBJECT (edit_win, hbox1, "hbox1"); + GLADE_HOOKUP_OBJECT (edit_win, label29, "label29"); + GLADE_HOOKUP_OBJECT (edit_win, name_entry, "name_entry"); + GLADE_HOOKUP_OBJECT (edit_win, label30, "label30"); + GLADE_HOOKUP_OBJECT (edit_win, type_option, "type_option"); + GLADE_HOOKUP_OBJECT (edit_win, menu1, "menu1"); + GLADE_HOOKUP_OBJECT (edit_win, item_buddy, "item_buddy"); + GLADE_HOOKUP_OBJECT (edit_win, item_group, "item_group"); + GLADE_HOOKUP_OBJECT (edit_win, hbuttonbox2, "hbuttonbox2"); + GLADE_HOOKUP_OBJECT (edit_win, applysave_but, "applysave_but"); + GLADE_HOOKUP_OBJECT (edit_win, apply_but, "apply_but"); + GLADE_HOOKUP_OBJECT (edit_win, cancel_but, "cancel_but"); + GLADE_HOOKUP_OBJECT (edit_win, frame3, "frame3"); + GLADE_HOOKUP_OBJECT (edit_win, table5, "table5"); + GLADE_HOOKUP_OBJECT (edit_win, label31, "label31"); + GLADE_HOOKUP_OBJECT (edit_win, label34, "label34"); + GLADE_HOOKUP_OBJECT (edit_win, label33, "label33"); + GLADE_HOOKUP_OBJECT (edit_win, label32, "label32"); + GLADE_HOOKUP_OBJECT (edit_win, unaway_sound_entry, "unaway_sound_entry"); + GLADE_HOOKUP_OBJECT (edit_win, unidle_sound_entry, "unidle_sound_entry"); + GLADE_HOOKUP_OBJECT (edit_win, signon_sound_entry, "signon_sound_entry"); + GLADE_HOOKUP_OBJECT (edit_win, unaway_test_but, "unaway_test_but"); + GLADE_HOOKUP_OBJECT (edit_win, unidle_test_but, "unidle_test_but"); + GLADE_HOOKUP_OBJECT (edit_win, signon_test_but, "signon_test_but"); + GLADE_HOOKUP_OBJECT (edit_win, unaway_browse_but, "unaway_browse_but"); + GLADE_HOOKUP_OBJECT (edit_win, unidle_browse_but, "unidle_browse_but"); + GLADE_HOOKUP_OBJECT (edit_win, signon_browse_but, "signon_browse_but"); + GLADE_HOOKUP_OBJECT (edit_win, im_browse_but, "im_browse_but"); + GLADE_HOOKUP_OBJECT (edit_win, im_sound_entry, "im_sound_entry"); + GLADE_HOOKUP_OBJECT (edit_win, im_test_but, "im_test_but"); + GLADE_HOOKUP_OBJECT (edit_win, label24, "label24"); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/smartear/old-smartear.c Thu Jul 05 05:46:17 2007 -0400
@@ -0,0 +1,1280 @@
+/* SmartEar by Matt Perry + * Works for purple 2.0.0 + * Plugin to assign different sounds to different buddies and groups. + * TODO: figure out contact support +# include "../pp_config.h" +#include "../common/i18n.h" +#define BUF_LEN 2048 /* lifted from libpurple's internal.h */ +#define RCFILE_VERSION 2 +#define RC_EMPTY_SOUND " " /* must be at least 1 char long for sscanf */ +#define SMARTEAR_PLUGIN_ID "smartear" +#define SMARTEAR_VERSION VERSION ".1" +#define FIND(s) lookup_widget(config, s) +#define EFIND(s) lookup_widget(edit_win, s) +/* an entry in the prefs file to indicate what to play for who and when. */ + char *sound[EVENT_NUM]; + PurpleAccount *account; + struct timer_data *timer; +enum { /* Treeview columns */ +#define DEFAULT_NAME "(Default)" +#define IS_DEFAULT(entry) (strcmp(entry->name, DEFAULT_NAME)==0) +#define IS_EMPTY(str) (!str || !str[0]) +struct smartear_entry default_entry = {'B', DEFAULT_NAME, {"", "", "", ""}}; +int focused_quiet = TRUE; +int smartear_timers = FALSE; +GtkWidget *config = NULL; +GtkWidget *edit_win = NULL; +GtkWidget *file_browse = NULL; +GtkWidget *file_browse_entry = NULL; +GtkTreeView *treeview = NULL; +GtkTreeSelection *treeselect = NULL; +GtkTreeModel *treemodel = NULL; +struct smartear_entry *selected_entry = NULL; +GtkTreeIter selected_iter; +GSList *sounds_list = NULL; +GSList *messages_list = NULL; +GtkWidget *create_file_browse(void); +GtkWidget *create_edit_win(void); +GtkWidget *create_config(void); +static void new_entry(struct smartear_entry *entry); +static gboolean get_selection(void); +static void clear_selection(void); +static void populate_edit_win(struct smartear_entry *entry); +static void update_list(void); +static void populate_list(GtkListStore *store); +static void list_set(GtkListStore *store, GtkTreeIter *iter, struct smartear_entry *entry); +static void set_option_entries(void); +static gint sound_cmp(gconstpointer p1, gconstpointer p2); +static struct smartear_entry *sound_new_with_name(gchar *name); +static struct smartear_entry *sound_dup(struct smartear_entry *entry); +static void sound_free(struct smartear_entry *entry, int free_entry); +static void soundlist_free(void); +static gchar *get_basename(gchar *path); +static void on_treeselect_changed(GtkTreeSelection *selection, gpointer data); +static void smartear_save(void); +static void smartear_load(void); +static void play_sound_alias(char *sound, PurpleAccount* account); +/*** Glade's Support Function ***/ +GtkWidget* lookup_widget (GtkWidget *widget, const gchar *widget_name) + GtkWidget *found_widget; + found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget), + g_warning ("smartear: Widget not found: %s", widget_name); +void on_delay_changed (GtkEditable *editable, gpointer user_data) + gchar *text = gtk_editable_get_chars(editable, 0, -1); + message_delay = atoi(text); +void on_focus_toggled (GtkToggleButton *togglebutton, gpointer user_data) + focused_quiet = gtk_toggle_button_get_active(togglebutton); +void on_timer_toggled (GtkToggleButton *togglebutton, gpointer user_data) + smartear_timers = gtk_toggle_button_get_active(togglebutton); +void on_cell_edited(GtkCellRendererText *cell, gchar *path, gchar *text, gpointer data) + gint col = GPOINTER_TO_INT(data); + struct smartear_entry *entry; + if (!gtk_tree_model_get_iter_from_string(treemodel, &iter, path)) + gtk_tree_model_get(treemodel, &iter, DATA_COL, &entry, -1); + entry->name = g_strdup(text); +void on_treeselect_changed(GtkTreeSelection *selection, gpointer data) +void on_new_clicked (GtkButton *button, gpointer user_data) + new_entry(sound_new_with_name("")); +void on_delete_clicked (GtkButton *button, gpointer user_data) + if (IS_DEFAULT(selected_entry)) + sounds_list = g_slist_remove(sounds_list, selected_entry); + sound_free(selected_entry, TRUE); + if (!gtk_tree_model_iter_next(treemodel, &iter)) { + GtkTreePath *path = gtk_tree_model_get_path(treemodel, &selected_iter); + gtk_tree_path_prev(path); + gtk_tree_model_get_iter(treemodel, &iter, path); + gtk_tree_path_free(path); + g_signal_handlers_block_by_func(G_OBJECT(treeselect), on_treeselect_changed, 0); + gtk_list_store_remove(GTK_LIST_STORE(treemodel), &selected_iter); + gtk_tree_selection_select_iter(treeselect, &iter); + g_signal_handlers_unblock_by_func(G_OBJECT(treeselect), on_treeselect_changed, 0); +void on_edit_clicked (GtkButton *button, gpointer user_data) + edit_win = create_edit_win(); + gtk_widget_show_all(edit_win); + populate_edit_win(selected_entry); +void on_save_clicked (GtkButton *button, gpointer user_data) +void on_revert_clicked (GtkButton *button, gpointer user_data) + gtk_widget_destroy(edit_win); + gtk_list_store_clear(GTK_LIST_STORE(treemodel)); + populate_list(GTK_LIST_STORE(treemodel)); +void on_browse_ok_clicked (GtkButton *button, gpointer user_data) + gtk_entry_set_text(GTK_ENTRY(file_browse_entry), + gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_browse))); +void on_file_browse_destroy (GtkObject *object, gpointer user_data) + file_browse_entry = NULL; +void on_browse_clicked (GtkButton *button, gchar *user_data) + file_browse = create_file_browse(); + gtk_widget_show(file_browse); + file_browse_entry = EFIND(user_data); + text = gtk_editable_get_chars(GTK_EDITABLE(file_browse_entry), 0, -1); + gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_browse), text); +void on_im_browse_clicked (GtkButton *button, gpointer user_data) + on_browse_clicked(button, "im_sound_entry"); +void on_signon_browse_clicked (GtkButton *button, gpointer user_data) + on_browse_clicked(button, "signon_sound_entry"); +void on_unidle_browse_clicked (GtkButton *button, gpointer user_data) + on_browse_clicked(button, "unidle_sound_entry"); +void on_unaway_browse_clicked (GtkButton *button, gpointer user_data) + on_browse_clicked(button, "unaway_sound_entry"); +void on_test_clicked (GtkButton *button, gchar *user_data) + gtk_editable_get_chars(GTK_EDITABLE(EFIND(user_data)), 0, -1); + play_sound_alias(text, NULL); +void on_im_test_clicked (GtkButton *button, gpointer user_data) + on_test_clicked(button, "im_sound_entry"); +void on_signon_test_clicked (GtkButton *button, gpointer user_data) + on_test_clicked(button, "signon_sound_entry"); +void on_unidle_test_clicked (GtkButton *button, gpointer user_data) + on_test_clicked(button, "unidle_sound_entry"); +void on_unaway_test_clicked (GtkButton *button, gpointer user_data) + on_test_clicked(button, "unaway_sound_entry"); +void on_apply_clicked (GtkButton *button, gpointer user_data) + struct smartear_entry *entry, tmp; + entry = selected_entry; + g_free(entry->sound[0]); + g_free(entry->sound[1]); + g_free(entry->sound[2]); + g_free(entry->sound[3]); + gtk_editable_get_chars(GTK_EDITABLE(EFIND("name_entry")), 0, -1); + gtk_option_menu_get_history(GTK_OPTION_MENU(EFIND("type_option"))) == 0 + entry->sound[EVENT_M] = + gtk_editable_get_chars(GTK_EDITABLE(EFIND("im_sound_entry")), 0, -1); + entry->sound[EVENT_S] = + gtk_editable_get_chars(GTK_EDITABLE(EFIND("signon_sound_entry")), 0, -1); + entry->sound[EVENT_I] = + gtk_editable_get_chars(GTK_EDITABLE(EFIND("unidle_sound_entry")), 0, -1); + entry->sound[EVENT_A] = + gtk_editable_get_chars(GTK_EDITABLE(EFIND("unaway_sound_entry")), 0, -1); + if ((lp = g_slist_find_custom(sounds_list, entry, (GCompareFunc)sound_cmp))) { + sound_free((struct smartear_entry*)lp->data, FALSE); + *(struct smartear_entry*)lp->data = *entry; + struct smartear_entry *copy = g_new(struct smartear_entry, 1); +void on_edit_win_destroy (GtkObject *object, gpointer user_data) + gtk_widget_set_sensitive(GTK_WIDGET(treeview), TRUE); +void on_config_destroy (GtkObject *object, gpointer user_data) +/*** Util Functions ***/ +static void new_entry(struct smartear_entry *entry) + GtkTreeViewColumn *name_column; + sounds_list = g_slist_append(sounds_list, entry); + gtk_list_store_insert_after(GTK_LIST_STORE(treemodel), &iter, &selected_iter); + gtk_list_store_append(GTK_LIST_STORE(treemodel), &iter); + path = gtk_tree_model_get_path(treemodel, &iter); + list_set(GTK_LIST_STORE(treemodel), &iter, entry); + g_signal_handlers_block_by_func(G_OBJECT(treeselect), on_treeselect_changed, 0); + path = gtk_tree_model_get_path(treemodel, &iter); + name_column = gtk_tree_view_get_column(treeview, NAME_COL); + if (entry->name && entry->name[0]) + gtk_tree_view_set_cursor(treeview, path, 0, FALSE); + gtk_tree_view_set_cursor(treeview, path, name_column, TRUE); + gtk_tree_path_free(path); + g_signal_handlers_unblock_by_func(G_OBJECT(treeselect), on_treeselect_changed, 0); +static gboolean get_selection(void) + if (!gtk_tree_selection_get_selected(treeselect, &treemodel, &selected_iter)) + gtk_tree_model_get(treemodel, &selected_iter, DATA_COL, &selected_entry, -1); +void clear_selection(void) + if (gtk_tree_selection_get_selected(treeselect, &treemodel, &selected_iter)) { + gtk_tree_selection_unselect_iter(treeselect, &selected_iter); +static void list_set(GtkListStore *store, GtkTreeIter *iter, struct smartear_entry *entry) + gtk_list_store_set(store, iter, DATA_COL, entry, -1); +static char *my_normalize(const char *input) + static char buf[BUF_LEN]; + g_return_val_if_fail((input != NULL), NULL); + beg = str = g_strdup(input); + while (*str && i <= BUF_LEN) { +static struct smartear_entry *sound_new_with_name(gchar *name) + struct smartear_entry tmp = default_entry; + return sound_dup(&tmp); +static struct smartear_entry *sound_dup(struct smartear_entry *entry) + struct smartear_entry *edup = g_new(struct smartear_entry, 1); + edup->type = entry->type; + edup->name = g_strdup(my_normalize(entry->name)); + for (i = 0; i < EVENT_NUM; i++) { + edup->sound[i] = g_strdup(entry->sound[i]); +static void sound_free(struct smartear_entry *entry, int free_entry) + g_free(entry->sound[0]); + g_free(entry->sound[1]); + g_free(entry->sound[2]); + g_free(entry->sound[3]); +static void soundlist_free(void) + for (lp = sounds_list; lp; lp = g_slist_next(lp)) + sound_free((struct smartear_entry*)lp->data, TRUE); + g_slist_free(sounds_list); +static gint sound_cmp(gconstpointer p1, gconstpointer p2) + struct smartear_entry *entry1 = (struct smartear_entry*)p1; + struct smartear_entry *entry2 = (struct smartear_entry*)p2; + if (!entry1 || IS_DEFAULT(entry1)) + if (!entry2 || IS_DEFAULT(entry2)) + /* only compare types if both are nonzero */ + if (entry1->type != entry2->type && + entry1->type != 0 && entry2->type != 0) + return (entry1->type - entry2->type); + return (g_strcasecmp(entry1->name, entry2->name)); +static void populate_edit_win(struct smartear_entry *entry) + gtk_entry_set_text(GTK_ENTRY(EFIND("name_entry")), entry->name); + gtk_entry_set_text(GTK_ENTRY(EFIND("im_sound_entry")), + entry->sound[EVENT_M]); + gtk_entry_set_text(GTK_ENTRY(EFIND("signon_sound_entry")), + entry->sound[EVENT_S]); + gtk_entry_set_text(GTK_ENTRY(EFIND("unidle_sound_entry")), + entry->sound[EVENT_I]); + gtk_entry_set_text(GTK_ENTRY(EFIND("unaway_sound_entry")), + entry->sound[EVENT_A]); + gtk_option_menu_set_history(GTK_OPTION_MENU(EFIND("type_option")), + (entry->type == 'B' ? 0 : 1)); + if (IS_DEFAULT(entry)) { + gtk_widget_set_sensitive(EFIND("name_entry"), FALSE); + gtk_widget_set_sensitive(EFIND("type_menu"), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(treeview), FALSE); +static void set_option_entries(void) + GtkSpinButton *delay_spin = GTK_SPIN_BUTTON(FIND("delay_spin")); + GtkToggleButton *focus_but = GTK_TOGGLE_BUTTON(FIND("focus_but")); + GtkToggleButton *timer_but = GTK_TOGGLE_BUTTON(FIND("timer_but")); + g_signal_handlers_block_by_func(G_OBJECT(delay_spin), on_delay_changed, 0); + g_signal_handlers_block_by_func(G_OBJECT(focus_but), on_focus_toggled, 0); + g_signal_handlers_block_by_func(G_OBJECT(timer_but), on_timer_toggled, 0); + gtk_spin_button_set_value(delay_spin, (gdouble)message_delay); + gtk_toggle_button_set_active(focus_but, focused_quiet); + gtk_toggle_button_set_active(timer_but, smartear_timers); + g_signal_handlers_unblock_by_func(G_OBJECT(delay_spin), on_delay_changed, 0); + g_signal_handlers_unblock_by_func(G_OBJECT(focus_but), on_focus_toggled, 0); + g_signal_handlers_unblock_by_func(G_OBJECT(timer_but), on_timer_toggled, 0); +static void update_list(void) + list_set(GTK_LIST_STORE(treemodel), &selected_iter, selected_entry); +static gchar* get_basename(gchar *path) + static gchar base[BUF_LEN]; + if (!path || !path[0]) { + if ((p = strrchr(path, '/'))) { + strncpy(base, p, sizeof(base)); +/*** Init Functions ***/ +static void populate_list(GtkListStore *store) + sounds_list = g_slist_sort(sounds_list, (GCompareFunc)sound_cmp); + for (lp = sounds_list; lp; lp = g_slist_next(lp)) { + struct smartear_entry *entry = (struct smartear_entry*)lp->data; + gtk_list_store_append(store, &iter); + list_set(store, &iter, entry); + if (entry == selected_entry) +static void render_type(GtkTreeViewColumn *column, GtkCellRenderer *cell, + GtkTreeModel *model, GtkTreeIter *iter, gpointer data) + struct smartear_entry *entry; + gtk_tree_model_get(model, iter, DATA_COL, &entry, -1); + g_object_set(cell, "text", "", NULL); + g_object_set(cell, "text", entry->type == 'B' ? "Buddy" : "Group", NULL); +static void render_name(GtkTreeViewColumn *column, GtkCellRenderer *cell, + GtkTreeModel *model, GtkTreeIter *iter, gpointer data) + struct smartear_entry *entry; + gtk_tree_model_get(model, iter, DATA_COL, &entry, -1); + g_object_set(cell, "text", entry->name, NULL); +static void render_when(GtkTreeViewColumn *column, GtkCellRenderer *cell, + GtkTreeModel *model, GtkTreeIter *iter, gpointer data) + struct smartear_entry *entry; + gint which = GPOINTER_TO_INT(data); + gtk_tree_model_get(model, iter, DATA_COL, &entry, -1); + g_object_set(cell, "text", get_basename(entry->sound[which]), NULL); +static gint list_cmp(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer d) + struct smartear_entry *entry1, *entry2; + gtk_tree_model_get(model, a, DATA_COL, &entry1, -1); + gtk_tree_model_get(model, b, DATA_COL, &entry2, -1); + return sound_cmp(entry1, entry2); +static void setup_list(void) + treeview = GTK_TREE_VIEW(FIND("treeview")); + store = gtk_list_store_new(1, G_TYPE_POINTER); + gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); // treeview has it's own copy. + treemodel = gtk_tree_view_get_model(treeview); + gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(treemodel), + (GtkTreeIterCompareFunc)&list_cmp, 0, 0); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(treemodel), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING); + treeselect = gtk_tree_view_get_selection(treeview); + gtk_tree_selection_set_mode(treeselect, GTK_SELECTION_SINGLE); + g_signal_connect(G_OBJECT(treeselect), "changed", + G_CALLBACK(on_treeselect_changed), NULL); + cell = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_data_func(treeview, -1, "Type", + cell, render_type, 0, 0); + cell = gtk_cell_renderer_text_new(); + g_signal_connect(G_OBJECT(cell), "edited", + G_CALLBACK(on_cell_edited), GINT_TO_POINTER(NAME_COL)); + g_object_set(G_OBJECT(cell), "editable", TRUE, NULL); + gtk_tree_view_insert_column_with_data_func(treeview, -1, "Name", + cell, render_name, 0, 0); + cell = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_data_func(treeview, -1, "On Message", + cell, render_when, GINT_TO_POINTER(EVENT_M), 0); + cell = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_data_func(treeview, -1, "On Signon", + cell, render_when, GINT_TO_POINTER(EVENT_S), 0); + cell = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_data_func(treeview, -1, "On Unidle", + cell, render_when, GINT_TO_POINTER(EVENT_I), 0); + cell = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_data_func(treeview, -1, "On Unaway", + cell, render_when, GINT_TO_POINTER(EVENT_A), 0); +static void smartear_save(void) + char file[BUF_LEN] = "smartear.rc - you have no home dir"; + struct smartear_entry *entry; + if (purple_user_dir()) { + g_snprintf(file, sizeof(file), "%s/smartear.rc", purple_user_dir()); + g_warning("couldn't open %s\n", file); + fprintf(fp, "version %d\n", RCFILE_VERSION); + fprintf(fp, "smartear_timers %d\n", smartear_timers); + fprintf(fp, "delay %d\n", message_delay); + fprintf(fp, "focused_quiet %d\n", focused_quiet); + /* save empties as a single space so scanf doesn't get confused */ +#define SAVE_STR(str) (IS_EMPTY(str) ? RC_EMPTY_SOUND : str) + for (lp = sounds_list; lp; lp = g_slist_next(lp)) { + entry = (struct smartear_entry*)lp->data; + fprintf(fp, "%c {%s} {%s} {%s} {%s} {%s}\n", + entry->type, entry->name, + SAVE_STR(entry->sound[EVENT_M]), + SAVE_STR(entry->sound[EVENT_S]), + SAVE_STR(entry->sound[EVENT_I]), + SAVE_STR(entry->sound[EVENT_A])); +static void smartear_load(void) + char file[BUF_LEN] = "smartear.rc - you have no home dir"; + struct smartear_entry *entry; + gboolean has_default = FALSE; + int rcfile_version = 1; + if (purple_user_dir()) { + g_snprintf(file, sizeof(file), "%s/smartear.rc", purple_user_dir()); + g_warning("smartear: couldn't open %s\n", file); + sounds_list = g_slist_append(sounds_list, sound_dup(&default_entry)); + while ((fgets(buf, sizeof(buf), fp))) { + if (sscanf(buf, "version %d", &tmp) == 1) + else if (sscanf(buf, "smarterear_timers %d", &tmp) == 1) + else if (sscanf(buf, "smartear_timers %d", &tmp) == 1) + else if (sscanf(buf, "delay %d", &tmp) == 1) + else if (sscanf(buf, "focused_quiet %d", &tmp) == 1) + else if (rcfile_version == 1) { + if (sscanf(buf, "%c {%[^}]} {%[^}]} {%[^}]}", + &type, name, flags, sound) == 4) { + if (type != 'G' && type != 'B') { + g_warning("smartear: rc no such type: %c", type); + entry = g_new(struct smartear_entry, 1); + entry->name = g_strdup(my_normalize(name)); + if (strchr(flags, 'M')) entry->sound[EVENT_M] = g_strdup(sound); + else entry->sound[EVENT_M] = g_strdup(""); + if (strchr(flags, 'S')) entry->sound[EVENT_S] = g_strdup(sound); + else entry->sound[EVENT_S] = g_strdup(""); + if (strchr(flags, 'I')) entry->sound[EVENT_I] = g_strdup(sound); + else entry->sound[EVENT_I] = g_strdup(""); + if (strchr(flags, 'A')) entry->sound[EVENT_A] = g_strdup(sound); + else entry->sound[EVENT_A] = g_strdup(""); + sounds_list = g_slist_append(sounds_list, entry); + if (IS_DEFAULT(entry)) { + default_entry = *entry; + g_warning("smartear: rc1 syntax error: %s", buf); + else if (rcfile_version == 2) { + char sound[EVENT_NUM][BUF_LEN]; + if (sscanf(buf, "%c {%[^}]} {%[^}]} {%[^}]} {%[^}]} {%[^}]}", + &type, name, sound[EVENT_M], sound[EVENT_S], + sound[EVENT_I], sound[EVENT_A]) == 6) { + if (type != 'G' && type != 'B') { + g_warning("smartear: rc no such type: %c", type); + for (i = 0; i < EVENT_NUM; i++) { + if (strcmp(sound[i], RC_EMPTY_SOUND) == 0) + entry = g_new(struct smartear_entry, 1); + entry->name = g_strdup(my_normalize(name)); + entry->sound[EVENT_M] = g_strdup(sound[EVENT_M]); + entry->sound[EVENT_S] = g_strdup(sound[EVENT_S]); + entry->sound[EVENT_I] = g_strdup(sound[EVENT_I]); + entry->sound[EVENT_A] = g_strdup(sound[EVENT_A]); + sounds_list = g_slist_append(sounds_list, entry); + if (IS_DEFAULT(entry)) { + default_entry = *entry; + g_warning("smartear: rc2 syntax error: %s", buf); + g_warning("smartear: rc syntax error: %s", buf); + sounds_list = g_slist_append(sounds_list, sound_dup(&default_entry)); +/*** Purple callbacks ***/ +static struct message_data *find_message_by_name(PurpleAccount *account, const char *pname) + struct message_data *msg = NULL; + char *name = g_strdup(my_normalize(pname)); + for (lp = messages_list; lp; lp = g_slist_next(lp)) { + msg = (struct message_data*)lp->data; + if (msg->account == account + && g_strcasecmp(msg->buddy, name) == 0) +static void message_timer_remove(struct message_data *msg) + if (msg && msg->timer) { + g_source_remove(msg->timer->id); +static void message_free(struct message_data *msg) + message_timer_remove(msg); +static void messagelist_free() + for (lp = messages_list; lp; lp = g_slist_next(lp)) + message_free((struct message_data*)lp->data); + g_slist_free(messages_list); +static void on_smartear_clicked(PurpleBlistNode* node, gpointer data) + struct smartear_entry tmp, *entry; + edit_win = create_edit_win(); + gtk_widget_show_all(edit_win); + else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { + tmp.name = ((PurpleBuddy*)node)->name; + purple_debug(PURPLE_DEBUG_INFO, "smartear", "adding buddy %s", tmp.name); + else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + tmp.name = ((PurpleContact*)node)->alias; + tmp.name = ((PurpleContact*)node)->priority->name; + purple_debug(PURPLE_DEBUG_INFO, "smartear", "adding contact %s", tmp.name); + else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + tmp.name = ((PurpleGroup*)node)->name; + g_warning("group %p, %p %s", node, tmp.name, tmp.name); + purple_debug(PURPLE_DEBUG_INFO, "smartear", "adding group %s", tmp.name); + if ((lp = g_slist_find_custom(sounds_list, &tmp, (GCompareFunc)sound_cmp))) { + entry = (struct smartear_entry*)lp->data; + populate_edit_win(entry); +static void play_sound_alias(char *sound, PurpleAccount* account) + struct smartear_entry tmp, *entry; + /* sound aliases: mostly so you can put (Default) for sound, and it'll + if ((lp = g_slist_find_custom(sounds_list, &tmp, (GCompareFunc)sound_cmp))) { + entry = (struct smartear_entry*)lp->data; + play_sound_alias(entry->sound[EVENT_M], account); + purple_sound_play_file(sound, account); +void play_matching_sound(PurpleBuddy *buddy, int event) + struct smartear_entry *entry; + char *name = buddy ? g_strdup(my_normalize(buddy->name)) : NULL; + PurpleGroup *g = buddy ? purple_buddy_get_group(buddy) : NULL; + for (lp = sounds_list; lp; lp = g_slist_next(lp)) { + entry = (struct smartear_entry*)lp->data; + if (!entry->sound[event]) + if (entry->type == 'B' && name + && g_strcasecmp(name, entry->name) == 0) { + sound = entry->sound[event]; + /* found a buddy match.. this takes precedence, so stop */ + if (entry->type == 'G' && g + && g_strcasecmp(my_normalize(g->name), entry->name) == 0) { + sound = entry->sound[event]; + /* keep going... buddy overrides group */ + if (IS_DEFAULT(entry)) { + sound = entry->sound[event]; + /* keep going.. other 2 override default */ + if (!IS_EMPTY(sound)) { + purple_debug(PURPLE_DEBUG_INFO, "smartear", + "found %s for %s on event %d\n", sound, name, event); + play_sound_alias(sound, purple_buddy_get_account(buddy)); + purple_debug(PURPLE_DEBUG_INFO, "smartear", + "no sound found for %s on event %d\n", name, event); +static gint play_sound_timer_hook(gpointer data) + struct message_data *msg = (struct message_data *)data; + if(!msg || !msg->timer) { + g_warning("smartear: timer called without being active!\n"); + play_matching_sound(purple_find_buddy(msg->account, msg->buddy), msg->timer->event); +static gboolean on_im_recv(PurpleAccount *account, char *who, char *what, gint32 flags, void *junk) + struct message_data *msg; + PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account); + PidginConversation *gtkconv = conv ? PIDGIN_CONVERSATION(conv) : NULL; + /* FIXME: message list is never trimmed. However, if we DO trim it, + * we need to consider race conditions on the msg field of the timer_hook */ + if ((msg = find_message_by_name(account, who))) { + /* only install a timer if there's no active timers and we've just + * sent a message. If we haven't just sent a message, then + * a beep will go off anyways when the IM arrives. */ + if(smartear_timers && !msg->timer + && msg->last_sent + message_delay > now) { + msg->timer = g_new0(struct timer_data, 1); + msg->timer->event = EVENT_M; + msg->timer->id = g_timeout_add(message_delay*1000, + play_sound_timer_hook, msg); + if (msg->last_active + message_delay > now && conv) { + purple_debug(PURPLE_DEBUG_INFO, "smartear", + "received IM from %s, but only %d/%d seconds passed.\n", + who, now - msg->last_active, message_delay); + msg = g_new0(struct message_data, 1); + msg->account = account; + msg->buddy = g_strdup(my_normalize(who)); + messages_list = g_slist_append(messages_list, msg); + msg->last_active = now; + if (focused_quiet && gtkconv && GTK_WIDGET_HAS_FOCUS(gtkconv->entry)) + return FALSE; /* don't play sounds for the focused convo */ + if (gtkconv && !gtkconv->make_sound) + play_matching_sound(purple_find_buddy(msg->account, msg->buddy), EVENT_M); +static void on_im_send(PurpleAccount *account, char *who, char *what, void *junk) + struct message_data *msg; + if ((msg = find_message_by_name(account, who))) { + message_timer_remove(msg); + msg = g_new0(struct message_data, 1); + msg->account = account; + msg->buddy = g_strdup(my_normalize(who)); + messages_list = g_slist_append(messages_list, msg); + msg->last_active = now; +static void on_buddy_signon(PurpleBuddy *buddy, void *data) + play_matching_sound(buddy, EVENT_S); +static void on_buddy_back(PurpleBuddy *buddy, void *data) + play_matching_sound(buddy, EVENT_A); +static void on_buddy_unidle(PurpleBuddy *buddy, void *data) + play_matching_sound(buddy, EVENT_I); +static void on_signon(PurpleConnection *gc, void *m) +static void on_signoff(PurpleConnection *gc, void *m) +static void on_blist_node_extended_menu(PurpleBlistNode *node, GList **menu) + PurpleMenuAction *menu_action; + menu_action = purple_menu_action_new(_("Edit SmartEar Entry"), + PURPLE_CALLBACK(on_smartear_clicked), NULL, NULL); + *menu = g_list_append(*menu, menu_action); +static gboolean purple_plugin_init(PurplePlugin *plugin) + void *blist_handle = purple_blist_get_handle(); + void *conv_handle = purple_conversations_get_handle(); + purple_signal_connect(blist_handle, "blist-node-extended-menu", + plugin, PURPLE_CALLBACK(on_blist_node_extended_menu), NULL); + purple_signal_connect(blist_handle, "buddy-signed-on", + plugin, PURPLE_CALLBACK(on_buddy_signon), NULL); + purple_signal_connect(blist_handle, "buddy-back", + plugin, PURPLE_CALLBACK(on_buddy_back), NULL); + purple_signal_connect(blist_handle, "buddy-unidle", + plugin, PURPLE_CALLBACK(on_buddy_unidle), NULL); + purple_signal_connect(conv_handle, "received-im-msg", + plugin, PURPLE_CALLBACK(on_im_recv), NULL); + purple_signal_connect(conv_handle, "sent-im-msg", + plugin, PURPLE_CALLBACK(on_im_send), NULL); + purple_signal_connect(purple_connections_get_handle(), "signed-on", + plugin, PURPLE_CALLBACK(on_signon), NULL); + purple_signal_connect(purple_connections_get_handle(), "signed-off", + plugin, PURPLE_CALLBACK(on_signoff), NULL); +static gboolean purple_plugin_remove(PurplePlugin *h) +static GtkWidget *purple_plugin_config_gtk(PurplePlugin *plugin) + config = create_config(); + gtk_widget_show_all(config); +static PurplePluginUiInfo ui_info = { + purple_plugin_config_gtk, + 0 /* page_num (reserved) */ +static PurplePluginInfo info = + PURPLE_PLUGIN_STANDARD, + PURPLE_PRIORITY_DEFAULT, + "Matt Perry <guy@fscked.org>", +static void init_plugin(PurplePlugin *plugin) + bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + info.name= _("Smart Ear"); + info.summary = _("Assign different sounds to different buddies and groups"); + _("Allows you to assign different sounds to play for different buddies " + "or whole groups of buddies. Smart Ear allows you to opt to play " + "sounds when a buddy sends you an IM, signs on, returns from away " + "or idle, or any combination of these, so you'll know by the sound " + "what the important people are doing."); +PURPLE_INIT_PLUGIN(smartear, init_plugin, info); --- a/smartear/smartear-interface.c Tue Jul 03 04:45:10 2007 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,546 +0,0 @@
-# include "../pp_config.h"
-#define GLADE_HOOKUP_OBJECT(component,widget,name) \
- g_object_set_data_full (G_OBJECT (component), name, \
- gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)
-#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \
- g_object_set_data (G_OBJECT (component), name, widget)
- GtkWidget *config_vbox;
- GtkObject *delay_spin_adj;
- GtkWidget *scrolledwindow2;
- GtkWidget *hbuttonbox1;
- config_vbox = gtk_vbox_new (FALSE, 0);
- gtk_widget_set_size_request (config_vbox, -1, 640);
- frame1 = gtk_frame_new (NULL);
- gtk_widget_show (frame1);
- gtk_box_pack_start (GTK_BOX (config_vbox), frame1, FALSE, TRUE, 0);
- gtk_container_set_border_width (GTK_CONTAINER (frame1), 5);
- table1 = gtk_table_new (3, 2, FALSE);
- gtk_widget_show (table1);
- gtk_container_add (GTK_CONTAINER (frame1), table1);
- gtk_container_set_border_width (GTK_CONTAINER (table1), 5);
- gtk_table_set_row_spacings (GTK_TABLE (table1), 10);
- gtk_table_set_col_spacings (GTK_TABLE (table1), 10);
- label9 = gtk_label_new ("Time delay between playing sounds for a particular buddy:");
- gtk_widget_show (label9);
- gtk_table_attach (GTK_TABLE (table1), label9, 0, 1, 0, 1,
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
- (GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label9), 0, 0.5);
- delay_spin_adj = gtk_adjustment_new (60, 0, 1000000, 1, 10, 10);
- delay_spin = gtk_spin_button_new (GTK_ADJUSTMENT (delay_spin_adj), 1, 0);
- gtk_widget_show (delay_spin);
- gtk_table_attach (GTK_TABLE (table1), delay_spin, 1, 2, 0, 1,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (delay_spin), GTK_UPDATE_IF_VALID);
- focus_but = gtk_check_button_new_with_mnemonic ("Don't play sounds for the conversation that has focus.");
- gtk_widget_show (focus_but);
- gtk_table_attach (GTK_TABLE (table1), focus_but, 0, 2, 1, 2,
- (GtkAttachOptions) (GTK_FILL),
- (GtkAttachOptions) (0), 0, 0);
- timer_but = gtk_check_button_new_with_mnemonic ("Also play sounds if you don't respond to a particular IM within a delay period.");
- gtk_widget_show (timer_but);
- gtk_table_attach (GTK_TABLE (table1), timer_but, 0, 2, 2, 3,
- (GtkAttachOptions) (GTK_FILL),
- (GtkAttachOptions) (0), 0, 0);
- label8 = gtk_label_new ("Options");
- gtk_widget_show (label8);
- gtk_frame_set_label_widget (GTK_FRAME (frame1), label8);
- frame2 = gtk_frame_new (NULL);
- gtk_widget_show (frame2);
- gtk_box_pack_start (GTK_BOX (config_vbox), frame2, TRUE, TRUE, 0);
- gtk_container_set_border_width (GTK_CONTAINER (frame2), 5);
- table2 = gtk_table_new (2, 3, FALSE);
- gtk_widget_show (table2);
- gtk_container_add (GTK_CONTAINER (frame2), table2);
- gtk_container_set_border_width (GTK_CONTAINER (table2), 5);
- gtk_table_set_row_spacings (GTK_TABLE (table2), 10);
- gtk_table_set_col_spacings (GTK_TABLE (table2), 10);
- delete_but = gtk_button_new_from_stock ("gtk-delete");
- gtk_widget_show (delete_but);
- gtk_table_attach (GTK_TABLE (table2), delete_but, 2, 3, 1, 2,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- GTK_WIDGET_SET_FLAGS (delete_but, GTK_CAN_DEFAULT);
- new_but = gtk_button_new_from_stock ("gtk-new");
- gtk_widget_show (new_but);
- gtk_table_attach (GTK_TABLE (table2), new_but, 0, 1, 1, 2,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- GTK_WIDGET_SET_FLAGS (new_but, GTK_CAN_DEFAULT);
- scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
- gtk_widget_show (scrolledwindow2);
- gtk_table_attach (GTK_TABLE (table2), scrolledwindow2, 0, 3, 0, 1,
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
- treeview = gtk_tree_view_new ();
- gtk_widget_show (treeview);
- gtk_container_add (GTK_CONTAINER (scrolledwindow2), treeview);
- edit_but = gtk_button_new_from_stock ("gtk-properties");
- gtk_widget_show (edit_but);
- gtk_table_attach (GTK_TABLE (table2), edit_but, 1, 2, 1, 2,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- label7 = gtk_label_new ("Entries");
- gtk_widget_show (label7);
- gtk_frame_set_label_widget (GTK_FRAME (frame2), label7);
- hbuttonbox1 = gtk_hbutton_box_new ();
- gtk_widget_show (hbuttonbox1);
- gtk_box_pack_start (GTK_BOX (config_vbox), hbuttonbox1, FALSE, TRUE, 0);
- gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox1), 5);
- gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_SPREAD);
- revert_but = gtk_button_new_from_stock ("gtk-revert-to-saved");
- gtk_widget_show (revert_but);
- gtk_container_add (GTK_CONTAINER (hbuttonbox1), revert_but);
- GTK_WIDGET_SET_FLAGS (revert_but, GTK_CAN_DEFAULT);
- save_but = gtk_button_new_from_stock ("gtk-save");
- gtk_widget_show (save_but);
- gtk_container_add (GTK_CONTAINER (hbuttonbox1), save_but);
- GTK_WIDGET_SET_FLAGS (save_but, GTK_CAN_DEFAULT);
- g_signal_connect ((gpointer) config_vbox, "destroy",
- G_CALLBACK (on_config_destroy),
- g_signal_connect ((gpointer) delay_spin, "changed",
- G_CALLBACK (on_delay_changed),
- g_signal_connect ((gpointer) focus_but, "toggled",
- G_CALLBACK (on_focus_toggled),
- g_signal_connect ((gpointer) timer_but, "toggled",
- G_CALLBACK (on_timer_toggled),
- g_signal_connect ((gpointer) delete_but, "clicked",
- G_CALLBACK (on_delete_clicked),
- g_signal_connect ((gpointer) new_but, "clicked",
- G_CALLBACK (on_new_clicked),
- g_signal_connect ((gpointer) edit_but, "clicked",
- G_CALLBACK (on_edit_clicked),
- g_signal_connect ((gpointer) revert_but, "clicked",
- G_CALLBACK (on_revert_clicked),
- g_signal_connect ((gpointer) save_but, "clicked",
- G_CALLBACK (on_save_clicked),
- /* Store pointers to all widgets, for use by lookup_widget(). */
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, config_vbox, "config_vbox");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, frame1, "frame1");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, table1, "table1");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, label9, "label9");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, delay_spin, "delay_spin");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, focus_but, "focus_but");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, timer_but, "timer_but");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, label8, "label8");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, frame2, "frame2");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, table2, "table2");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, delete_but, "delete_but");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, new_but, "new_but");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, scrolledwindow2, "scrolledwindow2");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, treeview, "treeview");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, edit_but, "edit_but");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, label7, "label7");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, hbuttonbox1, "hbuttonbox1");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, revert_but, "revert_but");
- GLADE_HOOKUP_OBJECT_NO_REF (config_vbox, save_but, "save_but");
-create_file_browse (void)
- GtkWidget *file_browse;
- GtkWidget *cancel_button1;
- file_browse = gtk_file_selection_new ("Select Sound");
- gtk_container_set_border_width (GTK_CONTAINER (file_browse), 10);
- gtk_window_set_destroy_with_parent (GTK_WINDOW (file_browse), TRUE);
- gtk_window_set_type_hint (GTK_WINDOW (file_browse), GDK_WINDOW_TYPE_HINT_DIALOG);
- ok_button1 = GTK_FILE_SELECTION (file_browse)->ok_button;
- gtk_widget_show (ok_button1);
- GTK_WIDGET_SET_FLAGS (ok_button1, GTK_CAN_DEFAULT);
- cancel_button1 = GTK_FILE_SELECTION (file_browse)->cancel_button;
- gtk_widget_show (cancel_button1);
- GTK_WIDGET_SET_FLAGS (cancel_button1, GTK_CAN_DEFAULT);
- g_signal_connect ((gpointer) file_browse, "destroy",
- G_CALLBACK (on_file_browse_destroy),
- g_signal_connect ((gpointer) ok_button1, "clicked",
- G_CALLBACK (on_browse_ok_clicked),
- g_signal_connect_swapped ((gpointer) ok_button1, "clicked",
- G_CALLBACK (gtk_widget_destroy),
- GTK_OBJECT (file_browse));
- g_signal_connect_swapped ((gpointer) cancel_button1, "clicked",
- G_CALLBACK (gtk_widget_destroy),
- GTK_OBJECT (file_browse));
- /* Store pointers to all widgets, for use by lookup_widget(). */
- GLADE_HOOKUP_OBJECT_NO_REF (file_browse, file_browse, "file_browse");
- GLADE_HOOKUP_OBJECT_NO_REF (file_browse, ok_button1, "ok_button1");
- GLADE_HOOKUP_OBJECT_NO_REF (file_browse, cancel_button1, "cancel_button1");
- GtkWidget *type_option;
- GtkWidget *hbuttonbox2;
- GtkWidget *applysave_but;
- GtkWidget *unaway_sound_entry;
- GtkWidget *unidle_sound_entry;
- GtkWidget *signon_sound_entry;
- GtkWidget *unaway_test_but;
- GtkWidget *unidle_test_but;
- GtkWidget *signon_test_but;
- GtkWidget *unaway_browse_but;
- GtkWidget *unidle_browse_but;
- GtkWidget *signon_browse_but;
- GtkWidget *im_browse_but;
- GtkWidget *im_sound_entry;
- GtkWidget *im_test_but;
- edit_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_widget_set_size_request (edit_win, 600, 300);
- gtk_window_set_title (GTK_WINDOW (edit_win), "Edit Entry");
- gtk_window_set_default_size (GTK_WINDOW (edit_win), 600, 300);
- vbox1 = gtk_vbox_new (FALSE, 0);
- gtk_widget_show (vbox1);
- gtk_container_add (GTK_CONTAINER (edit_win), vbox1);
- hbox1 = gtk_hbox_new (FALSE, 5);
- gtk_widget_show (hbox1);
- gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, TRUE, 10);
- gtk_container_set_border_width (GTK_CONTAINER (hbox1), 5);
- label29 = gtk_label_new ("Name:");
- gtk_widget_show (label29);
- gtk_box_pack_start (GTK_BOX (hbox1), label29, FALSE, FALSE, 0);
- gtk_misc_set_alignment (GTK_MISC (label29), 1, 0.5);
- name_entry = gtk_entry_new ();
- gtk_widget_show (name_entry);
- gtk_box_pack_start (GTK_BOX (hbox1), name_entry, TRUE, TRUE, 0);
- label30 = gtk_label_new ("Type:");
- gtk_widget_show (label30);
- gtk_box_pack_start (GTK_BOX (hbox1), label30, FALSE, TRUE, 0);
- gtk_misc_set_alignment (GTK_MISC (label30), 1, 0.5);
- type_option = gtk_option_menu_new ();
- gtk_widget_show (type_option);
- gtk_box_pack_start (GTK_BOX (hbox1), type_option, FALSE, FALSE, 0);
- menu1 = gtk_menu_new ();
- item_buddy = gtk_menu_item_new_with_mnemonic ("Buddy");
- gtk_widget_show (item_buddy);
- gtk_container_add (GTK_CONTAINER (menu1), item_buddy);
- item_group = gtk_menu_item_new_with_mnemonic ("Group");
- gtk_widget_show (item_group);
- gtk_container_add (GTK_CONTAINER (menu1), item_group);
- gtk_option_menu_set_menu (GTK_OPTION_MENU (type_option), menu1);
- hbuttonbox2 = gtk_hbutton_box_new ();
- gtk_widget_show (hbuttonbox2);
- gtk_box_pack_end (GTK_BOX (vbox1), hbuttonbox2, FALSE, TRUE, 0);
- gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox2), 5);
- gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox2), GTK_BUTTONBOX_SPREAD);
- applysave_but = gtk_button_new_with_mnemonic ("Apply and Save");
- gtk_widget_show (applysave_but);
- gtk_container_add (GTK_CONTAINER (hbuttonbox2), applysave_but);
- GTK_WIDGET_SET_FLAGS (applysave_but, GTK_CAN_DEFAULT);
- apply_but = gtk_button_new_from_stock ("gtk-apply");
- gtk_widget_show (apply_but);
- gtk_container_add (GTK_CONTAINER (hbuttonbox2), apply_but);
- GTK_WIDGET_SET_FLAGS (apply_but, GTK_CAN_DEFAULT);
- cancel_but = gtk_button_new_from_stock ("gtk-cancel");
- gtk_widget_show (cancel_but);
- gtk_container_add (GTK_CONTAINER (hbuttonbox2), cancel_but);
- GTK_WIDGET_SET_FLAGS (cancel_but, GTK_CAN_DEFAULT);
- frame3 = gtk_frame_new (NULL);
- gtk_widget_show (frame3);
- gtk_box_pack_start (GTK_BOX (vbox1), frame3, FALSE, FALSE, 0);
- gtk_widget_set_size_request (frame3, -1, 200);
- gtk_container_set_border_width (GTK_CONTAINER (frame3), 5);
- table5 = gtk_table_new (4, 5, FALSE);
- gtk_widget_show (table5);
- gtk_container_add (GTK_CONTAINER (frame3), table5);
- gtk_widget_set_size_request (table5, 600, 400);
- gtk_container_set_border_width (GTK_CONTAINER (table5), 5);
- gtk_table_set_row_spacings (GTK_TABLE (table5), 10);
- gtk_table_set_col_spacings (GTK_TABLE (table5), 10);
- label31 = gtk_label_new ("Play On IM:");
- gtk_widget_show (label31);
- gtk_table_attach (GTK_TABLE (table5), label31, 0, 1, 0, 1,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label31), 0, 0.5);
- label34 = gtk_label_new ("Play On Unaway:");
- gtk_widget_show (label34);
- gtk_table_attach (GTK_TABLE (table5), label34, 0, 1, 3, 4,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label34), 0, 0.5);
- label33 = gtk_label_new ("Play On Unidle:");
- gtk_widget_show (label33);
- gtk_table_attach (GTK_TABLE (table5), label33, 0, 1, 2, 3,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label33), 0, 0.5);
- label32 = gtk_label_new ("Play On Signon:");
- gtk_widget_show (label32);
- gtk_table_attach (GTK_TABLE (table5), label32, 0, 1, 1, 2,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- gtk_misc_set_alignment (GTK_MISC (label32), 0, 0.5);
- unaway_sound_entry = gtk_entry_new ();
- gtk_widget_show (unaway_sound_entry);
- gtk_table_attach (GTK_TABLE (table5), unaway_sound_entry, 1, 3, 3, 4,
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
- (GtkAttachOptions) (0), 0, 0);
- unidle_sound_entry = gtk_entry_new ();
- gtk_widget_show (unidle_sound_entry);
- gtk_table_attach (GTK_TABLE (table5), unidle_sound_entry, 1, 3, 2, 3,
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
- (GtkAttachOptions) (0), 0, 0);
- signon_sound_entry = gtk_entry_new ();
- gtk_widget_show (signon_sound_entry);
- gtk_table_attach (GTK_TABLE (table5), signon_sound_entry, 1, 3, 1, 2,
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
- (GtkAttachOptions) (0), 0, 0);
- unaway_test_but = gtk_button_new_with_mnemonic ("Test");
- gtk_widget_show (unaway_test_but);
- gtk_table_attach (GTK_TABLE (table5), unaway_test_but, 4, 5, 3, 4,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- unidle_test_but = gtk_button_new_with_mnemonic ("Test");
- gtk_widget_show (unidle_test_but);
- gtk_table_attach (GTK_TABLE (table5), unidle_test_but, 4, 5, 2, 3,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- signon_test_but = gtk_button_new_with_mnemonic ("Test");
- gtk_widget_show (signon_test_but);
- gtk_table_attach (GTK_TABLE (table5), signon_test_but, 4, 5, 1, 2,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- unaway_browse_but = gtk_button_new_from_stock ("gtk-open");
- gtk_widget_show (unaway_browse_but);
- gtk_table_attach (GTK_TABLE (table5), unaway_browse_but, 3, 4, 3, 4,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- unidle_browse_but = gtk_button_new_from_stock ("gtk-open");
- gtk_widget_show (unidle_browse_but);
- gtk_table_attach (GTK_TABLE (table5), unidle_browse_but, 3, 4, 2, 3,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- signon_browse_but = gtk_button_new_from_stock ("gtk-open");
- gtk_widget_show (signon_browse_but);
- gtk_table_attach (GTK_TABLE (table5), signon_browse_but, 3, 4, 1, 2,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- im_browse_but = gtk_button_new_from_stock ("gtk-open");
- gtk_widget_show (im_browse_but);
- gtk_table_attach (GTK_TABLE (table5), im_browse_but, 3, 4, 0, 1,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- im_sound_entry = gtk_entry_new ();
- gtk_widget_show (im_sound_entry);
- gtk_table_attach (GTK_TABLE (table5), im_sound_entry, 1, 3, 0, 1,
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
- (GtkAttachOptions) (0), 0, 0);
- im_test_but = gtk_button_new_with_mnemonic ("Test");
- gtk_widget_show (im_test_but);
- gtk_table_attach (GTK_TABLE (table5), im_test_but, 4, 5, 0, 1,
- (GtkAttachOptions) (0),
- (GtkAttachOptions) (0), 0, 0);
- label24 = gtk_label_new ("Sound Events");
- gtk_widget_show (label24);
- gtk_frame_set_label_widget (GTK_FRAME (frame3), label24);
- g_signal_connect ((gpointer) edit_win, "destroy",
- G_CALLBACK (on_edit_win_destroy),
- g_signal_connect ((gpointer) applysave_but, "clicked",
- G_CALLBACK (on_apply_clicked),
- g_signal_connect ((gpointer) applysave_but, "clicked",
- G_CALLBACK (on_save_clicked),
- g_signal_connect_swapped ((gpointer) applysave_but, "clicked",
- G_CALLBACK (gtk_widget_destroy),
- GTK_OBJECT (edit_win));
- g_signal_connect ((gpointer) apply_but, "clicked",
- G_CALLBACK (on_apply_clicked),
- g_signal_connect_swapped ((gpointer) apply_but, "clicked",
- G_CALLBACK (gtk_widget_destroy),
- GTK_OBJECT (edit_win));
- g_signal_connect_swapped ((gpointer) cancel_but, "clicked",
- G_CALLBACK (gtk_widget_destroy),
- GTK_OBJECT (edit_win));
- g_signal_connect ((gpointer) unaway_test_but, "clicked",
- G_CALLBACK (on_unaway_test_clicked),
- g_signal_connect ((gpointer) unidle_test_but, "clicked",
- G_CALLBACK (on_unidle_test_clicked),
- g_signal_connect ((gpointer) signon_test_but, "clicked",
- G_CALLBACK (on_signon_test_clicked),
- g_signal_connect ((gpointer) unaway_browse_but, "clicked",
- G_CALLBACK (on_unaway_browse_clicked),
- g_signal_connect ((gpointer) unidle_browse_but, "clicked",
- G_CALLBACK (on_unidle_browse_clicked),
- g_signal_connect ((gpointer) signon_browse_but, "clicked",
- G_CALLBACK (on_signon_browse_clicked),
- g_signal_connect ((gpointer) im_browse_but, "clicked",
- G_CALLBACK (on_im_browse_clicked),
- g_signal_connect ((gpointer) im_test_but, "clicked",
- G_CALLBACK (on_im_test_clicked),
- /* Store pointers to all widgets, for use by lookup_widget(). */
- GLADE_HOOKUP_OBJECT_NO_REF (edit_win, edit_win, "edit_win");
- GLADE_HOOKUP_OBJECT (edit_win, vbox1, "vbox1");
- GLADE_HOOKUP_OBJECT (edit_win, hbox1, "hbox1");
- GLADE_HOOKUP_OBJECT (edit_win, label29, "label29");
- GLADE_HOOKUP_OBJECT (edit_win, name_entry, "name_entry");
- GLADE_HOOKUP_OBJECT (edit_win, label30, "label30");
- GLADE_HOOKUP_OBJECT (edit_win, type_option, "type_option");
- GLADE_HOOKUP_OBJECT (edit_win, menu1, "menu1");
- GLADE_HOOKUP_OBJECT (edit_win, item_buddy, "item_buddy");
- GLADE_HOOKUP_OBJECT (edit_win, item_group, "item_group");
- GLADE_HOOKUP_OBJECT (edit_win, hbuttonbox2, "hbuttonbox2");
- GLADE_HOOKUP_OBJECT (edit_win, applysave_but, "applysave_but");
- GLADE_HOOKUP_OBJECT (edit_win, apply_but, "apply_but");
- GLADE_HOOKUP_OBJECT (edit_win, cancel_but, "cancel_but");
- GLADE_HOOKUP_OBJECT (edit_win, frame3, "frame3");
- GLADE_HOOKUP_OBJECT (edit_win, table5, "table5");
- GLADE_HOOKUP_OBJECT (edit_win, label31, "label31");
- GLADE_HOOKUP_OBJECT (edit_win, label34, "label34");
- GLADE_HOOKUP_OBJECT (edit_win, label33, "label33");
- GLADE_HOOKUP_OBJECT (edit_win, label32, "label32");
- GLADE_HOOKUP_OBJECT (edit_win, unaway_sound_entry, "unaway_sound_entry");
- GLADE_HOOKUP_OBJECT (edit_win, unidle_sound_entry, "unidle_sound_entry");
- GLADE_HOOKUP_OBJECT (edit_win, signon_sound_entry, "signon_sound_entry");
- GLADE_HOOKUP_OBJECT (edit_win, unaway_test_but, "unaway_test_but");
- GLADE_HOOKUP_OBJECT (edit_win, unidle_test_but, "unidle_test_but");
- GLADE_HOOKUP_OBJECT (edit_win, signon_test_but, "signon_test_but");
- GLADE_HOOKUP_OBJECT (edit_win, unaway_browse_but, "unaway_browse_but");
- GLADE_HOOKUP_OBJECT (edit_win, unidle_browse_but, "unidle_browse_but");
- GLADE_HOOKUP_OBJECT (edit_win, signon_browse_but, "signon_browse_but");
- GLADE_HOOKUP_OBJECT (edit_win, im_browse_but, "im_browse_but");
- GLADE_HOOKUP_OBJECT (edit_win, im_sound_entry, "im_sound_entry");
- GLADE_HOOKUP_OBJECT (edit_win, im_test_but, "im_test_but");
- GLADE_HOOKUP_OBJECT (edit_win, label24, "label24");
--- a/smartear/smartear.c Tue Jul 03 04:45:10 2007 -0400
+++ b/smartear/smartear.c Thu Jul 05 05:46:17 2007 -0400
@@ -1,1280 +1,105 @@
-/* SmartEar by Matt Perry
- * Works for purple 2.0.0
- * Plugin to assign different sounds to different buddies and groups.
- * TODO: figure out contact support
+ * smartear.c - SmartEar plugin for libpurple + * Copyright (c) 2007 John Bailey <rekkanoryo@rekkanoryo.org> + * Original code copyright (c) 2003-2007 Matt Perry + * 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 -# include "../pp_config.h"
-#include "../common/i18n.h"
-#define BUF_LEN 2048 /* lifted from libpurple's internal.h */
-#define RCFILE_VERSION 2
-#define RC_EMPTY_SOUND " " /* must be at least 1 char long for sscanf */
-#define SMARTEAR_PLUGIN_ID "smartear"
-#define SMARTEAR_VERSION VERSION ".1"
-#define FIND(s) lookup_widget(config, s)
-#define EFIND(s) lookup_widget(edit_win, s)
-/* an entry in the prefs file to indicate what to play for who and when. */
- char *sound[EVENT_NUM];
- PurpleAccount *account;
- struct timer_data *timer;
-enum { /* Treeview columns */
-#define DEFAULT_NAME "(Default)"
-#define IS_DEFAULT(entry) (strcmp(entry->name, DEFAULT_NAME)==0)
-#define IS_EMPTY(str) (!str || !str[0])
-struct smartear_entry default_entry = {'B', DEFAULT_NAME, {"", "", "", ""}};
-int focused_quiet = TRUE;
-int smartear_timers = FALSE;
-GtkWidget *config = NULL;
-GtkWidget *edit_win = NULL;
-GtkWidget *file_browse = NULL;
-GtkWidget *file_browse_entry = NULL;
-GtkTreeView *treeview = NULL;
-GtkTreeSelection *treeselect = NULL;
-GtkTreeModel *treemodel = NULL;
-struct smartear_entry *selected_entry = NULL;
-GtkTreeIter selected_iter;
-GSList *sounds_list = NULL;
-GSList *messages_list = NULL;
-GtkWidget *create_file_browse(void);
-GtkWidget *create_edit_win(void);
-GtkWidget *create_config(void);
-static void new_entry(struct smartear_entry *entry);
-static gboolean get_selection(void);
-static void clear_selection(void);
-static void populate_edit_win(struct smartear_entry *entry);
-static void update_list(void);
-static void populate_list(GtkListStore *store);
-static void list_set(GtkListStore *store, GtkTreeIter *iter, struct smartear_entry *entry);
-static void set_option_entries(void);
-static gint sound_cmp(gconstpointer p1, gconstpointer p2);
-static struct smartear_entry *sound_new_with_name(gchar *name);
-static struct smartear_entry *sound_dup(struct smartear_entry *entry);
-static void sound_free(struct smartear_entry *entry, int free_entry);
-static void soundlist_free(void);
-static gchar *get_basename(gchar *path);
-static void on_treeselect_changed(GtkTreeSelection *selection, gpointer data);
-static void smartear_save(void);
-static void smartear_load(void);
-static void play_sound_alias(char *sound, PurpleAccount* account);
-/*** Glade's Support Function ***/
-GtkWidget* lookup_widget (GtkWidget *widget, const gchar *widget_name)
- GtkWidget *found_widget;
- found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget),
- g_warning ("smartear: Widget not found: %s", widget_name);
-void on_delay_changed (GtkEditable *editable, gpointer user_data)
- gchar *text = gtk_editable_get_chars(editable, 0, -1);
- message_delay = atoi(text);
-void on_focus_toggled (GtkToggleButton *togglebutton, gpointer user_data)
- focused_quiet = gtk_toggle_button_get_active(togglebutton);
-void on_timer_toggled (GtkToggleButton *togglebutton, gpointer user_data)
- smartear_timers = gtk_toggle_button_get_active(togglebutton);
-void on_cell_edited(GtkCellRendererText *cell, gchar *path, gchar *text, gpointer data)
- gint col = GPOINTER_TO_INT(data);
- struct smartear_entry *entry;
- if (!gtk_tree_model_get_iter_from_string(treemodel, &iter, path))
- gtk_tree_model_get(treemodel, &iter, DATA_COL, &entry, -1);
- entry->name = g_strdup(text);
-void on_treeselect_changed(GtkTreeSelection *selection, gpointer data)
-void on_new_clicked (GtkButton *button, gpointer user_data)
- new_entry(sound_new_with_name(""));
-void on_delete_clicked (GtkButton *button, gpointer user_data)
- if (IS_DEFAULT(selected_entry))
- sounds_list = g_slist_remove(sounds_list, selected_entry);
- sound_free(selected_entry, TRUE);
- if (!gtk_tree_model_iter_next(treemodel, &iter)) {
- GtkTreePath *path = gtk_tree_model_get_path(treemodel, &selected_iter);
- gtk_tree_path_prev(path);
- gtk_tree_model_get_iter(treemodel, &iter, path);
- gtk_tree_path_free(path);
- g_signal_handlers_block_by_func(G_OBJECT(treeselect), on_treeselect_changed, 0);
- gtk_list_store_remove(GTK_LIST_STORE(treemodel), &selected_iter);
- gtk_tree_selection_select_iter(treeselect, &iter);
- g_signal_handlers_unblock_by_func(G_OBJECT(treeselect), on_treeselect_changed, 0);
-void on_edit_clicked (GtkButton *button, gpointer user_data)
- edit_win = create_edit_win();
- gtk_widget_show_all(edit_win);
- populate_edit_win(selected_entry);
-void on_save_clicked (GtkButton *button, gpointer user_data)
-void on_revert_clicked (GtkButton *button, gpointer user_data)
- gtk_widget_destroy(edit_win);
- gtk_list_store_clear(GTK_LIST_STORE(treemodel));
- populate_list(GTK_LIST_STORE(treemodel));
-void on_browse_ok_clicked (GtkButton *button, gpointer user_data)
- gtk_entry_set_text(GTK_ENTRY(file_browse_entry),
- gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_browse)));
-void on_file_browse_destroy (GtkObject *object, gpointer user_data)
- file_browse_entry = NULL;
-void on_browse_clicked (GtkButton *button, gchar *user_data)
- file_browse = create_file_browse();
- gtk_widget_show(file_browse);
+# include "../pp_config.h" +#endif /* HAVE_CONFIG_H */ - file_browse_entry = EFIND(user_data);
- text = gtk_editable_get_chars(GTK_EDITABLE(file_browse_entry), 0, -1);
- gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_browse), text);
-void on_im_browse_clicked (GtkButton *button, gpointer user_data)
- on_browse_clicked(button, "im_sound_entry");
-void on_signon_browse_clicked (GtkButton *button, gpointer user_data)
- on_browse_clicked(button, "signon_sound_entry");
-void on_unidle_browse_clicked (GtkButton *button, gpointer user_data)
- on_browse_clicked(button, "unidle_sound_entry");
-void on_unaway_browse_clicked (GtkButton *button, gpointer user_data)
- on_browse_clicked(button, "unaway_sound_entry");
-void on_test_clicked (GtkButton *button, gchar *user_data)
- gtk_editable_get_chars(GTK_EDITABLE(EFIND(user_data)), 0, -1);
- play_sound_alias(text, NULL);
-void on_im_test_clicked (GtkButton *button, gpointer user_data)
- on_test_clicked(button, "im_sound_entry");
-void on_signon_test_clicked (GtkButton *button, gpointer user_data)
- on_test_clicked(button, "signon_sound_entry");
-void on_unidle_test_clicked (GtkButton *button, gpointer user_data)
- on_test_clicked(button, "unidle_sound_entry");
-void on_unaway_test_clicked (GtkButton *button, gpointer user_data)
- on_test_clicked(button, "unaway_sound_entry");
-void on_apply_clicked (GtkButton *button, gpointer user_data)
- struct smartear_entry *entry, tmp;
- entry = selected_entry;
- g_free(entry->sound[0]);
- g_free(entry->sound[1]);
- g_free(entry->sound[2]);
- g_free(entry->sound[3]);
- gtk_editable_get_chars(GTK_EDITABLE(EFIND("name_entry")), 0, -1);
- gtk_option_menu_get_history(GTK_OPTION_MENU(EFIND("type_option"))) == 0
- entry->sound[EVENT_M] =
- gtk_editable_get_chars(GTK_EDITABLE(EFIND("im_sound_entry")), 0, -1);
- entry->sound[EVENT_S] =
- gtk_editable_get_chars(GTK_EDITABLE(EFIND("signon_sound_entry")), 0, -1);
- entry->sound[EVENT_I] =
- gtk_editable_get_chars(GTK_EDITABLE(EFIND("unidle_sound_entry")), 0, -1);
- entry->sound[EVENT_A] =
- gtk_editable_get_chars(GTK_EDITABLE(EFIND("unaway_sound_entry")), 0, -1);
- if ((lp = g_slist_find_custom(sounds_list, entry, (GCompareFunc)sound_cmp))) {
- sound_free((struct smartear_entry*)lp->data, FALSE);
- *(struct smartear_entry*)lp->data = *entry;
- struct smartear_entry *copy = g_new(struct smartear_entry, 1);
-void on_edit_win_destroy (GtkObject *object, gpointer user_data)
- gtk_widget_set_sensitive(GTK_WIDGET(treeview), TRUE);
-void on_config_destroy (GtkObject *object, gpointer user_data)
-/*** Util Functions ***/
-static void new_entry(struct smartear_entry *entry)
- GtkTreeViewColumn *name_column;
- sounds_list = g_slist_append(sounds_list, entry);
- gtk_list_store_insert_after(GTK_LIST_STORE(treemodel), &iter, &selected_iter);
- gtk_list_store_append(GTK_LIST_STORE(treemodel), &iter);
- path = gtk_tree_model_get_path(treemodel, &iter);
- list_set(GTK_LIST_STORE(treemodel), &iter, entry);
- g_signal_handlers_block_by_func(G_OBJECT(treeselect), on_treeselect_changed, 0);
- path = gtk_tree_model_get_path(treemodel, &iter);
- name_column = gtk_tree_view_get_column(treeview, NAME_COL);
- if (entry->name && entry->name[0])
- gtk_tree_view_set_cursor(treeview, path, 0, FALSE);
- gtk_tree_view_set_cursor(treeview, path, name_column, TRUE);
- gtk_tree_path_free(path);
- g_signal_handlers_unblock_by_func(G_OBJECT(treeselect), on_treeselect_changed, 0);
-static gboolean get_selection(void)
- if (!gtk_tree_selection_get_selected(treeselect, &treemodel, &selected_iter))
- gtk_tree_model_get(treemodel, &selected_iter, DATA_COL, &selected_entry, -1);
-void clear_selection(void)
- if (gtk_tree_selection_get_selected(treeselect, &treemodel, &selected_iter)) {
- gtk_tree_selection_unselect_iter(treeselect, &selected_iter);
-static void list_set(GtkListStore *store, GtkTreeIter *iter, struct smartear_entry *entry)
- gtk_list_store_set(store, iter, DATA_COL, entry, -1);
-static char *my_normalize(const char *input)
- static char buf[BUF_LEN];
- g_return_val_if_fail((input != NULL), NULL);
- beg = str = g_strdup(input);
- while (*str && i <= BUF_LEN) {
-static struct smartear_entry *sound_new_with_name(gchar *name)
- struct smartear_entry tmp = default_entry;
- return sound_dup(&tmp);
-static struct smartear_entry *sound_dup(struct smartear_entry *entry)
- struct smartear_entry *edup = g_new(struct smartear_entry, 1);
- edup->type = entry->type;
- edup->name = g_strdup(my_normalize(entry->name));
- for (i = 0; i < EVENT_NUM; i++) {
- edup->sound[i] = g_strdup(entry->sound[i]);
-static void sound_free(struct smartear_entry *entry, int free_entry)
- g_free(entry->sound[0]);
- g_free(entry->sound[1]);
- g_free(entry->sound[2]);
- g_free(entry->sound[3]);
-static void soundlist_free(void)
- for (lp = sounds_list; lp; lp = g_slist_next(lp))
- sound_free((struct smartear_entry*)lp->data, TRUE);
- g_slist_free(sounds_list);
-static gint sound_cmp(gconstpointer p1, gconstpointer p2)
- struct smartear_entry *entry1 = (struct smartear_entry*)p1;
- struct smartear_entry *entry2 = (struct smartear_entry*)p2;
- if (!entry1 || IS_DEFAULT(entry1))
- if (!entry2 || IS_DEFAULT(entry2))
- /* only compare types if both are nonzero */
- if (entry1->type != entry2->type &&
- entry1->type != 0 && entry2->type != 0)
- return (entry1->type - entry2->type);
- return (g_strcasecmp(entry1->name, entry2->name));
-static void populate_edit_win(struct smartear_entry *entry)
- gtk_entry_set_text(GTK_ENTRY(EFIND("name_entry")), entry->name);
- gtk_entry_set_text(GTK_ENTRY(EFIND("im_sound_entry")),
- entry->sound[EVENT_M]);
- gtk_entry_set_text(GTK_ENTRY(EFIND("signon_sound_entry")),
- entry->sound[EVENT_S]);
- gtk_entry_set_text(GTK_ENTRY(EFIND("unidle_sound_entry")),
- entry->sound[EVENT_I]);
- gtk_entry_set_text(GTK_ENTRY(EFIND("unaway_sound_entry")),
- entry->sound[EVENT_A]);
- gtk_option_menu_set_history(GTK_OPTION_MENU(EFIND("type_option")),
- (entry->type == 'B' ? 0 : 1));
- if (IS_DEFAULT(entry)) {
- gtk_widget_set_sensitive(EFIND("name_entry"), FALSE);
- gtk_widget_set_sensitive(EFIND("type_menu"), FALSE);
- gtk_widget_set_sensitive(GTK_WIDGET(treeview), FALSE);
-static void set_option_entries(void)
- GtkSpinButton *delay_spin = GTK_SPIN_BUTTON(FIND("delay_spin"));
- GtkToggleButton *focus_but = GTK_TOGGLE_BUTTON(FIND("focus_but"));
- GtkToggleButton *timer_but = GTK_TOGGLE_BUTTON(FIND("timer_but"));
- g_signal_handlers_block_by_func(G_OBJECT(delay_spin), on_delay_changed, 0);
- g_signal_handlers_block_by_func(G_OBJECT(focus_but), on_focus_toggled, 0);
- g_signal_handlers_block_by_func(G_OBJECT(timer_but), on_timer_toggled, 0);
- gtk_spin_button_set_value(delay_spin, (gdouble)message_delay);
- gtk_toggle_button_set_active(focus_but, focused_quiet);
- gtk_toggle_button_set_active(timer_but, smartear_timers);
+#include "../common/pp_internal.h" - g_signal_handlers_unblock_by_func(G_OBJECT(delay_spin), on_delay_changed, 0);
- g_signal_handlers_unblock_by_func(G_OBJECT(focus_but), on_focus_toggled, 0);
- g_signal_handlers_unblock_by_func(G_OBJECT(timer_but), on_timer_toggled, 0);
-static void update_list(void)
- list_set(GTK_LIST_STORE(treemodel), &selected_iter, selected_entry);
-static gchar* get_basename(gchar *path)
- static gchar base[BUF_LEN];
- if (!path || !path[0]) {
- if ((p = strrchr(path, '/'))) {
- strncpy(base, p, sizeof(base));
-/*** Init Functions ***/
-static void populate_list(GtkListStore *store)
- sounds_list = g_slist_sort(sounds_list, (GCompareFunc)sound_cmp);
- for (lp = sounds_list; lp; lp = g_slist_next(lp)) {
- struct smartear_entry *entry = (struct smartear_entry*)lp->data;
- gtk_list_store_append(store, &iter);
- list_set(store, &iter, entry);
- if (entry == selected_entry)
-static void render_type(GtkTreeViewColumn *column, GtkCellRenderer *cell,
- GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
- struct smartear_entry *entry;
- gtk_tree_model_get(model, iter, DATA_COL, &entry, -1);
- g_object_set(cell, "text", "", NULL);
- g_object_set(cell, "text", entry->type == 'B' ? "Buddy" : "Group", NULL);
-static void render_name(GtkTreeViewColumn *column, GtkCellRenderer *cell,
- GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
- struct smartear_entry *entry;
- gtk_tree_model_get(model, iter, DATA_COL, &entry, -1);
- g_object_set(cell, "text", entry->name, NULL);
-static void render_when(GtkTreeViewColumn *column, GtkCellRenderer *cell,
- GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
- struct smartear_entry *entry;
- gint which = GPOINTER_TO_INT(data);
- gtk_tree_model_get(model, iter, DATA_COL, &entry, -1);
- g_object_set(cell, "text", get_basename(entry->sound[which]), NULL);
-static gint list_cmp(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer d)
- struct smartear_entry *entry1, *entry2;
- gtk_tree_model_get(model, a, DATA_COL, &entry1, -1);
- gtk_tree_model_get(model, b, DATA_COL, &entry2, -1);
- return sound_cmp(entry1, entry2);
-static void setup_list(void)
- treeview = GTK_TREE_VIEW(FIND("treeview"));
- store = gtk_list_store_new(1, G_TYPE_POINTER);
- gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
- g_object_unref(G_OBJECT(store)); // treeview has it's own copy.
- treemodel = gtk_tree_view_get_model(treeview);
- gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(treemodel),
- (GtkTreeIterCompareFunc)&list_cmp, 0, 0);
- gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(treemodel),
- GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
- treeselect = gtk_tree_view_get_selection(treeview);
- gtk_tree_selection_set_mode(treeselect, GTK_SELECTION_SINGLE);
- g_signal_connect(G_OBJECT(treeselect), "changed",
- G_CALLBACK(on_treeselect_changed), NULL);
- cell = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_data_func(treeview, -1, "Type",
- cell, render_type, 0, 0);
- cell = gtk_cell_renderer_text_new();
- g_signal_connect(G_OBJECT(cell), "edited",
- G_CALLBACK(on_cell_edited), GINT_TO_POINTER(NAME_COL));
- g_object_set(G_OBJECT(cell), "editable", TRUE, NULL);
- gtk_tree_view_insert_column_with_data_func(treeview, -1, "Name",
- cell, render_name, 0, 0);
- cell = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_data_func(treeview, -1, "On Message",
- cell, render_when, GINT_TO_POINTER(EVENT_M), 0);
- cell = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_data_func(treeview, -1, "On Signon",
- cell, render_when, GINT_TO_POINTER(EVENT_S), 0);
- cell = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_data_func(treeview, -1, "On Unidle",
- cell, render_when, GINT_TO_POINTER(EVENT_I), 0);
- cell = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_data_func(treeview, -1, "On Unaway",
- cell, render_when, GINT_TO_POINTER(EVENT_A), 0);
-static void smartear_save(void)
- char file[BUF_LEN] = "smartear.rc - you have no home dir";
- struct smartear_entry *entry;
- if (purple_user_dir()) {
- g_snprintf(file, sizeof(file), "%s/smartear.rc", purple_user_dir());
- g_warning("couldn't open %s\n", file);
- fprintf(fp, "version %d\n", RCFILE_VERSION);
- fprintf(fp, "smartear_timers %d\n", smartear_timers);
- fprintf(fp, "delay %d\n", message_delay);
- fprintf(fp, "focused_quiet %d\n", focused_quiet);
- /* save empties as a single space so scanf doesn't get confused */
-#define SAVE_STR(str) (IS_EMPTY(str) ? RC_EMPTY_SOUND : str)
- for (lp = sounds_list; lp; lp = g_slist_next(lp)) {
- entry = (struct smartear_entry*)lp->data;
- fprintf(fp, "%c {%s} {%s} {%s} {%s} {%s}\n",
- entry->type, entry->name,
- SAVE_STR(entry->sound[EVENT_M]),
- SAVE_STR(entry->sound[EVENT_S]),
- SAVE_STR(entry->sound[EVENT_I]),
- SAVE_STR(entry->sound[EVENT_A]));
-static void smartear_load(void)
- char file[BUF_LEN] = "smartear.rc - you have no home dir";
- struct smartear_entry *entry;
- gboolean has_default = FALSE;
- int rcfile_version = 1;
- if (purple_user_dir()) {
- g_snprintf(file, sizeof(file), "%s/smartear.rc", purple_user_dir());
- g_warning("smartear: couldn't open %s\n", file);
- sounds_list = g_slist_append(sounds_list, sound_dup(&default_entry));
- while ((fgets(buf, sizeof(buf), fp))) {
- if (sscanf(buf, "version %d", &tmp) == 1)
- else if (sscanf(buf, "smarterear_timers %d", &tmp) == 1)
- else if (sscanf(buf, "smartear_timers %d", &tmp) == 1)
- else if (sscanf(buf, "delay %d", &tmp) == 1)
- else if (sscanf(buf, "focused_quiet %d", &tmp) == 1)
- else if (rcfile_version == 1) {
- if (sscanf(buf, "%c {%[^}]} {%[^}]} {%[^}]}",
- &type, name, flags, sound) == 4) {
- if (type != 'G' && type != 'B') {
- g_warning("smartear: rc no such type: %c", type);
- entry = g_new(struct smartear_entry, 1);
- entry->name = g_strdup(my_normalize(name));
- if (strchr(flags, 'M')) entry->sound[EVENT_M] = g_strdup(sound);
- else entry->sound[EVENT_M] = g_strdup("");
- if (strchr(flags, 'S')) entry->sound[EVENT_S] = g_strdup(sound);
- else entry->sound[EVENT_S] = g_strdup("");
- if (strchr(flags, 'I')) entry->sound[EVENT_I] = g_strdup(sound);
- else entry->sound[EVENT_I] = g_strdup("");
- if (strchr(flags, 'A')) entry->sound[EVENT_A] = g_strdup(sound);
- else entry->sound[EVENT_A] = g_strdup("");
- sounds_list = g_slist_append(sounds_list, entry);
- if (IS_DEFAULT(entry)) {
- default_entry = *entry;
- g_warning("smartear: rc1 syntax error: %s", buf);
- else if (rcfile_version == 2) {
- char sound[EVENT_NUM][BUF_LEN];
- if (sscanf(buf, "%c {%[^}]} {%[^}]} {%[^}]} {%[^}]} {%[^}]}",
- &type, name, sound[EVENT_M], sound[EVENT_S],
- sound[EVENT_I], sound[EVENT_A]) == 6) {
- if (type != 'G' && type != 'B') {
- g_warning("smartear: rc no such type: %c", type);
- for (i = 0; i < EVENT_NUM; i++) {
- if (strcmp(sound[i], RC_EMPTY_SOUND) == 0)
- entry = g_new(struct smartear_entry, 1);
- entry->name = g_strdup(my_normalize(name));
- entry->sound[EVENT_M] = g_strdup(sound[EVENT_M]);
- entry->sound[EVENT_S] = g_strdup(sound[EVENT_S]);
- entry->sound[EVENT_I] = g_strdup(sound[EVENT_I]);
- entry->sound[EVENT_A] = g_strdup(sound[EVENT_A]);
- sounds_list = g_slist_append(sounds_list, entry);
- if (IS_DEFAULT(entry)) {
- default_entry = *entry;
- g_warning("smartear: rc2 syntax error: %s", buf);
- g_warning("smartear: rc syntax error: %s", buf);
- sounds_list = g_slist_append(sounds_list, sound_dup(&default_entry));
-/*** Purple callbacks ***/
-static struct message_data *find_message_by_name(PurpleAccount *account, const char *pname)
- struct message_data *msg = NULL;
- char *name = g_strdup(my_normalize(pname));
- for (lp = messages_list; lp; lp = g_slist_next(lp)) {
- msg = (struct message_data*)lp->data;
- if (msg->account == account
- && g_strcasecmp(msg->buddy, name) == 0)
-static void message_timer_remove(struct message_data *msg)
- if (msg && msg->timer) {
- g_source_remove(msg->timer->id);
-static void message_free(struct message_data *msg)
- message_timer_remove(msg);
-static void messagelist_free()
- for (lp = messages_list; lp; lp = g_slist_next(lp))
- message_free((struct message_data*)lp->data);
- g_slist_free(messages_list);
-static void on_smartear_clicked(PurpleBlistNode* node, gpointer data)
+smartear_load(PurplePlugin *plugin) - struct smartear_entry tmp, *entry;
- edit_win = create_edit_win();
- gtk_widget_show_all(edit_win);
- else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
- tmp.name = ((PurpleBuddy*)node)->name;
- purple_debug(PURPLE_DEBUG_INFO, "smartear", "adding buddy %s", tmp.name);
- else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
- tmp.name = ((PurpleContact*)node)->alias;
- tmp.name = ((PurpleContact*)node)->priority->name;
- purple_debug(PURPLE_DEBUG_INFO, "smartear", "adding contact %s", tmp.name);
- else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
- tmp.name = ((PurpleGroup*)node)->name;
- g_warning("group %p, %p %s", node, tmp.name, tmp.name);
- purple_debug(PURPLE_DEBUG_INFO, "smartear", "adding group %s", tmp.name);
- if ((lp = g_slist_find_custom(sounds_list, &tmp, (GCompareFunc)sound_cmp))) {
- entry = (struct smartear_entry*)lp->data;
- populate_edit_win(entry);
-static void play_sound_alias(char *sound, PurpleAccount* account)
- struct smartear_entry tmp, *entry;
- /* sound aliases: mostly so you can put (Default) for sound, and it'll
- if ((lp = g_slist_find_custom(sounds_list, &tmp, (GCompareFunc)sound_cmp))) {
- entry = (struct smartear_entry*)lp->data;
- play_sound_alias(entry->sound[EVENT_M], account);
- purple_sound_play_file(sound, account);
-void play_matching_sound(PurpleBuddy *buddy, int event)
- struct smartear_entry *entry;
- char *name = buddy ? g_strdup(my_normalize(buddy->name)) : NULL;
- PurpleGroup *g = buddy ? purple_buddy_get_group(buddy) : NULL;
- for (lp = sounds_list; lp; lp = g_slist_next(lp)) {
- entry = (struct smartear_entry*)lp->data;
- if (!entry->sound[event])
+ /* TODO: make this unset all the pidgin/finch sound prefs */ - if (entry->type == 'B' && name
- && g_strcasecmp(name, entry->name) == 0) {
- sound = entry->sound[event];
- /* found a buddy match.. this takes precedence, so stop */
- if (entry->type == 'G' && g
- && g_strcasecmp(my_normalize(g->name), entry->name) == 0) {
- sound = entry->sound[event];
- /* keep going... buddy overrides group */
- if (IS_DEFAULT(entry)) {
- sound = entry->sound[event];
- /* keep going.. other 2 override default */
- if (!IS_EMPTY(sound)) {
- purple_debug(PURPLE_DEBUG_INFO, "smartear",
- "found %s for %s on event %d\n", sound, name, event);
- play_sound_alias(sound, purple_buddy_get_account(buddy));
- purple_debug(PURPLE_DEBUG_INFO, "smartear",
- "no sound found for %s on event %d\n", name, event);
-static gint play_sound_timer_hook(gpointer data)
- struct message_data *msg = (struct message_data *)data;
- if(!msg || !msg->timer) {
- g_warning("smartear: timer called without being active!\n");
- play_matching_sound(purple_find_buddy(msg->account, msg->buddy), msg->timer->event);
-static gboolean on_im_recv(PurpleAccount *account, char *who, char *what, gint32 flags, void *junk)
- struct message_data *msg;
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
- PidginConversation *gtkconv = conv ? PIDGIN_CONVERSATION(conv) : NULL;
- /* FIXME: message list is never trimmed. However, if we DO trim it,
- * we need to consider race conditions on the msg field of the timer_hook */
- if ((msg = find_message_by_name(account, who))) {
- /* only install a timer if there's no active timers and we've just
- * sent a message. If we haven't just sent a message, then
- * a beep will go off anyways when the IM arrives. */
- if(smartear_timers && !msg->timer
- && msg->last_sent + message_delay > now) {
- msg->timer = g_new0(struct timer_data, 1);
- msg->timer->event = EVENT_M;
- msg->timer->id = g_timeout_add(message_delay*1000,
- play_sound_timer_hook, msg);
- if (msg->last_active + message_delay > now && conv) {
- purple_debug(PURPLE_DEBUG_INFO, "smartear",
- "received IM from %s, but only %d/%d seconds passed.\n",
- who, now - msg->last_active, message_delay);
- msg = g_new0(struct message_data, 1);
- msg->account = account;
- msg->buddy = g_strdup(my_normalize(who));
- messages_list = g_slist_append(messages_list, msg);
- msg->last_active = now;
- if (focused_quiet && gtkconv && GTK_WIDGET_HAS_FOCUS(gtkconv->entry))
- return FALSE; /* don't play sounds for the focused convo */
- if (gtkconv && !gtkconv->make_sound)
- play_matching_sound(purple_find_buddy(msg->account, msg->buddy), EVENT_M);
-static void on_im_send(PurpleAccount *account, char *who, char *what, void *junk)
- struct message_data *msg;
- if ((msg = find_message_by_name(account, who))) {
- message_timer_remove(msg);
- msg = g_new0(struct message_data, 1);
- msg->account = account;
- msg->buddy = g_strdup(my_normalize(who));
- messages_list = g_slist_append(messages_list, msg);
- msg->last_active = now;
-static void on_buddy_signon(PurpleBuddy *buddy, void *data)
- play_matching_sound(buddy, EVENT_S);
-static void on_buddy_back(PurpleBuddy *buddy, void *data)
- play_matching_sound(buddy, EVENT_A);
-static void on_buddy_unidle(PurpleBuddy *buddy, void *data)
- play_matching_sound(buddy, EVENT_I);
-static void on_signon(PurpleConnection *gc, void *m)
-static void on_signoff(PurpleConnection *gc, void *m)
-static void on_blist_node_extended_menu(PurpleBlistNode *node, GList **menu)
- PurpleMenuAction *menu_action;
- menu_action = purple_menu_action_new(_("Edit SmartEar Entry"),
- PURPLE_CALLBACK(on_smartear_clicked), NULL, NULL);
- *menu = g_list_append(*menu, menu_action);
-static gboolean purple_plugin_init(PurplePlugin *plugin)
- void *blist_handle = purple_blist_get_handle();
- void *conv_handle = purple_conversations_get_handle();
- purple_signal_connect(blist_handle, "blist-node-extended-menu",
- plugin, PURPLE_CALLBACK(on_blist_node_extended_menu), NULL);
- purple_signal_connect(blist_handle, "buddy-signed-on",
- plugin, PURPLE_CALLBACK(on_buddy_signon), NULL);
- purple_signal_connect(blist_handle, "buddy-back",
- plugin, PURPLE_CALLBACK(on_buddy_back), NULL);
- purple_signal_connect(blist_handle, "buddy-unidle",
- plugin, PURPLE_CALLBACK(on_buddy_unidle), NULL);
- purple_signal_connect(conv_handle, "received-im-msg",
- plugin, PURPLE_CALLBACK(on_im_recv), NULL);
- purple_signal_connect(conv_handle, "sent-im-msg",
- plugin, PURPLE_CALLBACK(on_im_send), NULL);
- purple_signal_connect(purple_connections_get_handle(), "signed-on",
- plugin, PURPLE_CALLBACK(on_signon), NULL);
- purple_signal_connect(purple_connections_get_handle(), "signed-off",
- plugin, PURPLE_CALLBACK(on_signoff), NULL);
+ /* XXX: do we want to "migrate" the pidgin/finch sound prefs by making them + * the default for each group if they're turned on? */ -static gboolean purple_plugin_remove(PurplePlugin *h)
+smartear_unload(PurplePlugin *plugin)
+ /* XXX: since we're going to unset all the pidgin and finch sound prefs, + * do we want to keep track of their values and restore them on unload? */ -static GtkWidget *purple_plugin_config_gtk(PurplePlugin *plugin)
+PurplePluginInfo smartear_info = - config = create_config();
- gtk_widget_show_all(config);
+ PURPLE_PLUGIN_MAGIC, /* Magic, my ass */ + PURPLE_MAJOR_VERSION, /* libpurple major version */ + PURPLE_MINOR_VERSION, /* libpurple minor version */ + PURPLE_PLUGIN_STANDARD, /* plugin type - this is a normal plugin */ + NULL, /* UI requirement - we have none */ + 0, /* flags - we have none */ + NULL, /* dependencies - we have none */ + PURPLE_PRIORITY_DEFAULT, /* priority - nothing special here */ + "core-plugin_pack-smartear", /* Plugin ID */ + NULL, /* name - defined later for i18n */ + PP_VERSION, /* plugin version - use plugin pack version */ + NULL, /* summary - defined later for i18n */ + NULL, /* description - defined later for i18n */ + "John Bailey <rekkanoryo@rekkanoryo.org>", /* author */ + PP_WEBSITE, /* plugin website - use plugin pack website */ + gtksmartear_load, /* plugin load - purple calls this when loading */ + gtksmartear_unload, /* plugin unload - purple calls this when unloading */ + NULL, /* plugin destroy - we don't need one */ + NULL, /* ui_info - we don't need this */ + NULL, /* extra_info - we don't need this */ + NULL, /* prefs_info - we don't need this yet */ + NULL, /* actions - we don't have any */ -static PurplePluginUiInfo ui_info = {
- purple_plugin_config_gtk,
- 0 /* page_num (reserved) */
-static PurplePluginInfo info =
- PURPLE_PLUGIN_STANDARD,
- PURPLE_PRIORITY_DEFAULT,
- "Matt Perry <guy@fscked.org>",
-static void init_plugin(PurplePlugin *plugin)
+smartear_init(PurplePlugin *plugin) bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
- info.name= _("Smart Ear");
- info.summary = _("Assign different sounds to different buddies and groups");
- _("Allows you to assign different sounds to play for different buddies "
- "or whole groups of buddies. Smart Ear allows you to opt to play "
- "sounds when a buddy sends you an IM, signs on, returns from away "
- "or idle, or any combination of these, so you'll know by the sound "
- "what the important people are doing.");
+ info.name = _("Smart Ear"); + info.summary = _("Assign sounds on a per-buddy or per-group basis"); + info.description = _("Smart Ear allows you to assign sounds on a per-buddy or " + "per-group basis. You can configure sounds for sign on, sign off, IM, " + "and status change events. Using these features, you can know by the " + "sounds Pidgin emits which person on your buddy list is doing what."); -PURPLE_INIT_PLUGIN(smartear, init_plugin, info);
+PURPLE_INIT_PLUGIN(smartear, smartear_init, smartear_info)