diff --git a/plugins/triggers/__init__.py b/plugins/triggers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4f8d9f87b5e8f8b9f38cc582865826ba91c54048 --- /dev/null +++ b/plugins/triggers/__init__.py @@ -0,0 +1 @@ +from triggers import Triggers diff --git a/plugins/triggers/config_dialog.ui b/plugins/triggers/config_dialog.ui new file mode 100644 index 0000000000000000000000000000000000000000..68a53be20a9994b9d156c12a84ab3ad6c24b1862 --- /dev/null +++ b/plugins/triggers/config_dialog.ui @@ -0,0 +1,900 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy toplevel-contextual --> + <object class="GtkListStore" id="liststore1"> + <columns> + <!-- column-name item --> + <column type="gchararray"/> + </columns> + <data> + <row> + <col id="0" translatable="yes">contact(s)</col> + </row> + <row> + <col id="0" translatable="yes">group(s)</col> + </row> + <row> + <col id="0" translatable="yes">everybody</col> + </row> + </data> + </object> + <object class="GtkListStore" id="liststore2"> + <columns> + <!-- column-name item --> + <column type="gchararray"/> + </columns> + <data> + <row> + <col id="0" translatable="yes">Receive a Message</col> + </row> + </data> + </object> + <object class="GtkWindow" id="advanced_notifications_window"> + <property name="border_width">6</property> + <property name="title" translatable="yes">Advanced Notifications Control</property> + <property name="role">Advanced Notifications Control</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">True</property> + <child> + <object class="GtkVBox" id="vbox"> + <property name="visible">True</property> + <property name="border_width">12</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkVBox" id="vbox100"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">5</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow23"> + <property name="height_request">90</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">never</property> + <property name="vscrollbar_policy">automatic</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="conditions_treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <signal name="cursor_changed" handler="on_conditions_treeview_cursor_changed"/> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkAlignment" id="alignment99"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="left_padding">212</property> + <child> + <object class="GtkHBox" id="hbox3045"> + <property name="visible">True</property> + <child> + <object class="GtkHButtonBox" id="hbuttonbox2"> + <property name="visible">True</property> + <property name="spacing">10</property> + <child> + <object class="GtkButton" id="new_button"> + <property name="label">gtk-new</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_new_button_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="up_button"> + <property name="label">gtk-go-up</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_up_button_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="down_button"> + <property name="label">gtk-go-down</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_down_button_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="delete_button"> + <property name="label">gtk-delete</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_delete_button_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkVBox" id="config_vbox"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">5</property> + <child> + <object class="GtkLabel" id="label391"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Conditions</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkVBox" id="vbox101"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">5</property> + <child> + <object class="GtkHBox" id="hbox3042"> + <property name="visible">True</property> + <property name="spacing">5</property> + <child> + <object class="GtkLabel" id="label401"> + <property name="visible">True</property> + <property name="label" translatable="yes">When </property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="event_combobox"> + <property name="visible">True</property> + <property name="model">liststore2</property> + <signal name="changed" handler="on_event_combobox_changed"/> + <child> + <object class="GtkCellRendererText" id="cellrenderertext2"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox3048"> + <property name="visible">True</property> + <property name="spacing">5</property> + <child> + <object class="GtkLabel" id="label400"> + <property name="visible">True</property> + <property name="label" translatable="yes">for </property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="recipient_type_combobox"> + <property name="visible">True</property> + <property name="model">liststore1</property> + <signal name="changed" handler="on_recipient_type_combobox_changed"/> + <child> + <object class="GtkCellRendererText" id="cellrenderertext1"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="recipient_list_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="no_show_all">True</property> + <signal name="changed" handler="on_recipient_list_entry_changed"/> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox3049"> + <property name="visible">True</property> + <child> + <object class="GtkLabel" id="label402"> + <property name="visible">True</property> + <property name="label" translatable="yes">when I'm in</property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="status_hbox"> + <property name="visible">True</property> + <property name="spacing">3</property> + <child> + <object class="GtkRadioButton" id="all_status_rb"> + <property name="label" translatable="yes">All statuses</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_status_radiobutton_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="special_status_rb"> + <property name="label" translatable="yes">One or more special statuses...</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">all_status_rb</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="online_cb"> + <property name="label" translatable="yes">Online / Free For Chat</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="no_show_all">True</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_status_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="away_cb"> + <property name="label" translatable="yes">Away</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="no_show_all">True</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_status_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="xa_cb"> + <property name="label" translatable="yes">Not Available</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="no_show_all">True</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_status_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="dnd_cb"> + <property name="label" translatable="yes">Busy </property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="no_show_all">True</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_status_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">5</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="invisible_cb"> + <property name="label" translatable="yes">Invisible</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="no_show_all">True</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_status_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">6</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox3053"> + <property name="visible">True</property> + <child> + <object class="GtkLabel" id="label408"> + <property name="visible">True</property> + <property name="label" translatable="yes">and I </property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="tab_opened_cb"> + <property name="label" translatable="yes">Have </property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_tab_opened_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="not_tab_opened_cb"> + <property name="label" translatable="yes">Don't have </property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_not_tab_opened_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label409"> + <property name="visible">True</property> + <property name="label" translatable="yes"> a window/tab opened with that contact </property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label392"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Actions</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame35"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkHBox" id="hbox3027"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="use_popup_cb"> + <property name="label" translatable="yes">_Inform me with a popup window</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_use_popup_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="disable_popup_cb"> + <property name="label" translatable="yes">_Disable existing popup window</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_disable_popup_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <child type="label"> + <object class="GtkFrame" id="frame38"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment93"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkVBox" id="vbox98"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkHBox" id="hbox3028"> + <property name="visible">True</property> + <property name="spacing">6</property> + <property name="homogeneous">True</property> + <child> + <object class="GtkCheckButton" id="use_sound_cb"> + <property name="label" translatable="yes">Play a sound</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_use_sound_cb_toggled"/> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="sound_file_hbox"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkEntry" id="sound_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <signal name="changed" handler="on_sound_entry_changed"/> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button4"> + <property name="label">...</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <signal name="clicked" handler="on_browse_for_sounds_button_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="play_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <signal name="clicked" handler="on_play_button_clicked"/> + <child> + <object class="GtkImage" id="image1372"> + <property name="visible">True</property> + <property name="stock">gtk-media-play</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="disable_sound_cb"> + <property name="label" translatable="yes">_Disable existing sound for this event</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_disable_sound_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label394"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Sounds</b></property> + <property name="use_markup">True</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox3032"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="use_auto_open_cb"> + <property name="label" translatable="yes">_Open chat window with user</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_use_auto_open_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="disable_auto_open_cb"> + <property name="label" translatable="yes">_Disable auto opening chat window</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_disable_auto_open_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkExpander" id="expander1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="expanded">True</property> + <child> + <object class="GtkVBox" id="vbox99"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">5</property> + <child> + <object class="GtkHBox" id="hbox3033"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="run_command_cb"> + <property name="label" translatable="yes">Launch a command</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_run_command_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="command_entry"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <signal name="changed" handler="on_command_entry_changed"/> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox3035"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="use_systray_cb"> + <property name="label" translatable="yes">_Show event in notification area</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_use_systray_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="disable_systray_cb"> + <property name="label" translatable="yes">_Disable showing event in notification area</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_disable_systray_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox3052"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="use_roster_cb"> + <property name="label" translatable="yes">_Show event in roster</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_use_roster_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="disable_roster_cb"> + <property name="label" translatable="yes">_Disable showing event in roster</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_disable_roster_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="use_urgency_hint_cb"> + <property name="label" translatable="yes">_Activate window manager's UrgencyHint to make chat window in taskbar flash</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="no_show_all">True</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_use_urgency_hint_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="disable_urgency_hint_cb"> + <property name="label" translatable="yes">_Deactivate window manager's UrgencyHint</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="no_show_all">True</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_disable_urgency_hint_cb_toggled"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">4</property> + </packing> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label395"> + <property name="visible">True</property> + <property name="label" translatable="yes">Advanced Actions</property> + </object> + </child> + </object> + <packing> + <property name="position">5</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> +</interface> diff --git a/plugins/triggers/manifest.ini b/plugins/triggers/manifest.ini new file mode 100644 index 0000000000000000000000000000000000000000..79e85f38c6a41af1f065a89da96cda7a74c0c7c9 --- /dev/null +++ b/plugins/triggers/manifest.ini @@ -0,0 +1,7 @@ +[info] +name: Triggers +short_name: triggers +version: 0.0.1 +description: Configure Gajim's behaviour for each contact +authors: Yann Leboulanger <asterix@lagaule.org> +homepage: http://trac.gajim.org/wiki/ diff --git a/plugins/triggers/triggers.py b/plugins/triggers/triggers.py new file mode 100644 index 0000000000000000000000000000000000000000..5c968516ecb3e8bc4c8706427f8d5e1fcf6337d4 --- /dev/null +++ b/plugins/triggers/triggers.py @@ -0,0 +1,637 @@ +# -*- coding: utf-8 -*- +# +## plugins/triggers/triggers.py +## +## Copyright (C) 2011 Yann Leboulanger <asterix AT lagaule.org> +## +## This file is part of Gajim. +## +## Gajim 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; version 3 only. +## +## Gajim 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 Gajim. If not, see <http://www.gnu.org/licenses/>. +## + +import gtk +import sys + +from common import gajim +from plugins import GajimPlugin +from plugins.helpers import log_calls, log +from plugins.gui import GajimPluginConfigDialog +from common import i18n +from common import ged +from common import helpers + + +class Triggers(GajimPlugin): + + @log_calls('TriggersPlugin') + def init(self): + self.config_dialog = TriggersPluginConfigDialog(self) + self.config_default_values = {} + + self.events_handlers = {'notification' : (ged.PREGUI, self._nec_notif), + 'decrypted-message-received': (ged.PREGUI2, + self._nec_decrypted_message_received)} + + def _check_rule_recipients(self, obj, rule): + rule_recipients = [t.strip() for t in rule['recipients'].split(',')] + if rule['recipient_type'] == 'contact' and obj.jid not in \ + rule_recipients: + return False + contact_groups = gajim.contacts.get_first_contact_from_jid( + obj.conn.name, obj.jid).groups + group_found = False + for group in contact_groups: + if group in rule_recipients: + group_found = True + break + if rule['recipient_type'] == 'group' and not group_found: + return False + + return True + + def _check_rule_status(self, obj, rule): + rule_statuses = rule['status'].split() + our_status = gajim.SHOW_LIST[obj.conn.connected] + if rule['status'] != 'all' and our_status not in rule_statuses: + return False + + return True + + def _check_rule_tab_opened(self, obj, rule): + if rule['tab_opened'] == 'both': + return True + tab_opened = False + if gajim.interface.msg_win_mgr.get_control(obj.jid, obj.conn.name): + tab_opened = True + if tab_opened and rule['tab_opened'] == 'no': + return False + elif not tab_opened and rule['tab_opened'] == 'yes': + return False + + return True + + def check_rule_apply_notif(self, obj, rule): + # Check notification type + notif_type = '' + if obj.notif_type == 'msg': + notif_type = 'message_received' + if notif_type != rule['event']: + return False + + # notification type is ok. Now check recipient + if not self._check_rule_recipients(obj, rule): + return False + + # recipient is ok. Now check our status + if not self._check_rule_status(obj, rule): + return False + + # our_status is ok. Now check opened chat window + if not self._check_rule_tab_opened(obj, rule): + return False + + # All is ok + return True + + def check_rule_apply_decrypted_msg(self, obj, rule): + # Check notification type + if rule['event'] != 'message_received': + return False + + # notification type is ok. Now check recipient + if not self._check_rule_recipients(obj, rule): + return False + + # recipient is ok. Now check our status + if not self._check_rule_status(obj, rule): + return False + + # our_status is ok. Now check opened chat window + if not self._check_rule_tab_opened(obj, rule): + return False + + # All is ok + return True + + def apply_rule(self, obj, rule): + if rule['sound'] == 'no': + obj.do_sound = False + elif rule['sound'] == 'yes': + obj.do_sound = True + obj.sound_event = '' + obj.sound_file = rule['sound_file'] + + if rule['popup'] == 'no': + obj.do_popup = False + elif rule['popup'] == 'yes': + obj.do_popup = True + + if rule['run_command']: + obj.do_command = True + obj.command = rule['command'] + else: + obj.do_command = False + + if rule['systray'] == 'no': + obj.show_in_notification_area = False + elif rule['systray'] == 'yes': + obj.show_in_notification_area = True + + if rule['roster'] == 'no': + obj.show_in_roster = False + elif rule['roster'] == 'yes': + obj.show_in_roster = True + +# if rule['urgency_hint'] == 'no': +# ?? not in obj actions +# elif rule['urgency_hint'] == 'yes': + + def _nec_notif(self, obj): + # check rules in order + rules_num = [int(i) for i in self.config.keys()] + rules_num.sort() + for num in rules_num: + if self.check_rule_apply_notif(obj, self.config[str(num)]): + self.apply_rule(obj, self.config[str(num)]) + # Should we stop after first valid rule ? + # break + + def _nec_decrypted_message_received(self, obj): + rules_num = [int(i) for i in self.config.keys()] + rules_num.sort() + for num in rules_num: + rule = self.config[str(num)] + if self.check_rule_apply_decrypted_msg(obj, rule): + if rule['auto_open'] == 'no': + obj.popup = False + elif rule['auto_open'] == 'yes': + obj.popup = True + + +class TriggersPluginConfigDialog(GajimPluginConfigDialog): + events_list = ['message_received']#, 'contact_connected', + #'contact_disconnected', 'contact_change_status', 'gc_msg_highlight', + #'gc_msg'] + recipient_types_list = ['contact', 'group', 'all'] + config_options = ['event', 'recipient_type', 'recipients', 'status', + 'tab_opened', 'sound', 'sound_file', 'popup', 'auto_open', + 'run_command', 'command', 'systray', 'roster', 'urgency_hint'] + + def init(self): + self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path( + 'config_dialog.ui') + self.xml = gtk.Builder() + self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, + ['vbox', 'liststore1', 'liststore2']) + vbox = self.xml.get_object('vbox') + self.child.pack_start(vbox) + + self.xml.connect_signals(self) + self.connect('hide', self.on_hide) + + def on_run(self): + # fill window + for w in ('conditions_treeview', 'config_vbox', 'event_combobox', + 'recipient_type_combobox', 'recipient_list_entry', 'delete_button', + 'status_hbox', 'use_sound_cb', 'disable_sound_cb', 'use_popup_cb', + 'disable_popup_cb', 'use_auto_open_cb', 'disable_auto_open_cb', + 'use_systray_cb', 'disable_systray_cb', 'use_roster_cb', + 'disable_roster_cb', 'tab_opened_cb', 'not_tab_opened_cb', + 'sound_entry', 'sound_file_hbox', 'up_button', 'down_button', + 'run_command_cb', 'command_entry', 'use_urgency_hint_cb', + 'disable_urgency_hint_cb'): + self.__dict__[w] = self.xml.get_object(w) + + self.config = {} + for n in self.plugin.config: + self.config[int(n)] = self.plugin.config[n] + + # Contains status checkboxes + childs = self.status_hbox.get_children() + + self.all_status_rb = childs[0] + self.special_status_rb = childs[1] + self.online_cb = childs[2] + self.away_cb = childs[3] + self.xa_cb = childs[4] + self.dnd_cb = childs[5] + self.invisible_cb = childs[6] + + if not self.conditions_treeview.get_column(0): + # window never opened + model = gtk.ListStore(int, str) + model.set_sort_column_id(0, gtk.SORT_ASCENDING) + self.conditions_treeview.set_model(model) + + # means number + col = gtk.TreeViewColumn(_('#')) + self.conditions_treeview.append_column(col) + renderer = gtk.CellRendererText() + col.pack_start(renderer, expand=False) + col.set_attributes(renderer, text=0) + + col = gtk.TreeViewColumn(_('Condition')) + self.conditions_treeview.append_column(col) + renderer = gtk.CellRendererText() + col.pack_start(renderer, expand=True) + col.set_attributes(renderer, text=1) + else: + model = self.conditions_treeview.get_model() + + model.clear() + + # Fill conditions_treeview + num = 0 + while num in self.config: + iter_ = model.append((num, '')) + path = model.get_path(iter_) + self.conditions_treeview.set_cursor(path) + self.active_num = num + self.initiate_rule_state() + self.set_treeview_string() + num += 1 + + # No rule selected at init time + self.conditions_treeview.get_selection().unselect_all() + self.active_num = -1 + self.config_vbox.set_sensitive(False) + self.delete_button.set_sensitive(False) + self.down_button.set_sensitive(False) + self.up_button.set_sensitive(False) + + def initiate_rule_state(self): + """ + Set values for all widgets + """ + if self.active_num < 0: + return + # event + value = self.config[self.active_num]['event'] + if value: + self.event_combobox.set_active(self.events_list.index(value)) + else: + self.event_combobox.set_active(-1) + # recipient_type + value = self.config[self.active_num]['recipient_type'] + if value: + self.recipient_type_combobox.set_active( + self.recipient_types_list.index(value)) + else: + self.recipient_type_combobox.set_active(-1) + # recipient + value = self.config[self.active_num]['recipients'] + if not value: + value = '' + self.recipient_list_entry.set_text(value) + # status + value = self.config[self.active_num]['status'] + if value == 'all': + self.all_status_rb.set_active(True) + else: + self.special_status_rb.set_active(True) + values = value.split() + for v in ('online', 'away', 'xa', 'dnd', 'invisible'): + if v in values: + self.__dict__[v + '_cb'].set_active(True) + else: + self.__dict__[v + '_cb'].set_active(False) + self.on_status_radiobutton_toggled(self.all_status_rb) + + # tab_opened + value = self.config[self.active_num]['tab_opened'] + self.tab_opened_cb.set_active(True) + self.not_tab_opened_cb.set_active(True) + if value == 'no': + self.tab_opened_cb.set_active(False) + elif value == 'yes': + self.not_tab_opened_cb.set_active(False) + + # sound_file + value = self.config[self.active_num]['sound_file'] + self.sound_entry.set_text(value) + + # sound, popup, auto_open, systray, roster + for option in ('sound', 'popup', 'auto_open', 'systray', 'roster', + 'urgency_hint'): + value = self.config[self.active_num][option] + if value == 'yes': + self.__dict__['use_' + option + '_cb'].set_active(True) + else: + self.__dict__['use_' + option + '_cb'].set_active(False) + if value == 'no': + self.__dict__['disable_' + option + '_cb'].set_active(True) + else: + self.__dict__['disable_' + option + '_cb'].set_active(False) + + # run_command + value = self.config[self.active_num]['run_command'] + self.run_command_cb.set_active(value) + + # command + value = self.config[self.active_num]['command'] + self.command_entry.set_text(value) + + def set_treeview_string(self): + (model, iter_) = self.conditions_treeview.get_selection().get_selected() + if not iter_: + return + event = self.event_combobox.get_active_text() + recipient_type = self.recipient_type_combobox.get_active_text() + recipient = '' + if recipient_type != 'everybody': + recipient = self.recipient_list_entry.get_text() + if self.all_status_rb.get_active(): + status = '' + else: + status = _('when I am ') + for st in ('online', 'away', 'xa', 'dnd', 'invisible'): + if self.__dict__[st + '_cb'].get_active(): + status += helpers.get_uf_show(st) + ' ' + model[iter_][1] = "When %s for %s %s %s" % (event, recipient_type, + recipient, status) + + def on_conditions_treeview_cursor_changed(self, widget): + (model, iter_) = widget.get_selection().get_selected() + if not iter_: + self.active_num = '' + return + self.active_num = model[iter_][0] + if self.active_num == '0': + self.up_button.set_sensitive(False) + else: + self.up_button.set_sensitive(True) + max = self.conditions_treeview.get_model().iter_n_children(None) + if self.active_num == max - 1: + self.down_button.set_sensitive(False) + else: + self.down_button.set_sensitive(True) + self.initiate_rule_state() + self.config_vbox.set_sensitive(True) + self.delete_button.set_sensitive(True) + + def on_new_button_clicked(self, widget): + model = self.conditions_treeview.get_model() + num = self.conditions_treeview.get_model().iter_n_children(None) + self.config[num] = {'event': '', 'recipient_type': 'all', + 'recipients': '', 'status': 'all', 'tab_opened': 'both', + 'sound': '', 'sound_file': '', 'popup': '', 'auto_open': '', + 'run_command': False, 'command': '', 'systray': '', 'roster': '', + 'urgency_hint': False} + iter_ = model.append((num, '')) + path = model.get_path(iter_) + self.conditions_treeview.set_cursor(path) + self.active_num = num + self.set_treeview_string() + self.config_vbox.set_sensitive(True) + + def on_delete_button_clicked(self, widget): + (model, iter_) = self.conditions_treeview.get_selection().get_selected() + if not iter_: + return + # up all others + iter2 = model.iter_next(iter_) + num = self.active_num + while iter2: + num = model[iter2][0] + model[iter2][0] = num - 1 + self.config[num-1] = self.config[num].copy() + iter2 = model.iter_next(iter2) + model.remove(iter_) + del self.config[num] + self.active_num = '' + self.config_vbox.set_sensitive(False) + self.delete_button.set_sensitive(False) + self.up_button.set_sensitive(False) + self.down_button.set_sensitive(False) + + def on_up_button_clicked(self, widget): + (model, iter_) = self.conditions_treeview.get_selection().get_selected() + if not iter_: + return + conf = self.config[self.active_num].copy() + self.config[self.active_num] = self.config[self.active_num - 1] + self.config[self.active_num - 1] = conf + + model[iter_][0] =self.active_num - 1 + # get previous iter + path = model.get_path(iter_) + iter_ = model.get_iter((path[0] - 1,)) + model[iter_][0] = self.active_num + self.on_conditions_treeview_cursor_changed(self.conditions_treeview) + + def on_down_button_clicked(self, widget): + (model, iter_) = self.conditions_treeview.get_selection().get_selected() + if not iter_: + return + conf = self.config[self.active_num].copy() + self.config[self.active_num] = self.config[self.active_num + 1] + self.config[self.active_num + 1] = conf + + model[iter_][0] = self.active_num + 1 + iter_ = model.iter_next(iter_) + model[iter_][0] = self.active_num + self.on_conditions_treeview_cursor_changed(self.conditions_treeview) + + def on_event_combobox_changed(self, widget): + if self.active_num < 0: + return + active = self.event_combobox.get_active() + if active == -1: + event = '' + else: + event = self.events_list[active] + self.config[self.active_num]['event'] = event + self.set_treeview_string() + + def on_recipient_type_combobox_changed(self, widget): + if self.active_num < 0: + return + recipient_type = self.recipient_types_list[ + self.recipient_type_combobox.get_active()] + self.config[self.active_num]['recipient_type'] = recipient_type + if recipient_type == 'all': + self.recipient_list_entry.hide() + else: + self.recipient_list_entry.show() + self.set_treeview_string() + + def on_recipient_list_entry_changed(self, widget): + if self.active_num < 0: + return + recipients = widget.get_text().decode('utf-8') + #TODO: do some check + self.config[self.active_num]['recipients'] = recipients + self.set_treeview_string() + + def set_status_config(self): + if self.active_num < 0: + return + status = '' + for st in ('online', 'away', 'xa', 'dnd', 'invisible'): + if self.__dict__[st + '_cb'].get_active(): + status += st + ' ' + if status: + status = status[:-1] + self.config[self.active_num]['status'] = status + self.set_treeview_string() + + def on_status_radiobutton_toggled(self, widget): + if self.active_num < 0: + return + if self.all_status_rb.get_active(): + self.config[self.active_num]['status'] = 'all' + # 'All status' clicked + for st in ('online', 'away', 'xa', 'dnd', 'invisible'): + self.__dict__[st + '_cb'].hide() + + self.special_status_rb.show() + else: + self.set_status_config() + # 'special status' clicked + for st in ('online', 'away', 'xa', 'dnd', 'invisible'): + self.__dict__[st + '_cb'].show() + + self.special_status_rb.hide() + self.set_treeview_string() + + def on_status_cb_toggled(self, widget): + if self.active_num < 0: + return + self.set_status_config() + + # tab_opened OR (not xor) not_tab_opened must be active + def on_tab_opened_cb_toggled(self, widget): + if self.active_num < 0: + return + if self.tab_opened_cb.get_active(): + if self.not_tab_opened_cb.get_active(): + self.config[self.active_num]['tab_opened'] = 'both' + else: + self.config[self.active_num]['tab_opened'] = 'yes' + else: + self.not_tab_opened_cb.set_active(True) + self.config[self.active_num]['tab_opened'] = 'no' + + def on_not_tab_opened_cb_toggled(self, widget): + if self.active_num < 0: + return + if self.not_tab_opened_cb.get_active(): + if self.tab_opened_cb.get_active(): + self.config[self.active_num]['tab_opened'] = 'both' + else: + self.config[self.active_num]['tab_opened'] = 'no' + else: + self.tab_opened_cb.set_active(True) + self.config[self.active_num]['tab_opened'] = 'yes' + + def on_use_it_toggled(self, widget, oposite_widget, option): + if widget.get_active(): + if oposite_widget.get_active(): + oposite_widget.set_active(False) + self.config[self.active_num][option] = 'yes' + elif oposite_widget.get_active(): + self.config[self.active_num][option] = 'no' + else: + self.config[self.active_num][option] = '' + + def on_disable_it_toggled(self, widget, oposite_widget, option): + if widget.get_active(): + if oposite_widget.get_active(): + oposite_widget.set_active(False) + self.config[self.active_num][option] = 'no' + elif oposite_widget.get_active(): + self.config[self.active_num][option] = 'yes' + else: + self.config[self.active_num][option] = '' + + def on_use_sound_cb_toggled(self, widget): + self.on_use_it_toggled(widget, self.disable_sound_cb, 'sound') + if widget.get_active(): + self.sound_file_hbox.set_sensitive(True) + else: + self.sound_file_hbox.set_sensitive(False) + + def on_browse_for_sounds_button_clicked(self, widget, data=None): + if self.active_num < 0: + return + + def on_ok(widget, path_to_snd_file): + dialog.destroy() + if not path_to_snd_file: + path_to_snd_file = '' + self.config[self.active_num]['sound_file'] = path_to_snd_file + self.sound_entry.set_text(path_to_snd_file) + + path_to_snd_file = self.sound_entry.get_text().decode('utf-8') + path_to_snd_file = os.path.join(os.getcwd(), path_to_snd_file) + dialog = SoundChooserDialog(path_to_snd_file, on_ok) + + def on_play_button_clicked(self, widget): + helpers.play_sound_file(self.sound_entry.get_text().decode('utf-8')) + + def on_disable_sound_cb_toggled(self, widget): + self.on_disable_it_toggled(widget, self.use_sound_cb, 'sound') + + def on_sound_entry_changed(self, widget): + self.config[self.active_num]['sound_file'] = widget.get_text().\ + decode('utf-8') + + def on_use_popup_cb_toggled(self, widget): + self.on_use_it_toggled(widget, self.disable_popup_cb, 'popup') + + def on_disable_popup_cb_toggled(self, widget): + self.on_disable_it_toggled(widget, self.use_popup_cb, 'popup') + + def on_use_auto_open_cb_toggled(self, widget): + self.on_use_it_toggled(widget, self.disable_auto_open_cb, 'auto_open') + + def on_disable_auto_open_cb_toggled(self, widget): + self.on_disable_it_toggled(widget, self.use_auto_open_cb, 'auto_open') + + def on_run_command_cb_toggled(self, widget): + self.config[self.active_num]['run_command'] = widget.get_active() + if widget.get_active(): + self.command_entry.set_sensitive(True) + else: + self.command_entry.set_sensitive(False) + + def on_command_entry_changed(self, widget): + self.config[self.active_num]['command'] = widget.get_text().\ + decode('utf-8') + + def on_use_systray_cb_toggled(self, widget): + self.on_use_it_toggled(widget, self.disable_systray_cb, 'systray') + + def on_disable_systray_cb_toggled(self, widget): + self.on_disable_it_toggled(widget, self.use_systray_cb, 'systray') + + def on_use_roster_cb_toggled(self, widget): + self.on_use_it_toggled(widget, self.disable_roster_cb, 'roster') + + def on_disable_roster_cb_toggled(self, widget): + self.on_disable_it_toggled(widget, self.use_roster_cb, 'roster') + + def on_use_urgency_hint_cb_toggled(self, widget): + self.on_use_it_toggled(widget, self.disable_urgency_hint_cb, + 'uregency_hint') + + def on_disable_urgency_hint_cb_toggled(self, widget): + self.on_disable_it_toggled(widget, self.use_urgency_hint_cb, + 'uregency_hint') + + def on_hide(self, widget): + # save config + for n in self.plugin.config: + del self.plugin.config[n] + for n in self.config: + self.plugin.config[str(n)] = self.config[n]