merge diff from trunk to pep branch

This commit is contained in:
Yann Leboulanger 2007-08-09 15:39:18 +00:00
parent fca81d8795
commit ea24ee82e5
104 changed files with 17800 additions and 16783 deletions

View file

@ -3,10 +3,10 @@ CURRENT DEVELOPERS:
Yann Le Boulanger (asterix AT lagaule.org)
Nikos Kouremenos (kourem AT gmail.com)
Travis Shirk (travis AT pobox.com)
Jean-Marie Traissard (jim AT lapin.org)
PAST DEVELOPERS:
Jean-Marie Traissard (jim AT lapin.org)
Stefan Bethge (stefan AT lanpartei.de)
Dimitur Kirov (dkirov AT gmail.com)
Vincent Hanquez (tab AT snarc.org)

View file

@ -1,5 +1,15 @@
Gajim 0.11.1 (XX February 2007)
Gajim 0.11.1 (18 February 2007)
* Fixes in gajim-remote and the way XMPP URI are handled
* Fix Idle under Windows
* Fix Gajim under non-ascii languages Windows
* Fix International Domain Name usage
* Fix when removing active privacy list
* Fix problem with adhoc command and multi-step forms
* Fixed avatars cache problems in group chats
* KDE integration for XMPP URI
* Support of Banshee Music player
* Support of XEP-0202 (Entity Time)
* Support of XEP-0199 (XMPP Ping)
Gajim 0.11 (19 December 2006)
* New build system, using GNU autotools. See README.html

View file

@ -1,5 +1,5 @@
AC_INIT([Gajim - A Jabber Instant Messager],
[0.11.1.0],[http://trac.gajim.org/],[gajim])
[0.11.1.5],[http://trac.gajim.org/],[gajim])
AC_PREREQ([2.59])
AM_INIT_AUTOMAKE([1.8])
AC_CONFIG_HEADER(config.h)

View file

@ -1,177 +1,121 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkMenu" id="account_context_menu">
<child>
<widget class="GtkImageMenuItem" id="status_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1235">
<property name="visible">True</property>
<property name="stock">gtk-network</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="pep_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Personal Events</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1235">
<property name="visible">True</property>
<property name="stock">gtk-home</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="join_group_chat_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Group Chat</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1236">
<property name="visible">True</property>
<property name="stock">gtk-connect</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="open_gmail_inbox_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Open Gmail Inbox</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="new_message_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send Single _Message...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1237">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_contact_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Add Contact...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1238">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="service_discovery_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Discover Services...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1239">
<property name="visible">True</property>
<property name="stock">gtk-find</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="execute_command_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Execute Command...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1246">
<property name="visible">True</property>
<property name="stock">gtk-execute</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="edit_account_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Modify Account...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1240">
<property name="visible">True</property>
<property name="stock">gtk-preferences</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="account_context_menu">
<child>
<widget class="GtkImageMenuItem" id="status_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1235">
<property name="visible">True</property>
<property name="stock">gtk-network</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="pep_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Personal Events</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="menu-item-image7">
<property name="stock">gtk-home</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="seperator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="open_gmail_inbox_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Open Gmail Inbox</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="join_group_chat_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Group Chat</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1236">
<property name="visible">True</property>
<property name="stock">gtk-connect</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="service_discovery_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Discover Services</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1239">
<property name="visible">True</property>
<property name="stock">gtk-find</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="execute_command_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Execute Command</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1246">
<property name="visible">True</property>
<property name="stock">gtk-execute</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="seperator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_contact_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Add Contact</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1238">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="edit_account_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Modify Account...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1240">
<property name="visible">True</property>
<property name="stock">gtk-preferences</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -53,6 +53,7 @@ to the Jabber network.</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">I already have an account I want to use</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
@ -66,6 +67,7 @@ to the Jabber network.</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">I want to _register for a new account</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<property name="group">use_existing_account_radiobutton</property>
</widget>
@ -142,96 +144,30 @@ to the Jabber network.</property>
<placeholder/>
</child>
<child>
<widget class="GtkLabel" id="jid_label">
<widget class="GtkEntry" id="username_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="xalign">0</property>
<property name="use_markup">True</property>
<property name="selectable">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label258">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Your JID:</property>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label262">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Username:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">username_entry</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="password_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="has_focus">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
<signal name="changed" handler="on_username_entry_changed"/>
<signal name="key_press_event" handler="on_username_entry_key_press_event"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="save_password_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip" translatable="yes">If checked, Gajim will remember the password for this account</property>
<property name="label" translatable="yes">Save pass_word</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_save_password_checkbutton_toggled"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label263">
<widget class="GtkLabel" id="label267">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Password:</property>
<property name="label" translatable="yes">_Server:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">password_entry</property>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
@ -255,31 +191,98 @@ to the Jabber network.</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label267">
<widget class="GtkLabel" id="label263">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Server:</property>
<property name="label" translatable="yes">_Password:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">password_entry</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="username_entry">
<widget class="GtkCheckButton" id="save_password_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="tooltip" translatable="yes">If checked, Gajim will remember the password for this account</property>
<property name="label" translatable="yes">Save pass_word</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_save_password_checkbutton_toggled"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="password_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="invisible_char">*</property>
<signal name="changed" handler="on_username_entry_changed"/>
<signal name="key_press_event" handler="on_username_entry_key_press_event"/>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label262">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Username:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">username_entry</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label258">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Your JID:</property>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="jid_label">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="xalign">0</property>
<property name="use_markup">True</property>
<property name="selectable">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
@ -357,6 +360,7 @@ to the Jabber network.</property>
<property name="tooltip" translatable="yes">Click to see features (like MSN, ICQ transports) of jabber servers</property>
<property name="label" translatable="yes">Servers Features</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_register_server_features_button_clicked"/>
</widget>
<packing>
@ -384,6 +388,62 @@ to the Jabber network.</property>
<property name="n_columns">3</property>
<property name="column_spacing">5</property>
<property name="row_spacing">5</property>
<child>
<widget class="GtkLabel" id="label381">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Prox_y:</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="proxies_combobox">
<property name="visible">True</property>
<property name="items">None</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="manage_proxies_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Manage...</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_manage_proxies_button_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="custom_host_port_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Use custom hostname/port</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_custom_host_port_checkbutton_toggled"/>
</widget>
<packing>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="custom_host_hbox">
<property name="visible">True</property>
@ -446,60 +506,6 @@ to the Jabber network.</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="custom_host_port_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Use custom hostname/port</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_custom_host_port_checkbutton_toggled"/>
</widget>
<packing>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkButton" id="manage_proxies_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Manage...</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_manage_proxies_button_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="proxies_combobox">
<property name="visible">True</property>
<property name="items">None</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label381">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Prox_y:</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
</child>
<child>
@ -637,6 +643,7 @@ Please wait...</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Connect when I press Finish</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</widget>
@ -652,6 +659,7 @@ Please wait...</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Set my profile when I connect</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</widget>
@ -692,6 +700,7 @@ Please wait...</property>
<property name="can_default">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_cancel_button_clicked"/>
</widget>
</child>
@ -703,6 +712,7 @@ Please wait...</property>
<property name="can_default">True</property>
<property name="label">gtk-go-back</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_back_button_clicked"/>
</widget>
<packing>
@ -717,6 +727,7 @@ Please wait...</property>
<property name="has_default">True</property>
<property name="label">gtk-go-forward</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_forward_button_clicked"/>
</widget>
<packing>
@ -727,6 +738,8 @@ Please wait...</property>
<widget class="GtkButton" id="advanced_button">
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="no_show_all">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_advanced_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment96">
@ -772,6 +785,8 @@ Please wait...</property>
<widget class="GtkButton" id="finish_button">
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="no_show_all">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_finish_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment87">

File diff suppressed because it is too large Load diff

View file

@ -1,527 +1,335 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkWindow" id="add_new_contact_window">
<property name="border_width">6</property>
<property name="title" translatable="yes">Add New Contact</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">False</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="key_press_event" handler="on_add_new_contact_window_key_press_event" last_modification_time="Thu, 28 Apr 2005 12:59:51 GMT"/>
<signal name="destroy" handler="on_add_new_contact_window_destroy" last_modification_time="Thu, 03 Aug 2006 15:49:22 GMT"/>
<child>
<widget class="GtkVBox" id="vbox8">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="prompt_label">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="account_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="account_label">
<property name="visible">True</property>
<property name="label" translatable="yes">A_ccount:</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="account_combobox">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<property name="add_tearoffs">False</property>
<property name="focus_on_click">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="protocol_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="protocol_label">
<property name="visible">True</property>
<property name="label" translatable="yes">_Protocol:</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">uid_entry</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="protocol_combobox">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<property name="add_tearoffs">False</property>
<property name="focus_on_click">True</property>
<signal name="changed" handler="on_protocol_combobox_changed" last_modification_time="Wed, 23 Mar 2005 13:13:12 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="protocol_jid_combobox">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<property name="add_tearoffs">False</property>
<property name="focus_on_click">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="subscription_table">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="homogeneous">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<widget class="GtkLabel" id="uid_label">
<property name="visible">True</property>
<property name="label" translatable="yes">_User ID:</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">uid_entry</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="uid_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
<signal name="changed" handler="on_uid_entry_changed" last_modification_time="Mon, 28 Feb 2005 23:05:24 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label188">
<property name="visible">True</property>
<property name="label" translatable="yes">_Nickname:</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">nickname_entry</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="nickname_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label223">
<property name="visible">True</property>
<property name="label" translatable="yes">_Group:</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkComboBoxEntry" id="group_comboboxentry">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<property name="add_tearoffs">False</property>
<property name="has_frame">True</property>
<property name="focus_on_click">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="auto_authorize_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">A_llow this contact to view my status</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">True</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTextView" id="message_textview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="overwrite">False</property>
<property name="accepts_tab">True</property>
<property name="justification">GTK_JUSTIFY_LEFT</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="cursor_visible">True</property>
<property name="pixels_above_lines">0</property>
<property name="pixels_below_lines">0</property>
<property name="pixels_inside_wrap">0</property>
<property name="left_margin">0</property>
<property name="right_margin">0</property>
<property name="indent">0</property>
<property name="text" translatable="yes">I would like to add you to my contact list.</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="register_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label224">
<property name="visible">True</property>
<property name="label" translatable="yes">You have to register with this transport
<widget class="GtkWindow" id="add_new_contact_window">
<property name="border_width">6</property>
<property name="title" translatable="yes">Add New Contact</property>
<property name="resizable">False</property>
<signal name="key_press_event" handler="on_add_new_contact_window_key_press_event"/>
<signal name="destroy" handler="on_add_new_contact_window_destroy"/>
<child>
<widget class="GtkVBox" id="vbox8">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="prompt_label">
<property name="visible">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="account_hbox">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="account_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">A_ccount:</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="account_combobox">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="protocol_hbox">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="protocol_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Protocol:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">uid_entry</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="protocol_combobox">
<property name="visible">True</property>
<signal name="changed" handler="on_protocol_combobox_changed"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="protocol_jid_combobox">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="items" translatable="yes"></property>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="subscription_table">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="border_width">6</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="column_spacing">6</property>
<property name="row_spacing">6</property>
<child>
<widget class="GtkLabel" id="uid_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_User ID:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">uid_entry</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="uid_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
<signal name="changed" handler="on_uid_entry_changed"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label188">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Nickname:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">nickname_entry</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="nickname_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label223">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Group:</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkComboBoxEntry" id="group_comboboxentry">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<child internal-child="entry">
<widget class="GtkEntry" id="comboboxentry-entry1">
</widget>
</child>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="auto_authorize_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">A_llow this contact to view my status</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="border_width">6</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
<child>
<widget class="GtkTextView" id="message_textview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="text" translatable="yes">I would like to add you to my contact list.</property>
</widget>
</child>
</widget>
<packing>
<property name="position">5</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="register_hbox">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label224">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">You have to register with this transport
to be able to add a contact from this
protocol. Click on register button to
proceed.</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="register_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">_Register</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_register_button_clicked" last_modification_time="Thu, 03 Aug 2006 14:29:57 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="connected_label">
<property name="visible">True</property>
<property name="label" translatable="yes">You must be connected to the transport to be able
<property name="wrap">True</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="register_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label" translatable="yes">_Register</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_register_button_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">6</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="connected_label">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">You must be connected to the transport to be able
to add a contact from this protocol.</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="border_width">5</property>
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="spacing">12</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Mon, 28 Feb 2005 22:46:06 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="add_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_add_button_clicked" last_modification_time="Thu, 03 Aug 2006 14:27:55 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">7</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="spacing">12</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_cancel_button_clicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="add_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_add_button_clicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">8</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

File diff suppressed because it is too large Load diff

View file

@ -65,16 +65,6 @@
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="compact_view_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Compact View Alt+C</property>
<property name="use_underline">True</property>
<property name="active">False</property>
<signal name="activate" handler="_on_compact_view_menuitem_activate" last_modification_time="Tue, 03 Jan 2006 04:26:30 GMT"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_to_roster_menuitem">
<property name="visible">True</property>

View file

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.2.2 on Sat Jul 7 01:08:57 2007 by stephan@ThinkPad-->
<glade-interface>
<widget class="GtkWindow" id="features_window">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">12</property>
<property name="title" translatable="yes">Features</property>
<property name="default_width">300</property>
<property name="default_height">530</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;List of possible features in Gajim:&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_OUT</property>
<child>
<widget class="GtkTreeView" id="features_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="headers_clickable">True</property>
<signal name="cursor_changed" handler="on_features_treeview_cursor_changed"/>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="border_width">3</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="border_width">6</property>
<property name="xalign">0</property>
<property name="xscale">0.5</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkLabel" id="feature_desc_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="wrap">True</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Description&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-close</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_close_button_clicked"/>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -1,392 +1,281 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkWindow" id="file_transfers_window">
<property name="border_width">12</property>
<property name="title" translatable="yes">File Transfers</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<accessibility>
<atkproperty name="AtkObject::accessible_name" translatable="yes">File Transfers</atkproperty>
<atkproperty name="AtkObject::accessible_description" translatable="yes">Shows a list of file transfers between you and others</atkproperty>
</accessibility>
<signal name="delete_event" handler="on_file_transfers_dialog_delete_event" last_modification_time="Wed, 31 Aug 2005 22:17:01 GMT"/>
<signal name="key_press_event" handler="on_file_transfers_window_key_press_event" last_modification_time="Mon, 15 Aug 2005 12:42:20 GMT"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkScrolledWindow" id="transfers_scrolledwindow">
<property name="width_request">460</property>
<property name="height_request">150</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="transfers_list">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">True</property>
<property name="reorderable">False</property>
<property name="enable_search">False</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<accessibility>
<atkproperty name="AtkObject::accessible_name" translatable="yes">file transfers list</atkproperty>
<atkproperty name="AtkObject::accessible_description" translatable="yes">A list of active, completed and stopped file transfers</atkproperty>
</accessibility>
<signal name="row_activated" handler="on_transfers_list_row_activated" last_modification_time="Wed, 31 Aug 2005 22:32:25 GMT"/>
<signal name="motion_notify_event" handler="on_transfers_list_motion_notify_event" last_modification_time="Wed, 31 Aug 2005 22:33:10 GMT"/>
<signal name="leave_notify_event" handler="on_transfers_list_leave_notify_event" last_modification_time="Wed, 31 Aug 2005 22:33:23 GMT"/>
<signal name="button_press_event" handler="on_transfers_list_button_press_event" last_modification_time="Wed, 31 Aug 2005 22:33:23 GMT"/>
<signal name="button_release_event" handler="on_transfers_list_button_release_event" last_modification_time="Wed, 31 Aug 2005 22:33:23 GMT"/>
<signal name="key_press_event" handler="on_transfers_list_key_press_event" last_modification_time="Wed, 31 Aug 2005 22:33:23 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="spacing">6</property>
<child>
<widget class="GtkButton" id="cleanup_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="tooltip" translatable="yes">Removes completed, cancelled and failed file transfers from the list</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<accessibility>
<atkproperty name="AtkObject::accessible_name" translatable="yes">Remove file transfer from the list.</atkproperty>
<atkproperty name="AtkObject::accessible_description" translatable="yes">This action removes single file transfer from the list. If the transfer is active, it is first stopped and then removed</atkproperty>
</accessibility>
<signal name="clicked" handler="on_cleanup_button_clicked" last_modification_time="Sat, 03 Sep 2005 14:03:13 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment91">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox2992">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1143">
<property name="visible">True</property>
<property name="stock">gtk-clear</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label358">
<property name="visible">True</property>
<property name="label" translatable="yes">Clean _up</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="pause_restore_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_pause_restore_button_clicked" last_modification_time="Wed, 31 Aug 2005 22:31:50 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment92">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox2993">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1147">
<property name="visible">True</property>
<property name="stock">gtk-media-pause</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label359">
<property name="visible">True</property>
<property name="label" translatable="yes">_Pause</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="tooltip" translatable="yes">Cancels the selected file transfer and removes incomplete file</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<accessibility>
<atkproperty name="AtkObject::accessible_name" translatable="yes">Cancel file transfer</atkproperty>
<atkproperty name="AtkObject::accessible_description" translatable="yes">Cancels the selected file transfer</atkproperty>
</accessibility>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Tue, 09 Aug 2005 17:07:31 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Hides the window</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Wed, 03 Aug 2005 15:51:45 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="notify_ft_complete_checkbox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">_Notify me when a file transfer is complete</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
<accessibility>
<atkproperty name="AtkObject::accessible_description" translatable="yes">When a file transfer is complete show a popup notification</atkproperty>
</accessibility>
<signal name="toggled" handler="on_notify_ft_complete_checkbox_toggled" last_modification_time="Wed, 31 Aug 2005 22:52:01 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="file_transfers_menu">
<child>
<widget class="GtkImageMenuItem" id="remove_menuitem">
<property name="label">gtk-remove</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_remove_menuitem_activate" last_modification_time="Fri, 09 Sep 2005 22:48:03 GMT"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="continue_menuitem">
<property name="label" translatable="yes">_Continue</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_continue_menuitem_activate" last_modification_time="Fri, 09 Sep 2005 22:48:03 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1144">
<property name="visible">True</property>
<property name="stock">gtk-media-play</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="pause_menuitem">
<property name="label" translatable="yes">_Pause</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_pause_menuitem_activate" last_modification_time="Fri, 09 Sep 2005 22:48:03 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1145">
<property name="visible">True</property>
<property name="stock">gtk-media-pause</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="cancel_menuitem">
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_cancel_menuitem_activate" last_modification_time="Wed, 10 Aug 2005 18:57:29 GMT"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator11">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="open_folder_menuitem">
<property name="label" translatable="yes">_Open Containing Folder</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_open_folder_menuitem_activate" last_modification_time="Fri, 09 Sep 2005 22:48:03 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1146">
<property name="visible">True</property>
<property name="stock">gtk-directory</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
<widget class="GtkWindow" id="file_transfers_window">
<property name="border_width">12</property>
<property name="title" translatable="yes">File Transfers</property>
<accessibility>
<atkproperty name="AtkObject::accessible_name" translatable="yes">File Transfers</atkproperty>
<atkproperty name="AtkObject::accessible_description" translatable="yes">Shows a list of file transfers between you and others</atkproperty>
</accessibility>
<signal name="key_press_event" handler="on_file_transfers_window_key_press_event"/>
<signal name="delete_event" handler="on_file_transfers_dialog_delete_event"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkScrolledWindow" id="transfers_scrolledwindow">
<property name="width_request">460</property>
<property name="height_request">150</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="transfers_list">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="rules_hint">True</property>
<property name="enable_search">False</property>
<accessibility>
<atkproperty name="AtkObject::accessible_name" translatable="yes">file transfers list</atkproperty>
<atkproperty name="AtkObject::accessible_description" translatable="yes">A list of active, completed and stopped file transfers</atkproperty>
</accessibility>
<signal name="leave_notify_event" handler="on_transfers_list_leave_notify_event"/>
<signal name="button_press_event" handler="on_transfers_list_button_press_event"/>
<signal name="motion_notify_event" handler="on_transfers_list_motion_notify_event"/>
<signal name="key_press_event" handler="on_transfers_list_key_press_event"/>
<signal name="row_activated" handler="on_transfers_list_row_activated"/>
<signal name="button_release_event" handler="on_transfers_list_button_release_event"/>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkCheckButton" id="notify_ft_complete_checkbox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">_Notify me when a file transfer is complete</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<accessibility>
<atkproperty name="AtkObject::accessible_description" translatable="yes">When a file transfer is complete show a popup notification</atkproperty>
</accessibility>
<signal name="toggled" handler="on_notify_ft_complete_checkbox_toggled"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="spacing">6</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cleanup_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="tooltip" translatable="yes">Removes completed, cancelled and failed file transfers from the list</property>
<property name="response_id">0</property>
<accessibility>
<atkproperty name="AtkObject::accessible_name" translatable="yes">Remove file transfer from the list.</atkproperty>
<atkproperty name="AtkObject::accessible_description" translatable="yes">This action removes single file transfer from the list. If the transfer is active, it is first stopped and then removed</atkproperty>
</accessibility>
<signal name="clicked" handler="on_cleanup_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment91">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<widget class="GtkHBox" id="hbox2992">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1143">
<property name="visible">True</property>
<property name="stock">gtk-clear</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label358">
<property name="visible">True</property>
<property name="label" translatable="yes">Clean _up</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="pause_restore_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_pause_restore_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment92">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<widget class="GtkHBox" id="hbox2993">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1147">
<property name="visible">True</property>
<property name="stock">gtk-media-pause</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label359">
<property name="visible">True</property>
<property name="label" translatable="yes">_Pause</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="tooltip" translatable="yes">Cancels the selected file transfer and removes incomplete file</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<accessibility>
<atkproperty name="AtkObject::accessible_name" translatable="yes">Cancel file transfer</atkproperty>
<atkproperty name="AtkObject::accessible_description" translatable="yes">Cancels the selected file transfer</atkproperty>
</accessibility>
<signal name="clicked" handler="on_cancel_button_clicked"/>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="can_default">True</property>
<property name="tooltip" translatable="yes">Hides the window</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_close_button_clicked"/>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="file_transfers_menu">
<child>
<widget class="GtkImageMenuItem" id="remove_menuitem">
<property name="label">gtk-remove</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_remove_menuitem_activate"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="continue_menuitem">
<property name="no_show_all">True</property>
<property name="label" translatable="yes">_Continue</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_continue_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image1144">
<property name="visible">True</property>
<property name="stock">gtk-media-play</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="pause_menuitem">
<property name="label" translatable="yes">_Pause</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_pause_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image1145">
<property name="visible">True</property>
<property name="stock">gtk-media-pause</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="cancel_menuitem">
<property name="label">gtk-cancel</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_cancel_menuitem_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator11">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="open_folder_menuitem">
<property name="label" translatable="yes">_Open Containing Folder</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_open_folder_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image1146">
<property name="visible">True</property>
<property name="stock">gtk-missing-image</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -408,7 +408,7 @@ Chat Banner</property>
</child>
<child>
<widget class="GtkTable" id="table36">
<widget class="GtkTable" id="theme_options_table">
<property name="visible">True</property>
<property name="n_rows">9</property>
<property name="n_columns">2</property>

View file

@ -10,17 +10,17 @@
<child internal-child="image">
<widget class="GtkImage" id="image1409">
<property name="visible">True</property>
<property name="stock">gtk-redo</property>
<property name="stock">gtk-edit</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="manage_room_menuitem">
<widget class="GtkImageMenuItem" id="manage_room_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Manage room</property>
<property name="label" translatable="yes">_Manage Room</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu1">
@ -60,7 +60,7 @@
<child>
<widget class="GtkImageMenuItem" id="destroy_room_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Destroy room</property>
<property name="label" translatable="yes">_Destroy Room</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1407">
@ -73,30 +73,25 @@
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="compact_view_menuitem">
<property name="label" translatable="yes">_Compact View Alt+C</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="minimize_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Minimize</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1411">
<widget class="GtkImage" id="menu-item-image8">
<property name="visible">True</property>
<property name="stock">gtk-goto-bottom</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-properties</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separatormenuitem2">
<widget class="GtkCheckMenuItem" id="minimize_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Minimize on close</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="seperator">
<property name="visible">True</property>
</widget>
</child>
@ -113,6 +108,11 @@
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="seperator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="tooltip" translatable="yes">Click to see past conversation in this room</property>

View file

@ -17,20 +17,6 @@
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_to_roster_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Add to Roster</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1052">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="group_chat_actions_menuitem">
<property name="visible">True</property>
@ -101,6 +87,39 @@
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_file_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Send _File</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="menu-item-image4">
<property name="stock">gtk-save</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator6">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_to_roster_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Add to Roster</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1052">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator6">
<property name="visible">True</property>

View file

@ -1,392 +1,249 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkWindow" id="history_manager_window">
<property name="border_width">6</property>
<property name="title" translatable="yes">Gajim History Logs Manager</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">650</property>
<property name="default_height">500</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<signal name="delete_event" handler="on_history_manager_window_delete_event" last_modification_time="Thu, 02 Feb 2006 21:52:04 GMT"/>
<child>
<widget class="GtkVBox" id="vbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkHPaned" id="hpaned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">200</property>
<child>
<widget class="GtkScrolledWindow" id="jids_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="jids_listview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">True</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal name="key_press_event" handler="on_jids_listview_key_press_event" last_modification_time="Sat, 04 Feb 2006 22:27:05 GMT"/>
<signal name="button_press_event" handler="on_listview_button_press_event" last_modification_time="Fri, 10 Feb 2006 15:11:21 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">False</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="welcome_label">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;big&gt;&lt;b&gt;Welcome to Gajim History Logs Manager&lt;/b&gt;&lt;/big&gt;
<widget class="GtkWindow" id="history_manager_window">
<property name="border_width">6</property>
<property name="title" translatable="yes">Gajim History Logs Manager</property>
<property name="default_width">650</property>
<property name="default_height">500</property>
<signal name="delete_event" handler="on_history_manager_window_delete_event"/>
<child>
<widget class="GtkVBox" id="vbox">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkHPaned" id="hpaned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">200</property>
<child>
<widget class="GtkScrolledWindow" id="jids_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="jids_listview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="rules_hint">True</property>
<signal name="button_press_event" handler="on_listview_button_press_event"/>
<signal name="key_press_event" handler="on_jids_listview_key_press_event"/>
</widget>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="welcome_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;big&gt;&lt;b&gt;Welcome to Gajim History Logs Manager&lt;/b&gt;&lt;/big&gt;
You can select logs from the left and/or search database from below.
&lt;b&gt;WARNING:&lt;/b&gt;
If you plan to do massive deletions, please make sure Gajim is not running. Generally avoid deletions with contacts you currently chat with.</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="logs_scrolledwindow">
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="logs_listview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">True</property>
<property name="reorderable">False</property>
<property name="enable_search">False</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal name="button_press_event" handler="on_listview_button_press_event" last_modification_time="Fri, 10 Feb 2006 15:11:21 GMT"/>
<signal name="key_press_event" handler="on_logs_listview_key_press_event" last_modification_time="Sat, 04 Feb 2006 15:01:22 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="search_results_scrolledwindow">
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="search_results_listview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">True</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal name="button_press_event" handler="on_listview_button_press_event" last_modification_time="Fri, 10 Feb 2006 15:11:21 GMT"/>
<signal name="row_activated" handler="on_search_results_listview_row_activated" last_modification_time="Sat, 04 Feb 2006 23:48:46 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkEntry" id="search_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="search_db_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_search_db_button_clicked" last_modification_time="Thu, 02 Feb 2006 21:54:19 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-find</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Search Database</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="context_menu">
<child>
<widget class="GtkMenuItem" id="export_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Export</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_export_menuitem_activate" last_modification_time="Fri, 10 Feb 2006 15:15:30 GMT"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="delete_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Delete</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="stock">gtk-remove</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
<widget class="GtkFileChooserDialog" id="filechooserdialog">
<property name="action">GTK_FILE_CHOOSER_ACTION_SAVE</property>
<property name="local_only">True</property>
<property name="select_multiple">False</property>
<property name="show_hidden">False</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">24</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="save_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-save</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
<property name="use_markup">True</property>
<property name="wrap">True</property>
</widget>
</child>
<child>
<widget class="GtkScrolledWindow" id="logs_scrolledwindow">
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="logs_listview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="rules_hint">True</property>
<property name="enable_search">False</property>
<signal name="button_press_event" handler="on_listview_button_press_event"/>
<signal name="key_press_event" handler="on_logs_listview_key_press_event"/>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="search_results_scrolledwindow">
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="search_results_listview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="rules_hint">True</property>
<signal name="button_press_event" handler="on_listview_button_press_event"/>
<signal name="row_activated" handler="on_search_results_listview_row_activated"/>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkHBox" id="hbox">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkEntry" id="search_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="search_db_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_search_db_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-find</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Search Database</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="context_menu">
<child>
<widget class="GtkMenuItem" id="export_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Export</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_export_menuitem_activate"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="delete_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Delete</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="stock">gtk-remove</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
<widget class="GtkFileChooserDialog" id="filechooserdialog">
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="action">GTK_FILE_CHOOSER_ACTION_SAVE</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="spacing">24</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="save_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="label">gtk-save</property>
<property name="use_stock">True</property>
<property name="response_id">-5</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -1,499 +1,323 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkWindow" id="manage_bookmarks_window">
<property name="border_width">12</property>
<property name="title" translatable="yes">Manage Bookmarks</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">550</property>
<property name="default_height">300</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="button_press_event" handler="on_manage_bookmarks_window_button_press_event" last_modification_time="Sun, 12 Jun 2005 15:52:20 GMT"/>
<child>
<widget class="GtkVBox" id="vbox86">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">12</property>
<child>
<widget class="GtkHBox" id="hbox2965">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">12</property>
<child>
<widget class="GtkVBox" id="vbox94">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow37">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="bookmarks_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox25">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="spacing">6</property>
<child>
<widget class="GtkButton" id="add_bookmark_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_add_bookmark_button_clicked" last_modification_time="Wed, 08 Jun 2005 17:20:25 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="remove_bookmark_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-remove</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_remove_bookmark_button_clicked" last_modification_time="Wed, 08 Jun 2005 17:20:20 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table33">
<property name="visible">True</property>
<property name="n_rows">7</property>
<property name="n_columns">2</property>
<property name="homogeneous">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
<widget class="GtkCheckButton" id="autojoin_checkbutton">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">If checked, Gajim will join this group chat on startup</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Auto join</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_autojoin_checkbutton_toggled" last_modification_time="Wed, 08 Jun 2005 17:20:40 GMT"/>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label318">
<property name="visible">True</property>
<property name="label" translatable="yes">Password:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="pass_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">False</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="server_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label317">
<property name="visible">True</property>
<property name="label" translatable="yes">Server:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label316">
<property name="visible">True</property>
<property name="label" translatable="yes">Room:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="room_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="nick_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label315">
<property name="visible">True</property>
<property name="label" translatable="yes">Nickname:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label325">
<property name="visible">True</property>
<property name="label" translatable="yes">Title:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="title_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label326">
<property name="visible">True</property>
<property name="label" translatable="yes">Print status:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="print_status_combobox">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<property name="add_tearoffs">False</property>
<property name="focus_on_click">True</property>
<signal name="changed" handler="on_print_status_combobox_changed" last_modification_time="Sun, 07 May 2006 20:44:46 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">fill</property>
<property name="y_options">fill</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox20">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="spacing">12</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Wed, 08 Jun 2005 17:18:48 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_ok_button_clicked" last_modification_time="Wed, 08 Jun 2005 17:18:52 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkWindow" id="manage_bookmarks_window">
<property name="border_width">12</property>
<property name="title" translatable="yes">Manage Bookmarks</property>
<property name="default_width">550</property>
<property name="default_height">300</property>
<signal name="button_press_event" handler="on_manage_bookmarks_window_button_press_event"/>
<child>
<widget class="GtkVBox" id="vbox86">
<property name="visible">True</property>
<property name="spacing">12</property>
<child>
<widget class="GtkHBox" id="hbox2965">
<property name="visible">True</property>
<property name="spacing">12</property>
<child>
<widget class="GtkVBox" id="vbox94">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow37">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="bookmarks_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox25">
<property name="visible">True</property>
<property name="spacing">6</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="add_bookmark_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_add_bookmark_button_clicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="remove_bookmark_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-remove</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_remove_bookmark_button_clicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkTable" id="table33">
<property name="visible">True</property>
<property name="n_rows">7</property>
<property name="n_columns">2</property>
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child>
<widget class="GtkLabel" id="label318">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Password:</property>
</widget>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="pass_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<property name="invisible_char">*</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="server_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label317">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Server:</property>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label316">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Room:</property>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="room_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="nick_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label315">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Nickname:</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label325">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Title:</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="title_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label326">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Print status:</property>
</widget>
<packing>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="print_status_combobox">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<signal name="changed" handler="on_print_status_combobox_changed"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkCheckButton" id="autojoin_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip" translatable="yes">If checked, Gajim will join this group chat on startup</property>
<property name="label" translatable="yes">Auto join</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_autojoin_checkbutton_toggled"/>
</widget>
</child>
<child>
<widget class="GtkCheckButton" id="minimize_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Minimize on Auto Join</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_minimize_checkbutton_toggled"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox20">
<property name="visible">True</property>
<property name="spacing">12</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_cancel_button_clicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_ok_button_clicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -591,7 +591,7 @@ Status message</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="ellipsize">PANGO_ELLIPSIZE_END</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -65,9 +65,9 @@
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="send_custom_status_menuitem">
<widget class="GtkImageMenuItem" id="send_custom_status_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send cus_tom status</property>
<property name="label" translatable="yes">Send Cus_tom Status</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu5">
@ -95,7 +95,7 @@
<widget class="GtkImageMenuItem" id="manage_contact">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Manage contact</property>
<property name="label" translatable="yes">_Manage Contact</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu2">
@ -123,6 +123,7 @@
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-edit</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
@ -142,7 +143,7 @@
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="custom_avatar">
<widget class="GtkImageMenuItem" id="set_custom_avatar_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Set Custom _Avatar</property>
@ -152,6 +153,7 @@
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-orientation-portrait</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
@ -159,6 +161,7 @@
<child>
<widget class="GtkImageMenuItem" id="add_special_notification_menuitem">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">Add Special _Notification</property>
<property name="use_underline">True</property>
<child internal-child="image">
@ -244,7 +247,7 @@
<child internal-child="image">
<widget class="GtkImage" id="image1715">
<property name="visible">True</property>
<property name="stock">gtk-yes</property>
<property name="stock">gtk-stop</property>
<property name="icon_size">1</property>
</widget>
</child>
@ -298,6 +301,7 @@
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-properties</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
@ -309,7 +313,7 @@
</child>
<child>
<widget class="GtkImageMenuItem" id="information_menuitem">
<property name="label">gtk-dialog-info</property>
<property name="label">gtk-info</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>

View file

@ -266,12 +266,40 @@
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="faq_menuitem">
<widget class="GtkImageMenuItem" id="faq_menuitem">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Frequently Asked Questions (online)</property>
<property name="label" translatable="yes">_FAQ</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_faq_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image_faq">
<property name="visible">True</property>
<property name="stock">gtk-dialog-question</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="menuitem1">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="features_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Fea_tures</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_features_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image_features">
<property name="visible">True</property>
<property name="stock">gtk-properties</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>

View file

@ -1,191 +1,215 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkWindow" id="search_window">
<property name="border_width">12</property>
<property name="title" translatable="yes">Search</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="key_press_event" handler="on_search_window_key_press_event" last_modification_time="Wed, 04 Apr 2007 18:39:27 GMT"/>
<signal name="destroy" handler="on_search_window_destroy" last_modification_time="Wed, 04 Apr 2007 18:39:35 GMT"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkVBox" id="search_vbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="label" translatable="yes">Please wait while retrieving search form...</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkProgressBar" id="progressbar">
<property name="visible">True</property>
<property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
<property name="fraction">0</property>
<property name="pulse_step">0.10000000149</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="spacing">6</property>
<child>
<widget class="GtkButton" id="search_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_search_button_clicked" last_modification_time="Thu, 19 Apr 2007 09:43:28 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-find</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label58">
<property name="visible">True</property>
<property name="label" translatable="yes">_Search</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Mon, 25 Sep 2006 05:08:55 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkWindow" id="search_window">
<property name="border_width">12</property>
<property name="title" translatable="yes">Search</property>
<signal name="key_press_event" handler="on_search_window_key_press_event"/>
<signal name="destroy" handler="on_search_window_destroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkVBox" id="search_vbox">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="label" translatable="yes">Please wait while retrieving search form...</property>
</widget>
<packing>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkProgressBar" id="progressbar">
<property name="visible">True</property>
<property name="pulse_step">0.10000000149</property>
</widget>
<packing>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="spacing">6</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="add_contact_button">
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="no_show_all">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_add_contact_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-add</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Add contact</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="information_button">
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="no_show_all">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_information_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-info</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Information</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="search_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_search_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<widget class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-find</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label58">
<property name="visible">True</property>
<property name="label" translatable="yes">_Search</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_close_button_clicked"/>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -1,421 +1,269 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkWindow" id="service_discovery_window">
<property name="border_width">6</property>
<property name="title" translatable="yes"></property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">450</property>
<property name="default_height">420</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="role">Service Discovery</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="destroy" handler="on_service_discovery_window_destroy" last_modification_time="Sun, 27 Mar 2005 18:05:35 GMT"/>
<child>
<widget class="GtkVBox" id="vbox11">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkEventBox" id="banner_agent_eventbox">
<property name="visible">True</property>
<property name="visible_window">True</property>
<property name="above_child">False</property>
<child>
<widget class="GtkHBox" id="banner_agent_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="banner_agent_label">
<property name="visible">True</property>
<property name="label">&lt;span weight=&quot;heavy&quot; size=&quot;large&quot;&gt;Agent name&lt;/span&gt;
<widget class="GtkWindow" id="service_discovery_window">
<property name="border_width">6</property>
<property name="role">Service Discovery</property>
<property name="default_width">450</property>
<property name="default_height">420</property>
<signal name="destroy" handler="on_service_discovery_window_destroy"/>
<child>
<widget class="GtkVBox" id="vbox11">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkEventBox" id="banner_agent_eventbox">
<property name="visible">True</property>
<child>
<widget class="GtkHBox" id="banner_agent_hbox">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="banner_agent_label">
<property name="visible">True</property>
<property name="xalign">0.05000000074505806</property>
<property name="ypad">6</property>
<property name="label">&lt;span weight="heavy" size="large"&gt;Agent name&lt;/span&gt;
Agent JID - node</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.0500000007451</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">6</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkImage" id="banner_agent_icon">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">6</property>
<property name="ypad">6</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="address_table">
<property name="visible">True</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="homogeneous">False</property>
<property name="row_spacing">0</property>
<property name="column_spacing">6</property>
<child>
<widget class="GtkComboBoxEntry" id="address_comboboxentry">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<property name="add_tearoffs">False</property>
<property name="has_frame">True</property>
<property name="focus_on_click">True</property>
<signal name="changed" handler="on_address_comboboxentry_changed" last_modification_time="Tue, 29 Mar 2005 17:24:11 GMT"/>
<signal name="key_press_event" handler="on_address_comboboxentry_key_press_event" last_modification_time="Tue, 29 Mar 2005 17:41:10 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">fill</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="browse_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_go_button_clicked" last_modification_time="Sun, 04 Sep 2005 20:37:26 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment93">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox2995">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1148">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label362">
<property name="visible">True</property>
<property name="label" translatable="yes">G_o</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">0</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label224">
<property name="visible">True</property>
<property name="label" translatable="yes">_Address:</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">address_comboboxentry</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">0</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="services_scrollwin">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="services_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal name="row_activated" handler="on_services_treeview_row_activated" last_modification_time="Mon, 28 Mar 2005 00:28:25 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="filter_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label361">
<property name="visible">True</property>
<property name="label" translatable="yes">_Filter:</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">filter_entry</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="filter_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
<signal name="changed" handler="on_filter_entry_changed" last_modification_time="Sun, 04 Sep 2005 11:35:21 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2994">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">12</property>
<child>
<widget class="GtkProgressBar" id="services_progressbar">
<property name="visible">True</property>
<property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
<property name="fraction">0</property>
<property name="pulse_step">0.10000000149</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label363">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="action_buttonbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Sat, 26 Mar 2005 15:05:59 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">2</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<property name="use_markup">True</property>
</widget>
</child>
<child>
<widget class="GtkImage" id="banner_agent_icon">
<property name="visible">True</property>
<property name="xpad">6</property>
<property name="ypad">6</property>
<property name="stock">gtk-missing-image</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="address_table">
<property name="visible">True</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="column_spacing">6</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkLabel" id="label224">
<property name="visible">True</property>
<property name="label" translatable="yes">_Address:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">address_comboboxentry</property>
</widget>
<packing>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkButton" id="browse_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_go_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment93">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<widget class="GtkHBox" id="hbox2995">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1148">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label362">
<property name="visible">True</property>
<property name="label" translatable="yes">G_o</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkComboBoxEntry" id="address_comboboxentry">
<property name="visible">True</property>
<property name="items" translatable="yes"></property>
<signal name="changed" handler="on_address_comboboxentry_changed"/>
<signal name="key_press_event" handler="on_address_comboboxentry_key_press_event"/>
<child internal-child="entry">
<widget class="GtkEntry" id="comboboxentry-entry1">
</widget>
</child>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="services_scrollwin">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
<child>
<widget class="GtkTreeView" id="services_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<signal name="row_activated" handler="on_services_treeview_row_activated"/>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="filter_hbox">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label361">
<property name="visible">True</property>
<property name="label" translatable="yes">_Filter:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">filter_entry</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="filter_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
<signal name="changed" handler="on_filter_entry_changed"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2994">
<property name="visible">True</property>
<property name="spacing">12</property>
<child>
<widget class="GtkProgressBar" id="services_progressbar">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="pulse_step">0.10000000149</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label363">
<property name="visible">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="action_buttonbox">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_close_button_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">2</property>
<property name="position">4</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -1,548 +1,346 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkWindow" id="single_message_window">
<property name="border_width">6</property>
<property name="title" translatable="yes"></property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">550</property>
<property name="default_height">280</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<signal name="key_press_event" handler="on_single_message_window_key_press_event" last_modification_time="Tue, 05 Jul 2005 22:02:15 GMT"/>
<signal name="delete_event" handler="on_single_message_window_delete_event" last_modification_time="Mon, 17 Oct 2005 15:32:50 GMT"/>
<child>
<widget class="GtkVBox" id="vbox97">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkTable" id="headers_table">
<property name="visible">True</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="homogeneous">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
<widget class="GtkLabel" id="label335">
<property name="visible">True</property>
<property name="label" translatable="yes">Subject:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="from_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="subject_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="count_chars_label">
<property name="visible">True</property>
<property name="label" translatable="yes">0</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="to_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="from_label">
<property name="visible">True</property>
<property name="label" translatable="yes">From:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="to_label">
<property name="visible">True</property>
<property name="label" translatable="yes">To:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="conversation_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTextView" id="message_textview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="overwrite">False</property>
<property name="accepts_tab">True</property>
<property name="justification">GTK_JUSTIFY_LEFT</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="cursor_visible">True</property>
<property name="pixels_above_lines">0</property>
<property name="pixels_below_lines">0</property>
<property name="pixels_inside_wrap">0</property>
<property name="left_margin">0</property>
<property name="right_margin">0</property>
<property name="indent">0</property>
<property name="text" translatable="yes"></property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox26">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="spacing">12</property>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Thu, 08 Dec 2005 14:01:10 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Sat, 02 Jul 2005 15:27:45 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="send_button">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Send message</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_send_button_clicked" last_modification_time="Tue, 05 Jul 2005 18:37:42 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment98">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox3003">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1326">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label370">
<property name="visible">True</property>
<property name="label" translatable="yes">Sen_d</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="reply_button">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Reply to this message</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_reply_button_clicked" last_modification_time="Tue, 05 Jul 2005 18:47:49 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment82">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox2982">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image862">
<property name="visible">True</property>
<property name="stock">gtk-ok</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label346">
<property name="visible">True</property>
<property name="label" translatable="yes">_Reply</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="send_and_close_button">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Send message and close window</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_send_and_close_button_clicked" last_modification_time="Tue, 05 Jul 2005 22:03:11 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment83">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox2983">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image878">
<property name="visible">True</property>
<property name="stock">gtk-ok</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label347">
<property name="visible">True</property>
<property name="label" translatable="yes">_Send &amp; Close</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">6</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkWindow" id="single_message_window">
<property name="border_width">6</property>
<property name="default_width">550</property>
<property name="default_height">280</property>
<signal name="key_press_event" handler="on_single_message_window_key_press_event"/>
<signal name="delete_event" handler="on_single_message_window_delete_event"/>
<child>
<widget class="GtkVBox" id="vbox97">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkTable" id="headers_table">
<property name="visible">True</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child>
<widget class="GtkLabel" id="to_label">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">To:</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="from_label">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">From:</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="to_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="invisible_char">*</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="count_chars_label">
<property name="visible">True</property>
<property name="label" translatable="yes">0</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="subject_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">*</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="from_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="invisible_char">*</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label335">
<property name="visible">True</property>
<property name="label" translatable="yes">Subject:</property>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="conversation_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTextView" id="message_textview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox26">
<property name="visible">True</property>
<property name="spacing">12</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="no_show_all">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_close_button_clicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="no_show_all">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_cancel_button_clicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="send_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip" translatable="yes">Send message</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_send_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment98">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<widget class="GtkHBox" id="hbox3003">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1326">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label370">
<property name="visible">True</property>
<property name="label" translatable="yes">Sen_d</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="reply_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip" translatable="yes">Reply to this message</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_reply_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment82">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<widget class="GtkHBox" id="hbox2982">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image862">
<property name="visible">True</property>
<property name="stock">gtk-ok</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label346">
<property name="visible">True</property>
<property name="label" translatable="yes">_Reply</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="send_and_close_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip" translatable="yes">Send message and close window</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_send_and_close_button_clicked"/>
<child>
<widget class="GtkAlignment" id="alignment83">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<widget class="GtkHBox" id="hbox2983">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image878">
<property name="visible">True</property>
<property name="stock">gtk-ok</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label347">
<property name="visible">True</property>
<property name="label" translatable="yes">_Send &amp; Close</property>
<property name="use_underline">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">4</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">6</property>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

File diff suppressed because it is too large Load diff

View file

@ -181,8 +181,8 @@
<widget class="GtkScrolledWindow" id="scrolledwindow42">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>

View file

@ -1,153 +1,113 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkMenu" id="zeroconf_contact_context_menu">
<child>
<widget class="GtkImageMenuItem" id="start_chat_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Start _Chat</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1534">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="rename_menuitem">
<property name="label" translatable="yes">_Rename</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1535">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="edit_groups_menuitem">
<property name="label" translatable="yes">Edit _Groups</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_send_file_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_file_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send _File</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1536">
<property name="visible">True</property>
<property name="stock">gtk-file</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="assign_openpgp_key_menuitem">
<property name="label" translatable="yes">Assign Open_PGP Key</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_assign_openpgp_key_menuitem_activate" last_modification_time="Thu, 30 Jun 2005 22:57:59 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1537">
<property name="visible">True</property>
<property name="stock">gtk-dialog-authentication</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_special_notification_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Add Special _Notification</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1538">
<property name="visible">True</property>
<property name="stock">gtk-info</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_information_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="information_menuitem">
<property name="label">gtk-info</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1539">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="zeroconf_contact_context_menu">
<child>
<widget class="GtkImageMenuItem" id="start_chat_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Start _Chat</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1534">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="rename_menuitem">
<property name="label" translatable="yes">_Rename</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1535">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="edit_groups_menuitem">
<property name="label" translatable="yes">Edit _Groups</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_send_file_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_file_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send _File</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1536">
<property name="visible">True</property>
<property name="stock">gtk-missing-image</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="assign_openpgp_key_menuitem">
<property name="label" translatable="yes">Assign Open_PGP Key</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_assign_openpgp_key_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image1537">
<property name="visible">True</property>
<property name="stock">gtk-missing-image</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_special_notification_menuitem">
<property name="visible">True</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">Add Special _Notification</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1538">
<property name="visible">True</property>
<property name="stock">gtk-info</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_information_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="information_menuitem">
<property name="label">gtk-info</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1539">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

Before

Width:  |  Height:  |  Size: 541 B

After

Width:  |  Height:  |  Size: 541 B

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 428 B

View file

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 815 B

View file

Before

Width:  |  Height:  |  Size: 523 B

After

Width:  |  Height:  |  Size: 523 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 308 B

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

2474
data/other/cacerts.pem Normal file

File diff suppressed because it is too large Load diff

View file

@ -9,8 +9,8 @@ msgstr ""
"Project-Id-Version: gajim 0.11\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-03-29 13:38+0200\n"
"PO-Revision-Date: 2007-03-29 13:41+0100\n"
"Last-Translator: Benjamin Drung <benjamin.drung@gmail.com>\n"
"PO-Revision-Date: 2007-08-03 23:11+0100\n"
"Last-Translator: Michael Skiba <michael@michael-skiba.de>\n"
"Language-Team: German <translators@gajim.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -204,9 +204,8 @@ msgid "Account Modification"
msgstr "Kontoänderung"
#: ../data/glade/account_modification_window.glade.h:6
#, fuzzy
msgid "Administration operations"
msgstr "Administratorliste"
msgstr "Administrative Aktionen"
#: ../data/glade/account_modification_window.glade.h:7
msgid "Auto-reconnect when connection is lost"
@ -379,9 +378,8 @@ msgid "Synch_ronize account status with global status"
msgstr "Konto-Status mit globalem _Status abgleichen"
#: ../data/glade/account_modification_window.glade.h:42
#, fuzzy
msgid "Synchronise contacts"
msgstr "_Abgemeldete Kontakte anzeigen"
msgstr "Synchronisiere Kontakte"
#: ../data/glade/account_modification_window.glade.h:43
msgid "Use _SSL (legacy)"
@ -1039,9 +1037,8 @@ msgid "_Bookmark This Room"
msgstr "Raum zu _Lesezeichen hinzufügen"
#: ../data/glade/gc_control_popup_menu.glade.h:7
#, fuzzy
msgid "_Destroy room"
msgstr "_Neuer Raum"
msgstr "_Raum zerstören"
#: ../data/glade/gc_occupants_menu.glade.h:1
msgid "Mo_derator"
@ -2338,9 +2335,8 @@ msgid "Bulgarian"
msgstr "Bulgarisch"
#: ../src/chat_control.py:52
#, fuzzy
msgid "Breton"
msgstr "Briton"
msgstr "Bretonisch"
#: ../src/chat_control.py:52
msgid "Czech"
@ -2367,7 +2363,6 @@ msgid "Spanish"
msgstr "Spanisch"
#: ../src/chat_control.py:52
#, fuzzy
msgid "Basque"
msgstr "Baskisch"
@ -2752,11 +2747,11 @@ msgstr "Sperrliste"
#: ../src/config.py:2180
msgid "Member List"
msgstr "Mitgliedsliste"
msgstr "Mitgliederliste"
#: ../src/config.py:2181
msgid "Owner List"
msgstr "Listenbesitzer"
msgstr "Besitzerliste"
#: ../src/config.py:2182
msgid "Administrator List"
@ -3203,23 +3198,20 @@ msgid "%s is not the name of a group chat."
msgstr "%s ist kein Name eines Gruppenchats."
#: ../src/dialogs.py:1327
#, fuzzy
msgid "Without a connection, you can not synchronise your contacts."
msgstr "Sie müssen verbunden sein, um Ihr Passwort zu ändern"
msgstr "Sie müssen verbunden sein, um Ihre Kontakte zu Synchronisieren."
#: ../src/dialogs.py:1374
#, fuzzy
msgid "This account is not connected to the server"
msgstr "Konto \"%s\" ist mit Server verbunden"
msgstr "Konto \"%s\" ist nicht mit dem Server verbunden"
#: ../src/dialogs.py:1375
#, fuzzy
msgid "You cannot synchronize with an account unless it is connected."
msgstr "Sie können einem Gruppenchat erst beitreten, wenn Sie verbunden sind."
msgstr "Sie können nicht mit einem Konto Synchronisieren, solange es nicht verbunden ist."
#: ../src/dialogs.py:1399
msgid "Synchronise"
msgstr ""
msgstr "Synchronisieren"
#: ../src/dialogs.py:1457
#, python-format
@ -4557,19 +4549,21 @@ msgstr "Bitte geben Sie an, welchen Spitznamen Sie verwenden möchten:"
#. Ask for a reason
#: ../src/groupchat_control.py:1457
#, fuzzy, python-format
#, python-format
msgid "Destroying %s"
msgstr "Beschreibung: %s"
msgstr "Zerstöre %s"
#: ../src/groupchat_control.py:1458
msgid ""
"You are going to definitively destroy this room.\n"
"You may specify a reason below:"
msgstr ""
"Sie werden den Raum endgültig zerstören.\n"
"Sie können hier einen Grund angeben:"
#: ../src/groupchat_control.py:1460
msgid "You may also enter an alternate venue:"
msgstr ""
msgstr "Sie können auch einen alternativen Raum eintragen:"
#: ../src/groupchat_control.py:1490
msgid "Bookmark already set"
@ -5161,11 +5155,11 @@ msgid "Metacontacts are a way to regroup several contacts in one line. Generally
msgstr "Metakontakte sind eine Möglichkeit, mehrere Kontakte in einer Zeile zu gruppieren. Normalerweise benutzt man Sie, wenn eine Person mehrere Jabber- oder Transport-Konten hat."
#: ../src/roster_window.py:4132
#, fuzzy, python-format
#, python-format
msgid "Do you want to send that file to %s:"
msgid_plural "Do you want to send those files to %s:"
msgstr[0] "%s möchte ihnen eine Datei senden:"
msgstr[1] "%s möchte ihnen eine Datei senden:"
msgstr[1] "%s möchte ihnen diese Datei senden:"
#: ../src/roster_window.py:4237
#, python-format
@ -5722,7 +5716,7 @@ msgstr "Kann leer, 'chat' oder 'normal' sein. Wenn nicht leer, dann werden alle
#: ../src/common/config.py:227
msgid "If True, Gajim will scroll and select the contact who sent you the last message, if chat window is not already opened."
msgstr ""
msgstr "Wenn aktiviert, wird Gajim automatisch zu der Person springen und auswählen, die die letzte Nachricht geschickt hat, falls das Chat Fenster nicht bereits offen ist."
#: ../src/common/config.py:238
msgid "Priority will change automatically according to your status. Priorities are defined in autopriority_* options."
@ -5874,13 +5868,13 @@ msgstr "Falscher Host"
#: ../src/common/connection_handlers.py:180
#: ../src/common/zeroconf/connection_handlers_zeroconf.py:236
#, fuzzy, python-format
#, python-format
msgid "The host %s you configured as the ft_add_hosts_to_send advanced option is not valid, so ignored."
msgstr "Der Host, den Sie für die erweiterte Option ft_override_host_to_send angeben haben ist ungültig und wird ignoriert."
msgstr "Der Host %s, den Sie für die erweiterte Option ft_override_host_to_send angeben haben ist ungültig und wird ignoriert."
#: ../src/common/connection_handlers.py:216
msgid "Invalid local address? :-O"
msgstr ""
msgstr "Ungültige Lokale Adresse? :-O"
#: ../src/common/connection_handlers.py:607
#, python-format
@ -5940,14 +5934,13 @@ msgstr ""
#. Room has been destroyed. see
#. http://www.xmpp.org/extensions/xep-0045.html#destroyroom
#: ../src/common/connection_handlers.py:1676
#, fuzzy
msgid "Room has been destroyed"
msgstr "Autorisierung wurde entfernt"
msgstr "Raum wurde zerstört"
#: ../src/common/connection_handlers.py:1683
#, python-format
msgid "You can join this room instead: %s"
msgstr ""
msgstr "Sie können stattdessen diesem Raum beitreten: %s"
#: ../src/common/connection_handlers.py:1709
msgid "I would like to add you to my roster."
@ -5996,9 +5989,8 @@ msgid "Invalid answer"
msgstr "Ungültige Antwort"
#: ../src/common/connection.py:379
#, fuzzy
msgid "Connection to proxy failed"
msgstr "Verbindung fehlgeschlagen"
msgstr "Verbindung mit Proxy fehlgeschlagen"
#: ../src/common/connection.py:433
#: ../src/common/connection.py:531

461
po/ru.po

File diff suppressed because it is too large Load diff

View file

@ -53,6 +53,7 @@ class CommandWindow:
# retrieving widgets from xml
self.xml = gtkgui_helpers.get_glade('adhoc_commands_window.glade')
self.window = self.xml.get_widget('adhoc_commands_window')
self.window.connect('delete-event', self.on_adhoc_commands_window_delete_event)
for name in ('back_button', 'forward_button',
'execute_button','close_button','stages_notebook',
'retrieving_commands_stage_vbox',
@ -101,7 +102,7 @@ class CommandWindow:
self.remove_pulsing()
def on_adhoc_commands_window_delete_event(self, *anything):
return self.stage_adhoc_commands_window_delete_event(self, *anything)
return self.stage_adhoc_commands_window_delete_event(self.window)
def __del__(self):
print "Object has been deleted."
@ -171,7 +172,7 @@ class CommandWindow:
for (commandnode, commandname) in self.commandlist:
radio = gtk.RadioButton(first_radio, label=commandname)
radio.connect("toggled", self.on_command_radiobutton_toggled, commandnode)
if first_radio is None:
if not first_radio:
first_radio = radio
self.commandnode = commandnode
self.command_list_vbox.pack_start(radio, expand=False)
@ -252,6 +253,7 @@ class CommandWindow:
else:
self.window.destroy()
return False
return True
def stage3_back_button_clicked(self, widget):
self.stage3_submit_form('prev')
@ -264,10 +266,10 @@ class CommandWindow:
def stage3_submit_form(self, action='execute'):
self.data_form_widget.set_sensitive(False)
if self.data_form_widget.get_data_form() is None:
self.data_form_widget.hide()
else:
if self.data_form_widget.get_data_form():
self.data_form_widget.data_form.type='submit'
else:
self.data_form_widget.hide()
self.close_button.set_sensitive(True)
self.back_button.set_sensitive(False)
@ -284,13 +286,13 @@ class CommandWindow:
self.remove_pulsing()
self.sending_form_progressbar.hide()
if self.sessionid is None:
if not self.sessionid:
self.sessionid = command.getAttr('sessionid')
self.form_status = command.getAttr('status')
self.commandnode = command.getAttr('node')
if command.getTag('x') is not None:
if command.getTag('x'):
self.dataform = dataforms.ExtendForm(node=command.getTag('x'))
self.data_form_widget.set_sensitive(True)
@ -307,18 +309,18 @@ class CommandWindow:
else:
self.data_form_widget.hide()
action = command.getTag('action')
if action is None:
actions = command.getTag('actions')
if actions:
# actions, actions, actions...
self.close_button.set_sensitive(True)
self.back_button.set_sensitive(actions.getTag('prev') is not None)
self.forward_button.set_sensitive(actions.getTag('next') is not None)
self.execute_button.set_sensitive(True)
else:
self.close_button.set_sensitive(True)
self.back_button.set_sensitive(False)
self.forward_button.set_sensitive(False)
self.execute_button.set_sensitive(True)
else:
# actions, actions, actions...
self.close_button.set_sensitive(True)
self.back_button.set_sensitive(action.getTag('prev') is not None)
self.forward_button.set_sensitive(action.getTag('next') is not None)
self.execute_button.set_sensitive(True)
if self.form_status == 'completed':
self.close_button.set_sensitive(True)
@ -329,7 +331,7 @@ class CommandWindow:
self.stage_adhoc_commands_window_delete_event = self.stage3_close_button_clicked
note = command.getTag('note')
if note is not None:
if note:
self.notes_label.set_text(note.getData().decode('utf-8'))
self.notes_label.set_no_show_all(False)
self.notes_label.show()
@ -369,9 +371,9 @@ class CommandWindow:
# close old stage
self.stage_finish()
assert errorid is not None or error is not None
assert errorid or error
if errorid is not None:
if errorid:
# we've got error code, display appropriate message
try:
errorname = xmpp.NS_STANZAS + ' ' + str(errorid)
@ -380,7 +382,7 @@ class CommandWindow:
del errorname, errordesc
except KeyError: # when stanza doesn't have error description
error = 'Service returned an error.'
elif error is not None:
elif error:
# we've got error message
pass
else:
@ -409,7 +411,7 @@ class CommandWindow:
def setup_pulsing(self, progressbar):
'''Set the progressbar to pulse. Makes a custom
function to repeatedly call progressbar.pulse() method.'''
assert self.pulse_id is None
assert not self.pulse_id
assert isinstance(progressbar, gtk.ProgressBar)
def callback():
@ -421,7 +423,7 @@ class CommandWindow:
def remove_pulsing(self):
'''Stop pulsing, useful when especially when removing widget.'''
if self.pulse_id is not None:
if self.pulse_id:
gobject.source_remove(self.pulse_id)
self.pulse_id=None
@ -436,7 +438,7 @@ class CommandWindow:
# FIXME: move to connection_handlers.py
# is error => error stage
error = response.getError()
if error is not None:
if error:
# extracting error description from xmpp/protocol.py
self.stage5(errorid = error)
return
@ -470,10 +472,10 @@ class CommandWindow:
'action':action
})
if self.sessionid is not None:
if self.sessionid:
cmdnode.setAttr('sessionid', self.sessionid)
if self.data_form_widget.data_form is not None:
if self.data_form_widget.data_form:
# cmdnode.addChild(node=dataforms.DataForm(tofill=self.data_form_widget.data_form))
# FIXME: simplified form to send
@ -482,7 +484,7 @@ class CommandWindow:
def callback(response):
# FIXME: move to connection_handlers.py
err = response.getError()
if err is not None:
if err:
self.stage5(errorid = err)
else:
self.stage3_next_form(response.getTag('command'))
@ -491,8 +493,8 @@ class CommandWindow:
def send_cancel(self):
'''Send the command with action='cancel'. '''
assert self.commandnode is not None
if self.sessionid is not None and self.account.connection:
assert self.commandnode
if self.sessionid and self.account.connection:
# we already have sessionid, so the service sent at least one reply.
stanza = xmpp.Iq(typ='set', to=self.jid)
stanza.addChild('command', attrs={

View file

@ -4,6 +4,8 @@
## Copyright (C) 2006-2007 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2006 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -129,13 +131,39 @@ class ChatControlBase(MessageControl):
id = self.widget.connect('key_press_event', self._on_keypress_event)
self.handlers[id] = self.widget
# Create banner and connect signals
widget = self.xml.get_widget('banner_eventbox')
widget.set_property('height-request', gajim.config.get('chat_avatar_height'))
id = widget.connect('button-press-event',
self._on_banner_eventbox_button_press_event)
self.handlers[id] = widget
# Init DND
self.TARGET_TYPE_URI_LIST = 80
self.dnd_list = [ ( 'text/uri-list', 0, self.TARGET_TYPE_URI_LIST ),
('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_APP, 0)]
id = self.widget.connect('drag_data_received',
self._on_drag_data_received)
self.handlers[id] = self.widget
self.widget.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
gtk.DEST_DEFAULT_HIGHLIGHT |
gtk.DEST_DEFAULT_DROP,
self.dnd_list, gtk.gdk.ACTION_COPY)
# Create textviews and connect signals
self.conv_textview = ConversationTextview(self.account)
# FIXME: DND on non editable TextView, find a better way
self.drag_entered = False
id = self.conv_textview.tv.connect('drag_data_received',
self._on_drag_data_received)
self.handlers[id] = self.conv_textview.tv
id = self.conv_textview.tv.connect('drag_motion', self._on_drag_motion)
self.handlers[id] = self.conv_textview.tv
id = self.conv_textview.tv.connect('drag_leave', self._on_drag_leave)
self.handlers[id] = self.conv_textview.tv
self.conv_textview.tv.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
gtk.DEST_DEFAULT_HIGHLIGHT |
gtk.DEST_DEFAULT_DROP,
self.dnd_list, gtk.gdk.ACTION_COPY)
self.conv_scrolledwindow = self.xml.get_widget(
'conversation_scrolledwindow')
@ -149,6 +177,7 @@ class ChatControlBase(MessageControl):
self.handlers[id] = widget
self.scroll_to_end_id = None
self.was_at_the_end = True
# add MessageTextView to UI and connect signals
self.msg_scrolledwindow = self.xml.get_widget('message_scrolledwindow')
self.msg_textview = MessageTextView()
@ -164,6 +193,13 @@ class ChatControlBase(MessageControl):
id = self.msg_textview.connect('populate_popup',
self.on_msg_textview_populate_popup)
self.handlers[id] = self.msg_textview
# Setup DND
id = self.msg_textview.connect('drag_data_received',
self._on_drag_data_received)
self.handlers[id] = self.msg_textview
self.msg_textview.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
gtk.DEST_DEFAULT_HIGHLIGHT,
self.dnd_list, gtk.gdk.ACTION_COPY)
self.update_font()
@ -481,6 +517,22 @@ class ChatControlBase(MessageControl):
self.handle_message_textview_mykey_press(widget, event_keyval,
event_keymod)
def _on_drag_data_received(self, widget, context, x, y, selection,
target_type, timestamp):
pass # Derived classes SHOULD implement this method
def _on_drag_leave(self, widget, context, time):
# FIXME: DND on non editable TextView, find a better way
self.drag_entered = False
self.conv_textview.tv.set_editable(False)
def _on_drag_motion(self, widget, context, x, y, time):
# FIXME: DND on non editable TextView, find a better way
if not self.drag_entered:
# We drag new data over the TextView, make it editable to catch dnd
self.drag_entered_conv = True
self.conv_textview.tv.set_editable(True)
def _process_command(self, message):
if not message or message[0] != '/':
return False
@ -496,23 +548,23 @@ class ChatControlBase(MessageControl):
self.clear(self.msg_textview) # clear message textview too
return True
elif message == 'compact' and not len(message_array):
self.chat_buttons_set_visible(not self.hide_chat_buttons_current)
self.chat_buttons_set_visible(not self.hide_chat_buttons)
self.clear(self.msg_textview)
return True
return False
def send_message(self, message, keyID = '', type = 'chat', chatstate = None,
msg_id = None, composing_jep = None, resource = None):
msg_id = None, composing_xep = None, resource = None,
process_command = True):
'''Send the given message to the active tab. Doesn't return None if error
'''
if not message or message == '\n':
return 1
if not self._process_command(message):
if not process_command or not self._process_command(message):
ret = MessageControl.send_message(self, message, keyID, type = type,
chatstate = chatstate, msg_id = msg_id,
composing_jep = composing_jep, resource = resource,
composing_xep = composing_xep, resource = resource,
user_nick = self.user_nick)
if ret:
return ret
@ -569,7 +621,6 @@ class ChatControlBase(MessageControl):
full_jid != self.parent_win.get_active_jid() or \
not self.parent_win.is_active() or not end)) or \
(gc_message and \
gajim.interface.minimized_controls.has_key(self.account) and \
jid in gajim.interface.minimized_controls[self.account])) and \
kind in ('incoming', 'incoming_queue'):
# we want to have save this message in events list
@ -667,31 +718,21 @@ class ChatControlBase(MessageControl):
gajim.interface.instances['logs'][jid] = \
history_window.HistoryWindow(jid, self.account)
def _on_compact_view_menuitem_activate(self, widget):
isactive = widget.get_active()
self.chat_buttons_set_visible(isactive)
def _on_minimize_menuitem_activate(self, widget):
def on_minimize_menuitem_toggled(self, widget):
'''When a grouchat is minimized, unparent the tab, put it in roster etc'''
win = gajim.interface.msg_win_mgr.get_window(self.contact.jid, self.account)
ctrl = win.get_control(self.contact.jid, self.account)
ctrl_page = win.notebook.page_num(ctrl.widget)
control = win.notebook.get_nth_page(ctrl_page)
win.notebook.remove_page(ctrl_page)
control.unparent()
ctrl.parent_win = None
if not gajim.interface.minimized_controls.has_key(self.account):
gajim.interface.minimized_controls[self.account] = {}
gajim.interface.minimized_controls[self.account][self.contact.jid] = ctrl
del win._controls[self.account][self.contact.jid]
win.check_tabs()
gajim.interface.roster.add_groupchat_to_roster(self.account,
self.contact.jid, status = self.subject)
old_value = False
minimized_gc = gajim.config.get_per('accounts', self.account,
'minimized_gc').split()
if self.contact.jid in minimized_gc:
old_value = True
minimize = widget.get_active()
if minimize and not self.contact.jid in minimized_gc:
minimized_gc.append(self.contact.jid)
if not minimize and self.contact.jid in minimized_gc:
minimized_gc.remove(self.contact.jid)
if old_value != minimize:
gajim.config.set_per('accounts', self.account, 'minimized_gc',
' '.join(minimized_gc))
def set_control_active(self, state):
if state:
@ -825,11 +866,14 @@ class ChatControlBase(MessageControl):
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
groupchat_control = gajim.interface.msg_win_mgr.get_control(
room_jid, self.account)
if not groupchat_control and \
gajim.interface.minimized_controls.has_key(self.account) and \
room_jid in gajim.interface.minimized_controls[self.account]:
if room_jid in gajim.interface.minimized_controls[self.account]:
groupchat_control = \
gajim.interface.minimized_controls[self.account][room_jid]
contact = \
gajim.contacts.get_contact_with_highest_priority(self.account, \
room_jid)
if contact:
gajim.interface.roster.draw_contact(room_jid, self.account)
groupchat_control.draw_contact(nick)
mw = gajim.interface.msg_win_mgr.get_window(room_jid, self.account)
if mw:
@ -903,7 +947,7 @@ class ChatControl(ChatControlBase):
'''A control for standard 1-1 chat'''
TYPE_ID = message_control.TYPE_CHAT
old_msg_kind = None # last kind of the printed message
CHAT_CMDS = ['clear', 'compact', 'help', 'ping']
CHAT_CMDS = ['clear', 'compact', 'help', 'me', 'ping', 'say']
def __init__(self, parent_win, contact, acct, resource = None):
ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
@ -915,21 +959,10 @@ class ChatControl(ChatControlBase):
id = widget.connect('clicked', self.on_actions_button_clicked)
self.handlers[id] = widget
hide_chat_buttons_always = gajim.config.get(
'always_hide_chat_buttons')
self.chat_buttons_set_visible(hide_chat_buttons_always)
compact_view = gajim.config.get('compact_view')
self.chat_buttons_set_visible(compact_view)
self.widget_set_visible(self.xml.get_widget('banner_eventbox'),
gajim.config.get('hide_chat_banner'))
# Initialize drag-n-drop
self.TARGET_TYPE_URI_LIST = 80
self.dnd_list = [ ( 'text/uri-list', 0, self.TARGET_TYPE_URI_LIST ) ]
id = self.widget.connect('drag_data_received',
self._on_drag_data_received)
self.handlers[id] = self.widget
self.widget.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
gtk.DEST_DEFAULT_HIGHLIGHT |
gtk.DEST_DEFAULT_DROP,
self.dnd_list, gtk.gdk.ACTION_COPY)
# keep timeout id and window obj for possible big avatar
# it is on enter-notify and leave-notify so no need to be per jid
@ -1085,6 +1118,8 @@ class ChatControl(ChatControlBase):
jid = contact.jid
banner_name_label = self.xml.get_widget('banner_name_label')
banner_eventbox = self.xml.get_widget('banner_eventbox')
name = contact.get_shown_name()
if self.resource:
name += '/' + self.resource
@ -1111,8 +1146,10 @@ class ChatControl(ChatControlBase):
status = contact.status
if status is not None:
self.status_tooltip.set_tip(banner_eventbox, status)
self.status_tooltip.enable()
banner_name_label.set_ellipsize(pango.ELLIPSIZE_END)
status = helpers.reduce_chars_newlines(status, max_lines = 2)
status = helpers.reduce_chars_newlines(status, max_lines = 1)
status_escaped = gobject.markup_escape_text(status)
font_attrs, font_attrs_small = self.get_font_attrs()
@ -1121,12 +1158,12 @@ class ChatControl(ChatControlBase):
if cs and st in ('composing_only', 'all'):
if contact.show == 'offline':
chatstate = ''
elif contact.composing_jep == 'JEP-0085':
elif contact.composing_xep == 'XEP-0085':
if st == 'all' or cs == 'composing':
chatstate = helpers.get_uf_chatstate(cs)
else:
chatstate = ''
elif contact.composing_jep == 'JEP-0022':
elif contact.composing_xep == 'XEP-0022':
if cs in ('composing', 'paused'):
# only print composing, paused
chatstate = helpers.get_uf_chatstate(cs)
@ -1145,9 +1182,6 @@ class ChatControl(ChatControlBase):
if status_escaped:
label_text += '\n<span %s>%s</span>' %\
(font_attrs_small, status_escaped)
banner_eventbox = self.xml.get_widget('banner_eventbox')
self.status_tooltip.set_tip(banner_eventbox, status)
self.status_tooltip.enable()
else:
self.status_tooltip.disable()
# setup the label that holds name and jid
@ -1196,6 +1230,9 @@ class ChatControl(ChatControlBase):
if message_array == ['']:
message_array = []
if command == 'me':
return False # This is not really a command
if command == 'help':
if len(message_array):
subcommand = message_array.pop(0)
@ -1204,10 +1241,19 @@ class ChatControl(ChatControlBase):
self.get_command_help(command)
self.clear(self.msg_textview)
return True
elif command == 'ping' and not len(message_array):
gajim.connections[self.account].sendPing(self.contact)
elif command == 'ping':
if not len(message_array):
gajim.connections[self.account].sendPing(self.contact)
else:
self.get_command_help(command)
self.clear(self.msg_textview)
return True
elif command == 'say':
return False
else:
self.print_conversation(_('No such command: /%s (if you want to send '
'this, prefix it with /say)') % command, 'info')
return True
return False
def get_command_help(self, command):
@ -1215,13 +1261,21 @@ class ChatControl(ChatControlBase):
self.print_conversation(_('Commands: %s') % ChatControl.CHAT_CMDS,
'info')
elif command == 'clear':
self.print_conversation(_('Usage: /%s, clears the text window.'),
'info')
self.print_conversation(_('Usage: /%s, clears the text window.') % \
command, 'info')
elif command == 'compact':
self.print_conversation(_('Usage: /%s, hide the chat buttons.'),
'info')
self.print_conversation(_('Usage: /%s, hide the chat buttons.') % \
command, 'info')
elif command == 'me':
self.print_conversation(_('Usage: /%s <action>, sends action to the '
'current group chat. Use third person. (e.g. /%s explodes.)') % \
(command, command), 'info')
elif command == 'ping':
self.print_conversation(_(''), 'info')
self.print_conversation(_('Usage: /%s, sends a ping to the contact') %\
command, 'info')
elif command == 'say':
self.print_conversation(_('Usage: /%s, send the message to the contact') %\
command, 'info')
else:
self.print_conversation(_('No help info for /%s') % command, 'info')
@ -1230,6 +1284,12 @@ class ChatControl(ChatControlBase):
if message in ('', None, '\n') or self._process_command(message):
return
# Do we need to process command for the message ?
process_command = True
if message.startswith('/say'):
message = message[5:]
process_command = False
# refresh timers
self.reset_kbd_mouse_timeout_vars()
@ -1244,10 +1304,10 @@ class ChatControl(ChatControlBase):
chatstates_on = gajim.config.get('outgoing_chat_state_notifications') != \
'disabled'
composing_jep = contact.composing_jep
composing_xep = contact.composing_xep
chatstate_to_send = None
if chatstates_on and contact is not None:
if composing_jep is None:
if composing_xep is None:
# no info about peer
# send active to discover chat state capabilities
# this is here (and not in send_chatstate)
@ -1256,13 +1316,13 @@ class ChatControl(ChatControlBase):
if contact.our_chatstate:
# We already asked for xep 85, don't ask it twice
composing_jep = 'asked_once'
composing_xep = 'asked_once'
chatstate_to_send = 'active'
contact.our_chatstate = 'ask' # pseudo state
# if peer supports jep85 and we are not 'ask', send 'active'
# NOTE: first active and 'ask' is set in gajim.py
elif composing_jep is not False:
elif composing_xep is not False:
#send active chatstate on every message (as JEP says)
chatstate_to_send = 'active'
contact.our_chatstate = 'active'
@ -1272,7 +1332,8 @@ class ChatControl(ChatControlBase):
self._schedule_activity_timers()
if not ChatControlBase.send_message(self, message, keyID, type = 'chat',
chatstate = chatstate_to_send, composing_jep = composing_jep):
chatstate = chatstate_to_send, composing_xep = composing_xep,
process_command = process_command):
self.print_conversation(message, self.contact.jid,
encrypted = encrypted)
@ -1441,13 +1502,13 @@ class ChatControl(ChatControlBase):
jid = self.contact.jid
num_unread = len(gajim.events.get_events(self.account, jid,
['printed_' + self.type_id, self.type_id]))
# Set tab image (always 16x16); unread messages show the 'message' image
# Set tab image (always 16x16); unread messages show the 'event' image
tab_img = None
if num_unread and gajim.config.get('show_unread_tab_icon'):
img_16 = gajim.interface.roster.get_appropriate_state_images(
self.contact.jid, icon_name = 'message')
tab_img = img_16['message']
self.contact.jid, icon_name = 'event')
tab_img = img_16['event']
else:
contact = gajim.contacts.get_contact_with_highest_priority(
self.account, self.contact.jid)
@ -1474,7 +1535,6 @@ class ChatControl(ChatControlBase):
toggle_gpg_menuitem = xml.get_widget('toggle_gpg_menuitem')
add_to_roster_menuitem = xml.get_widget('add_to_roster_menuitem')
send_file_menuitem = xml.get_widget('send_file_menuitem')
compact_view_menuitem = xml.get_widget('compact_view_menuitem')
information_menuitem = xml.get_widget('information_menuitem')
contact = self.parent_win.get_active_contact()
@ -1493,14 +1553,14 @@ class ChatControl(ChatControlBase):
# If we don't have resource, we can't do file transfer
# in transports, contact holds our info we need to disable it too
if contact.resource and contact.jid.find('@') != -1:
if self.TYPE_ID == message_control.TYPE_PM and self.gc_contact.jid and \
self.gc_contact.resource:
send_file_menuitem.set_sensitive(True)
elif contact.resource and contact.jid.find('@') != -1:
send_file_menuitem.set_sensitive(True)
else:
send_file_menuitem.set_sensitive(False)
# compact_view_menuitem
compact_view_menuitem.set_active(self.hide_chat_buttons_current)
# add_to_roster_menuitem
if _('Not in Roster') in contact.groups:
add_to_roster_menuitem.show()
@ -1517,9 +1577,6 @@ class ChatControl(ChatControlBase):
id = send_file_menuitem.connect('activate',
self._on_send_file_menuitem_activate)
self.handlers[id] = send_file_menuitem
id = compact_view_menuitem.connect('activate',
self._on_compact_view_menuitem_activate)
self.handlers[id] = compact_view_menuitem
id = add_to_roster_menuitem.connect('activate',
self._on_add_to_roster_menuitem_activate)
self.handlers[id] = add_to_roster_menuitem
@ -1565,7 +1622,7 @@ class ChatControl(ChatControlBase):
if contact.show == 'offline':
return
if contact.composing_jep is False: # jid cannot do jep85 nor jep22
if contact.composing_xep is False: # jid cannot do xep85 nor xep22
return
# if the new state we wanna send (state) equals
@ -1573,7 +1630,7 @@ class ChatControl(ChatControlBase):
if contact.our_chatstate == state:
return
if contact.composing_jep is None:
if contact.composing_xep is None:
# we don't know anything about jid, so return
# NOTE:
# send 'active', set current state to 'ask' and return is done
@ -1587,7 +1644,7 @@ class ChatControl(ChatControlBase):
# in JEP22, when we already sent stop composing
# notification on paused, don't resend it
if contact.composing_jep == 'JEP-0022' and \
if contact.composing_xep == 'XEP-0022' and \
contact.our_chatstate in ('paused', 'active', 'inactive') and \
state is not 'composing': # not composing == in (active, inactive, gone)
contact.our_chatstate = 'active'
@ -1609,7 +1666,7 @@ class ChatControl(ChatControlBase):
self.reset_kbd_mouse_timeout_vars()
MessageControl.send_message(self, None, chatstate = state,
msg_id = contact.msg_id, composing_jep = contact.composing_jep)
msg_id = contact.msg_id, composing_xep = contact.composing_xep)
contact.our_chatstate = state
if contact.our_chatstate == 'active':
self.reset_kbd_mouse_timeout_vars()
@ -1638,20 +1695,19 @@ class ChatControl(ChatControlBase):
del self.handlers[i]
self.conv_textview.del_handlers()
self.msg_textview.destroy()
def allow_shutdown(self, method):
if time.time() - gajim.last_message_time[self.account]\
[self.get_full_jid()] < 2:
# 2 seconds
dialog = dialogs.ConfirmationDialog(
#%s is being replaced in the code with JID
# %s is being replaced in the code with JID
_('You just received a new message from "%s"') % self.contact.jid,
_('If you close this tab and you have history disabled, '\
'this message will be lost.'))
if dialog.get_response() != gtk.RESPONSE_OK:
return False #stop the propagation of the event
return True
return 'no' # stop the propagation of the event
return 'yes'
def handle_incoming_chatstate(self):
''' handle incoming chatstate that jid SENT TO us '''
@ -1710,17 +1766,23 @@ class ChatControl(ChatControlBase):
def _on_drag_data_received(self, widget, context, x, y, selection,
target_type, timestamp):
# If not resource, we can't send file
if not self.contact.resource:
# If no resource is known, we can't send a file
if self.TYPE_ID == message_control.TYPE_PM:
c = self.gc_contact
else:
c = self.contact
if not c.resource:
return
if target_type == self.TARGET_TYPE_URI_LIST:
if not selection.data:
return
uri = selection.data.strip()
uri_splitted = uri.split() # we may have more than one file dropped
for uri in uri_splitted:
path = helpers.get_file_path_from_dnd_dropped_uri(uri)
if os.path.isfile(path): # is it file?
ft = gajim.interface.instances['file_transfers']
ft.send_file(self.account, self.contact, path)
ft.send_file(self.account, c, path)
def _on_message_tv_buffer_changed(self, textbuffer):
self.kbd_activity_in_last_5_secs = True
@ -1833,7 +1895,7 @@ class ChatControl(ChatControlBase):
show_transports = gajim.config.get('show_transports_group')
if (not show_transports and gajim.jid_is_transport(jid)) or \
(not show_offline and typ == 'chat' and \
len(gajim.contacts.get_contact(self.account, jid)) < 2):
len(gajim.contacts.get_contacts(self.account, jid)) < 2):
gajim.interface.roster.really_remove_contact(self.contact,
self.account)
elif typ == 'pm':
@ -1919,8 +1981,12 @@ class ChatControl(ChatControlBase):
self.bigger_avatar_window.window.set_cursor(cursor)
def _on_send_file_menuitem_activate(self, widget):
if self.TYPE_ID == message_control.TYPE_PM:
c = self.gc_contact
else:
c = self.contact
gajim.interface.instances['file_transfers'].show_file_send_request(
self.account, self.contact)
self.account, c)
def _on_add_to_roster_menuitem_activate(self, widget):
dialogs.AddNewContactWindow(self.account, self.contact.jid)

View file

@ -63,10 +63,11 @@ class OldEntry(xmpp.Node, object):
else:
main_feed = None
if self.getTag('source-feed') is not None:
source_feed = self.getTag('source-feed').getTagData('title')
if self.getTag('feed') is not None:
source_feed = self.getTag('feed').getTagData('title')
else:
source_feed = None
if main_feed is not None and source_feed is not None:
return u'%s: %s' % (main_feed, source_feed)
@ -78,14 +79,13 @@ class OldEntry(xmpp.Node, object):
return u''
feed_title = property(get_feed_title, None, None,
''' Title of feed. It is built from entry's original feed title and title of feed
''' Title of feed. It is built from entry''s original feed title and title of feed
which delivered this entry. ''')
def get_feed_link(self):
''' Get a link to main page of feed (in pubsub.com: second link of rel='alternate',
first contains raw xml data). '''
''' Get source link '''
try:
return self.getTag('source-feed').getTags('link', {'rel':'alternate'})[1].getData()
return self.getTag('feed').getTags('link',{'rel':'alternate'})[1].getData()
except:
return None

259
src/common/caps.py Normal file
View file

@ -0,0 +1,259 @@
##
## Copyright (C) 2006 Gajim Team
##
## 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; version 2 only.
##
## 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.
##
from itertools import *
import xmpp
import xmpp.features_nb
import gajim
class CapsCache(object):
''' This object keeps the mapping between caps data and real disco
features they represent, and provides simple way to query that info.
It is application-wide, that is there's one object for all
connections.
Goals:
* handle storing/retrieving info from database
* cache info in memory
* expose simple interface
Properties:
* one object for all connections (move to logger.py?)
* store info efficiently (a set() of urls -- we can assume there won't be
too much of these, ensure that (X,Y,Z1) and (X,Y,Z2) has different
features.
Connections with other objects: (TODO)
Interface:
# object creation
>>> cc=CapsCache(logger_object)
>>> caps=('http://exodus.jabberstudio.org/caps', '0.9', None) # node, ver, ext
>>> muc='http://jabber.org/protocol/muc'
>>> chatstates='http://jabber.org/protocol/chatstates'
# retrieving data
>>> muc in cc[caps].features
True
>>> muc in cc[caps]
True
>>> chatstates in cc[caps]
False
>>> cc[caps].identities
set({'category':'client', 'type':'pc'})
>>> x=cc[caps] # more efficient if making several queries for one set of caps
ATypicalBlackBoxObject
>>> muc in x
True
>>> x.node
'http://exodus.jabberstudio.org/caps'
# retrieving data (multiple exts case)
>>> caps=('http://gajim.org/caps', '0.9', ('csn', 'ft'))
>>> muc in cc[caps]
True
# setting data
>>> newcaps=('http://exodus.jabberstudio.org/caps', '0.9a', None)
>>> cc[newcaps].identities.add({'category':'client', 'type':'pc', 'name':'Gajim'})
>>> cc[newcaps].features+=muc # same as:
>>> cc[newcaps]+=muc
>>> cc[newcaps]['csn']+=chatstates # adding data as if ext was 'csn'
# warning: no feature removal!
'''
def __init__(self, logger=None):
''' Create a cache for entity capabilities. '''
# our containers:
# __names is a string cache; every string long enough is given
# another object, and we will have plenty of identical long
# strings. therefore we can cache them
# TODO: maybe put all known xmpp namespace strings here
# (strings given in xmpppy)?
# __cache is a dictionary mapping: pair of node and version maps
# to CapsCacheItem object
# __CacheItem is a class that stores data about particular
# client (node/version pair)
self.__names = {}
self.__cache = {}
class CacheItem(object):
''' TODO: logging data into db '''
def __init__(ciself, node, version, ext=None):
# cached into db
ciself.node = node
ciself.version = version
ciself.features = set()
ciself.ext = ext
ciself.exts = {}
# set of tuples: (category, type, name)
# (dictionaries are not hashable, so cannot be in sets)
ciself.identities = set()
# not cached into db:
# have we sent the query?
# 0 == not queried
# 1 == queried
# 2 == got the answer
ciself.queried = 0
class CacheQuery(object):
def __init__(cqself, proxied):
cqself.proxied=proxied
def __getattr__(cqself, obj):
if obj!='exts': return getattr(cqself.proxied[0], obj)
return set(chain(ci.features for ci in cqself.proxied))
def __getitem__(ciself, exts):
if not exts: # (), [], None, False, whatever
return ciself
if isinstance(exts, basestring):
exts=(exts,)
if len(exts)==1:
ext=exts[0]
if ext in ciself.exts:
return ciself.exts[ext]
x=CacheItem(ciself.node, ciself.version, ext)
ciself.exts[ext]=x
return x
proxied = [ciself]
proxied.extend(ciself[(e,)] for e in exts)
return ciself.CacheQuery(proxied)
def update(ciself, identities, features):
# NOTE: self refers to CapsCache object, not to CacheItem
self.identities=identities
self.features=features
self.logger.add_caps_entry(
ciself.node, ciself.version, ciself.ext,
identities, features)
self.__CacheItem = CacheItem
# prepopulate data which we are sure of; note: we do not log these info
gajimnode = 'http://gajim.org/caps'
gajimcaps=self[(gajimnode, '0.11.1')]
gajimcaps.category='client'
gajimcaps.type='pc'
gajimcaps.features=set((xmpp.NS_BYTESTREAM, xmpp.NS_SI,
xmpp.NS_FILE, xmpp.NS_MUC, xmpp.NS_COMMANDS,
xmpp.NS_DISCO_INFO, xmpp.NS_PING, xmpp.NS_TIME_REVISED))
gajimcaps['cstates'].features=set((xmpp.NS_CHATSTATES,))
gajimcaps['xhtml'].features=set((xmpp.NS_XHTML_IM,))
# TODO: older gajim versions
# start logging data from the net
self.logger = logger
def load_from_db(self):
# get data from logger...
if self.logger is not None:
for node, ver, ext, identities, features in self.logger.iter_caps_data():
x=self[(node, ver, ext)]
x.identities=identities
x.features=features
x.queried=2
def __getitem__(self, caps):
node_version = caps[:2]
if node_version in self.__cache:
return self.__cache[node_version][caps[2]]
node, version = self.__names.setdefault(caps[0], caps[0]), caps[1]
x=self.__CacheItem(node, version)
self.__cache[(node, version)]=x
return x
def preload(self, account, jid, node, ver, exts):
''' Preload data about (node, ver, exts) caps using disco
query to jid using proper connection. Don't query if
the data is already in cache. '''
q=self[(node, ver, ())]
qq=q
if q.queried==0:
# do query for bare node+version pair
# this will create proper object
q.queried=1
account.discoverInfo(jid, '%s#%s' % (node, ver))
for ext in exts:
qq=q[ext]
if qq.queried==0:
# do query for node+version+ext triple
qq.queried=1
account.discoverInfo(jid, '%s#%s' % (node, ext))
gajim.capscache = CapsCache(gajim.logger)
class ConnectionCaps(object):
''' This class highly depends on that it is a part of Connection class. '''
def _capsPresenceCB(self, con, presence):
''' Handle incoming presence stanzas... This is a callback
for xmpp registered in connection_handlers.py'''
# get the caps element
caps=presence.getTag('c')
if not caps: return
node, ver=caps['node'], caps['ver']
if node is None or ver is None:
# improper caps in stanza, ignoring
return
try:
exts=caps['ext'].split(' ')
except AttributeError:
# no exts means no exts, a perfectly valid case
exts=[]
# we will put these into proper Contact object and ask
# for disco... so that disco will learn how to interpret
# these caps
jid=str(presence.getFrom())
# start disco query...
gajim.capscache.preload(self, jid, node, ver, exts)
contact=gajim.contacts.get_contact_from_full_jid(self.name, jid)
if contact in [None, []]:
return # TODO: a way to put contact not-in-roster into Contacts
elif isinstance(contact, list):
contact = contact[0]
# overwriting old data
contact.caps_node=node
contact.caps_ver=ver
contact.caps_exts=exts
def _capsDiscoCB(self, jid, node, identities, features, data):
contact=gajim.contacts.get_contact_from_full_jid(self.name, jid)
if not contact: return
if not contact.caps_node: return # we didn't asked for that?
if not node.startswith(contact.caps_node+'#'): return
node, ext = node.split('#')
if ext==contact.caps_ver: # this can be also version (like '0.9')
exts=None
else:
exts=(ext,)
# if we don't have this info already...
caps=gajim.capscache[(node, contact.caps_ver, exts)]
if caps.queried==2: return
identities=set((i['category'], i['type'], i.get('name')) for i in identities)
caps.update(identities, features)

View file

@ -42,6 +42,7 @@ def create_log_db():
# logs.jid_id --> jids.jid_id but Sqlite doesn't do FK etc so it's done in python code
# jids.jid text column will be JID if TC-related, room_jid if GC-related,
# ROOM_JID/nick if pm-related.
# also check optparser.py, which updates databases on gajim updates
cur.executescript(
'''
CREATE TABLE jids(
@ -74,6 +75,12 @@ def create_log_db():
);
CREATE INDEX idx_logs_jid_id_kind ON logs (jid_id, kind);
CREATE TABLE caps_cache (
node TEXT,
ver TEXT,
ext TEXT,
data BLOB);
'''
)

View file

@ -236,12 +236,42 @@ class LeaveGroupchatsCommand(AdHocCommand):
return False
class ForwardMessagesCommand(AdHocCommand):
# http://www.xmpp.org/extensions/xep-0146.html#forward
commandnode = 'forward-messages'
commandname = _('Forward unread messages')
@staticmethod
def isVisibleFor(samejid):
''' Change status is visible only if the entity has the same bare jid. '''
return samejid
def execute(self, request):
account = self.connection.name
# Forward messages
events = gajim.events.get_events(account, types=['chat', 'normal'])
j, resource = gajim.get_room_and_nick_from_fjid(self.jid)
for jid in events:
for event in events[jid]:
self.connection.send_message(j, event.parameters[0], '',
type=event.type_, subject=event.parameters[1],
resource=resource, forward_from=jid)
# Inform other client of completion
response, cmd = self.buildResponse(request, status = 'completed')
cmd.addChild('note', {}, _('All unread messages have been forwarded.'))
self.connection.connection.send(response)
return False # finish the session
class ConnectionCommands:
''' This class depends on that it is a part of Connection() class. '''
def __init__(self):
# a list of all commands exposed: node -> command class
self.__commands = {}
for cmdobj in (ChangeStatusCommand, LeaveGroupchatsCommand):
for cmdobj in (ChangeStatusCommand, ForwardMessagesCommand,
LeaveGroupchatsCommand):
self.__commands[cmdobj.commandnode] = cmdobj
# a list of sessions; keys are tuples (jid, sessionid, node)

View file

@ -7,6 +7,7 @@
## Copyright (C) 2005 Travis Shirk <travis@pobox.com>
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -81,7 +82,7 @@ class Config:
'markedmsgcolor': [ opt_color, '#ff8080', '', True ],
'urlmsgcolor': [ opt_color, '#0000ff', '', True ],
'collapsed_rows': [ opt_str, '', _('List (space separated) of rows (accounts and groups) that are collapsed.'), True ],
'roster_theme': [ opt_str, 'gtk+', '', True ],
'roster_theme': [ opt_str, _('default'), '', True ],
'saveposition': [ opt_bool, True ],
'mergeaccounts': [ opt_bool, False, '', True ],
'sort_by_show': [ opt_bool, True, '', True ],
@ -182,12 +183,12 @@ class Config:
'tooltip_avatar_height': [opt_int, 125],
'vcard_avatar_width': [opt_int, 200],
'vcard_avatar_height': [opt_int, 200],
'notification_preview_message': [opt_bool, True, _('Preview new messages in notification popup?')],
'notification_position_x': [opt_int, -1],
'notification_position_y': [opt_int, -1],
'notification_avatar_width': [opt_int, 48],
'notification_avatar_height': [opt_int, 48],
'muc_highlight_words': [opt_str, '', _('A semicolon-separated list of words that will be highlighted in group chats.')],
'minimize_autojoined_rooms': [opt_bool, False, _('If True, autojoined bookmarked rooms will be minimized on startup.')],
'quit_on_roster_x_button': [opt_bool, False, _('If True, quits Gajim when X button of Window Manager is clicked. This setting is taken into account only if trayicon is used.')],
'check_if_gajim_is_default': [opt_bool, True, _('If True, Gajim will check if it\'s the default jabber client on each startup.')],
'show_unread_tab_icon': [opt_bool, False, _('If True, Gajim will display an icon on each tab containing unread messages. Depending on the theme, this icon may be animated.')],
@ -211,8 +212,7 @@ class Config:
_('Controls the window where new messages are placed.\n\'always\' - All messages are sent to a single window.\n\'never\' - All messages get their own window.\n\'peracct\' - Messages for each account are sent to a specific window.\n\'pertype\' - Each message type (e.g., chats vs. groupchats) are sent to a specific window. Note, changing this option requires restarting Gajim before the changes will take effect.')],
'show_avatar_in_chat': [opt_bool, True, _('If False, you will no longer see the avatar in the chat window.')],
'escape_key_closes': [opt_bool, True, _('If True, pressing the escape key closes a tab/window.')],
'always_hide_groupchat_buttons': [opt_bool, False, _('Hides the buttons in group chat window.')],
'always_hide_chat_buttons': [opt_bool, False, _('Hides the buttons in two persons chat window.')],
'compact_view': [opt_bool, False, _('Hides the buttons in chat windows.')],
'hide_groupchat_banner': [opt_bool, False, _('Hides the banner in a group chat window')],
'hide_chat_banner': [opt_bool, False, _('Hides the banner in two persons chat window')],
'hide_groupchat_occupants_list': [opt_bool, False, _('Hides the group chat occupants list in group chat window.')],
@ -228,6 +228,7 @@ class Config:
'scroll_roster_to_last_message': [opt_bool, True, _('If True, Gajim will scroll and select the contact who sent you the last message, if chat window is not already opened.')],
'use_latex': [opt_bool, False, _('If True, Gajim will convert string between $$ and $$ to an image using dvips and convert before insterting it in chat window.')],
'change_status_window_timeout': [opt_int, 15, _('Time of inactivity needed before the change status window closes down.')],
'max_conversation_lines': [opt_int, 500, _('Maximum number of lines that are printed in conversations. Oldest lines are cleared.')],
}
__options_per_key = {
@ -260,6 +261,7 @@ class Config:
'gpgpassword': [ opt_str, '' ],
'sync_with_global_status': [ opt_bool, False, ],
'no_log_for': [ opt_str, '' ],
'minimized_gc': [ opt_str, '' ],
'attached_gpg_keys': [ opt_str, '' ],
'keep_alives_enabled': [ opt_bool, True],
# send keepalive every N seconds of inactivity
@ -387,8 +389,8 @@ class Config:
themes_default = {
# sorted alphanum
'gtk+': [ '', '', '', 'B', '', '','', 'I', '', '', '', '', '','', '',
'B' ],
_('default'): [ '', '', '', 'B', '', '','', 'I', '', '', '', '', '','',
'', 'B' ],
_('green'): [ '', '#94aa8c', '', 'B', '#0000ff', '#eff3e7',
'', 'I', '#000000', '', '', '', '',

View file

@ -25,6 +25,11 @@ def fse(s):
'''Convert from filesystem encoding if not already Unicode'''
return unicode(s, sys.getfilesystemencoding())
def windowsify(s):
if os.name == 'nt':
return s.capitalize()
return s
class ConfigPaths:
def __init__(self, root=None):
self.root = root
@ -68,51 +73,47 @@ class ConfigPaths:
for key in self.paths.iterkeys():
yield (key, self[key])
def windowsify(s):
if os.name == 'nt':
return s.capitalize()
return s
def init(self, root = None):
if root is not None:
self.root = root
def init():
paths = ConfigPaths()
# LOG is deprecated
k = ( 'LOG', 'LOG_DB', 'VCARD', 'AVATAR', 'MY_EMOTS',
'MY_ICONSETS' )
v = (u'logs', u'logs.db', u'vcards', u'avatars', u'emoticons',
u'iconsets')
# LOG is deprecated
k = ( 'LOG', 'LOG_DB', 'VCARD', 'AVATAR', 'MY_EMOTS' )
v = (u'logs', u'logs.db', u'vcards', u'avatars', u'emoticons')
if os.name == 'nt':
v = map(lambda x: x.capitalize(), v)
if os.name == 'nt':
v = map(lambda x: x.capitalize(), v)
for n, p in zip(k, v):
self.add_from_root(n, p)
for n, p in zip(k, v):
paths.add_from_root(n, p)
self.add('DATA', os.path.join(u'..', windowsify(u'data')))
self.add('HOME', fse(os.path.expanduser('~')))
self.add('TMP', fse(tempfile.gettempdir()))
paths.add('DATA', os.path.join(u'..', windowsify(u'data')))
paths.add('HOME', fse(os.path.expanduser('~')))
paths.add('TMP', fse(tempfile.gettempdir()))
try:
import svn_config
svn_config.configure(self)
except (ImportError, AttributeError):
pass
try:
import svn_config
svn_config.configure(paths)
except (ImportError, AttributeError):
pass
# for k, v in paths.iteritems():
# print "%s: %s" % (repr(k), repr(v))
# for k, v in paths.iteritems():
# print "%s: %s" % (repr(k), repr(v))
def init_profile(self, profile):
conffile = windowsify(u'config')
pidfile = windowsify(u'gajim')
return paths
if len(profile) > 0:
conffile += u'.' + profile
pidfile += u'.' + profile
pidfile += u'.pid'
self.add_from_root('CONFIG_FILE', conffile)
self.add_from_root('PID_FILE', pidfile)
gajimpaths = init()
# for k, v in paths.iteritems():
# print "%s: %s" % (repr(k), repr(v))
def init_profile(profile, paths=gajimpaths):
conffile = windowsify(u'config')
pidfile = windowsify(u'gajim')
if len(profile) > 0:
conffile += u'.' + profile
pidfile += u'.' + profile
pidfile += u'.pid'
paths.add_from_root('CONFIG_FILE', conffile)
paths.add_from_root('PID_FILE', pidfile)
# for k, v in paths.iteritems():
# print "%s: %s" % (repr(k), repr(v))
gajimpaths = ConfigPaths()

View file

@ -6,6 +6,7 @@
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -36,6 +37,7 @@ from common import helpers
from common import gajim
from common import GnuPG
from common import passwords
from common import exceptions
from connection_handlers import *
USE_GPG = GnuPG.USE_GPG
@ -48,12 +50,50 @@ log = logging.getLogger('gajim.c.connection')
import gtkgui_helpers
ssl_error = {
2: "Unable to get issuer certificate",
3: "Unable to get certificate CRL",
4: "Unable to decrypt certificate's signature",
5: "Unable to decrypt CRL's signature",
6: "Unable to decode issuer public key",
7: "Certificate signature failure",
8: "CRL signature failure",
9: "Certificate is not yet valid",
10: "Certificate has expired",
11: "CRL is not yet valid",
12: "CRL has expired",
13: "Format error in certificate's notBefore field",
14: "Format error in certificate's notAfter field",
15: "Format error in CRL's lastUpdate field",
16: "Format error in CRL's nextUpdate field",
17: "Out of memory",
18: "Self signed certificate in certificate chain",
19: "Unable to get local issuer certificate",
20: "Unable to verify the first certificate",
21: "Unable to verify the first certificate",
22: "Certificate chain too long",
23: "Certificate revoked",
24: "Invalid CA certificate",
25: "Path length constraint exceeded",
26: "Unsupported certificate purpose",
27: "Certificate not trusted",
28: "Certificate rejected",
29: "Subject issuer mismatch",
30: "Authority and subject key identifier mismatch",
31: "Authority and issuer serial number mismatch",
32: "Key usage does not include certificate signing",
50: "Application verification failure"
}
class Connection(ConnectionHandlers):
'''Connection class'''
def __init__(self, name):
ConnectionHandlers.__init__(self)
self.name = name
self.connected = 0 # offline
# self.connected:
# 0=>offline,
# 1=>connection in progress,
# 2=>authorised
self.connected = 0
self.connection = None # xmpppy ClientCommon instance
# this property is used to prevent double connections
self.last_connection = None # last ClientCommon instance
@ -93,6 +133,10 @@ class Connection(ConnectionHandlers):
self.pep_supported = False
# Do we continue connection when we get roster (send presence,get vcard...)
self.continue_connect_info = None
# To know the groupchat jid associated with a sranza ID. Useful to
# request vcard or os info... to a real JID but act as if it comes from
# the fake jid
self.groupchat_jids = {} # {ID : groupchat_jid}
if USE_GPG:
self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
gajim.config.set('usegpg', True)
@ -210,10 +254,7 @@ class Connection(ConnectionHandlers):
% (data[0], data[3])))
return
is_form = data[2]
if is_form:
conf = data[1]
else:
conf = data[1].asDict()
conf = data[1]
self.dispatch('NEW_ACC_CONNECTED', (conf, is_form))
return
if not data[1]: # wrong answer
@ -222,10 +263,7 @@ class Connection(ConnectionHandlers):
(data[0], data[3])))
return
is_form = data[2]
if is_form:
conf = data[1]
else:
conf = data[1].asDict()
conf = data[1]
self.dispatch('REGISTER_AGENT_INFO', (data[0], conf, is_form))
elif realm == common.xmpp.NS_PRIVACY:
if event == common.xmpp.features_nb.PRIVACY_LISTS_RECEIVED:
@ -262,6 +300,8 @@ class Connection(ConnectionHandlers):
self.dispatch('STANZA_SENT', unicode(data))
def select_next_host(self, hosts):
'''Chooses best 'real' host basing on the SRV priority and weight data;
more info in RFC2782'''
hosts_best_prio = []
best_prio = 65535
sum_weight = 0
@ -440,72 +480,20 @@ class Connection(ConnectionHandlers):
name = gajim.config.get_per('accounts', self.name, 'name')
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
self.connection = con
fpr_good = self._check_fingerprint(con, con_type)
if fpr_good == False:
self.disconnect(on_purpose = True)
self.dispatch('STATUS', 'offline')
self.dispatch('CONNECTION_LOST',
(_('Security error connecting to "%s"') % self._hostname,
_("The server's key has changed, or someone is trying to hack your connection.")))
if self.on_connect_auth:
self.on_connect_auth(None)
self.on_connect_auth = None
return
if fpr_good == None:
log.warning(_("Unable to check fingerprint for %s. Connection could be insecure."), hostname)
if fpr_good == True:
log.info("Fingerprint found and matched for %s.", hostname)
try:
errnum = con.Connection.ssl_errnum
except AttributeError:
errnum = -1 # we don't have an errnum
if errnum > 0:
# FIXME: tell the user that the certificat is untrusted, and ask him what to do
try:
log.warning("The authenticity of the "+hostname+" certificate could be unvalid.\nSSL Error: "+ssl_error[errnum])
except KeyError:
log.warning("Unknown SSL error: %d" % errnum)
con.auth(name, self.password, self.server_resource, 1, self.__on_auth)
return True
def _check_fingerprint(self, con, con_type):
fpr_good = None # None: Unable to check fpr, False: mismatch, True: match
# FIXME: not tidy
if not common.xmpp.transports_nb.USE_PYOPENSSL: return None
# FIXME: find a more permanent place for loading servers.xml
servers_xml = os.path.join(gajim.DATA_DIR, 'other', 'servers.xml')
servers = gtkgui_helpers.parse_server_xml(servers_xml)
servers = dict(map(lambda e: (e[0], e), servers))
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
try:
log.debug("con: %s", con)
log.debug("con.Connection: %s", con.Connection)
log.debug("con.Connection.serverDigestSHA1: %s", con.Connection.serverDigestSHA1)
log.debug("con.Connection.serverDigestMD5: %s", con.Connection.serverDigestMD5)
sha1 = gtkgui_helpers.HashDigest('sha1', con.Connection.serverDigestSHA1)
md5 = gtkgui_helpers.HashDigest('md5', con.Connection.serverDigestMD5)
log.debug("sha1: %s", repr(sha1))
log.debug("md5: %s", repr(md5))
sv = servers.get(hostname)
if sv:
for got in (sha1, md5):
expected = sv[2]['digest'].get(got.algo)
if expected:
fpr_good = (got == expected)
break
except AttributeError:
if con_type in ('ssl', 'tls'):
log.error(_("Missing fingerprint in SSL connection to %s") + ':', hostname, exc_info=True)
# fpr_good = False # FIXME: enable this when sequence is sorted
else:
log.debug("Connection to %s doesn't seem to have a fingerprint:", hostname, exc_info=True)
if fpr_good == False:
log.error(_("Fingerprint mismatch for %s: Got %s, expected %s"), hostname, got, expected)
return fpr_good
def _register_handlers(self, con, con_type):
self.peerhost = con.get_peerhost()
# notify the gui about con_type
@ -566,14 +554,14 @@ class Connection(ConnectionHandlers):
iq = common.xmpp.Iq('get', to = pingTo.get_full_jid())
iq.addChild(name = 'ping', namespace = common.xmpp.NS_PING)
def _on_response(resp):
timePong = time.time()
timePong = time_time()
if not common.xmpp.isResultNode(resp):
self.dispatch('PING_ERROR', (pingTo))
return
timeDiff = round(timePong - timePing,2)
self.dispatch('PING_REPLY', (pingTo, timeDiff))
self.dispatch('PING_SENT', (pingTo))
timePing = time.time()
timePing = time_time()
self.connection.SendAndCallForResponse(iq, _on_response)
def get_active_default_lists(self):
@ -765,6 +753,12 @@ class Connection(ConnectionHandlers):
self.on_purpose = False
self.server_resource = gajim.config.get_per('accounts', self.name,
'resource')
# All valid resource substitution strings should be added to this hash.
if self.server_resource:
self.server_resource = Template(self.server_resource).\
safe_substitute({
'hostname': socket.gethostname()
})
self.connect_and_init(show, msg, signed)
elif show == 'offline':
@ -825,8 +819,8 @@ class Connection(ConnectionHandlers):
self.connection.send(msg_iq)
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
chatstate = None, msg_id = None, composing_jep = None, resource = None,
user_nick = None, xhtml = None):
chatstate = None, msg_id = None, composing_xep = None, resource = None,
user_nick = None, xhtml = None, forward_from = None):
if not self.connection:
return 1
if msg and not xhtml and gajim.config.get('rst_formatting_outgoing_messages'):
@ -850,7 +844,7 @@ class Connection(ConnectionHandlers):
' ([This message is *encrypted* (See :JEP:`27`])'
else:
# Encryption failed, do not send message
tim = time.localtime()
tim = localtime()
self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim))
return 3
if msgtxt and not xhtml and gajim.config.get(
@ -879,12 +873,12 @@ class Connection(ConnectionHandlers):
# please note that the only valid tag inside a message containing a <body>
# tag is the active event
if chatstate is not None:
if (composing_jep == 'JEP-0085' or not composing_jep) and \
composing_jep != 'asked_once':
# JEP-0085
if (composing_xep == 'XEP-0085' or not composing_xep) and \
composing_xep != 'asked_once':
# XEP-0085
msg_iq.setTag(chatstate, namespace = common.xmpp.NS_CHATSTATES)
if composing_jep in ('JEP-0022', 'asked_once') or not composing_jep:
# JEP-0022
if composing_xep in ('XEP-0022', 'asked_once') or not composing_xep:
# XEP-0022
chatstate_node = msg_iq.setTag('x',
namespace = common.xmpp.NS_EVENT)
if not msgtxt: # when no <body>, add <id>
@ -895,21 +889,30 @@ class Connection(ConnectionHandlers):
if chatstate is 'composing' or msgtxt:
chatstate_node.addChild(name = 'composing')
if forward_from:
addresses = msg_iq.addChild('addresses',
namespace=common.xmpp.NS_ADDRESS)
addresses.addChild('address', attrs = {'type': 'ofrom',
'jid': forward_from})
self.connection.send(msg_iq)
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')\
.split()
ji = gajim.get_jid_without_resource(jid)
if self.name not in no_log_for and ji not in no_log_for:
log_msg = msg
if subject:
log_msg = _('Subject: %s\n%s') % (subject, msg)
if log_msg:
if type == 'chat':
kind = 'chat_msg_sent'
else:
kind = 'single_msg_sent'
gajim.logger.write(kind, jid, log_msg)
self.dispatch('MSGSENT', (jid, msg, keyID))
if not forward_from:
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')\
.split()
ji = gajim.get_jid_without_resource(jid)
if self.name not in no_log_for and ji not in no_log_for:
log_msg = msg
if subject:
log_msg = _('Subject: %s\n%s') % (subject, msg)
if log_msg:
if type == 'chat':
kind = 'chat_msg_sent'
else:
kind = 'single_msg_sent'
try:
gajim.logger.write(kind, jid, log_msg)
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
self.dispatch('MSGSENT', (jid, msg, keyID))
def send_stanza(self, stanza):
''' send a stanza untouched '''
@ -1059,7 +1062,9 @@ class Connection(ConnectionHandlers):
def account_changed(self, new_name):
self.name = new_name
def request_last_status_time(self, jid, resource):
def request_last_status_time(self, jid, resource, groupchat_jid=None):
'''groupchat_jid is used when we want to send a request to a real jid
and act as if the answer comes from the groupchat_jid'''
if not self.connection:
return
to_whom_jid = jid
@ -1067,9 +1072,15 @@ class Connection(ConnectionHandlers):
to_whom_jid += '/' + resource
iq = common.xmpp.Iq(to = to_whom_jid, typ = 'get', queryNS =\
common.xmpp.NS_LAST)
id = self.connection.getAnID()
iq.setID(id)
if groupchat_jid:
self.groupchat_jids[id] = groupchat_jid
self.connection.send(iq)
def request_os_info(self, jid, resource):
def request_os_info(self, jid, resource, groupchat_jid=None):
'''groupchat_jid is used when we want to send a request to a real jid
and act as if the answer comes from the groupchat_jid'''
if not self.connection:
return
# If we are invisible, do not request
@ -1081,6 +1092,10 @@ class Connection(ConnectionHandlers):
to_whom_jid += '/' + resource
iq = common.xmpp.Iq(to = to_whom_jid, typ = 'get', queryNS =\
common.xmpp.NS_VERSION)
id = self.connection.getAnID()
iq.setID(id)
if groupchat_jid:
self.groupchat_jids[id] = groupchat_jid
self.connection.send(iq)
def get_settings(self):
@ -1088,7 +1103,7 @@ class Connection(ConnectionHandlers):
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
iq3 = iq2.addChild(name='gajim', namespace='gajim:prefs')
self.connection.send(iq)
@ -1098,7 +1113,7 @@ class Connection(ConnectionHandlers):
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
iq2.addChild(name='storage', namespace='storage:bookmarks')
self.connection.send(iq)
@ -1107,12 +1122,13 @@ class Connection(ConnectionHandlers):
if not self.connection:
return
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
iq3 = iq2.addChild(name='storage', namespace='storage:bookmarks')
for bm in self.bookmarks:
iq4 = iq3.addChild(name = "conference")
iq4.setAttr('jid', bm['jid'])
iq4.setAttr('autojoin', bm['autojoin'])
iq4.setAttr('minimize', bm['minimize'])
iq4.setAttr('name', bm['name'])
# Only add optional elements if not empty
# Note: need to handle both None and '' as empty
@ -1131,7 +1147,7 @@ class Connection(ConnectionHandlers):
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
iq2.addChild(name='storage', namespace='storage:rosternotes')
self.connection.send(iq)
@ -1140,7 +1156,7 @@ class Connection(ConnectionHandlers):
if not self.connection:
return
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
iq3 = iq2.addChild(name='storage', namespace='storage:rosternotes')
for jid in self.annotations.keys():
if self.annotations[jid]:
@ -1155,7 +1171,7 @@ class Connection(ConnectionHandlers):
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
iq2.addChild(name='storage', namespace='storage:metacontacts')
id = self.connection.getAnID()
iq.setID(id)
@ -1167,7 +1183,7 @@ class Connection(ConnectionHandlers):
if not self.connection:
return
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
iq3 = iq2.addChild(name='storage', namespace='storage:metacontacts')
for tag in tags_list:
for data in tags_list[tag]:
@ -1264,7 +1280,7 @@ class Connection(ConnectionHandlers):
self.connection.send(p)
# Save the time we quit to avoid duplicate logs AND be faster than
# get that date from DB
self.last_history_line[jid] = time.time()
self.last_history_line[jid] = time_time()
def gc_set_role(self, room_jid, nick, role, reason = ''):
'''role is for all the life of the room so it's based on nick'''
@ -1419,7 +1435,8 @@ class Connection(ConnectionHandlers):
return
df = []
for item in tag.getTags('item'):
f = {}
# We also show attributes. jid is there
f = item.attrs
for i in item.getPayload():
f[i.getName()] = i.getData()
df.append(f)

View file

@ -18,13 +18,13 @@
##
import os
import time
import base64
import sha
import socket
import sys
from time import localtime, strftime, gmtime
from time import (altzone, daylight, gmtime, localtime, mktime, strftime,
time as time_time, timezone, tzname)
from calendar import timegm
import socks5
@ -35,8 +35,10 @@ from common import helpers
from common import gajim
from common import atom
from common import pep
from common import exceptions
from common.commands import ConnectionCommands
from common.pubsub import ConnectionPubSub
from common.caps import ConnectionCaps
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible', 'error']
@ -414,7 +416,7 @@ class ConnectionBytestream:
def _ResultCB(self, con, iq_obj):
gajim.log.debug('_ResultCB')
# if we want to respect jep-0065 we have to check for proxy
# if we want to respect xep-0065 we have to check for proxy
# activation result in any result iq
real_id = unicode(iq_obj.getAttr('id'))
if real_id[:3] != 'au_':
@ -562,6 +564,7 @@ class ConnectionBytestream:
file_props['sender'] = helpers.get_full_jid_from_iq(iq_obj)
file_props['request-id'] = unicode(iq_obj.getAttr('id'))
file_props['sid'] = unicode(si.getAttr('id'))
file_props['transfered_size'] = []
gajim.socks5queue.add_file_props(self.name, file_props)
self.dispatch('FILE_REQUEST', (jid, file_props))
raise common.xmpp.NodeProcessed
@ -588,12 +591,12 @@ class ConnectionBytestream:
class ConnectionDisco:
''' hold xmpppy handlers and public methods for discover services'''
def discoverItems(self, jid, node = None, id_prefix = None):
'''According to JEP-0030: jid is mandatory,
'''According to XEP-0030: jid is mandatory,
name, node, action is optional.'''
self._discover(common.xmpp.NS_DISCO_ITEMS, jid, node, id_prefix)
def discoverInfo(self, jid, node = None, id_prefix = None):
'''According to JEP-0030:
'''According to XEP-0030:
For identity: category, type is mandatory, name is optional.
For feature: var is mandatory'''
self._discover(common.xmpp.NS_DISCO_INFO, jid, node, id_prefix)
@ -702,6 +705,10 @@ class ConnectionDisco:
def _DiscoverItemsGetCB(self, con, iq_obj):
gajim.log.debug('DiscoverItemsGetCB')
node = iq_obj.getTagAttr('query', 'node')
if node is None:
result = iq_obj.buildReply('result')
self.connection.send(result)
raise common.xmpp.NodeProcessed
if node==common.xmpp.NS_COMMANDS:
self.commandListQuery(con, iq_obj)
raise common.xmpp.NodeProcessed
@ -746,6 +753,10 @@ class ConnectionDisco:
if (node is None or extension == 'xhtml') and not gajim.config.get('ignore_incoming_xhtml'):
q.addChild('feature', attrs = {'var': common.xmpp.NS_XHTML_IM})
if node is None:
q.addChild('feature', attrs = {'var': common.xmpp.NS_PING})
q.addChild('feature', attrs = {'var': common.xmpp.NS_TIME_REVISED})
if q.getChildren():
self.connection.send(iq)
raise common.xmpp.NodeProcessed
@ -757,7 +768,7 @@ class ConnectionDisco:
def _DiscoverInfoCB(self, con, iq_obj):
gajim.log.debug('DiscoverInfoCB')
# According to JEP-0030:
# According to XEP-0030:
# For identity: category, type is mandatory, name is optional.
# For feature: var is mandatory
identities, features, data = [], [], []
@ -775,11 +786,13 @@ class ConnectionDisco:
attr = {}
for key in i.getAttrs().keys():
attr[key] = i.getAttr(key)
if attr.has_key('category') and attr['category'] in ('gateway', 'headline')\
and attr.has_key('type'):
if attr.has_key('category') and \
attr['category'] in ('gateway', 'headline') and \
attr.has_key('type'):
transport_type = attr['type']
if attr.has_key('category') and attr['category'] == 'conference' \
and attr.has_key('type') and attr['type'] == 'text':
if attr.has_key('category') and \
attr['category'] == 'conference' and \
attr.has_key('type') and attr['type'] == 'text':
is_muc = True
identities.append(attr)
elif i.getName() == 'feature':
@ -796,6 +809,9 @@ class ConnectionDisco:
identities = [{'category': 'server', 'type': 'im', 'name': node}]
if id[0] == 'p':
if jid == gajim.config.get_per('accounts', self.name, 'hostname'):
if features.__contains__(common.xmpp.NS_GMAILNOTIFY):
gajim.gmail_domains.append(jid)
self.request_gmail_notifications()
for identity in identities:
if identity['category'] == 'pubsub' and identity['type'] == \
'pep':
@ -811,16 +827,17 @@ class ConnectionDisco:
self.available_transports[transport_type].append(jid)
else:
self.available_transports[transport_type] = [jid]
self.dispatch('AGENT_INFO_INFO', (jid, node, identities,
features, data))
self._capsDiscoCB(jid, node, identities, features, data)
class ConnectionVcard:
def __init__(self):
self.vcard_sha = None
self.vcard_shas = {} # sha of contacts
self.room_jids = [] # list of gc jids so that vcard are saved in a folder
self.groupchat_jids = {} # {ID : groupchat_jid}
def add_sha(self, p, send_caps = True):
c = p.setTag('x', namespace = common.xmpp.NS_VCARD_UPDATE)
if self.vcard_sha is not None:
@ -830,7 +847,7 @@ class ConnectionVcard:
return p
def add_caps(self, p):
''' advertise our capabilities in presence stanza (jep-0115)'''
''' advertise our capabilities in presence stanza (xep-0115)'''
c = p.setTag('c', namespace = common.xmpp.NS_CAPS)
c.setAttr('node', 'http://gajim.org/caps')
ext = []
@ -880,9 +897,12 @@ class ConnectionVcard:
path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
else:
path_to_file = path
fil = open(path_to_file, 'w')
fil.write(str(card))
fil.close()
try:
fil = open(path_to_file, 'w')
fil.write(str(card))
fil.close()
except IOError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
def get_cached_vcard(self, fjid, is_fake_jid = False):
'''return the vcard as a dict
@ -1042,7 +1062,7 @@ class ConnectionVcard:
elif self.awaiting_answers[id][0] == METACONTACTS_ARRIVED:
if iq_obj.getType() == 'result':
# Metacontact tags
# http://www.jabber.org/jeps/jep-XXXX.html
# http://www.xmpp.org/extensions/xep-0209.html
meta_list = {}
query = iq_obj.getTag('query')
storage = query.getTag('storage')
@ -1171,12 +1191,13 @@ class ConnectionVcard:
#('VCARD', {entry1: data, entry2: {entry21: data, ...}, ...})
self.dispatch('VCARD', vcard)
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub):
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionCaps):
def __init__(self):
ConnectionVcard.__init__(self)
ConnectionBytestream.__init__(self)
ConnectionCommands.__init__(self)
ConnectionPubSub.__init__(self)
self.gmail_url=None
# List of IDs we are waiting answers for {id: (type_of_request, data), }
self.awaiting_answers = {}
# List of IDs that will produce a timeout is answer doesn't arrive
@ -1229,7 +1250,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
def _PrivateCB(self, con, iq_obj):
'''
Private Data (JEP 048 and 049)
Private Data (XEP 048 and 049)
'''
gajim.log.debug('PrivateCB')
query = iq_obj.getTag('query')
@ -1238,18 +1259,22 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
ns = storage.getNamespace()
if ns == 'storage:bookmarks':
# Bookmarked URLs and Conferences
# http://www.jabber.org/jeps/jep-0048.html
# http://www.xmpp.org/extensions/xep-0048.html
confs = storage.getTags('conference')
for conf in confs:
autojoin_val = conf.getAttr('autojoin')
if autojoin_val is None: # not there (it's optional)
autojoin_val = False
minimize_val = conf.getAttr('minimize')
if minimize_val is None: # not there (it's optional)
minimize_val = False
print_status = conf.getTagData('print_status')
if not print_status:
print_status = conf.getTagData('show_status')
bm = {'name': conf.getAttr('name'),
'jid': conf.getAttr('jid'),
'autojoin': autojoin_val,
'minimize': minimize_val,
'password': conf.getTagData('password'),
'nick': conf.getTagData('nick'),
'print_status': print_status}
@ -1259,7 +1284,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
elif ns == 'gajim:prefs':
# Preferences data
# http://www.jabber.org/jeps/jep-0049.html
# http://www.xmpp.org/extensions/xep-0049.html
#TODO: implement this
pass
elif ns == 'storage:rosternotes':
@ -1279,7 +1304,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
ns = storage_tag.getNamespace()
if ns == 'storage:metacontacts':
self.metacontacts_supported = False
# Private XML Storage (JEP49) is not supported by server
# Private XML Storage (XEP49) is not supported by server
# Continue connecting
self.connection.initRoster()
@ -1329,7 +1354,12 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
seconds = int(seconds)
except:
return
who = helpers.get_full_jid_from_iq(iq_obj)
id = iq_obj.getID()
if id in self.groupchat_jids:
who = self.groupchat_jids[id]
del self.groupchat_jids[id]
else:
who = helpers.get_full_jid_from_iq(iq_obj)
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
self.dispatch('LAST_STATUS_TIME', (jid_stripped, resource, seconds, status))
@ -1344,7 +1374,12 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
client_info += ' ' + qp.getTag('version').getData()
if qp.getTag('os'):
os_info += qp.getTag('os').getData()
who = helpers.get_full_jid_from_iq(iq_obj)
id = iq_obj.getID()
if id in self.groupchat_jids:
who = self.groupchat_jids[id]
del self.groupchat_jids[id]
else:
who = helpers.get_full_jid_from_iq(iq_obj)
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
self.dispatch('OS_INFO', (jid_stripped, resource, client_info, os_info))
@ -1352,18 +1387,21 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
gajim.log.debug('TimeCB')
iq_obj = iq_obj.buildReply('result')
qp = iq_obj.getTag('query')
qp.setTagData('utc', strftime("%Y%m%dT%T", gmtime()))
qp.setTagData('tz', strftime("%Z", gmtime()))
qp.setTagData('display', strftime("%c", localtime()))
qp.setTagData('utc', strftime('%Y%m%dT%T', gmtime()))
qp.setTagData('tz', tzname[daylight])
qp.setTagData('display', strftime('%c', localtime()))
self.connection.send(iq_obj)
raise common.xmpp.NodeProcessed
def _TimeRevisedCB(self, con, iq_obj):
gajim.log.debug('TimeRevisedCB')
iq_obj = iq_obj.buildReply('result')
qp = iq_obj.setTag('time')
qp.setTagData('utc', strftime("%Y-%m-%dT%TZ", gmtime()))
qp.setTagData('tzo', "%+03d:00"% (-time.timezone/(60*60)))
qp = iq_obj.setTag('time',
namespace=common.xmpp.NS_TIME_REVISED)
qp.setTagData('utc', strftime('%Y-%m-%dT%TZ', gmtime()))
zone = -(timezone, altzone)[daylight] / 60
tzo = (zone / 60, abs(zone % 60))
qp.setTagData('tzo', '%+03d:%02d' % (tzo))
self.connection.send(iq_obj)
raise common.xmpp.NodeProcessed
@ -1386,6 +1424,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
'''Called when we receive results from Querying the server for mail messages in gmail account'''
if not gm.getTag('mailbox'):
return
self.gmail_url = gm.getTag('mailbox').getAttr('url')
if gm.getTag('mailbox').getNamespace() == common.xmpp.NS_GMAILNOTIFY:
newmsgs = gm.getTag('mailbox').getAttr('total-matched')
if newmsgs != '0':
@ -1425,10 +1464,17 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
mtype = msg.getType()
subject = msg.getSubject() # if not there, it's None
tim = msg.getTimestamp()
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
tim = time.localtime(timegm(tim))
tim = helpers.datetime_tuple(tim)
tim = localtime(timegm(tim))
frm = helpers.get_full_jid_from_iq(msg)
jid = helpers.get_jid_from_iq(msg)
addressTag = msg.getTag('addresses', namespace = common.xmpp.NS_ADDRESS)
# Be sure it comes from one of our resource, else ignore address element
if addressTag and jid == gajim.get_jid_from_account(self.name):
address = addressTag.getTag('address', attrs={'type': 'ofrom'})
if address:
frm = address.getAttr('jid')
jid = gajim.get_jid_without_resource(frm)
no_log_for = gajim.config.get_per('accounts', self.name,
'no_log_for')
if not no_log_for:
@ -1446,10 +1492,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
invite = None
delayed = msg.getTag('x', namespace = common.xmpp.NS_DELAY) != None
msg_id = None
composing_jep = None
composing_xep = None
# FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) do NOT RECOMMENDED
# invitation
# stanza (MUC JEP) remove in 2007, as we do not do NOT RECOMMENDED
# stanza (MUC XEP) remove in 2007, as we do not do NOT RECOMMENDED
xtags = msg.getTags('x')
for xtag in xtags:
if xtag.getNamespace() == common.xmpp.NS_CONFERENCE and not invite:
@ -1458,22 +1504,22 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
return
# chatstates - look for chatstate tags in a message if not delayed
if not delayed:
composing_jep = False
composing_xep = False
children = msg.getChildren()
for child in children:
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
chatstate = child.getName()
composing_jep = 'JEP-0085'
composing_xep = 'XEP-0085'
break
# No JEP-0085 support, fallback to JEP-0022
# No XEP-0085 support, fallback to XEP-0022
if not chatstate:
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
if chatstate_child:
chatstate = 'active'
composing_jep = 'JEP-0022'
composing_xep = 'XEP-0022'
if not msgtxt and chatstate_child.getTag('composing'):
chatstate = 'composing'
# JEP-0172 User Nickname
# XEP-0172 User Nickname
user_nick = msg.getTagData('nick')
if not user_nick:
user_nick = ''
@ -1494,8 +1540,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
error_msg = msgtxt
msgtxt = None
if self.name not in no_log_for:
gajim.logger.write('error', frm, error_msg, tim = tim,
subject = subject)
try:
gajim.logger.write('error', frm, error_msg, tim = tim,
subject = subject)
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt,
tim))
return
@ -1507,22 +1556,34 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp))
else:
if not msg.getTag('body'): #no <body>
# It could be a config change. See
# http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
if msg.getTag('x'):
statusCode = msg.getStatusCode()
if statusCode != []:
self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode))
return
# Ignore message from room in which we are not
if not self.last_history_line.has_key(jid):
return
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msghtml))
if self.name not in no_log_for and not int(float(time.mktime(tim)))\
if self.name not in no_log_for and not int(float(mktime(tim)))\
<= self.last_history_line[jid] and msgtxt:
gajim.logger.write('gc_msg', frm, msgtxt, tim = tim)
try:
gajim.logger.write('gc_msg', frm, msgtxt, tim = tim)
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
return
elif mtype == 'chat': # it's type 'chat'
if not msg.getTag('body') and chatstate is None: #no <body>
return
if msg.getTag('body') and self.name not in no_log_for and jid not in\
no_log_for and msgtxt:
msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, tim = tim,
subject = subject)
try:
msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt,
tim = tim, subject = subject)
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
else: # it's single message
if invite is not None:
item = invite.getTag('invite')
@ -1533,14 +1594,17 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('GC_INVITATION',(frm, jid_from, reason, password))
return
if self.name not in no_log_for and jid not in no_log_for and msgtxt:
gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim,
subject = subject)
try:
gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim,
subject = subject)
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
mtype = 'normal'
treat_as = gajim.config.get('treat_incoming_messages')
if treat_as:
mtype = treat_as
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype,
subject, chatstate, msg_id, composing_jep, user_nick, msghtml))
subject, chatstate, msg_id, composing_xep, user_nick, msghtml))
# END messageCB
def _pubsubEventCB(self, con, msg):
@ -1567,20 +1631,13 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if items is None: return
for item in items.getTags('item'):
# check for event type (for now only one type supported: pubsub.com events)
child = item.getTag('pubsub-message')
if child is not None:
# we have pubsub.com notification
child = child.getTag('feed')
if child is None: continue
for entry in child.getTags('entry'):
# for each entry in feed (there shouldn't be more than one,
# but to be sure...
self.dispatch('ATOM_ENTRY', (atom.OldEntry(node=entry),))
entry = item.getTag('entry')
if entry is not None:
# for each entry in feed (there shouldn't be more than one,
# but to be sure...
self.dispatch('ATOM_ENTRY', (atom.OldEntry(node=entry),))
continue
# unknown type... probably user has another client who understands that event
raise common.xmpp.NodeProcessed
def _presenceCB(self, con, prs):
@ -1609,7 +1666,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
sigTag = None
ns_muc_user_x = None
avatar_sha = None
# JEP-0172 User Nickname
# XEP-0172 User Nickname
user_nick = prs.getTagData('nick')
if not user_nick:
user_nick = ''
@ -1628,10 +1685,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
avatar_sha = x.getTagData('photo')
contact_nickname = x.getTagData('nickname')
elif namespace == common.xmpp.NS_DELAY:
# JEP-0091
# XEP-0091
tim = prs.getTimestamp()
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
timestamp = time.localtime(timegm(tim))
tim = helpers.datetime_tuple(tim)
timestamp = localtime(timegm(tim))
elif namespace == 'http://delx.cjb.net/protocol/roster-subsync':
# see http://trac.gajim.org/ticket/326
agent = gajim.get_server_from_jid(jid_stripped)
@ -1664,27 +1721,29 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if ptype == 'error':
errmsg = prs.getError()
errcode = prs.getErrorCode()
room_jid, nick = gajim.get_room_and_nick_from_fjid(who)
if errcode == '502': # Internal Timeout:
self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource,
prio, keyID, timestamp, None))
elif errcode == '401': # password required to join
self.dispatch('ERROR', (_('Unable to join group chat'),
_('A password is required to join this group chat.')))
self.dispatch('GC_PASSWORD_REQUIRED', (room_jid, nick))
elif errcode == '403': # we are banned
self.dispatch('ERROR', (_('Unable to join group chat'),
_('You are banned from this group chat.')))
_('You are banned from group chat %s.') % room_jid))
elif errcode == '404': # group chat does not exist
self.dispatch('ERROR', (_('Unable to join group chat'),
_('Such group chat does not exist.')))
_('Group chat %s does not exist.') % room_jid))
elif errcode == '405':
self.dispatch('ERROR', (_('Unable to join group chat'),
_('Group chat creation is restricted.')))
elif errcode == '406':
self.dispatch('ERROR', (_('Unable to join group chat'),
_('Your registered nickname must be used.')))
_('Your registered nickname must be used in group chat %s.') \
% room_jid))
elif errcode == '407':
self.dispatch('ERROR', (_('Unable to join group chat'),
_('You are not in the members list.')))
_('You are not in the members list in groupchat %s.') % \
room_jid))
elif errcode == '409': # nick conflict
# the jid_from in this case is FAKE JID: room_jid/nick
# resource holds the bad nick so propose a new one
@ -1692,7 +1751,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
gajim.config.get('gc_proposed_nick_char')
room_jid = gajim.get_room_from_fjid(who)
self.dispatch('ASK_NEW_NICK', (room_jid, _('Unable to join group chat'),
_('Your desired nickname is in use or registered by another occupant.\nPlease specify another nickname below:'), proposed_nickname))
_('Your desired nickname in group chat %s is in use or registered by another occupant.\nPlease specify another nickname below:') % room_jid, proposed_nickname))
else: # print in the window the error
self.dispatch('ERROR_ANSWER', ('', jid_stripped,
errmsg, errcode))
@ -1709,7 +1768,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if jid:
# we know real jid, save it in db
st += ' (%s)' % jid
gajim.logger.write('gcstatus', who, st, show)
try:
gajim.logger.write('gcstatus', who, st, show)
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
if avatar_sha or avatar_sha == '':
if avatar_sha == '':
# contact has no avatar
@ -1728,7 +1790,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
jid = destroy.getAttr('jid')
if jid:
reason += '\n' + _('You can join this room instead: %s') % jid
statusCode = 'destroyed'
statusCode = ['destroyed']
else:
reason = prs.getReason()
statusCode = prs.getStatusCode()
@ -1763,10 +1825,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# detect a subscription loop
if not self.subscribed_events.has_key(jid_stripped):
self.subscribed_events[jid_stripped] = []
self.subscribed_events[jid_stripped].append(time.time())
self.subscribed_events[jid_stripped].append(time_time())
block = False
if len(self.subscribed_events[jid_stripped]) > 5:
if time.time() - self.subscribed_events[jid_stripped][0] < 5:
if time_time() - self.subscribed_events[jid_stripped][0] < 5:
block = True
self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:]
if block:
@ -1783,10 +1845,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# detect a unsubscription loop
if not self.subscribed_events.has_key(jid_stripped):
self.subscribed_events[jid_stripped] = []
self.subscribed_events[jid_stripped].append(time.time())
self.subscribed_events[jid_stripped].append(time_time())
block = False
if len(self.subscribed_events[jid_stripped]) > 5:
if time.time() - self.subscribed_events[jid_stripped][0] < 5:
if time_time() - self.subscribed_events[jid_stripped][0] < 5:
block = True
self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:]
if block:
@ -1816,9 +1878,12 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# avatar has been updated
self.request_vcard(jid_stripped)
if not ptype or ptype == 'unavailable':
if gajim.config.get('log_contact_status_changes') and self.name\
not in no_log_for and jid_stripped not in no_log_for:
gajim.logger.write('status', jid_stripped, status, show)
if gajim.config.get('log_contact_status_changes') and self.name \
not in no_log_for and jid_stripped not in no_log_for:
try:
gajim.logger.write('status', jid_stripped, status, show)
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
self.dispatch('NOTIFY', (jid_stripped, show, status, resource, prio,
keyID, timestamp, contact_nickname))
# END presenceCB
@ -1875,6 +1940,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if not self.connection:
return
self.connection.getRoster(self._on_roster_set)
self.discoverItems(gajim.config.get_per('accounts', self.name,
'hostname'), id_prefix='p')
self.discoverInfo(gajim.config.get_per('accounts', self.name,
'hostname'), id_prefix='p')
if gajim.config.get_per('accounts', self.name, 'use_ft_proxies'):
self.discover_ft_proxies()
@ -1885,10 +1954,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
proxies = map(lambda e:e.strip(), cfg_proxies.split(','))
for proxy in proxies:
gajim.proxy65_manager.resolve(proxy, self.connection)
self.discoverItems(gajim.config.get_per('accounts', self.name,
'hostname'), id_prefix='p')
self.discoverInfo(gajim.config.get_per('accounts', self.name,
'hostname'), id_prefix='p')
def _on_roster_set(self, roster):
raw_roster = roster.getRaw()
@ -1951,29 +2016,31 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# Get annotations from private namespace
self.get_annotations()
# If it's a gmail account,
# inform the server that we want e-mail notifications
if gajim.get_server_from_jid(our_jid) in gajim.gmail_domains:
gajim.log.debug(('%s is a gmail account. Setting option '
'to get e-mail notifications on the server.') % (our_jid))
iq = common.xmpp.Iq(typ = 'set', to = our_jid)
iq.setAttr('id', 'MailNotify')
query = iq.setTag('usersetting')
query.setNamespace(common.xmpp.NS_GTALKSETTING)
query = query.setTag('mailnotifications')
query.setAttr('value', 'true')
self.connection.send(iq)
# Ask how many messages there are now
iq = common.xmpp.Iq(typ = 'get')
iq.setAttr('id', '13')
query = iq.setTag('query')
query.setNamespace(common.xmpp.NS_GMAILNOTIFY)
self.connection.send(iq)
#Inform GUI we just signed in
self.dispatch('SIGNED_IN', ())
self.continue_connect_info = None
def request_gmail_notifications(self):
# It's a gmail account,
# inform the server that we want e-mail notifications
our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name))
gajim.log.debug(('%s is a gmail account. Setting option '
'to get e-mail notifications on the server.') % (our_jid))
iq = common.xmpp.Iq(typ = 'set', to = our_jid)
iq.setAttr('id', 'MailNotify')
query = iq.setTag('usersetting')
query.setNamespace(common.xmpp.NS_GTALKSETTING)
query = query.setTag('mailnotifications')
query.setAttr('value', 'true')
self.connection.send(iq)
# Ask how many messages there are now
iq = common.xmpp.Iq(typ = 'get')
iq.setID(self.connection.getAnID())
query = iq.setTag('query')
query.setNamespace(common.xmpp.NS_GMAILNOTIFY)
self.connection.send(iq)
def _search_fields_received(self, con, iq_obj):
jid = jid = helpers.get_jid_from_iq(iq_obj)
tag = iq_obj.getTag('query', namespace = common.xmpp.NS_SEARCH)
@ -1999,6 +2066,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# that defines handlers
con.RegisterHandler('message', self._messageCB)
con.RegisterHandler('presence', self._presenceCB)
con.RegisterHandler('presence', self._capsPresenceCB)
con.RegisterHandler('iq', self._vCardCB, 'result',
common.xmpp.NS_VCARD)
con.RegisterHandler('iq', self._rosterSetCB, 'set',

View file

@ -2,6 +2,8 @@
##
## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
##
##
## This program is free software; you can redistribute it and/or modify
@ -20,7 +22,7 @@ class Contact:
'''Information concerning each contact'''
def __init__(self, jid='', name='', groups=[], show='', status='', sub='',
ask='', resource='', priority=0, keyID='', our_chatstate=None,
chatstate=None, last_status_time=None, msg_id = None, composing_jep = None):
chatstate=None, last_status_time=None, msg_id = None, composing_xep = None):
self.jid = jid
self.name = name
self.contact_name = '' # nick choosen by contact
@ -37,6 +39,12 @@ class Contact:
self.priority = priority
self.keyID = keyID
# Capabilities; filled by caps.py/ConnectionCaps object
# every time it gets these from presence stanzas
self.caps_node=None
self.caps_ver=None
self.caps_exts=None
# please read jep-85 http://www.jabber.org/jeps/jep-0085.html
# we keep track of jep85 support with the peer by three extra states:
# None, False and 'ask'
@ -47,9 +55,9 @@ class Contact:
self.our_chatstate = our_chatstate
self.msg_id = msg_id
# tell which JEP we're using for composing state
# None = have to ask, JEP-0022 = use this jep,
# JEP-0085 = use this jep, False = no composing support
self.composing_jep = composing_jep
# None = have to ask, XEP-0022 = use this jep,
# XEP-0085 = use this jep, False = no composing support
self.composing_xep = composing_xep
# this is contact's chatstate
self.chatstate = chatstate
self.last_status_time = last_status_time
@ -153,10 +161,10 @@ class Contacts:
def create_contact(self, jid='', name='', groups=[], show='', status='',
sub='', ask='', resource='', priority=0, keyID='', our_chatstate=None,
chatstate=None, last_status_time=None, composing_jep=None):
chatstate=None, last_status_time=None, composing_xep=None):
return Contact(jid, name, groups, show, status, sub, ask, resource,
priority, keyID, our_chatstate, chatstate, last_status_time,
composing_jep)
composing_xep)
def copy_contact(self, contact):
return self.create_contact(jid = contact.jid, name = contact.name,
@ -208,28 +216,28 @@ class Contacts:
# remove metacontacts info
self.remove_metacontact(account, jid)
def get_contact(self, account, jid, resource = None):
'''Returns the list of contact instances for this jid (one per resource)
or [] if no resource is given
returns the contact instance for the given resource if it's given
or None if there is not'''
def get_contacts(self, account, jid):
'''Returns the list of contact instances for this jid.'''
if jid in self._contacts[account]:
return self._contacts[account][jid]
else:
return []
def get_contact(self, account, jid, resource = None):
'''Returns the contact instance for the given resource if it's given else
the first contact is no resource is given or None if there is not'''
if jid in self._contacts[account]:
contacts = self._contacts[account][jid]
if not resource:
return contacts
for c in contacts:
return self._contacts[account][jid][0]
for c in self._contacts[account][jid]:
if c.resource == resource:
return c
if resource:
return None
return []
return None
def get_contacts_from_jid(self, account, jid):
'''we may have two or more resources on that jid'''
if jid in self._contacts[account]:
contacts_instances = self._contacts[account][jid]
return contacts_instances
return []
def get_contact_from_full_jid(self, account, fjid):
''' Get Contact object for specific resource of given jid'''
barejid, resource = common.gajim.get_room_and_nick_from_fjid(fjid)
return self.get_contact(account, barejid, resource)
def get_highest_prio_contact_from_contacts(self, contacts):
if not contacts:
@ -241,7 +249,7 @@ class Contacts:
return prim_contact
def get_contact_with_highest_priority(self, account, jid):
contacts = self.get_contacts_from_jid(account, jid)
contacts = self.get_contacts(account, jid)
if not contacts and '/' in jid:
# jid may be a fake jid, try it
room, nick = jid.split('/', 1)
@ -258,7 +266,7 @@ class Contacts:
'''Returns all contacts in the given group'''
group_contacts = []
for jid in self._contacts[account]:
contacts = self.get_contacts_from_jid(account, jid)
contacts = self.get_contacts(account, jid)
if group in contacts[0].groups:
group_contacts += contacts
return group_contacts

View file

@ -13,7 +13,7 @@ class WrongFieldValue(Error): pass # when we get xmpp.Node which contains bad fi
class ExtendedNode(xmpp.Node, object):
@classmethod
def __new__(cls, *a, **b):
if 'extend' not in b.keys():
if 'extend' not in b.keys() or not b['extend']:
return object.__new__(cls)
extend = b['extend']
@ -76,7 +76,9 @@ def ExtendForm(node):
return SimpleDataForm(extend=node)
class DataField(ExtendedNode):
""" Keeps data about one field - var, field type, labels, instructions... """
""" Keeps data about one field - var, field type, labels, instructions...
Base class for different kinds of fields. Use Field() function to
construct one of these. """
def __init__(self, typ=None, var=None, value=None, label=None, desc=None, required=False,
options=None, extend=None):
@ -390,8 +392,6 @@ class MultipleDataForm(DataForm):
DataForm.__init__(self, type=type, title=title, instructions=instructions, extend=extend)
# all records, recorded into DataRecords
if extend is None:
# we have to build this object from scratch
xmpp.Node.__init__(self)
if items is not None: self.items = items
else:

View file

@ -60,6 +60,8 @@ class SystemBus:
return False
if self.system_bus is None:
return False
# Don't exit Gajim when dbus is stopped
self.system_bus.set_exit_on_disconnect(False)
return True
system_bus = SystemBus()

View file

@ -2,7 +2,7 @@ docdir = '../'
datadir = '../'
version = '0.11.1.0'
version = '0.11.1.5'
import sys, os.path
for base in ('.', 'common'):

View file

@ -143,8 +143,8 @@ class Events:
self.fire_event_removed(removed_list)
return
# no event nor type given, remove them all
del self._events[account][jid]
self.fire_event_removed(self._events[account][jid])
del self._events[account][jid]
def change_jid(self, account, old_jid, new_jid):
if not self._events[account].has_key(old_jid):

View file

@ -21,6 +21,15 @@ class PysqliteNotAvailable(Exception):
def __str__(self):
return _('pysqlite2 (aka python-pysqlite2) dependency is missing. Exiting...')
class PysqliteOperationalError(Exception):
'''sqlite2 raised pysqlite2.dbapi2.OperationalError'''
def __init__(self, text=''):
Exception.__init__(self)
self.text = text
def __str__(self):
return self.text
class ServiceNotAvailable(Exception):
'''This exception is raised when we cannot use Gajim remotely'''
def __init__(self):

View file

@ -71,6 +71,7 @@ LOGPATH = gajimpaths['LOG'] # deprecated
VCARD_PATH = gajimpaths['VCARD']
AVATAR_PATH = gajimpaths['AVATAR']
MY_EMOTS_PATH = gajimpaths['MY_EMOTS']
MY_ICONSETS_PATH = gajimpaths['MY_ICONSETS']
TMP = gajimpaths['TMP']
DATA_DIR = gajimpaths['DATA']
HOME_DIR = gajimpaths['HOME']

View file

@ -5,6 +5,7 @@
## Copyright (C) 2005
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -206,6 +207,8 @@ def get_contact_dict_for_account(account):
contacts_dict['%s (%s)' % (name, contact1.jid)] = contact1
contacts_dict['%s (%s)' % (name, jid)] = contact
else:
if contact.name == gajim.get_nick_from_jid(jid):
del contacts_dict[jid]
contacts_dict[name] = contact
return contacts_dict
@ -462,7 +465,7 @@ def play_sound(event):
def play_sound_file(path_to_soundfile):
if path_to_soundfile == 'beep':
print '\a' # make a speaker beep
exec_command('beep')
return
if path_to_soundfile is None or not os.path.exists(path_to_soundfile):
return
@ -544,14 +547,19 @@ def get_global_status():
def get_icon_name_to_show(contact, account = None):
'''Get the icon name to show in online, away, requested, ...'''
if account and gajim.events.get_nb_roster_events(account, contact.jid):
return 'message'
return 'event'
if account and gajim.events.get_nb_roster_events(account,
contact.get_full_jid()):
return 'message'
return 'event'
if account and gajim.interface.minimized_controls.has_key(account) and \
contact.jid in gajim.interface.minimized_controls[account] and gajim.interface.\
minimized_controls[account][contact.jid].get_nb_unread_pm() > 0:
return 'message'
return 'event'
if account and gajim.gc_connected[account].has_key(contact.jid):
if gajim.gc_connected[account][contact.jid]:
return 'muc_active'
else:
return 'muc_inactive'
if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
return contact.show
if contact.sub in ('both', 'to'):
@ -863,66 +871,127 @@ def reduce_chars_newlines(text, max_chars = 0, max_lines = 0):
lines = map(lambda e: _cut_if_long(e), lines)
if lines:
reduced_text = reduce(lambda e, e1: e + '\n' + e1, lines)
if reduced_text != text:
reduced_text += '...'
else:
reduced_text = ''
return reduced_text
def get_notification_icon_tooltip_text():
text = None
unread_chat = gajim.events.get_nb_events(types = ['printed_chat',
'chat'])
unread_single_chat = gajim.events.get_nb_events(types = ['normal'])
unread_gc = gajim.events.get_nb_events(types = ['printed_gc_msg',
'printed_marked_gc_msg', 'gc_msg'])
unread_pm = gajim.events.get_nb_events(types = ['printed_pm', 'pm'])
def get_account_status(account):
status = reduce_chars_newlines(account['status_line'], 100, 1)
return status
def get_notification_icon_tooltip_dict():
'''returns a dict of the form {acct: {'show': show, 'message': message,
'event_lines': [list of text lines to show in tooltip]}'''
# How many events must there be before they're shown summarized, not per-user
max_ungrouped_events = 10
accounts = get_accounts_info()
if unread_chat or unread_single_chat or unread_gc or unread_pm:
text = 'Gajim '
awaiting_events = unread_chat + unread_single_chat + unread_gc + unread_pm
if awaiting_events == unread_chat or awaiting_events == unread_single_chat \
or awaiting_events == unread_gc or awaiting_events == unread_pm:
# This condition is like previous if but with xor...
# Print in one line
text += '-'
else:
# Print in multiple lines
text += '\n '
if unread_chat:
text += ngettext(
' %d unread message',
' %d unread messages',
unread_chat, unread_chat, unread_chat)
text += '\n '
if unread_single_chat:
text += ngettext(
' %d unread single message',
' %d unread single messages',
unread_single_chat, unread_single_chat, unread_single_chat)
text += '\n '
if unread_gc:
text += ngettext(
' %d unread group chat message',
' %d unread group chat messages',
unread_gc, unread_gc, unread_gc)
text += '\n '
if unread_pm:
text += ngettext(
' %d unread private message',
' %d unread private messages',
unread_pm, unread_pm, unread_pm)
text += '\n '
text = text[:-4] # remove latest '\n '
elif len(accounts) > 1:
text = _('Gajim')
elif len(accounts) == 1:
message = accounts[0]['status_line']
message = reduce_chars_newlines(message, 100, 1)
text = _('Gajim - %s') % message
else:
text = _('Gajim - %s') % get_uf_show('offline')
# Gather events. (With accounts, when there are more.)
for account in accounts:
account_name = account['name']
account['event_lines'] = []
# Gather events per-account
pending_events = gajim.events.get_events(account = account_name)
messages, non_messages, total_messages, total_non_messages = {}, {}, 0, 0
for jid in pending_events:
for event in pending_events[jid]:
if event.type_.count('file') > 0:
# This is a non-messagee event.
messages[jid] = non_messages.get(jid, 0) + 1
total_non_messages = total_non_messages + 1
else:
# This is a message.
messages[jid] = messages.get(jid, 0) + 1
total_messages = total_messages + 1
# Display unread messages numbers, if any
if total_messages > 0:
if total_messages > max_ungrouped_events:
text = ngettext(
'%d message pending',
'%d messages pending',
total_messages, total_messages, total_messages)
account['event_lines'].append(text)
else:
for jid in messages.keys():
text = ngettext(
'%d message pending',
'%d messages pending',
messages[jid], messages[jid], messages[jid])
contact = gajim.contacts.get_first_contact_from_jid(
account['name'], jid)
if jid in gajim.gc_connected[account['name']]:
text += _(' from room %s') % (jid)
elif contact:
name = contact.get_shown_name()
text += _(' from user %s') % (name)
else:
text += _(' from %s') % (jid)
account['event_lines'].append(text)
# Display unseen events numbers, if any
if total_non_messages > 0:
if total_non_messages > max_ungrouped_events:
text = ngettext(
'%d event pending',
'%d events pending',
total_non_messages, total_non_messages, total_non_messages)
accounts[account]['event_lines'].append(text)
else:
for jid in non_messages.keys():
text = ngettext(
'%d event pending',
'%d events pending',
non_messages[jid], non_messages[jid], non_messages[jid])
text += _(' from user %s') % (jid)
accounts[account]['event_lines'].append(text)
return accounts
def get_notification_icon_tooltip_text():
text = None
# How many events must there be before they're shown summarized, not per-user
max_ungrouped_events = 10
# Character which should be used to indent in the tooltip.
indent_with = ' '
accounts = get_notification_icon_tooltip_dict()
if len(accounts) == 0:
# No configured account
return _('Gajim')
# at least one account present
# Is there more that one account?
if len(accounts) == 1:
show_more_accounts = False
else:
show_more_accounts = True
# If there is only one account, its status is shown on the first line.
if show_more_accounts:
text = _('Gajim')
else:
text = _('Gajim - %s') % (get_account_status(accounts[0]))
# Gather and display events. (With accounts, when there are more.)
for account in accounts:
account_name = account['name']
# Set account status, if not set above
if (show_more_accounts):
message = '\n' + indent_with + ' %s - %s'
text += message % (account_name, get_account_status(account))
# Account list shown, messages need to be indented more
indent_how = 2
else:
# If no account list is shown, messages could have default indenting.
indent_how = 1
for line in account['event_lines']:
text += '\n' + indent_with * indent_how + ' '
text += line
return text
def get_accounts_info():
@ -963,3 +1032,21 @@ def get_avatar_path(prefix):
if os.path.exists(file_):
return file_
return None
def datetime_tuple(timestamp):
'''Converts timestamp using strptime and the format: %Y%m%dT%H:%M:%S
Because of various datetime formats are used the following exceptions
are handled:
- Optional milliseconds appened to the string are removed
- XEP-082 datetime strings have all '-' cahrs removed to meet
the above format.'''
timestamp = timestamp.split('.')[0]
timestamp = timestamp.replace('-', '')
from time import strptime
return strptime(timestamp, '%Y%m%dT%H:%M:%S')
def get_iconset_path(iconset):
if os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', iconset)):
return os.path.join(gajim.DATA_DIR, 'iconsets', iconset)
elif os.path.isdir(os.path.join(gajim.MY_ICONSETS_PATH, iconset)):
return os.path.join(gajim.MY_ICONSETS_PATH, iconset)

View file

@ -13,10 +13,14 @@
## GNU General Public License for more details.
##
''' This module allows to access the on-disk database of logs. '''
import os
import sys
import time
import datetime
from gzip import GzipFile
from cStringIO import StringIO
import exceptions
import gajim
@ -169,19 +173,21 @@ class Logger:
jid = jid.split('/', 1)[0] # remove the resource
if jid in self.jids_already_in: # we already have jids in DB
self.cur.execute('SELECT jid_id FROM jids WHERE jid=?', [jid])
jid_id = self.cur.fetchone()[0]
else: # oh! a new jid :), we add it now
if typestr == 'ROOM':
typ = constants.JID_ROOM_TYPE
else:
typ = constants.JID_NORMAL_TYPE
self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)', (jid, typ))
try:
self.con.commit()
except sqlite.OperationalError, e:
print >> sys.stderr, str(e)
jid_id = self.cur.lastrowid
self.jids_already_in.append(jid)
row = self.cur.fetchone()
if row:
return row[0]
# oh! a new jid :), we add it now
if typestr == 'ROOM':
typ = constants.JID_ROOM_TYPE
else:
typ = constants.JID_NORMAL_TYPE
self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)', (jid, typ))
try:
self.con.commit()
except sqlite.OperationalError, e:
print >> sys.stderr, str(e)
jid_id = self.cur.lastrowid
self.jids_already_in.append(jid)
return jid_id
def convert_human_values_to_db_api_values(self, kind, show):
@ -285,7 +291,10 @@ class Logger:
def commit_to_db(self, values, write_unread = False):
#print 'saving', values
sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message, subject) VALUES (?, ?, ?, ?, ?, ?, ?)'
self.cur.execute(sql, values)
try:
self.cur.execute(sql, values)
except sqlite.OperationalError, e:
raise exceptions.PysqliteOperationalError(str(e))
message_id = None
try:
self.con.commit()
@ -625,3 +634,77 @@ class Logger:
for result in results:
answer[result[0]] = self.convert_api_values_to_human_transport_type(result[1])
return answer
# A longer note here:
# The database contains a blob field. Pysqlite seems to need special care for such fields.
# When storing, we need to convert string into buffer object (1).
# When retrieving, we need to convert it back to a string to decompress it. (2)
# GzipFile needs a file-like object, StringIO emulates file for plain strings.
def iter_caps_data(self):
''' Iterate over caps cache data stored in the database.
The iterator values are pairs of (node, ver, ext, identities, features):
identities == {'category':'foo', 'type':'bar', 'name':'boo'},
features being a list of feature namespaces. '''
# get data from table
# the data field contains binary object (gzipped data), this is a hack
# to get that data without trying to convert it to unicode
#tmp, self.con.text_factory = self.con.text_factory, str
try:
self.cur.execute('''SELECT node, ver, ext, data FROM caps_cache;''');
except sqlite.OperationalError:
# might happen when there's no caps_cache table yet
# -- there's no data to read anyway then
#self.con.text_factory = tmp
return
#self.con.text_factory = tmp
for node, ver, ext, data in self.cur:
# for each row: unpack the data field
# (format: (category, type, name, category, type, name, ...
# ..., 'FEAT', feature1, feature2, ...).join(' '))
# NOTE: if there's a need to do more gzip, put that to a function
data=GzipFile(fileobj=StringIO(str(data))).read().split('\0') # (2) -- note above
i=0
identities=set()
features=set()
while i<(len(data)-2) and data[i]!='FEAT':
category=data[i]
type=data[i+1]
name=data[i+2]
identities.add((category,type,name))
i+=3
i+=1
while i<len(data):
features.add(data[i])
i+=1
if not ext: ext=None # to make '' a None
# yield the row
yield node, ver, ext, identities, features
def add_caps_entry(self, node, ver, ext, identities, features):
data=[]
for identity in identities:
# there is no FEAT category
if identity[0]=='FEAT': return
if len(identity)<2 or not identity[2]:
data.extend((identity[0], identity[1], ''))
else:
data.extend(identity)
data.append('FEAT')
data.extend(features)
data = '\0'.join(data)
string = StringIO() # if there's a need to do more gzip, put that to a function
gzip=GzipFile(fileobj=string, mode='w')
gzip.write(data)
gzip.close()
data = string.getvalue()
self.cur.execute('''
INSERT INTO caps_cache ( node, ver, ext, data )
VALUES (?, ?, ?, ?);
''', (node, ver, ext, buffer(data))) # (1) -- note above
try:
self.con.commit()
except sqlite.OperationalError, e:
print >> sys.stderr, str(e)

View file

@ -153,9 +153,21 @@ class OptionsParser:
self.update_config_to_01101()
if old < [0, 11, 0, 2] and new >= [0, 11, 0, 2]:
self.update_config_to_01102()
if old < [0, 11, 1, 1] and new >= [0, 11, 1, 1]:
self.update_config_to_01111()
if old < [0, 11, 1, 2] and new >= [0, 11, 1, 2]:
self.update_config_to_01112()
if old < [0, 11, 1, 3] and new >= [0, 11, 1, 3]:
self.update_config_to_01113()
if old < [0, 11, 1, 4] and new >= [0, 11, 1, 4]:
self.update_config_to_01114()
if old < [0, 11, 1, 5] and new >= [0, 11, 1, 5]:
self.update_config_to_01115()
gajim.logger.init_vars()
gajim.config.set('version', new_version)
gajim.capscache.load_from_db()
def update_config_x_to_09(self):
# Var name that changed:
@ -178,7 +190,7 @@ class OptionsParser:
'groupfontattrs', 'contacttextcolor', 'contactbgcolor', 'contactfont',
'contactfontattrs', 'bannertextcolor', 'bannerbgcolor', 'bannerfont',
'bannerfontattrs']
for theme_name in (_('grocery'), _('gtk+')):
for theme_name in (_('grocery'), _('default')):
if theme_name not in gajim.config.get_per('themes'):
gajim.config.add_per('themes', theme_name)
theme = gajim.config.themes_default[theme_name]
@ -209,7 +221,6 @@ class OptionsParser:
def assert_unread_msgs_table_exists(self):
'''create table unread_messages if there is no such table'''
#FIXME see #2812
back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER)
con = sqlite.connect(logger.LOG_DB_FILE)
@ -384,3 +395,84 @@ class OptionsParser:
gajim.config.set('ft_add_hosts_to_send',
self.old_values['ft_override_host_to_send'])
gajim.config.set('version', '0.11.0.2')
def update_config_to_01111(self):
'''always_hide_chatbuttons -> compact_view'''
if self.old_values.has_key('always_hide_groupchat_buttons') and \
self.old_values.has_key('always_hide_chat_buttons'):
gajim.config.set('compact_view', self.old_values['always_hide_groupchat_buttons'] and \
self.old_values['always_hide_chat_buttons'])
gajim.config.set('version', '0.11.1.1')
def update_config_to_01112(self):
'''gtk+ theme is renamed to default'''
if self.old_values.has_key('roster_theme') and \
self.old_values['roster_theme'] == 'gtk+':
gajim.config.set('roster_theme', _('default'))
gajim.config.set('version', '0.11.1.2')
def update_config_to_01113(self):
# copy&pasted from update_config_to_01013, possibly 'FIXME see #2812' applies too
back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER)
con = sqlite.connect(logger.LOG_DB_FILE)
os.chdir(back)
cur = con.cursor()
try:
cur.executescript(
'''
CREATE TABLE caps_cache (
node TEXT,
ver TEXT,
ext TEXT,
data BLOB
);
'''
)
con.commit()
except sqlite.OperationalError, e:
pass
con.close()
gajim.config.set('version', '0.11.1.3')
def update_config_to_01114(self):
# add default theme if it doesn't exist
d = ['accounttextcolor', 'accountbgcolor', 'accountfont',
'accountfontattrs', 'grouptextcolor', 'groupbgcolor', 'groupfont',
'groupfontattrs', 'contacttextcolor', 'contactbgcolor', 'contactfont',
'contactfontattrs', 'bannertextcolor', 'bannerbgcolor', 'bannerfont',
'bannerfontattrs']
theme_name = _('default')
if theme_name not in gajim.config.get_per('themes'):
gajim.config.add_per('themes', theme_name)
if gajim.config.get_per('themes', 'gtk+'):
# copy from old gtk+ theme
for o in d:
val = gajim.config.get_per('themes', 'gtk+', o)
gajim.config.set_per('themes', theme_name, o, val)
gajim.config.del_per('themes', 'gtk+')
else:
# copy from default theme
theme = gajim.config.themes_default[theme_name]
for o in d:
gajim.config.set_per('themes', theme_name, o, theme[d.index(o)])
gajim.config.set('version', '0.11.1.4')
def update_config_to_01115(self):
# copy&pasted from update_config_to_01013, possibly 'FIXME see #2812' applies too
back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER)
con = sqlite.connect(logger.LOG_DB_FILE)
os.chdir(back)
cur = con.cursor()
try:
cur.executescript(
'''
DELETE FROM caps_cache;
'''
)
con.commit()
except sqlite.OperationalError, e:
pass
con.close()
gajim.config.set('version', '0.11.1.5')

View file

@ -81,12 +81,18 @@ class GnomePasswordStorage(PasswordStorage):
def save_password(self, account_name, password, update=True):
display_name = _('Gajim account %s') % account_name
attributes = dict(account_name=str(account_name), gajim=1)
auth_token = gnomekeyring.item_create_sync(
self.keyring, gnomekeyring.ITEM_GENERIC_SECRET,
display_name, attributes, password, update)
try:
auth_token = gnomekeyring.item_create_sync(
self.keyring, gnomekeyring.ITEM_GENERIC_SECRET,
display_name, attributes, password, update)
except gnomekeyring.DeniedError:
set_storage(SimplePasswordStorage())
storage.save_password(account_name, password)
return
token = 'gnomekeyring:%i' % auth_token
gajim.config.set_per('accounts', account_name, 'password', token)
if gajim.connections.has_key(account_name):
gajim.connections[account_name].password = password
storage = None
def get_storage():
@ -111,6 +117,8 @@ def get_storage():
storage = GnomePasswordStorage()
except gnomekeyring.NoKeyringDaemonError:
storage = SimplePasswordStorage()
except gnomekeyring.DeniedError:
storage = SimplePasswordStorage()
else:
storage = SimplePasswordStorage()
return storage

View file

@ -92,7 +92,7 @@ class ConnectionPubSub:
try:
cb, args, kwargs = self.__callbacks.pop(stanza.getID())
cb(conn, stanza, *args, **kwargs)
except KeyError:
except:
pass
def request_pb_configuration(self, jid, node):

View file

@ -348,9 +348,10 @@ class Socks5:
def __init__(self, idlequeue, host, port, initiator, target, sid):
if host is not None:
try:
self.host = socket.gethostbyname(host)
self.host = host
self.ais = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
except socket.gaierror:
self.host = None
self.ais = None
self.idlequeue = idlequeue
self.fd = -1
self.port = port
@ -793,6 +794,8 @@ class Socks5Listener(IdleObject):
only pollin events though
'''
self.port = port
self.ais = socket.getaddrinfo(None, port, socket.AF_UNSPEC,
socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_PASSIVE)
self.queue_idx = -1
self.idlequeue = idlequeue
self.queue = None
@ -801,14 +804,21 @@ class Socks5Listener(IdleObject):
self.fd = -1
def bind(self):
self._serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# will fail when port as busy, or we don't have rights to bind
try:
self._serv.bind(('0.0.0.0', self.port))
except Exception, e:
for ai in self.ais:
#try the different possibilities (ipv6, ipv4, etc.)
self._serv = socket.socket(*ai[:3])
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# will fail when port as busy, or we don't have rights to bind
try:
self._serv.bind(ai[4])
self.ai = ai
break
except:
self.ai = None
continue
if not self.ai:
# unable to bind, show error dialog
return None
self._serv.listen(socket.SOMAXCONN)
@ -884,9 +894,18 @@ class Socks5Receiver(Socks5, IdleObject):
def connect(self):
''' create the socket and plug it to the idlequeue '''
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# this will not block the GUI
self._sock.setblocking(False)
for ai in self.ais:
try:
self._sock=socket.socket(*ai[:3])
# this will not block the GUI
self._sock.setblocking(False)
self._server=ai[4]
break
except:
if sys.exc_value[0] == errno.EINPROGRESS:
break
#for all errors, we try other addresses
continue
self.fd = self._sock.fileno()
self.state = 0 # about to be connected
self.idlequeue.plug_idle(self, True, False)
@ -950,7 +969,7 @@ class Socks5Receiver(Socks5, IdleObject):
def do_connect(self):
try:
self._sock.connect((self.host, self.port))
self._sock.connect(self._server)
self._sock.setblocking(False)
self._send=self._sock.send
self._recv=self._sock.recv

View file

@ -87,7 +87,7 @@ class Commands(PlugIn):
elif self._handlers[''].has_key(node):
self._handlers[''][node]['execute'](conn,request)
else:
conn.send(Error(requet,ERR_ITEM_NOT_FOUND))
conn.send(Error(request,ERR_ITEM_NOT_FOUND))
raise NodeProcessed
def _DiscoHandler(self,conn,request,typ):

View file

@ -23,7 +23,7 @@ Contains one tunable attribute: DefaultTimeout (25 seconds by default). It defin
Dispatcher.SendAndWaitForResponce method will wait for reply stanza before giving up.
'''
import simplexml, sys
import simplexml, sys, locale
from xml.parsers.expat import ExpatError
from protocol import *
from client import PlugIn
@ -111,6 +111,9 @@ class Dispatcher(PlugIn):
self._metastream.setAttr('version', '1.0')
self._metastream.setAttr('xmlns:stream', NS_STREAMS)
self._metastream.setAttr('to', self._owner.Server)
if locale.getdefaultlocale()[0]:
self._metastream.setAttr('xml:lang',
locale.getdefaultlocale()[0].split('_')[0])
self._owner.send("<?xml version='1.0'?>%s>" % str(self._metastream)[:-2])
def _check_stream_start(self, ns, tag, attrs):

View file

@ -1,6 +1,7 @@
## features.py
##
## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
##
## 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
@ -71,6 +72,7 @@ def discoverItems(disp,jid,node=None, cb=None):
cb(ret)
_discover(disp, NS_DISCO_ITEMS, jid, node, _on_response)
# this one is
def discoverInfo(disp,jid,node=None, cb=None):
""" Query remote object about info that it publishes. Returns identities and features lists."""
""" According to JEP-0030:
@ -128,16 +130,13 @@ def _ReceivedRegInfo(con, resp, agent):
return
df=tag.getTag('x',namespace=NS_DATA)
if df:
con.Event(NS_REGISTER,REGISTER_DATA_RECEIVED,(agent,DataForm(node=df),True,''))
con.Event(NS_REGISTER,REGISTER_DATA_RECEIVED,(agent,df,True,''))
return
df=DataForm(typ='form')
df={}
for i in resp.getQueryPayload():
if not isinstance(i, Node):
pass
elif i.getName()=='instructions':
df.addInstructions(i.getData())
else:
df.setField(i.getName()).setValue(i.getData())
continue
df[i.getName()] = i.getData()
con.Event(NS_REGISTER, REGISTER_DATA_RECEIVED, (agent,df,False,''))
def register(disp, host, info, cb):

View file

@ -21,8 +21,8 @@ xmpp-related data structures.
from simplexml import Node,NodeBuilder,ustr
import time
NS_ACTIVITY ='http://jabber.org/protocol/activity' # JEP-0108
NS_ADDRESS ='http://jabber.org/protocol/address' # JEP-0033
NS_ACTIVITY ='http://jabber.org/protocol/activity' # XEP-0108
NS_ADDRESS ='http://jabber.org/protocol/address' # XEP-0033
NS_AGENTS ='jabber:iq:agents'
NS_AMP ='http://jabber.org/protocol/amp'
NS_AMP_ERRORS =NS_AMP+'#errors'
@ -39,58 +39,58 @@ NS_CLIENT ='jabber:client'
NS_COMMANDS ='http://jabber.org/protocol/commands'
NS_COMPONENT_ACCEPT='jabber:component:accept'
NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0'
NS_COMPRESS ='http://jabber.org/protocol/compress' # JEP-0138
NS_COMPRESS ='http://jabber.org/protocol/compress' # XEP-0138
NS_CONFERENCE ='jabber:x:conference'
NS_DATA ='jabber:x:data' # JEP-0004
NS_DATA ='jabber:x:data' # XEP-0004
NS_DELAY ='jabber:x:delay'
NS_DIALBACK ='jabber:server:dialback'
NS_DISCO ='http://jabber.org/protocol/disco'
NS_DISCO_INFO =NS_DISCO+'#info'
NS_DISCO_ITEMS =NS_DISCO+'#items'
NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027
NS_EVENT ='jabber:x:event' # JEP-0022
NS_ENCRYPTED ='jabber:x:encrypted' # XEP-0027
NS_EVENT ='jabber:x:event' # XEP-0022
NS_FEATURE ='http://jabber.org/protocol/feature-neg'
NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096
NS_GAMING ='http://jabber.org/protocol/gaming' # XEP-0196
NS_GEOLOC ='http://jabber.org/protocol/geoloc' # JEP-0080
NS_GROUPCHAT ='gc-1.0'
NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # JEP-0070
NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # JEP-0124
NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # XEP-0070
NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # XEP-0124
NS_IBB ='http://jabber.org/protocol/ibb'
NS_INVISIBLE ='presence-invisible' # Jabberd2
NS_IQ ='iq' # Jabberd2
NS_LAST ='jabber:iq:last'
NS_MESSAGE ='message' # Jabberd2
NS_MOOD ='http://jabber.org/protocol/mood' # JEP-0107
NS_MOOD ='http://jabber.org/protocol/mood' # XEP-0107
NS_MUC ='http://jabber.org/protocol/muc'
NS_MUC_USER =NS_MUC+'#user'
NS_MUC_ADMIN =NS_MUC+'#admin'
NS_MUC_OWNER =NS_MUC+'#owner'
NS_NICK ='http://jabber.org/protocol/nick' # JEP-0172
NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # JEP-0013
NS_PHYSLOC ='http://jabber.org/protocol/physloc' # JEP-0112
NS_NICK ='http://jabber.org/protocol/nick' # XEP-0172
NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # XEP-0013
NS_PHYSLOC ='http://jabber.org/protocol/physloc' # XEP-0112
NS_PRESENCE ='presence' # Jabberd2
NS_PRIVACY ='jabber:iq:privacy'
NS_PRIVATE ='jabber:iq:private'
NS_PROFILE ='http://jabber.org/protocol/profile' # JEP-0154
NS_PUBSUB ='http://jabber.org/protocol/pubsub' # JEP-0060
NS_PROFILE ='http://jabber.org/protocol/profile' # XEP-0154
NS_PUBSUB ='http://jabber.org/protocol/pubsub' # XEP-0060
NS_PUBSUB_OWNER ='http://jabber.org/protocol/pubsub#owner' # JEP-0060
NS_REGISTER ='jabber:iq:register'
NS_ROSTER ='jabber:iq:roster'
NS_ROSTERX ='http://jabber.org/protocol/rosterx' # JEP-0144
NS_RPC ='jabber:iq:rpc' # JEP-0009
NS_ROSTERX ='http://jabber.org/protocol/rosterx' # XEP-0144
NS_RPC ='jabber:iq:rpc' # XEP-0009
NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl'
NS_SEARCH ='jabber:iq:search'
NS_SERVER ='jabber:server'
NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session'
NS_SI ='http://jabber.org/protocol/si' # JEP-0096
NS_SI_PUB ='http://jabber.org/protocol/sipub' # JEP-0137
NS_SIGNED ='jabber:x:signed' # JEP-0027
NS_SI ='http://jabber.org/protocol/si' # XEP-0096
NS_SI_PUB ='http://jabber.org/protocol/sipub' # XEP-0137
NS_SIGNED ='jabber:x:signed' # XEP-0027
NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas'
NS_STREAM ='http://affinix.com/jabber/stream'
NS_STREAMS ='http://etherx.jabber.org/streams'
NS_TIME ='jabber:iq:time' # JEP-0900
NS_TIME_REVISED ='http://www.xmpp.org/extensions/xep-0202.html#ns' # JEP-0202
NS_TIME ='jabber:iq:time' # XEP-0900
NS_TIME_REVISED ='urn:xmpp:time' # XEP-0202
NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls'
NS_TUNE ='http://jabber.org/protocol/tune' # XEP-0118
NS_VACATION ='http://jabber.org/protocol/vacation'
@ -101,11 +101,11 @@ NS_VCARD_UPDATE =NS_VCARD+':x:update'
NS_VERSION ='jabber:iq:version'
NS_VIEWING ='http://jabber.org/protocol/viewing' # XEP--197
NS_PING ='urn:xmpp:ping' # XEP-0199
NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # JEP-0130
NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # JEP-0071
NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # XEP-0130
NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # XEP-0071
NS_XHTML = 'http://www.w3.org/1999/xhtml' # "
NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # JEP-0141
NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # JEP-0122
NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # XEP-0141
NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # XEP-0122
NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams'
xmpp_stream_error_conditions="""
@ -429,7 +429,7 @@ class Message(Protocol):
self.setTagData('body',val)
def setXHTML(self,val,xmllang=None):
""" Sets the xhtml text of the message (JEP-0071).
""" Sets the xhtml text of the message (XEP-0071).
The parameter is the "inner html" to the body."""
try:
if xmllang:
@ -456,6 +456,14 @@ class Message(Protocol):
th=self.getThread()
if th: m.setThread(th)
return m
def getStatusCode(self):
"""Returns the status code of the message (for groupchat config
change)"""
attrs = []
for xtag in self.getTags('x'):
for child in xtag.getTags('status'):
attrs.append(child.getAttr('code'))
return attrs
class Presence(Protocol):
""" XMPP Presence object."""
@ -516,7 +524,11 @@ class Presence(Protocol):
return self._muc_getSubTagDataAttr('actor','jid')[1]
def getStatusCode(self):
"""Returns the status code of the presence (for groupchat)"""
return self._muc_getItemAttr('status','code')
attrs = []
for xtag in self.getTags('x'):
for child in xtag.getTags('status'):
attrs.append(child.getAttr('code'))
return attrs
class Iq(Protocol):
""" XMPP Iq object - get/set dialog mechanism. """
@ -597,7 +609,7 @@ class Error(Protocol):
class DataField(Node):
""" This class is used in the DataForm class to describe the single data item.
If you are working with jabber:x:data (JEP-0004, JEP-0068, JEP-0122)
If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122)
then you will need to work with instances of this class. """
def __init__(self,name=None,value=None,typ=None,required=0,desc=None,options=[],node=None):
""" Create new data field of specified name,value and type.
@ -674,7 +686,7 @@ class DataField(Node):
class DataForm(Node):
""" DataForm class. Used for manipulating dataforms in XMPP.
Relevant JEPs: 0004, 0068, 0122.
Relevant XEPs: 0004, 0068, 0122.
Can be used in disco, pub-sub and many other applications."""
def __init__(self, typ=None, data=[], title=None, node=None):
"""

View file

@ -33,6 +33,8 @@ import thread
import logging
log = logging.getLogger('gajim.c.x.transports_nb')
from common import gajim
USE_PYOPENSSL = False
try:
@ -296,7 +298,7 @@ class NonBlockingTcp(PlugIn, IdleObject):
self.renew_send_timeout()
def connect(self,server=None, proxy = None, secure = None):
''' Try to establish connection. Returns non-empty string on success. '''
''' Try to establish connection. Returns True/False on success/failure. '''
if not server:
server=self._server
else:
@ -416,6 +418,12 @@ class NonBlockingTcp(PlugIn, IdleObject):
self.idlequeue.remove_timeout(self.fd)
def onreceive(self, recv_handler):
''' Sets the on_receive callback. Do not confuse it with
on_receive() method, which is the callback itself.
If recv_handler==None, it tries to set that callback assuming that
our owner also has a Dispatcher object plugged in, to its
ProcessNonBlocking method.'''
if not recv_handler:
if hasattr(self._owner, 'Dispatcher'):
self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
@ -735,7 +743,12 @@ class NonBlockingTLS(PlugIn):
# FIXME: should method be configurable?
tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
#tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
tcpsock._sslContext.set_info_callback(self._ssl_info_callback)
tcpsock.ssl_errnum = 0
tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback)
try:
tcpsock._sslContext.load_verify_locations(os.path.join(gajim.DATA_DIR, 'other', 'cacerts.pem'))
except:
log.warning(_("Unable to load SSL certificats from file %s" % os.path.abspath(os.path.join(gajim.DATA_DIR,'other','ca.crt'))))
tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock)
tcpsock._sslObj.set_connect_state() # set to client mode
@ -759,29 +772,6 @@ class NonBlockingTLS(PlugIn):
# fake it, for now
self.starttls='success'
def _on_ssl_handshake_done(self):
log.debug("Handshake done!")
#self.starttls='success'
tcpsock = self._owner.Connection
cert = tcpsock._sslObj.get_peer_certificate()
peer = cert.get_subject()
issuer = cert.get_issuer()
tcpsock._sslIssuer = unicode(issuer)
tcpsock._sslServer = unicode(peer)
tcpsock.serverDigestSHA1 = cert.digest('sha1')
tcpsock.serverDigestMD5 = cert.digest('md5')
if log.getEffectiveLevel() <= logging.DEBUG:
peercert = tcpsock._sslObj.get_peer_certificate()
ciphers = tcpsock._sslObj.get_cipher_list()
print >> sys.stderr, "Ciphers:", ciphers
print >> sys.stderr, "Peer cert:", peercert
self._dumpX509(peercert)
print >> sys.stderr, OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, peercert)
def _startSSL_stdlib(self):
log.debug("_startSSL_stdlib called")
tcpsock=self._owner.Connection
@ -795,37 +785,17 @@ class NonBlockingTLS(PlugIn):
tcpsock._send = wrapper.send
self.starttls='success'
def _ssl_info_callback(self, sslconn, type, st):
def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok):
# Exceptions can't propagate up through this callback, so print them here.
try:
self._ssl_info_callback_guarded(sslconn, type, st)
if errnum == 0:
return True
self._owner.Connection.ssl_errnum = errnum
return True
except:
log.error("Exception caught in _ssl_info_callback:", exc_info=True)
traceback.print_exc() # Make sure something is printed, even if log is disabled.
def _ssl_info_callback_guarded(self, sslconn, type, st):
b = self.ssl_h_bits
#if type & b['SSL_CB_LOOP']:
# if type & SSL_ST_CONNECT: tls_state = "connect"
# elif type & SSL_ST_ACCEPT: tls_state = "accept"
# else: tls_state = "undefined"
# print "tls_state: %s: %s" % (tls_state, sslconn.state_string())
#if type & b['SSL_CB_ALERT']:
# if type & SSL_CB_READ: rdwr = "read"
# elif type & SSL_CB_WRITE: rdwr = "write"
# else: rdwr = "unknown"
# print "tls_alert: %s:%d: %s" % (rdwr, st, sslconn.state_string())
#mask = ""
#for k, v in b.iteritems():
# if type & v: mask += " " + k
#print "mask:", mask, st
if type & b['SSL_CB_HANDSHAKE_DONE']:
self._on_ssl_handshake_done()
def StartTLSHandler(self, conn, starttls):
''' Handle server reply if TLS is allowed to process. Behaves accordingly.
Used internally.'''

View file

@ -121,6 +121,7 @@ class P2PClient(IdleObject):
self.sock_type = TYPE_SERVER
else:
self.sock_type = TYPE_CLIENT
self.fd = -1
conn = P2PConnection('', _sock, host, port, self._caller, self.on_connect, self)
self.sock_hash = conn._sock.__hash__
self.fd = conn.fd
@ -129,10 +130,14 @@ class P2PClient(IdleObject):
for val in self.stanzaqueue:
stanza, is_message = val
if is_message:
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
self.conn_holder.number_of_awaiting_messages[self.fd]+=1
if self.fd == -1:
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
_('Connection to host could not be established'), None, None])
else:
self.conn_holder.number_of_awaiting_messages[self.fd]=1
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
self.conn_holder.number_of_awaiting_messages[self.fd]+=1
else:
self.conn_holder.number_of_awaiting_messages[self.fd]=1
def add_stanza(self, stanza, is_message = False):
if self.Connection:
@ -207,11 +212,11 @@ class P2PClient(IdleObject):
def on_disconnect(self):
if self.conn_holder:
if self.conn_holder.number_of_awaiting_messages.has_key(self.fd):
if self.conn_holder.number_of_awaiting_messages[self.fd] > 0:
if self.conn_holder.number_of_awaiting_messages.has_key(self.conn_holder.fd):
if self.conn_holder.number_of_awaiting_messages[self.conn_holder.fd] > 0:
self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \
_('Connection to host could not be established'), None, None])
del self.conn_holder.number_of_awaiting_messages[self.fd]
del self.conn_holder.number_of_awaiting_messages[self.conn_holder.fd]
self.conn_holder.remove_connection(self.sock_hash)
if self.__dict__.has_key('Dispatcher'):
self.Dispatcher.PlugOut()
@ -518,13 +523,6 @@ class ClientZeroconf:
self.listener = None
self.number_of_awaiting_messages = {}
def test_avahi(self):
try:
import avahi
except ImportError:
return False
return True
def connect(self, show, msg):
self.port = self.start_listener(self.caller.port)
if not self.port:

View file

@ -640,7 +640,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
mtype = msg.getType()
subject = msg.getSubject() # if not there, it's None
tim = msg.getTimestamp()
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
tim = helpers.datetime_tuple(tim)
tim = time.localtime(timegm(tim))
frm = msg.getFrom()
if frm == None:
@ -663,23 +663,23 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
invite = None
delayed = msg.getTag('x', namespace = common.xmpp.NS_DELAY) != None
msg_id = None
composing_jep = None
composing_xep = None
xtags = msg.getTags('x')
# chatstates - look for chatstate tags in a message if not delayed
if not delayed:
composing_jep = False
composing_xep = False
children = msg.getChildren()
for child in children:
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
chatstate = child.getName()
composing_jep = 'JEP-0085'
composing_xep = 'XEP-0085'
break
# No JEP-0085 support, fallback to JEP-0022
if not chatstate:
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
if chatstate_child:
chatstate = 'active'
composing_jep = 'JEP-0022'
composing_xep = 'XEP-0022'
if not msgtxt and chatstate_child.getTag('composing'):
chatstate = 'composing'
# JEP-0172 User Nickname
@ -715,14 +715,14 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, tim = tim,
subject = subject)
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, subject,
chatstate, msg_id, composing_jep, user_nick, msghtml))
chatstate, msg_id, composing_xep, user_nick, msghtml))
elif mtype == 'normal': # it's single message
if self.name not in no_log_for and jid not in no_log_for and msgtxt:
gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim,
subject = subject)
if invite:
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, 'normal',
subject, chatstate, msg_id, composing_jep, user_nick))
subject, chatstate, msg_id, composing_xep, user_nick))
# END messageCB
def parse_data_form(self, node):

View file

@ -41,6 +41,7 @@ import gobject
from common import gajim
from common import GnuPG
from common.zeroconf import client_zeroconf
from common.zeroconf import zeroconf
from connection_handlers_zeroconf import *
USE_GPG = GnuPG.USE_GPG
@ -103,12 +104,11 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'password', 'zeroconf')
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'sync_with_global_status', True)
#XXX make sure host is US-ASCII
self.host = unicode(socket.gethostname())
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname', self.host)
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port', 5298)
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'is_zeroconf', True)
self.host = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname')
#XXX make sure host is US-ASCII
self.host = unicode(socket.gethostname())
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname', self.host)
self.port = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port')
self.autoconnect = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'autoconnect')
self.sync_with_global_status = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'sync_with_global_status')
@ -229,7 +229,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
self.get_config_values_or_default()
if not self.connection:
self.connection = client_zeroconf.ClientZeroconf(self)
if not self.connection.test_avahi():
if not zeroconf.test_zeroconf():
self.dispatch('STATUS', 'offline')
self.status = 'offline'
self.dispatch('CONNECTION_LOST',
@ -348,7 +348,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
return STATUS_LIST[self.connected]
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
chatstate = None, msg_id = None, composing_jep = None, resource = None,
chatstate = None, msg_id = None, composing_xep = None, resource = None,
user_nick = None):
fjid = jid
@ -396,10 +396,10 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
# please note that the only valid tag inside a message containing a <body>
# tag is the active event
if chatstate is not None:
if composing_jep == 'JEP-0085' or not composing_jep:
if composing_xep == 'XEP-0085' or not composing_xep:
# JEP-0085
msg_iq.setTag(chatstate, namespace = common.xmpp.NS_CHATSTATES)
if composing_jep == 'JEP-0022' or not composing_jep:
if composing_xep == 'XEP-0022' or not composing_xep:
# JEP-0022
chatstate_node = msg_iq.setTag('x', namespace = common.xmpp.NS_EVENT)
if not msgtxt: # when no <body>, add <id>

415
src/common/zeroconf/zeroconf.py Executable file → Normal file
View file

@ -12,404 +12,29 @@
## GNU General Public License for more details.
##
from common import gajim
try:
import dbus.glib
except ImportError, e:
pass
C_NAME, C_DOMAIN, C_INTERFACE, C_PROTOCOL, C_HOST, \
C_ADDRESS, C_PORT, C_BARE_NAME, C_TXT = range(9)
class Zeroconf:
def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB,
disconnected_CB, error_CB, name, host, port):
self.avahi = None
self.domain = None # specific domain to browse
self.stype = '_presence._tcp'
self.port = port # listening port that gets announced
self.username = name
self.host = host
self.txt = {} # service data
#XXX these CBs should be set to None when we destroy the object
# (go offline), because they create a circular reference
self.new_serviceCB = new_serviceCB
self.remove_serviceCB = remove_serviceCB
self.name_conflictCB = name_conflictCB
self.disconnected_CB = disconnected_CB
self.error_CB = error_CB
self.service_browser = None
self.domain_browser = None
self.bus = None
self.server = None
self.contacts = {} # all current local contacts with data
self.entrygroup = None
self.connected = False
self.announced = False
self.invalid_self_contact = {}
def test_avahi():
try:
import avahi
except ImportError:
return False
return True
def test_bonjour():
try:
import pybonjour
except ImportError:
return False
return True
## handlers for dbus callbacks
def entrygroup_commit_error_CB(self, err):
# left blank for possible later usage
pass
def error_callback1(self, err):
gajim.log.debug('Error while resolving: ' + str(err))
def error_callback(self, err):
gajim.log.debug(str(err))
# timeouts are non-critical
if str(err) != 'Timeout reached':
self.disconnect()
self.disconnected_CB()
def test_zeroconf():
return test_avahi() or test_bonjour()
def new_service_callback(self, interface, protocol, name, stype, domain, flags):
gajim.log.debug('Found service %s in domain %s on %i.%i.' % (name, domain, interface, protocol))
if not self.connected:
return
# synchronous resolving
self.server.ResolveService( int(interface), int(protocol), name, stype, \
domain, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), \
reply_handler=self.service_resolved_callback, error_handler=self.error_callback1)
def remove_service_callback(self, interface, protocol, name, stype, domain, flags):
gajim.log.debug('Service %s in domain %s on %i.%i disappeared.' % (name, domain, interface, protocol))
if not self.connected:
return
if name != self.name:
for key in self.contacts.keys():
if self.contacts[key][C_BARE_NAME] == name:
del self.contacts[key]
self.remove_serviceCB(key)
return
def new_service_type(self, interface, protocol, stype, domain, flags):
# Are we already browsing this domain for this type?
if self.service_browser:
return
object_path = self.server.ServiceBrowserNew(interface, protocol, \
stype, domain, dbus.UInt32(0))
self.service_browser = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \
object_path) , self.avahi.DBUS_INTERFACE_SERVICE_BROWSER)
self.service_browser.connect_to_signal('ItemNew', self.new_service_callback)
self.service_browser.connect_to_signal('ItemRemove', self.remove_service_callback)
self.service_browser.connect_to_signal('Failure', self.error_callback)
def new_domain_callback(self,interface, protocol, domain, flags):
if domain != "local":
self.browse_domain(interface, protocol, domain)
def txt_array_to_dict(self, txt_array):
txt_dict = {}
for els in txt_array:
key, val = '', None
for c in els:
#FIXME: remove when outdated, this is for avahi < 0.6.14
if c < 0 or c > 255:
c = '.'
else:
c = chr(c)
if val is None:
if c == '=':
val = ''
else:
key += c
else:
val += c
if val is None: # missing '='
val = ''
txt_dict[key] = val.decode('utf-8')
return txt_dict
def service_resolved_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
gajim.log.debug('Service data for service %s in domain %s on %i.%i:'
% (name, domain, interface, protocol))
gajim.log.debug('Host %s (%s), port %i, TXT data: %s' % (host, address, port,
self.txt_array_to_dict(txt)))
if not self.connected:
return
bare_name = name
if name.find('@') == -1:
name = name + '@' + name
# we don't want to see ourselves in the list
if name != self.name:
self.contacts[name] = (name, domain, interface, protocol, host, address, port,
bare_name, txt)
self.new_serviceCB(name)
else:
# remember data
# In case this is not our own record but of another
# gajim instance on the same machine,
# it will be used when we get a new name.
self.invalid_self_contact[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt)
# different handler when resolving all contacts
def service_resolved_all_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
if not self.connected:
return
bare_name = name
if name.find('@') == -1:
name = name + '@' + name
self.contacts[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt)
def service_added_callback(self):
gajim.log.debug('Service successfully added')
def service_committed_callback(self):
gajim.log.debug('Service successfully committed')
def service_updated_callback(self):
gajim.log.debug('Service successfully updated')
def service_add_fail_callback(self, err):
gajim.log.debug('Error while adding service. %s' % str(err))
if str(err) == 'Local name collision':
alternative_name = self.server.GetAlternativeServiceName(self.username)
self.name_conflictCB(alternative_name)
return
self.error_CB(_('Error while adding service. %s') % str(err))
self.disconnect()
def server_state_changed_callback(self, state, error):
if state == self.avahi.SERVER_RUNNING:
self.create_service()
elif state == self.avahi.SERVER_COLLISION:
self.entrygroup.Reset()
elif state == self.avahi.CLIENT_FAILURE:
# does it ever go here?
gajim.log.debug('CLIENT FAILURE')
def entrygroup_state_changed_callback(self, state, error):
# the name is already present, so recreate
if state == self.avahi.ENTRY_GROUP_COLLISION:
self.service_add_fail_callback('Local name collision')
elif state == self.avahi.ENTRY_GROUP_FAILURE:
gajim.log.debug('zeroconf.py: ENTRY_GROUP_FAILURE reached(that'
' should not happen)')
# make zeroconf-valid names
def replace_show(self, show):
if show in ['chat', 'online', '']:
return 'avail'
elif show == 'xa':
return 'away'
return show
def avahi_txt(self):
utf8_dict = {}
for key in self.txt:
val = self.txt[key]
if isinstance(val, unicode):
utf8_dict[key] = val.encode('utf-8')
else:
utf8_dict[key] = val
return self.avahi.dict_to_txt_array(utf8_dict)
def create_service(self):
try:
if not self.entrygroup:
# create an EntryGroup for publishing
self.entrygroup = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, self.server.EntryGroupNew()), self.avahi.DBUS_INTERFACE_ENTRY_GROUP)
self.entrygroup.connect_to_signal('StateChanged', self.entrygroup_state_changed_callback)
txt = {}
#remove empty keys
for key,val in self.txt.iteritems():
if val:
txt[key] = val
txt['port.p2pj'] = self.port
txt['version'] = 1
txt['txtvers'] = 1
# replace gajim's show messages with compatible ones
if self.txt.has_key('status'):
txt['status'] = self.replace_show(self.txt['status'])
else:
txt['status'] = 'avail'
self.txt = txt
gajim.log.debug('Publishing service %s of type %s' % (self.name, self.stype))
self.entrygroup.AddService(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype, '', '', self.port, self.avahi_txt(), reply_handler=self.service_added_callback, error_handler=self.service_add_fail_callback)
self.entrygroup.Commit(reply_handler=self.service_committed_callback,
error_handler=self.entrygroup_commit_error_CB)
return True
except dbus.DBusException, e:
gajim.log.debug(str(e))
return False
def announce(self):
if not self.connected:
return False
state = self.server.GetState()
if state == self.avahi.SERVER_RUNNING:
self.create_service()
self.announced = True
return True
def remove_announce(self):
if self.announced == False:
return False
try:
if self.entrygroup.GetState() != self.avahi.ENTRY_GROUP_FAILURE:
self.entrygroup.Reset()
self.entrygroup.Free()
# .Free() has mem leaks
obj = self.entrygroup._obj
obj._bus = None
self.entrygroup._obj = None
self.entrygroup = None
self.announced = False
return True
else:
return False
except dbus.DBusException, e:
gajim.log.debug("Can't remove service. That should not happen")
def browse_domain(self, interface, protocol, domain):
self.new_service_type(interface, protocol, self.stype, domain, '')
def avahi_dbus_connect_cb(self, a, connect, disconnect):
if connect != "":
gajim.log.debug('Lost connection to avahi-daemon')
self.disconnect()
if self.disconnected_CB:
self.disconnected_CB()
else:
gajim.log.debug('We are connected to avahi-daemon')
# connect to dbus
def connect_dbus(self):
try:
import dbus
except ImportError:
gajim.log.debug('Error: python-dbus needs to be installed. No zeroconf support.')
return False
if self.bus:
return True
try:
self.bus = dbus.SystemBus()
self.bus.add_signal_receiver(self.avahi_dbus_connect_cb,
"NameOwnerChanged", "org.freedesktop.DBus",
arg0="org.freedesktop.Avahi")
except Exception, e:
# System bus is not present
self.bus = None
gajim.log.debug(str(e))
return False
else:
return True
# connect to avahi
def connect_avahi(self):
if not self.connect_dbus():
return False
try:
import avahi
self.avahi = avahi
except ImportError:
gajim.log.debug('Error: python-avahi needs to be installed. No zeroconf support.')
return False
if self.server:
return True
try:
self.server = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \
self.avahi.DBUS_PATH_SERVER), self.avahi.DBUS_INTERFACE_SERVER)
self.server.connect_to_signal('StateChanged',
self.server_state_changed_callback)
except Exception, e:
# Avahi service is not present
self.server = None
gajim.log.debug(str(e))
return False
else:
return True
def connect(self):
self.name = self.username + '@' + self.host # service name
if not self.connect_avahi():
return False
self.connected = True
# start browsing
if self.domain is None:
# Explicitly browse .local
self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, "local")
# Browse for other browsable domains
self.domain_browser = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \
self.server.DomainBrowserNew(self.avahi.IF_UNSPEC, \
self.avahi.PROTO_UNSPEC, '', self.avahi.DOMAIN_BROWSER_BROWSE,\
dbus.UInt32(0))), self.avahi.DBUS_INTERFACE_DOMAIN_BROWSER)
self.domain_browser.connect_to_signal('ItemNew', self.new_domain_callback)
self.domain_browser.connect_to_signal('Failure', self.error_callback)
else:
self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, self.domain)
return True
def disconnect(self):
if self.connected:
self.connected = False
if self.service_browser:
self.service_browser.Free()
self.service_browser._obj._bus = None
self.service_browser._obj = None
if self.domain_browser:
self.domain_browser.Free()
self.domain_browser._obj._bus = None
self.domain_browser._obj = None
self.remove_announce()
self.server._obj._bus = None
self.server._obj = None
self.server = None
self.service_browser = None
self.domain_browser = None
# refresh txt data of all contacts manually (no callback available)
def resolve_all(self):
if not self.connected:
return
for val in self.contacts.values():
self.server.ResolveService(int(val[C_INTERFACE]), int(val[C_PROTOCOL]), val[C_BARE_NAME], \
self.stype, val[C_DOMAIN], self.avahi.PROTO_UNSPEC, dbus.UInt32(0),\
reply_handler=self.service_resolved_all_callback, error_handler=self.error_callback)
def get_contacts(self):
return self.contacts
def get_contact(self, jid):
if not jid in self.contacts:
return None
return self.contacts[jid]
def update_txt(self, show = None):
if show:
self.txt['status'] = self.replace_show(show)
txt = self.avahi_txt()
if self.connected and self.entrygroup:
self.entrygroup.UpdateServiceTxt(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype,'', txt, reply_handler=self.service_updated_callback, error_handler=self.error_callback)
return True
else:
return False
# END Zeroconf
if test_avahi():
from common.zeroconf import zeroconf_avahi
Zeroconf = zeroconf_avahi.Zeroconf
elif test_bonjour():
from common.zeroconf import zeroconf_bonjour
Zeroconf = zeroconf_bonjour.Zeroconf

View file

@ -0,0 +1,415 @@
## common/zeroconf/zeroconf.py
##
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
##
## 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; version 2 only.
##
## 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.
##
from common import gajim
try:
import dbus.glib
except ImportError, e:
pass
from common.zeroconf.zeroconf import C_BARE_NAME, C_INTERFACE, C_PROTOCOL, C_DOMAIN
class Zeroconf:
def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB,
disconnected_CB, error_CB, name, host, port):
self.avahi = None
self.domain = None # specific domain to browse
self.stype = '_presence._tcp'
self.port = port # listening port that gets announced
self.username = name
self.host = host
self.txt = {} # service data
#XXX these CBs should be set to None when we destroy the object
# (go offline), because they create a circular reference
self.new_serviceCB = new_serviceCB
self.remove_serviceCB = remove_serviceCB
self.name_conflictCB = name_conflictCB
self.disconnected_CB = disconnected_CB
self.error_CB = error_CB
self.service_browser = None
self.domain_browser = None
self.bus = None
self.server = None
self.contacts = {} # all current local contacts with data
self.entrygroup = None
self.connected = False
self.announced = False
self.invalid_self_contact = {}
## handlers for dbus callbacks
def entrygroup_commit_error_CB(self, err):
# left blank for possible later usage
pass
def error_callback1(self, err):
gajim.log.debug('Error while resolving: ' + str(err))
def error_callback(self, err):
gajim.log.debug(str(err))
# timeouts are non-critical
if str(err) != 'Timeout reached':
self.disconnect()
self.disconnected_CB()
def new_service_callback(self, interface, protocol, name, stype, domain, flags):
gajim.log.debug('Found service %s in domain %s on %i.%i.' % (name, domain, interface, protocol))
if not self.connected:
return
# synchronous resolving
self.server.ResolveService( int(interface), int(protocol), name, stype, \
domain, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), \
reply_handler=self.service_resolved_callback, error_handler=self.error_callback1)
def remove_service_callback(self, interface, protocol, name, stype, domain, flags):
gajim.log.debug('Service %s in domain %s on %i.%i disappeared.' % (name, domain, interface, protocol))
if not self.connected:
return
if name != self.name:
for key in self.contacts.keys():
if self.contacts[key][C_BARE_NAME] == name:
del self.contacts[key]
self.remove_serviceCB(key)
return
def new_service_type(self, interface, protocol, stype, domain, flags):
# Are we already browsing this domain for this type?
if self.service_browser:
return
object_path = self.server.ServiceBrowserNew(interface, protocol, \
stype, domain, dbus.UInt32(0))
self.service_browser = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \
object_path) , self.avahi.DBUS_INTERFACE_SERVICE_BROWSER)
self.service_browser.connect_to_signal('ItemNew', self.new_service_callback)
self.service_browser.connect_to_signal('ItemRemove', self.remove_service_callback)
self.service_browser.connect_to_signal('Failure', self.error_callback)
def new_domain_callback(self,interface, protocol, domain, flags):
if domain != "local":
self.browse_domain(interface, protocol, domain)
def txt_array_to_dict(self, txt_array):
txt_dict = {}
for els in txt_array:
key, val = '', None
for c in els:
#FIXME: remove when outdated, this is for avahi < 0.6.14
if c < 0 or c > 255:
c = '.'
else:
c = chr(c)
if val is None:
if c == '=':
val = ''
else:
key += c
else:
val += c
if val is None: # missing '='
val = ''
txt_dict[key] = val.decode('utf-8')
return txt_dict
def service_resolved_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
gajim.log.debug('Service data for service %s in domain %s on %i.%i:'
% (name, domain, interface, protocol))
gajim.log.debug('Host %s (%s), port %i, TXT data: %s' % (host, address, port,
self.txt_array_to_dict(txt)))
if not self.connected:
return
bare_name = name
if name.find('@') == -1:
name = name + '@' + name
# we don't want to see ourselves in the list
if name != self.name:
self.contacts[name] = (name, domain, interface, protocol, host, address, port,
bare_name, txt)
self.new_serviceCB(name)
else:
# remember data
# In case this is not our own record but of another
# gajim instance on the same machine,
# it will be used when we get a new name.
self.invalid_self_contact[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt)
# different handler when resolving all contacts
def service_resolved_all_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
if not self.connected:
return
bare_name = name
if name.find('@') == -1:
name = name + '@' + name
self.contacts[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt)
def service_added_callback(self):
gajim.log.debug('Service successfully added')
def service_committed_callback(self):
gajim.log.debug('Service successfully committed')
def service_updated_callback(self):
gajim.log.debug('Service successfully updated')
def service_add_fail_callback(self, err):
gajim.log.debug('Error while adding service. %s' % str(err))
if str(err) == 'Local name collision':
alternative_name = self.server.GetAlternativeServiceName(self.username)
self.name_conflictCB(alternative_name)
return
self.error_CB(_('Error while adding service. %s') % str(err))
self.disconnect()
def server_state_changed_callback(self, state, error):
if state == self.avahi.SERVER_RUNNING:
self.create_service()
elif state == self.avahi.SERVER_COLLISION:
self.entrygroup.Reset()
elif state == self.avahi.CLIENT_FAILURE:
# does it ever go here?
gajim.log.debug('CLIENT FAILURE')
def entrygroup_state_changed_callback(self, state, error):
# the name is already present, so recreate
if state == self.avahi.ENTRY_GROUP_COLLISION:
self.service_add_fail_callback('Local name collision')
elif state == self.avahi.ENTRY_GROUP_FAILURE:
gajim.log.debug('zeroconf.py: ENTRY_GROUP_FAILURE reached(that'
' should not happen)')
# make zeroconf-valid names
def replace_show(self, show):
if show in ['chat', 'online', '']:
return 'avail'
elif show == 'xa':
return 'away'
return show
def avahi_txt(self):
utf8_dict = {}
for key in self.txt:
val = self.txt[key]
if isinstance(val, unicode):
utf8_dict[key] = val.encode('utf-8')
else:
utf8_dict[key] = val
return self.avahi.dict_to_txt_array(utf8_dict)
def create_service(self):
try:
if not self.entrygroup:
# create an EntryGroup for publishing
self.entrygroup = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, self.server.EntryGroupNew()), self.avahi.DBUS_INTERFACE_ENTRY_GROUP)
self.entrygroup.connect_to_signal('StateChanged', self.entrygroup_state_changed_callback)
txt = {}
#remove empty keys
for key,val in self.txt.iteritems():
if val:
txt[key] = val
txt['port.p2pj'] = self.port
txt['version'] = 1
txt['txtvers'] = 1
# replace gajim's show messages with compatible ones
if self.txt.has_key('status'):
txt['status'] = self.replace_show(self.txt['status'])
else:
txt['status'] = 'avail'
self.txt = txt
gajim.log.debug('Publishing service %s of type %s' % (self.name, self.stype))
self.entrygroup.AddService(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype, '', '', self.port, self.avahi_txt(), reply_handler=self.service_added_callback, error_handler=self.service_add_fail_callback)
self.entrygroup.Commit(reply_handler=self.service_committed_callback,
error_handler=self.entrygroup_commit_error_CB)
return True
except dbus.DBusException, e:
gajim.log.debug(str(e))
return False
def announce(self):
if not self.connected:
return False
state = self.server.GetState()
if state == self.avahi.SERVER_RUNNING:
self.create_service()
self.announced = True
return True
def remove_announce(self):
if self.announced == False:
return False
try:
if self.entrygroup.GetState() != self.avahi.ENTRY_GROUP_FAILURE:
self.entrygroup.Reset()
self.entrygroup.Free()
# .Free() has mem leaks
obj = self.entrygroup._obj
obj._bus = None
self.entrygroup._obj = None
self.entrygroup = None
self.announced = False
return True
else:
return False
except dbus.DBusException, e:
gajim.log.debug("Can't remove service. That should not happen")
def browse_domain(self, interface, protocol, domain):
self.new_service_type(interface, protocol, self.stype, domain, '')
def avahi_dbus_connect_cb(self, a, connect, disconnect):
if connect != "":
gajim.log.debug('Lost connection to avahi-daemon')
self.disconnect()
if self.disconnected_CB:
self.disconnected_CB()
else:
gajim.log.debug('We are connected to avahi-daemon')
# connect to dbus
def connect_dbus(self):
try:
import dbus
except ImportError:
gajim.log.debug('Error: python-dbus needs to be installed. No zeroconf support.')
return False
if self.bus:
return True
try:
self.bus = dbus.SystemBus()
self.bus.add_signal_receiver(self.avahi_dbus_connect_cb,
"NameOwnerChanged", "org.freedesktop.DBus",
arg0="org.freedesktop.Avahi")
except Exception, e:
# System bus is not present
self.bus = None
gajim.log.debug(str(e))
return False
else:
return True
# connect to avahi
def connect_avahi(self):
if not self.connect_dbus():
return False
try:
import avahi
self.avahi = avahi
except ImportError:
gajim.log.debug('Error: python-avahi needs to be installed. No zeroconf support.')
return False
if self.server:
return True
try:
self.server = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \
self.avahi.DBUS_PATH_SERVER), self.avahi.DBUS_INTERFACE_SERVER)
self.server.connect_to_signal('StateChanged',
self.server_state_changed_callback)
except Exception, e:
# Avahi service is not present
self.server = None
gajim.log.debug(str(e))
return False
else:
return True
def connect(self):
self.name = self.username + '@' + self.host # service name
if not self.connect_avahi():
return False
self.connected = True
# start browsing
if self.domain is None:
# Explicitly browse .local
self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, "local")
# Browse for other browsable domains
self.domain_browser = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \
self.server.DomainBrowserNew(self.avahi.IF_UNSPEC, \
self.avahi.PROTO_UNSPEC, '', self.avahi.DOMAIN_BROWSER_BROWSE,\
dbus.UInt32(0))), self.avahi.DBUS_INTERFACE_DOMAIN_BROWSER)
self.domain_browser.connect_to_signal('ItemNew', self.new_domain_callback)
self.domain_browser.connect_to_signal('Failure', self.error_callback)
else:
self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, self.domain)
return True
def disconnect(self):
if self.connected:
self.connected = False
if self.service_browser:
self.service_browser.Free()
self.service_browser._obj._bus = None
self.service_browser._obj = None
if self.domain_browser:
self.domain_browser.Free()
self.domain_browser._obj._bus = None
self.domain_browser._obj = None
self.remove_announce()
self.server._obj._bus = None
self.server._obj = None
self.server = None
self.service_browser = None
self.domain_browser = None
# refresh txt data of all contacts manually (no callback available)
def resolve_all(self):
if not self.connected:
return
for val in self.contacts.values():
self.server.ResolveService(int(val[C_INTERFACE]), int(val[C_PROTOCOL]),
val[C_BARE_NAME], self.stype, val[C_DOMAIN],
self.avahi.PROTO_UNSPEC, dbus.UInt32(0),
reply_handler=self.service_resolved_all_callback,
error_handler=self.error_callback)
def get_contacts(self):
return self.contacts
def get_contact(self, jid):
if not jid in self.contacts:
return None
return self.contacts[jid]
def update_txt(self, show = None):
if show:
self.txt['status'] = self.replace_show(show)
txt = self.avahi_txt()
if self.connected and self.entrygroup:
self.entrygroup.UpdateServiceTxt(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype,'', txt, reply_handler=self.service_updated_callback, error_handler=self.error_callback)
return True
else:
return False
# END Zeroconf

View file

@ -0,0 +1,329 @@
## common/zeroconf/zeroconf_bonjour.py
##
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
##
## 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; version 2 only.
##
## 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.
##
from common import gajim
import sys
import select
from string import split
from common.zeroconf.zeroconf import C_BARE_NAME, C_DOMAIN
try:
import pybonjour
except ImportError, e:
pass
resolve_timeout = 1
class Zeroconf:
def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB,
disconnected_CB, error_CB, name, host, port):
self.domain = None # specific domain to browse
self.stype = '_presence._tcp'
self.port = port # listening port that gets announced
self.username = name
self.host = host
self.txt = pybonjour.TXTRecord() # service data
# XXX these CBs should be set to None when we destroy the object
# (go offline), because they create a circular reference
self.new_serviceCB = new_serviceCB
self.remove_serviceCB = remove_serviceCB
self.name_conflictCB = name_conflictCB
self.disconnected_CB = disconnected_CB
self.error_CB = error_CB
self.contacts = {} # all current local contacts with data
self.connected = False
self.announced = False
self.invalid_self_contact = {}
self.resolved = []
def browse_callback(self, sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain):
gajim.log.debug('Found service %s in domain %s on %i(type: %s).' % (serviceName, replyDomain, interfaceIndex, regtype))
if not self.connected:
return
if errorCode != pybonjour.kDNSServiceErr_NoError:
return
if not (flags & pybonjour.kDNSServiceFlagsAdd):
self.remove_service_callback(serviceName)
return
# asynchronous resolving
resolve_sdRef = pybonjour.DNSServiceResolve(0, interfaceIndex, serviceName, regtype, replyDomain, self.service_resolved_callback)
try:
while not self.resolved:
ready = select.select([resolve_sdRef], [], [], resolve_timeout)
if resolve_sdRef not in ready[0]:
gajim.log.debug('Resolve timed out')
break
pybonjour.DNSServiceProcessResult(resolve_sdRef)
else:
self.resolved.pop()
finally:
resolve_sdRef.close()
def remove_service_callback(self, name):
gajim.log.debug('Service %s disappeared.' % name)
if not self.connected:
return
if name != self.name:
for key in self.contacts.keys():
if self.contacts[key][C_BARE_NAME] == name:
del self.contacts[key]
self.remove_serviceCB(key)
return
def new_domain_callback(self,interface, protocol, domain, flags):
if domain != "local":
self.browse_domain(interface, protocol, domain)
# takes a TXTRecord instance
def txt_array_to_dict(self, txt):
items = pybonjour.TXTRecord.parse(txt)._items
dict = {}
for val in items.values():
dict[val[0]] = val[1]
return dict
def service_resolved_callback(self, sdRef, flags, interfaceIndex, errorCode, fullname,
hosttarget, port, txtRecord):
# TODO: do proper decoding...
escaping= {
r'\.': '.',
r'\032': ' ',
r'\064': '@',
}
name, stype, protocol, domain, dummy = split(fullname, '.')
# Replace the escaped values
for src, trg in escaping.items():
name = name.replace(src, trg)
txt = pybonjour.TXTRecord.parse(txtRecord)
gajim.log.debug('Service data for service %s on %i:' % (fullname, interfaceIndex))
gajim.log.debug('Host %s, port %i, TXT data: %s' % (hosttarget, port, txt._items))
if not self.connected:
return
bare_name = name
if '@' not in name:
name = name + '@' + name
# we don't want to see ourselves in the list
if name != self.name:
self.contacts[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord)
self.new_serviceCB(name)
else:
# remember data
# In case this is not our own record but of another
# gajim instance on the same machine,
# it will be used when we get a new name.
self.invalid_self_contact[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord)
# count services
self.resolved.append(True)
# different handler when resolving all contacts
def service_resolved_all_callback(self, sdRef, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtRecord):
if not self.connected:
return
escaping= {
r'\.': '.',
r'\032': ' ',
r'\064': '@',
}
name, stype, protocol, domain, dummy = split(fullname, '.')
# Replace the escaped values
for src, trg in escaping.items():
name = name.replace(src, trg)
bare_name = name
if name.find('@') == -1:
name = name + '@' + name
# we don't want to see ourselves in the list
if name != self.name:
self.contacts[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord)
def service_added_callback(self, sdRef, flags, errorCode, name, regtype, domain):
if errorCode == pybonjour.kDNSServiceErr_NoError:
gajim.log.debug('Service successfully added')
def service_add_fail_callback(self, err):
if err[0][0] == pybonjour.kDNSServiceErr_NameConflict:
gajim.log.debug('Error while adding service. %s' % str(err))
parts = self.username.split(' ')
#check if last part is a number and if, increment it
try:
stripped = str(int(parts[-1]))
except:
stripped = 1
alternative_name = self.username + str(stripped+1)
self.name_conflictCB(alternative_name)
return
self.error_CB(_('Error while adding service. %s') % str(err))
self.disconnect()
# make zeroconf-valid names
def replace_show(self, show):
if show in ['chat', 'online', '']:
return 'avail'
elif show == 'xa':
return 'away'
return show
def create_service(self):
txt = {}
#remove empty keys
for key,val in self.txt:
if val:
txt[key] = val
txt['port.p2pj'] = self.port
txt['version'] = 1
txt['txtvers'] = 1
# replace gajim's show messages with compatible ones
if 'status' in self.txt:
txt['status'] = self.replace_show(self.txt['status'])
else:
txt['status'] = 'avail'
self.txt = pybonjour.TXTRecord(txt, strict=True)
try:
sdRef = pybonjour.DNSServiceRegister(name = self.name,
regtype = self.stype, port = self.port, txtRecord = self.txt,
callBack = self.service_added_callback)
self.service_sdRef = sdRef
except pybonjour.BonjourError, e:
self.service_add_fail_callback(e)
else:
gajim.log.debug('Publishing service %s of type %s' % (self.name, self.stype))
ready = select.select([sdRef], [], [], resolve_timeout)
if sdRef in ready[0]:
pybonjour.DNSServiceProcessResult(sdRef)
def announce(self):
if not self.connected:
return False
self.create_service()
self.announced = True
return True
def remove_announce(self):
if self.announced == False:
return False
try:
self.service_sdRef.close()
self.announced = False
return True
except pybonjour.BonjourError, e:
geajim.log.debug(e)
return False
def connect(self):
self.name = self.username + '@' + self.host # service name
self.connected = True
# start browsing
if self.domain is None:
# Explicitly browse .local
self.browse_domain()
# Browse for other browsable domains
#self.domain_sdRef = pybonjour.DNSServiceEnumerateDomains(flags, interfaceIndex=0, callBack=self.new_domain_callback)
else:
self.browse_domain(self.domain)
return True
def disconnect(self):
if self.connected:
self.connected = False
self.browse_sdRef.close()
self.remove_announce()
def browse_domain(self, domain=None):
gajim.log.debug('starting to browse')
try:
self.browse_sdRef = pybonjour.DNSServiceBrowse(regtype=self.stype, domain=domain, callBack=self.browse_callback)
except pybonjour.BonjourError, e:
self.error_CB("Error while browsing: %s" % e)
def browse_loop(self):
ready = select.select([self.browse_sdRef], [], [], 2)
if self.browse_sdRef in ready[0]:
pybonjour.DNSServiceProcessResult(self.browse_sdRef)
# refresh txt data of all contacts manually (no callback available)
def resolve_all(self):
if not self.connected:
return
# for now put here as this is synchronous
self.browse_loop()
for val in self.contacts.values():
resolve_sdRef = pybonjour.DNSServiceResolve(0,
pybonjour.kDNSServiceInterfaceIndexAny, val[C_BARE_NAME],
self.stype + '.', val[C_DOMAIN] + '.',
self.service_resolved_all_callback)
try:
ready = select.select([resolve_sdRef], [], [], resolve_timeout)
if resolve_sdRef not in ready[0]:
gajim.log.debug('Resolve timed out (in resolve_all)')
break
pybonjour.DNSServiceProcessResult(resolve_sdRef)
finally:
resolve_sdRef.close()
def get_contacts(self):
return self.contacts
def get_contact(self, jid):
if not jid in self.contacts:
return None
return self.contacts[jid]
def update_txt(self, show = None):
if show:
self.txt['status'] = self.replace_show(show)
try:
pybonjour.DNSServiceUpdateRecord(self.service_sdRef, None, 0, self.txt)
except pybonjour.BonjourError, e:
return False
return True

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,7 @@ import os
import tooltips
import dialogs
import locale
import Queue
import gtkgui_helpers
from common import gajim
@ -141,9 +142,14 @@ class ConversationTextview:
buffer.create_tag('focus-out-line', justification = gtk.JUSTIFY_CENTER)
# One mark at the begining then 2 marks between each lines
size = gajim.config.get('max_conversation_lines')
size = 2 * size - 1
self.marks_queue = Queue.Queue(size)
self.allow_focus_out_line = True
# holds the iter's offset which points to the end of --- line
self.focus_out_end_iter_offset = None
# holds a mark at the end of --- line
self.focus_out_end_mark = None
self.line_tooltip = tooltips.BaseTooltip()
# use it for hr too
@ -213,13 +219,14 @@ class ConversationTextview:
print_focus_out_line = False
buffer = self.tv.get_buffer()
if self.focus_out_end_iter_offset is None:
if self.focus_out_end_mark is None:
# this happens only first time we focus out on this room
print_focus_out_line = True
else:
if self.focus_out_end_iter_offset != buffer.get_end_iter().\
get_offset():
focus_out_end_iter = buffer.get_iter_at_mark(self.focus_out_end_mark)
focus_out_end_iter_offset = focus_out_end_iter.get_offset()
if focus_out_end_iter_offset != buffer.get_end_iter().get_offset():
# this means after last-focus something was printed
# (else end_iter's offset is the same as before)
# only then print ---- line (eg. we avoid printing many following
@ -230,9 +237,9 @@ class ConversationTextview:
buffer.begin_user_action()
# remove previous focus out line if such focus out line exists
if self.focus_out_end_iter_offset is not None:
end_iter_for_previous_line = buffer.get_iter_at_offset(
self.focus_out_end_iter_offset)
if self.focus_out_end_mark is not None:
end_iter_for_previous_line = buffer.get_iter_at_mark(
self.focus_out_end_mark)
begin_iter_for_previous_line = end_iter_for_previous_line.copy()
# img_char+1 (the '\n')
begin_iter_for_previous_line.backward_chars(2)
@ -240,6 +247,7 @@ class ConversationTextview:
# remove focus out line
buffer.delete(begin_iter_for_previous_line,
end_iter_for_previous_line)
buffer.delete_mark(self.focus_out_end_mark)
# add the new focus out line
end_iter = buffer.get_end_iter()
@ -255,7 +263,8 @@ class ConversationTextview:
self.allow_focus_out_line = False
# update the iter we hold to make comparison the next time
self.focus_out_end_iter_offset = buffer.get_end_iter().get_offset()
self.focus_out_end_mark = buffer.create_mark(None,
buffer.get_end_iter(), left_gravity=True)
buffer.end_user_action()
@ -315,7 +324,10 @@ class ConversationTextview:
buffer = self.tv.get_buffer()
start, end = buffer.get_bounds()
buffer.delete(start, end)
self.focus_out_end_iter_offset = None
size = gajim.config.get('max_conversation_lines')
size = 2 * size - 1
self.marks_queue = Queue.Queue(size)
self.focus_out_end_mark = None
def visit_url_from_menuitem(self, widget, link):
'''basically it filters out the widget instance'''
@ -767,13 +779,29 @@ class ConversationTextview:
'''prints 'chat' type messages'''
buffer = self.tv.get_buffer()
buffer.begin_user_action()
if self.marks_queue.full():
# remove oldest line
m1 = self.marks_queue.get()
m2 = self.marks_queue.get()
i1 = buffer.get_iter_at_mark(m1)
i2 = buffer.get_iter_at_mark(m2)
buffer.delete(i1, i2)
buffer.delete_mark(m1)
end_iter = buffer.get_end_iter()
at_the_end = False
if self.at_the_end():
at_the_end = True
# Create one mark and add it to queue once if it's the first line
# else twice (one for end bound, one for start bound)
mark = None
if buffer.get_char_count() > 0:
buffer.insert_with_tags_by_name(end_iter, '\n', 'eol')
mark = buffer.create_mark(None, end_iter, left_gravity=True)
self.marks_queue.put(mark)
if not mark:
mark = buffer.create_mark(None, end_iter, left_gravity=True)
self.marks_queue.put(mark)
if kind == 'incoming_queue':
kind = 'incoming'
if old_kind == 'incoming_queue':

View file

@ -8,6 +8,7 @@
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -115,7 +116,7 @@ class EditGroupsDialog:
if not gajim.interface.roster.regroup and _account != account:
continue
for _jid in all_jid[_account]:
contacts = gajim.contacts.get_contact(_account, _jid)
contacts = gajim.contacts.get_contacts(_account, _jid)
for c in contacts:
if group in c.groups:
c.groups.remove(group)
@ -133,7 +134,7 @@ class EditGroupsDialog:
if not gajim.interface.roster.regroup and _account != account:
continue
for _jid in all_jid[_account]:
contacts = gajim.contacts.get_contact(_account, _jid)
contacts = gajim.contacts.get_contacts(_account, _jid)
for c in contacts:
if not group in c.groups:
c.groups.append(group)
@ -547,13 +548,20 @@ class ChangeStatusMessageDialog:
if not msg_name: # msg_name was ''
msg_name = msg_text_1l
msg_name = msg_name.decode('utf-8')
iter_ = self.message_liststore.append((msg_name,))
gajim.config.add_per('statusmsg', msg_name)
if msg_name in self.preset_messages_dict:
dlg2 = ConfirmationDialog(_('Overwrite Status Message?'),
_('This name is already used. Do you want to overwrite this status message?'))
resp = dlg2.run()
if resp != gtk.RESPONSE_OK:
return
else:
iter_ = self.message_liststore.append((msg_name,))
gajim.config.add_per('statusmsg', msg_name)
# select in combobox the one we just saved
self.message_combobox.set_active_iter(iter_)
gajim.config.set_per('statusmsg', msg_name, 'message', msg_text_1l)
self.preset_messages_dict[msg_name] = msg_text
# select in combobox the one we just saved
self.message_combobox.set_active_iter(iter_)
class AddNewContactWindow:
@ -610,6 +618,8 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
for j in gajim.contacts.get_jid_list(acct):
if gajim.jid_is_transport(j):
type_ = gajim.get_transport_name_from_jid(j, False)
if not type_:
continue
if self.agents.has_key(type_):
self.agents[type_].append(j)
else:
@ -625,30 +635,37 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
self.agents[type_].append(jid_)
self.available_types.append(type_)
liststore = gtk.ListStore(str)
self.group_comboboxentry.set_model(liststore)
liststore = gtk.ListStore(str, str)
self.group_comboboxentry.set_model(liststore)
# Combobox with transport/jabber icons
liststore = gtk.ListStore(str, gtk.gdk.Pixbuf, str)
cell = gtk.CellRendererPixbuf()
self.protocol_combobox.pack_start(cell, False)
self.protocol_combobox.add_attribute(cell, 'pixbuf', 1)
cell = gtk.CellRendererText()
cell.set_property('xpad', 5)
self.protocol_combobox.pack_start(cell, True)
self.protocol_combobox.add_attribute(cell, 'text', 0)
self.protocol_combobox.set_model(liststore)
uf_type = {'jabber': 'Jabber', 'aim': 'AIM', 'gadu-gadu': 'Gadu Gadu',
'icq': 'ICQ', 'msn': 'MSN', 'yahoo': 'Yahoo'}
# Jabber as first
liststore.append(['Jabber', 'jabber'])
img = gajim.interface.roster.jabber_state_images['16']['online']
liststore.append(['Jabber', img.get_pixbuf(), 'jabber'])
for type_ in self.agents:
if type_ == 'jabber':
continue
if type_ in uf_type:
liststore.append([uf_type[type_], type_])
imgs = gajim.interface.roster.transports_state_images
img = None
if imgs['16'].has_key(type_) and imgs['16'][type_].has_key('online'):
img = imgs['16'][type_]['online']
if type_ in uf_type:
liststore.append([uf_type[type_], img.get_pixbuf(), type_])
else:
liststore.append([type_, img.get_pixbuf(), type_])
else:
liststore.append([type_, type_])
self.protocol_combobox.set_model(liststore)
liststore.append([type_, img, type_])
self.protocol_combobox.set_active(0)
self.protocol_jid_combobox.set_no_show_all(True)
self.protocol_jid_combobox.hide()
self.subscription_table.set_no_show_all(True)
self.auto_authorize_checkbutton.show()
self.message_scrolledwindow.set_no_show_all(True)
self.register_hbox.set_no_show_all(True)
self.register_hbox.hide()
self.connected_label.set_no_show_all(True)
self.connected_label.hide()
liststore = gtk.ListStore(str)
self.protocol_jid_combobox.set_model(liststore)
self.xml.signal_autoconnect(self)
@ -666,7 +683,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
iter = model.get_iter_first()
i = 0
while iter:
if model[iter][1] == type_:
if model[iter][2] == type_:
self.protocol_combobox.set_active(i)
break
iter = model.iter_next(iter)
@ -741,7 +758,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
model = self.protocol_combobox.get_model()
iter = self.protocol_combobox.get_active_iter()
type_ = model[iter][1]
type_ = model[iter][2]
if type_ != 'jabber':
transport = self.protocol_jid_combobox.get_active_text().decode(
'utf-8')
@ -795,7 +812,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
def on_protocol_combobox_changed(self, widget):
model = widget.get_model()
iter = widget.get_active_iter()
type_ = model[iter][1]
type_ = model[iter][2]
model = self.protocol_jid_combobox.get_model()
model.clear()
if len(self.agents[type_]):
@ -1192,6 +1209,7 @@ class InputDialog:
def on_okbutton_clicked(self, widget):
user_input = self.input_entry.get_text().decode('utf-8')
self.cancel_handler = None
self.dialog.destroy()
if isinstance(self.ok_handler, tuple):
self.ok_handler[0](user_input, *self.ok_handler[1:])
@ -1327,7 +1345,8 @@ class SubscriptionRequestWindow:
class JoinGroupchatWindow:
def __init__(self, account, room_jid = '', nick = '', automatic = False):
def __init__(self, account, room_jid = '', nick = '', password = '',
automatic = False):
'''automatic is a dict like {'invities': []}
If automatic is not empty, this means room must be automaticaly configured
and when done, invities must be automatically invited'''
@ -1351,9 +1370,12 @@ class JoinGroupchatWindow:
self.window = self.xml.get_widget('join_groupchat_window')
self._room_jid_entry = self.xml.get_widget('room_jid_entry')
self._nickname_entry = self.xml.get_widget('nickname_entry')
self._password_entry = self.xml.get_widget('password_entry')
self._room_jid_entry.set_text(room_jid)
self._nickname_entry.set_text(nick)
if password:
self._password_entry.set_text(password)
self.xml.signal_autoconnect(self)
gajim.interface.instances[account]['join_gc'] = self #now add us to open windows
if len(gajim.connections) > 1:
@ -1421,8 +1443,7 @@ class JoinGroupchatWindow:
'''When Join button is clicked'''
nickname = self._nickname_entry.get_text().decode('utf-8')
room_jid = self._room_jid_entry.get_text().decode('utf-8')
password = self.xml.get_widget('password_entry').get_text().decode(
'utf-8')
password = self._password_entry.get_text().decode('utf-8')
user, server, resource = helpers.decompose_jid(room_jid)
if not user or not server or resource:
ErrorDialog(_('Invalid group chat Jabber ID'),
@ -1460,7 +1481,7 @@ class JoinGroupchatWindow:
if not room_jid_bookmarked:
name = gajim.get_nick_from_jid(room_jid)
bmdict = { 'name': name, 'jid': room_jid, 'autojoin': u'1',
'password': password, 'nick': nickname,
'minimize': '0', 'password': password, 'nick': nickname,
'print_status': gajim.config.get('print_status_in_muc')}
gajim.connections[self.account].bookmarks.append(bmdict)
@ -1858,17 +1879,6 @@ class SingleMessageWindow:
spell2.set_language(lang)
except gobject.GError, msg:
dialogs.AspellDictError(lang)
self.send_button.set_no_show_all(True)
self.reply_button.set_no_show_all(True)
self.send_and_close_button.set_no_show_all(True)
self.to_label.set_no_show_all(True)
self.to_entry.set_no_show_all(True)
self.from_label.set_no_show_all(True)
self.from_entry.set_no_show_all(True)
self.close_button.set_no_show_all(True)
self.cancel_button.set_no_show_all(True)
self.message_scrolledwindow.set_no_show_all(True)
self.conversation_scrolledwindow.set_no_show_all(True)
self.prepare_widgets_for(self.action)
@ -2217,7 +2227,7 @@ class PrivacyListWindow:
jid_entry_completion.set_text_column(0)
jid_entry_completion.set_model(jids_list_store)
jid_entry_completion.set_popup_completion(True)
self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
if action == 'EDIT':
self.refresh_rules()
@ -2231,7 +2241,6 @@ class PrivacyListWindow:
self.window.set_title(title)
self.add_edit_vbox.set_no_show_all(True)
self.window.show_all()
self.add_edit_vbox.hide()
@ -2683,6 +2692,7 @@ class InvitationReceivedDialog:
self.room_jid = room_jid
self.account = account
self.password = password
xml = gtkgui_helpers.get_glade('invitation_received_dialog.glade')
self.dialog = xml.get_widget('invitation_received_dialog')
@ -2716,7 +2726,8 @@ class InvitationReceivedDialog:
def on_accept_button_clicked(self, widget):
self.dialog.destroy()
try:
JoinGroupchatWindow(self.account, self.room_jid)
JoinGroupchatWindow(self.account, self.room_jid,
password=self.password)
except GajimGeneralException:
pass
@ -2796,7 +2807,10 @@ class ImageChooserDialog(FileChooserDialog):
path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
(path_to_file,))[0]
if os.path.exists(path_to_file):
callback(widget, path_to_file)
if isinstance(callback, tuple):
callback[0](widget, path_to_file, *callback[1:])
else:
callback(widget, path_to_file)
try:
if os.name == 'nt':
@ -2856,12 +2870,19 @@ class AvatarChooserDialog(ImageChooserDialog):
ImageChooserDialog.__init__(self, path_to_file, on_response_ok,
on_response_cancel)
button = gtk.Button(None, gtk.STOCK_CLEAR)
self.response_clear = on_response_clear
if on_response_clear:
button.connect('clicked', on_response_clear)
button.connect('clicked', self.on_clear)
button.show_all()
self.action_area.pack_start(button)
self.action_area.reorder_child(button, 0)
def on_clear(self, widget):
if isinstance(self.response_clear, tuple):
self.response_clear[0](widget, *self.response_clear[1:])
else:
self.response_clear(widget)
class AddSpecialNotificationDialog:
def __init__(self, jid):
'''jid is the jid for which we want to add special notification
@ -2983,9 +3004,6 @@ class AdvancedNotificationsWindow:
self.delete_button.set_sensitive(False)
self.down_button.set_sensitive(False)
self.up_button.set_sensitive(False)
self.recipient_list_entry.set_no_show_all(True)
for st in ['online', 'away', 'xa', 'dnd', 'invisible']:
self.__dict__[st + '_cb'].set_no_show_all(True)
self.window.show_all()

View file

@ -76,7 +76,7 @@ def _gen_agent_type_info():
('_jid', 'weather'): (False, 'weather.png'),
('gateway', 'sip'): (False, 'sip.png'),
('directory', 'user'): (None, 'jud.png'),
('pubsub', 'generic'): (None, 'pubsub.png'),
('pubsub', 'generic'): (PubSubBrowser, 'pubsub.png'),
('pubsub', 'service'): (PubSubBrowser, 'pubsub.png'),
('proxy', 'bytestreams'): (None, 'bytestreams.png'), # Socks5 FT proxy
@ -438,8 +438,6 @@ _('Without a connection, you can not browse available services'))
self.on_services_treeview_selection_changed)
self.services_scrollwin = self.xml.get_widget('services_scrollwin')
self.progressbar = self.xml.get_widget('services_progressbar')
self.progressbar.set_no_show_all(True)
self.progressbar.hide()
self.banner = self.xml.get_widget('banner_agent_label')
self.banner_icon = self.xml.get_widget('banner_agent_icon')
self.banner_eventbox = self.xml.get_widget('banner_agent_eventbox')
@ -447,8 +445,6 @@ _('Without a connection, you can not browse available services'))
self.banner.realize()
self.paint_banner()
self.filter_hbox = self.xml.get_widget('filter_hbox')
self.filter_hbox.set_no_show_all(True)
self.filter_hbox.hide()
self.action_buttonbox = self.xml.get_widget('action_buttonbox')
# Address combobox
@ -1047,7 +1043,7 @@ class ToplevelAgentBrowser(AgentBrowser):
# as it was before setting the timeout
if props and self.tooltip.id == props[0]:
# bounding rectangle of coordinates for the cell within the treeview
rect = view.get_cell_area(props[0], props[1])
rect = view.get_cell_area(props[0], props[1])
# position of the treeview on the screen
position = view.window.get_origin()
self.tooltip.show_tooltip(state, rect.height, position[1] + rect.y)
@ -1554,10 +1550,10 @@ class MucBrowser(AgentBrowser):
self.join_button = None
def _create_treemodel(self):
# JID, node, name, users, description, fetched
# JID, node, name, users_int, users_str, description, fetched
# This is rather long, I'd rather not use a data_func here though.
# Users is a string, because want to be able to leave it empty.
self.model = gtk.ListStore(str, str, str, str, str, bool)
self.model = gtk.ListStore(str, str, str, int, str, str, bool)
self.model.set_sort_column_id(2, gtk.SORT_ASCENDING)
self.window.services_treeview.set_model(self.model)
# Name column
@ -1567,20 +1563,23 @@ class MucBrowser(AgentBrowser):
renderer = gtk.CellRendererText()
col.pack_start(renderer)
col.set_attributes(renderer, text = 2)
col.set_sort_column_id(2)
self.window.services_treeview.insert_column(col, -1)
col.set_resizable(True)
# Users column
col = gtk.TreeViewColumn(_('Users'))
renderer = gtk.CellRendererText()
col.pack_start(renderer)
col.set_attributes(renderer, text = 3)
col.set_attributes(renderer, text = 4)
col.set_sort_column_id(3)
self.window.services_treeview.insert_column(col, -1)
col.set_resizable(True)
# Description column
col = gtk.TreeViewColumn(_('Description'))
renderer = gtk.CellRendererText()
col.pack_start(renderer)
col.set_attributes(renderer, text = 4)
col.set_attributes(renderer, text = 5)
col.set_sort_column_id(4)
self.window.services_treeview.insert_column(col, -1)
col.set_resizable(True)
# Id column
@ -1588,9 +1587,11 @@ class MucBrowser(AgentBrowser):
renderer = gtk.CellRendererText()
col.pack_start(renderer)
col.set_attributes(renderer, text = 0)
col.set_sort_column_id(0)
self.window.services_treeview.insert_column(col, -1)
col.set_resizable(True)
self.window.services_treeview.set_headers_visible(True)
self.window.services_treeview.set_headers_clickable(True)
# Source id for idle callback used to start disco#info queries.
self._fetch_source = None
# Query failure counter
@ -1692,7 +1693,7 @@ class MucBrowser(AgentBrowser):
# We're at the end of the model, we can leave end=None though.
pass
while iter and self.model.get_path(iter) != end:
if not self.model.get_value(iter, 5):
if not self.model.get_value(iter, 6):
jid = self.model.get_value(iter, 0).decode('utf-8')
node = self.model.get_value(iter, 1).decode('utf-8')
self.cache.get_info(jid, node, self._agent_info)
@ -1723,13 +1724,14 @@ class MucBrowser(AgentBrowser):
if iter:
if name:
self.model[iter][2] = name
self.model[iter][3] = len(items) # The number of users
self.model[iter][5] = True
self.model[iter][3] = len(items) # The number of users
self.model[iter][4] = str(len(items)) # The number of users
self.model[iter][6] = True
self._fetch_source = None
self._query_visible()
def _add_item(self, jid, node, item, force):
self.model.append((jid, node, item.get('name', ''), '', '', False))
self.model.append((jid, node, item.get('name', ''), -1, '', '', False))
if not self._fetch_source:
self._fetch_source = gobject.idle_add(self._start_info_query)
@ -1743,14 +1745,15 @@ class MucBrowser(AgentBrowser):
users = form.getField('muc#roominfo_occupants')
descr = form.getField('muc#roominfo_description')
if users:
self.model[iter][3] = users.getValue()
self.model[iter][3] = int(users.getValue())
self.model[iter][4] = users.getValue()
if descr:
self.model[iter][4] = descr.getValue()
self.model[iter][5] = descr.getValue()
# Only set these when we find a form with additional info
# Some servers don't support forms and put extra info in
# the name attribute, so we preserve it in that case.
self.model[iter][2] = name
self.model[iter][5] = True
self.model[iter][6] = True
break
else:
# We didn't find a form, switch to alternate query mode
@ -1791,8 +1794,9 @@ class DiscussionGroupsBrowser(AgentBrowser):
def _create_treemodel(self):
''' Create treemodel for the window. '''
# JID, node, name (with description) - pango markup, dont have info?, subscribed?
self.model = gtk.ListStore(str, str, str, bool, bool)
self.model.set_sort_column_id(3, gtk.SORT_ASCENDING)
self.model = gtk.TreeStore(str, str, str, bool, bool)
# sort by name
self.model.set_sort_column_id(2, gtk.SORT_ASCENDING)
self.window.services_treeview.set_model(self.model)
# Name column
@ -1804,6 +1808,7 @@ class DiscussionGroupsBrowser(AgentBrowser):
col.set_attributes(renderer, markup=2)
col.set_resizable(True)
self.window.services_treeview.insert_column(col, -1)
self.window.services_treeview.set_headers_visible(True)
# Subscription state
renderer = gtk.CellRendererToggle()
@ -1813,7 +1818,29 @@ class DiscussionGroupsBrowser(AgentBrowser):
col.set_resizable(False)
self.window.services_treeview.insert_column(col, -1)
self.window.services_treeview.set_headers_visible(True)
# Node Column
renderer = gtk.CellRendererText()
col = gtk.TreeViewColumn(_('Node'))
col.pack_start(renderer)
col.set_attributes(renderer, markup=1)
col.set_resizable(True)
self.window.services_treeview.insert_column(col, -1)
def _add_items(self, jid, node, items, force):
for item in items:
jid = item['jid']
node = item.get('node', '')
self._total_items += 1
self._add_item(jid, node, item, force)
def _in_list_foreach(self, model, path, iter, node):
if model[path][1] == node:
self.in_list = True
def _in_list(self, node):
self.in_list = False
self.model.foreach(self._in_list_foreach, node)
return self.in_list
def _add_item(self, jid, node, item, force):
''' Called when we got basic information about new node from query.
@ -1830,7 +1857,24 @@ class DiscussionGroupsBrowser(AgentBrowser):
name = gobject.markup_escape_text(name)
name = '<b>%s</b>' % name
self.model.append((jid, node, name, dunno, subscribed))
node_splitted = node.split('/')
parent_iter = None
while len(node_splitted) > 1:
parent_node = node_splitted.pop(0)
parent_iter = self._get_child_iter(parent_iter, parent_node)
node_splitted[0] = parent_node + '/' + node_splitted[0]
if not self._in_list(node):
self.model.append(parent_iter, (jid, node, name, dunno, subscribed))
self.cache.get_items(jid, node, self._add_items, force = force,
args = (force,))
def _get_child_iter(self, parent_iter, node):
child_iter = self.model.iter_children(parent_iter)
while child_iter:
if self.model[child_iter][1] == node:
return child_iter
child_iter = self.model.iter_next(child_iter)
return None
def _add_actions(self):
self.post_button = gtk.Button(label=_('New post'), use_underline=True)
@ -1905,7 +1949,7 @@ class DiscussionGroupsBrowser(AgentBrowser):
model, iter = self.window.services_treeview.get_selection().get_selected()
if iter is None: return
groupnode = model.get_value(iter, 1) # 1 = groupnode
groupnode = model.get_value(iter, 1) # 1 = groupnode
gajim.connections[self.account].send_pb_unsubscribe(self.jid, groupnode, self._unsubscribeCB, groupnode)

276
src/features_window.py Normal file
View file

@ -0,0 +1,276 @@
## features_window.py
##
## Copyright (C) 2007 Yann Le Boulanger <asterix@lagaule.org>
##
## 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; version 2 only.
##
## 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.
##
import os
import gtk
import gobject
import gtkgui_helpers
import dialogs
from common import gajim
from common import helpers
import random
from tempfile import gettempdir
from subprocess import Popen
class FeaturesWindow:
'''Class for features window'''
def __init__(self):
self.xml = gtkgui_helpers.get_glade('features_window.glade')
self.window = self.xml.get_widget('features_window')
treeview = self.xml.get_widget('features_treeview')
self.desc_label = self.xml.get_widget('feature_desc_label')
# {name: (available_function, unix_text, windows_text)}
self.features = {
_('PyOpenSSL'): (self.pyopenssl_available,
_('A library used to validate server certificates to ensure a secure connection.'),
_('Requires python-pyopenssl.'),
_('Requires python-pyopenssl.')),
_('Bonjour / Zeroconf'): (self.zeroconf_available,
_('Serverless chatting with autodetected clients in a local network.'),
_('Requires python-avahi.'),
_('Requires pybonjour (http://o2s.csail.mit.edu/o2s-wiki/pybonjour).')),
_('gajim-remote'): (self.dbus_available,
_('A script to controle gajim via commandline.'),
_('Requires python-dbus.'),
_('Feature not available under Windows.')),
_('OpenGPG'): (self.gpg_available,
_('Encrypting chatmessages with gpg keys.'),
_('Requires gpg and python-GnuPGInterface.'),
_('Feature not available under Windows.')),
_('network-manager'): (self.network_manager_available,
_('Autodetection of network status.'),
_('Requires gnome-network-manager and python-dbus.'),
_('Feature not available under Windows.')),
_('Session Management'): (self.session_management_available,
_('Gajim session is stored on logout and restored on login.'),
_('Requires python-gnome2.'),
_('Feature not available under Windows.')),
_('gnome-keyring'): (self.gnome_keyring_available,
_('Passwords can be stored securely and not just in plaintext.'),
_('Requires gnome-keyring and python-gnome2-desktop.'),
_('Feature not available under Windows.')),
_('SRV'): (self.srv_available,
_('Ability to connect to servers which is using SRV records.'),
_('Requires dnsutils.'),
_('Requires nslookup to use SRV records.')),
_('Spell Checker'): (self.speller_available,
_('Spellchecking of composed messages.'),
_('Requires python-gnome2-extras or compilation of gtkspell module from Gajim sources.'),
_('Feature not available under Windows.')),
_('Notification-daemon'): (self.notification_available,
_('Passive popups notifying for new events.'),
_('Requires python-notify or instead python-dbus in conjunction with notification-daemon.'),
_('Feature not available under Windows.')),
_('Trayicon'): (self.trayicon_available,
_('A icon in systemtray reflecting the current presence.'),
_('Requires python-gnome2-extras or compiled trayicon module from Gajim sources.'),
_('Requires PyGTK >= 2.10.')),
_('Idle'): (self.idle_available,
_('Ability to measure idle time, in order to set auto status.'),
_('Requires compilation of the idle module from Gajim sources.'),
_('Requires compilation of the idle module from Gajim sources.')),
_('LaTeX'): (self.latex_available,
_('Transform LaTeX espressions between $$ $$.'),
_('Requires texlive-latex-base, dvips and imagemagick. You have to set \'use_latex\' to True in the Advanced Configuration Editor.'),
_('Feature not available under Windows.')),
}
# name, supported
self.model = gtk.ListStore(str, bool)
treeview.set_model(self.model)
col = gtk.TreeViewColumn(_('Available'))
treeview.append_column(col)
cell = gtk.CellRendererToggle()
cell.set_property('radio', True)
col.pack_start(cell)
col.set_attributes(cell, active = 1)
col = gtk.TreeViewColumn(_('Feature'))
treeview.append_column(col)
cell = gtk.CellRendererText()
col.pack_start(cell, expand = True)
col.add_attribute(cell, 'text', 0)
# Fill model
for feature in self.features:
func = self.features[feature][0]
rep = func()
self.model.append([feature, rep])
self.xml.signal_autoconnect(self)
self.window.show_all()
self.xml.get_widget('close_button').grab_focus()
def on_close_button_clicked(self, widget):
self.window.destroy()
def on_features_treeview_cursor_changed(self, widget):
selection = widget.get_selection()
path = selection.get_selected_rows()[1][0]
available = self.model[path][1]
feature = self.model[path][0]
text = self.features[feature][1] + '\n'
if os.name == 'nt':
text = text + self.features[feature][3]
else:
text = text + self.features[feature][2]
self.desc_label.set_text(text)
def pyopenssl_available(self):
try:
import OpenSSL.SSL
import OpenSSL.crypto
except:
return False
return True
def zeroconf_available(self):
try:
import avahi
except:
try:
import pybonjour
except:
return False
return True
def dbus_available(self):
if os.name == 'nt':
return False
from common import dbus_support
return dbus_support.supported
def gpg_available(self):
if os.name == 'nt':
return False
from common import GnuPG
return GnuPG.USE_GPG
def network_manager_available(self):
if os.name == 'nt':
return False
import network_manager_listener
return network_manager_listener.supported
def session_management_available(self):
if os.name == 'nt':
return False
try:
import gnome.ui
except:
return False
return True
def gnome_keyring_available(self):
if os.name == 'nt':
return False
try:
import gnomekeyring
except:
return False
return True
def srv_available(self):
return helpers.is_in_path('nslookup')
def speller_available(self):
if os.name == 'nt':
return False
try:
import gtkspell
except:
return False
return True
def notification_available(self):
if os.name == 'nt':
return False
from common import dbus_support
if self.dbus_available() and dbus_support.get_notifications_interface():
return True
try:
import pynotify
except:
return False
return True
def trayicon_available(self):
if os.name == 'nt' and gtk.pygtk_version >= (2, 10, 0) and \
gtk.gtk_version >= (2, 10, 0):
return True
try:
import systray
except:
return False
return True
def idle_available(self):
from common import sleepy
return sleepy.SUPPORTED
def latex_available(self):
'''check is latex is available and if it can create a picture.'''
if os.name == 'nt':
return False
exitcode = 0
random.seed()
tmpfile = os.path.join(gettempdir(), "gajimtex_" + \
random.randint(0,100).__str__())
# build latex string
texstr = '\\documentclass[12pt]{article}\\usepackage[dvips]{graphicx}'
texstr += '\\usepackage{amsmath}\\usepackage{amssymb}\\pagestyle{empty}'
texstr += '\\begin{document}\\begin{large}\\begin{gather*}test'
texstr += '\\end{gather*}\\end{large}\\end{document}'
file = open(os.path.join(tmpfile + ".tex"), "w+")
file.write(texstr)
file.flush()
file.close()
try:
p = Popen(['latex', '--interaction=nonstopmode', tmpfile + '.tex'],
cwd=gettempdir())
exitcode = p.wait()
except:
exitcode = 1
if exitcode == 0:
try:
p = Popen(['dvips', '-E', '-o', tmpfile + '.ps', tmpfile + '.dvi'],
cwd=gettempdir())
exitcode = p.wait()
except:
exitcode = 1
if exitcode == 0:
try:
p = Popen(['convert', tmpfile + '.ps', tmpfile + '.png'],
cwd=gettempdir())
exitcode = p.wait()
except:
exitcode = 1
extensions = [".tex", ".log", ".aux", ".dvi", ".ps", ".png"]
for ext in extensions:
try:
os.remove(tmpfile + ext)
except Exception:
pass
if exitcode == 0:
return True
return False

View file

@ -116,8 +116,6 @@ class FileTransfersWindow:
self.cancel_menuitem = self.xml.get_widget('cancel_menuitem')
self.pause_menuitem = self.xml.get_widget('pause_menuitem')
self.continue_menuitem = self.xml.get_widget('continue_menuitem')
self.continue_menuitem.hide()
self.continue_menuitem.set_no_show_all(True)
self.remove_menuitem = self.xml.get_widget('remove_menuitem')
self.xml.signal_autoconnect(self)
@ -420,10 +418,18 @@ _('Connection with peer cannot be established.'))
#they are not translatable.
return _('%(hours)02.d:%(minutes)02.d:%(seconds)02.d') % times
def _get_eta_and_speed(self, full_size, transfered_size, elapsed_time):
if elapsed_time == 0:
def _get_eta_and_speed(self, full_size, transfered_size, file_props):
if len(file_props['transfered_size']) == 0:
return 0., 0.
speed = round(float(transfered_size) / elapsed_time)
elif len(file_props['transfered_size']) == 1:
speed = round(float(transfered_size) / file_props['elapsed-time'])
else:
# first and last are (time, transfered_size)
first = file_props['transfered_size'][0]
last = file_props['transfered_size'][-1]
transfered = last[1] - first[1]
tim = last[0] - first[0]
speed = round(float(transfered) / tim)
if speed == 0.:
return 0., 0.
remaining_size = full_size - transfered_size
@ -484,8 +490,13 @@ _('Connection with peer cannot be established.'))
if file_props.has_key('offset') and file_props['offset']:
transfered_size -= file_props['offset']
full_size -= file_props['offset']
if file_props['elapsed-time'] > 0:
file_props['transfered_size'].append((file_props['last-time'], transfered_size))
if len(file_props['transfered_size']) > 6:
file_props['transfered_size'].pop(0)
eta, speed = self._get_eta_and_speed(full_size, transfered_size,
file_props['elapsed-time'])
file_props)
self.model.set(iter, C_PROGRESS, text)
self.model.set(iter, C_PERCENT, int(percent))
@ -547,6 +558,8 @@ _('Connection with peer cannot be established.'))
file_props['sender'] = account
file_props['receiver'] = contact
file_props['tt_account'] = account
# keep the last time: transfered_size to compute transfer speed
file_props['transfered_size'] = []
return file_props
def add_transfer(self, account, contact, file_props):
@ -784,6 +797,8 @@ _('Connection with peer cannot be established.'))
elif self.is_transfer_active(file_props):
file_props['paused'] = True
self.set_status(file_props['type'], file_props['sid'], 'pause')
# reset that to compute speed only when we resume
file_props['transfered_size'] = []
self.toggle_pause_continue(False)
def on_cancel_button_clicked(self, widget):

View file

@ -137,6 +137,15 @@ class GajimRemote:
'using this account'), False),
]
],
'send_groupchat_message':[
_('Sends new message to a groupchat you\'ve joined.'),
[
('room_jid', _('JID of the room that will receive the message'), True),
(_('message'), _('message contents'), True),
(_('account'), _('if specified, the message will be sent '
'using this account'), False),
]
],
'contact_info': [
_('Gets detailed info on a contact'),
[

View file

@ -6,6 +6,8 @@
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005 Travis Shirk <travis@pobox.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -70,10 +72,11 @@ def parseAndSetLogLevels(arg):
def parseOpts():
profile = ''
verbose = False
config_path = None
try:
shortargs = 'hqvl:p:'
longargs = 'help quiet verbose loglevel= profile='
shortargs = 'hqvl:p:c:'
longargs = 'help quiet verbose loglevel= profile= config_path='
opts, args = getopt.getopt(sys.argv[1:], shortargs, longargs.split())
except getopt.error, msg:
print msg
@ -93,11 +96,22 @@ def parseOpts():
profile = a
elif o in ('-l', '--loglevel'):
parseAndSetLogLevels(a)
return profile, verbose
elif o in ('-c', '--config-path'):
config_path = a
return profile, verbose, config_path
profile, verbose = parseOpts()
profile, verbose, config_path = parseOpts()
del parseOpts, parseAndSetLogLevels, parseLogTarget, parseLogLevel
import locale
profile = unicode(profile, locale.getpreferredencoding())
import common.configpaths
common.configpaths.gajimpaths.init(config_path)
del config_path
common.configpaths.gajimpaths.init_profile(profile)
del profile
import message_control
from chat_control import ChatControlBase
@ -106,6 +120,8 @@ from atom_window import AtomWindow
from common import exceptions
from common.zeroconf import connection_zeroconf
from common import dbus_support
if dbus_support.supported:
import dbus
if os.name == 'posix': # dl module is Unix Only
try: # rename the process name to gajim
@ -196,12 +212,6 @@ from common import optparser
if verbose: gajim.verbose = True
del verbose
import locale
profile = unicode(profile, locale.getpreferredencoding())
import common.configpaths
common.configpaths.init_profile(profile)
del profile
gajimpaths = common.configpaths.gajimpaths
pid_filename = gajimpaths['PID_FILE']
@ -316,9 +326,15 @@ pid_dir = os.path.dirname(pid_filename)
if not os.path.exists(pid_dir):
check_paths.create_path(pid_dir)
# Create pid file
f = open(pid_filename, 'w')
f.write(str(os.getpid()))
f.close()
try:
f = open(pid_filename, 'w')
f.write(str(os.getpid()))
f.close()
except IOError, e:
dlg = dialogs.ErrorDialog(_('Disk Write Error'), str(e))
dlg.run()
dlg.destroy()
sys.exit()
del pid_dir
del f
@ -396,6 +412,9 @@ class Interface:
prompt = data[2]
proposed_nick = data[3]
gc_control = self.msg_win_mgr.get_control(room_jid, account)
if not gc_control and \
room_jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][room_jid]
if gc_control: # user may close the window before we are here
gc_control.show_change_nick_input_dialog(title, prompt, proposed_nick)
@ -479,16 +498,21 @@ class Interface:
model[self.roster.status_message_menuitem_iter][3] = True
# Inform all controls for this account of the connection state change
for ctrl in self.msg_win_mgr.get_controls():
ctrls = self.msg_win_mgr.get_controls()
if self.minimized_controls.has_key(account):
# Can not be the case when we remove account
ctrls += self.minimized_controls[account].values()
for ctrl in ctrls:
if ctrl.account == account:
if status == 'offline' or (status == 'invisible' and \
gajim.connections[account].is_zeroconf):
gajim.connections[account].is_zeroconf):
ctrl.got_disconnected()
else:
# Other code rejoins all GCs, so we don't do it here
if not ctrl.type_id == message_control.TYPE_GC:
ctrl.got_connected()
ctrl.parent_win.redraw_tab(ctrl)
if ctrl.parent_win:
ctrl.parent_win.redraw_tab(ctrl)
self.roster.on_status_changed(account, status)
if account in self.show_vcard_when_connect:
@ -535,7 +559,7 @@ class Interface:
# Update contact
jid_list = gajim.contacts.get_jid_list(account)
if ji in jid_list or jid == gajim.get_jid_from_account(account):
lcontact = gajim.contacts.get_contacts_from_jid(account, ji)
lcontact = gajim.contacts.get_contacts(account, ji)
contact1 = None
resources = []
for c in lcontact:
@ -636,7 +660,7 @@ class Interface:
# (when contact signs out or has errors)
if array[1] in ('offline', 'error'):
contact1.our_chatstate = contact1.chatstate = \
contact1.composing_jep = None
contact1.composing_xep = None
gajim.connections[account].remove_transfers_for_contact(contact1)
self.roster.chg_contact_status(contact1, array[1], status_message,
account)
@ -665,7 +689,7 @@ class Interface:
def handle_event_msg(self, account, array):
# 'MSG' (account, (jid, msg, time, encrypted, msg_type, subject,
# chatstate, msg_id, composing_jep, user_nick, xhtml))
# chatstate, msg_id, composing_xep, user_nick, xhtml))
# user_nick is JEP-0172
full_jid_with_resource = array[0]
@ -678,7 +702,7 @@ class Interface:
subject = array[5]
chatstate = array[6]
msg_id = array[7]
composing_jep = array[8]
composing_xep = array[8]
xhtml = array[10]
if gajim.config.get('ignore_incoming_xhtml'):
xhtml = None
@ -687,9 +711,8 @@ class Interface:
groupchat_control = self.msg_win_mgr.get_control(jid, account)
if not groupchat_control and \
gajim.interface.minimized_controls.has_key(account) and \
jid in gajim.interface.minimized_controls[account]:
groupchat_control = gajim.interface.minimized_controls[account][jid]
jid in self.minimized_controls[account]:
groupchat_control = self.minimized_controls[account][jid]
pm = False
if groupchat_control and groupchat_control.type_id == \
message_control.TYPE_GC:
@ -720,11 +743,9 @@ class Interface:
# Handle chat states
contact = gajim.contacts.get_contact(account, jid, resource)
if contact and isinstance(contact, list):
contact = contact[0]
if contact:
if contact.composing_jep != 'JEP-0085': # We cache xep85 support
contact.composing_jep = composing_jep
if contact.composing_xep != 'XEP-0085': # We cache xep85 support
contact.composing_xep = composing_xep
if chat_control and chat_control.type_id == message_control.TYPE_CHAT:
if chatstate is not None:
# other peer sent us reply, so he supports jep85 or jep22
@ -748,7 +769,7 @@ class Interface:
return
if gajim.config.get('ignore_unknown_contacts') and \
not gajim.contacts.get_contact(account, jid) and not pm:
not gajim.contacts.get_contacts(account, jid) and not pm:
return
if not contact:
# contact is not in the roster, create a fake one to display
@ -769,7 +790,7 @@ class Interface:
if pm:
nickname = resource
groupchat_control.on_private_message(nickname, message, array[2],
xhtml)
xhtml, msg_id)
else:
# array: (jid, msg, time, encrypted, msg_type, subject)
if encrypted:
@ -798,6 +819,9 @@ class Interface:
jids = full_jid_with_resource.split('/', 1)
jid = jids[0]
gc_control = self.msg_win_mgr.get_control(jid, account)
if not gc_control and \
jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][jid]
if gc_control and gc_control.type_id != message_control.TYPE_GC:
gc_control = None
if gc_control:
@ -821,7 +845,7 @@ class Interface:
return
gc_control.print_conversation('Error %s: %s' % (array[1], array[2]))
if gc_control.parent_win.get_active_jid() == jid:
if gc_control.parent_win and gc_control.parent_win.get_active_jid() == jid:
gc_control.set_subject(gc_control.subject)
return
@ -991,6 +1015,9 @@ class Interface:
win.set_values(array)
if account in self.show_vcard_when_connect:
self.show_vcard_when_connect.remove(account)
jid = array['jid']
if self.instances[account]['infos'].has_key(jid):
self.instances[account]['infos'][jid].set_values(array)
def handle_event_vcard(self, account, vcard):
# ('VCARD', account, data)
@ -1026,6 +1053,9 @@ class Interface:
# Show avatar in roster or gc_roster
gc_ctrl = self.msg_win_mgr.get_control(jid, account)
if not gc_ctrl and \
jid in self.minimized_controls[account]:
gc_ctrl = self.minimized_controls[account][jid]
if gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC:
gc_ctrl.draw_avatar(resource)
else:
@ -1042,10 +1072,6 @@ class Interface:
win = self.instances[account]['infos'][array[0] + '/' + array[1]]
if win:
c = gajim.contacts.get_contact(account, array[0], array[1])
# c is a list when no resource is given. it probably means that contact
# is offline, so only on Contact instance
if isinstance(c, list) and len(c):
c = c[0]
if c: # c can be none if it's a gc contact
c.last_status_time = time.localtime(time.time() - array[2])
if array[3]:
@ -1081,7 +1107,6 @@ class Interface:
# PrivateChatControl
control = self.msg_win_mgr.get_control(room_jid, account)
if not control and \
self.minimized_controls.has_key(account) and \
room_jid in self.minimized_controls[account]:
control = self.minimized_controls[account][room_jid]
@ -1090,8 +1115,11 @@ class Interface:
if control:
control.chg_contact_status(nick, show, status, array[4], array[5],
array[6], array[7], array[8], array[9], array[10], array[11])
if control and not control.parent_win:
gajim.interface.roster.draw_contact(room_jid, account)
contact = gajim.contacts.\
get_contact_with_highest_priority(account, room_jid)
if contact:
self.roster.draw_contact(room_jid, account)
ctrl = self.msg_win_mgr.get_control(fjid, account)
@ -1119,7 +1147,6 @@ class Interface:
gc_control = self.msg_win_mgr.get_control(room_jid, account)
if not gc_control and \
self.minimized_controls.has_key(account) and \
room_jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][room_jid]
@ -1141,7 +1168,7 @@ class Interface:
contact = gajim.contacts.\
get_contact_with_highest_priority(account, room_jid)
if contact:
gajim.interface.roster.draw_contact(room_jid, account)
self.roster.draw_contact(room_jid, account)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('GCMessage', (account, array))
@ -1154,7 +1181,6 @@ class Interface:
gc_control = self.msg_win_mgr.get_control(jid, account)
if not gc_control and \
self.minimized_controls.has_key(account) and \
jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][jid]
@ -1162,7 +1188,7 @@ class Interface:
get_contact_with_highest_priority(account, jid)
if contact:
contact.status = array[1]
gajim.interface.roster.draw_contact(jid, account)
self.roster.draw_contact(jid, account)
if not gc_control:
return
@ -1197,6 +1223,46 @@ class Interface:
self.instances[account]['gc_config'][room_jid] = \
config.GroupchatConfigWindow(account, room_jid, array[1])
def handle_event_gc_config_change(self, account, array):
#('GC_CONFIG_CHANGE', account, (jid, statusCode)) statuscode is a list
# http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
# http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes-init
jid = array[0]
statusCode = array[1]
gc_control = self.msg_win_mgr.get_control(jid, account)
if not gc_control and \
jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][jid]
if not gc_control:
return
changes = []
if '100' in statusCode:
# Can be a presence (see chg_contact_status in groupchat_contol.py)
changes.append(_('Any occupant is allowed to see your full JID'))
if '102' in statusCode:
changes.append(_('Room now shows unavailable member'))
if '103' in statusCode:
changes.append(_('room now does not show unavailable members'))
if '104' in statusCode:
changes.append(\
_('A non-privacy-related room configuration change has occurred'))
if '170' in statusCode:
# Can be a presence (see chg_contact_status in groupchat_contol.py)
changes.append(_('Room logging is now enabled'))
if '171' in statusCode:
changes.append(_('Room logging is now disabled'))
if '172' in statusCode:
changes.append(_('Room is now non-anonymous'))
if '173' in statusCode:
changes.append(_('Room is now semi-anonymous'))
if '174' in statusCode:
changes.append(_('Room is now fully-anonymous'))
for change in changes:
gc_control.print_conversation(change)
def handle_event_gc_affiliation(self, account, array):
#('GC_AFFILIATION', account, (room_jid, affiliation, list)) list is list
room_jid = array[0]
@ -1204,6 +1270,29 @@ class Interface:
self.instances[account]['gc_config'][room_jid].\
affiliation_list_received(array[1], array[2])
def handle_event_gc_password_required(self, account, array):
#('GC_PASSWORD_REQUIRED', account, (room_jid, nick))
room_jid = array[0]
nick = array[1]
def on_ok(text):
gajim.connections[account].join_gc(nick, room_jid, text)
gajim.gc_passwords[room_jid] = text
def on_cancel():
# get and destroy window
if room_jid in gajim.interface.minimized_controls[account]:
self.roster.on_disconnect(None, room_jid, account)
else:
win = self.msg_win_mgr.get_window(room_jid, account)
ctrl = win.get_control(room_jid, account)
win.remove_tab(ctrl, 3)
dlg = dialogs.InputDialog(_('Password Required'),
_('A Password is required to join the room %s. Please type it') % \
room_jid, is_modal=False, ok_handler=on_ok, cancel_handler=on_cancel)
dlg.input_entry.set_visibility(False)
def handle_event_gc_invitation(self, account, array):
#('GC_INVITATION', (room_jid, jid_from, reason, password))
jid = gajim.get_jid_without_resource(array[1])
@ -1240,7 +1329,7 @@ class Interface:
sub = array[2]
ask = array[3]
groups = array[4]
contacts = gajim.contacts.get_contacts_from_jid(account, jid)
contacts = gajim.contacts.get_contacts(account, jid)
# contact removes us.
if (not sub or sub == 'none') and (not ask or ask == 'none') and \
not name and not groups:
@ -1304,11 +1393,11 @@ class Interface:
# join autojoinable rooms
for bm in bms:
minimize = bm['minimize'] in ('1', 'true')
if bm['autojoin'] in ('1', 'true'):
self.roster.join_gc_room(account, bm['jid'], bm['nick'],
bm['password'],
minimize = gajim.config.get('minimize_autojoined_rooms'))
bm['password'], minimize = minimize)
def handle_event_file_send_error(self, account, array):
jid = array[0]
file_props = array[1]
@ -1620,7 +1709,8 @@ class Interface:
if self.instances[account].has_key('profile'):
win = self.instances[account]['profile']
win.vcard_published()
for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC):
for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC) + \
self.minimized_controls[account].values():
if gc_control.account == account:
show = gajim.SHOW_LIST[gajim.connections[account].connected]
status = gajim.connections[account].status
@ -1648,7 +1738,8 @@ class Interface:
if gajim.connections[account].connected == invisible_show:
return
# join already open groupchats
for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC):
for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC) + \
self.minimized_controls[account].values():
if account != gc_control.account:
continue
room_jid = gc_control.room_jid
@ -2080,8 +2171,10 @@ class Interface:
'GC_MSG': self.handle_event_gc_msg,
'GC_SUBJECT': self.handle_event_gc_subject,
'GC_CONFIG': self.handle_event_gc_config,
'GC_CONFIG_CHANGE': self.handle_event_gc_config_change,
'GC_INVITATION': self.handle_event_gc_invitation,
'GC_AFFILIATION': self.handle_event_gc_affiliation,
'GC_PASSWORD_REQUIRED': self.handle_event_gc_password_required,
'BAD_PASSPHRASE': self.handle_event_bad_passphrase,
'ROSTER_INFO': self.handle_event_roster_info,
'BOOKMARKS': self.handle_event_bookmarks,
@ -2138,6 +2231,12 @@ class Interface:
jid = gajim.get_jid_without_resource(fjid)
if type_ in ('printed_gc_msg', 'printed_marked_gc_msg', 'gc_msg'):
w = self.msg_win_mgr.get_window(jid, account)
if self.minimized_controls[account].has_key(jid):
if not w:
ctrl = self.minimized_controls[account][jid]
w = self.msg_win_mgr.create_window(ctrl.contact, \
ctrl.account, ctrl.type_id)
self.roster.on_groupchat_maximized(None, jid, account)
elif type_ in ('printed_chat', 'chat', ''):
# '' is for log in/out notifications
if self.msg_win_mgr.has_window(fjid, account):
@ -2153,8 +2252,10 @@ class Interface:
gajim.events.change_jid(account, fjid, jid)
resource = None
fjid = jid
contact = gajim.contacts.get_contact(account, jid, resource)
if not contact or isinstance(contact, list):
contact = None
if resource:
contact = gajim.contacts.get_contact(account, jid, resource)
if not contact:
contact = highest_contact
self.roster.new_chat(contact, account, resource = resource)
w = self.msg_win_mgr.get_window(fjid, account)
@ -2173,8 +2274,7 @@ class Interface:
show = 'offline'
gc_contact = gajim.contacts.create_gc_contact(
room_jid = room_jid, name = nick, show = show)
c = gajim.contacts.contact_from_gc_contact(gc_contact)
self.roster.new_chat(c, account, private_chat = True)
self.roster.new_private_chat(gc_contact, account)
w = self.msg_win_mgr.get_window(fjid, account)
elif type_ in ('normal', 'file-request', 'file-request-error',
'file-send-error', 'file-error', 'file-stopped', 'file-completed'):
@ -2183,15 +2283,17 @@ class Interface:
if not event:
# default to jid without resource
event = gajim.events.get_first_event(account, jid, type_)
if not event:
return
# Open the window
self.roster.open_event(account, jid, event)
else:
# Open the window
self.roster.open_event(account, fjid, event)
elif type_ == 'gmail':
url = 'http://mail.google.com/mail?account_id=%s' % urllib.quote(
gajim.config.get_per('accounts', account, 'name'))
helpers.launch_browser_mailer('url', url)
url=gajim.connections[account].gmail_url
if url:
helpers.launch_browser_mailer('url', url)
elif type_ == 'gc-invitation':
event = gajim.events.get_first_event(account, jid, type_)
data = event.parameters
@ -2217,6 +2319,8 @@ class Interface:
# handler when an emoticon is clicked in emoticons_menu
self.emoticon_menuitem_clicked = None
self.minimized_controls = {}
self.status_sent_to_users = {}
self.status_sent_to_groups = {}
self.default_colors = {
'inmsgcolor': gajim.config.get('inmsgcolor'),
'outmsgcolor': gajim.config.get('outmsgcolor'),
@ -2252,7 +2356,7 @@ class Interface:
#add default themes if there is not in the config file
theme = gajim.config.get('roster_theme')
if not theme in gajim.config.get_per('themes'):
gajim.config.set('roster_theme', 'gtk+')
gajim.config.set('roster_theme', _('default'))
if len(gajim.config.get_per('themes')) == 0:
d = ['accounttextcolor', 'accountbgcolor', 'accountfont',
'accountfontattrs', 'grouptextcolor', 'groupbgcolor', 'groupfont',
@ -2309,6 +2413,7 @@ class Interface:
for a in gajim.connections:
self.instances[a] = {'infos': {}, 'disco': {}, 'gc_config': {},
'search': {}}
self.minimized_controls[a] = {}
gajim.contacts.add_account(a)
gajim.groups[a] = {}
gajim.gc_connected[a] = {}
@ -2335,11 +2440,35 @@ class Interface:
self.remote_ctrl = None
if gajim.config.get('networkmanager_support') and dbus_support.supported:
try:
import network_manager_listener
except:
import network_manager_listener
if not network_manager_listener.supported:
print >> sys.stderr, _('Network Manager support not available')
# Handle gnome screensaver
if dbus_support.supported:
def gnome_screensaver_ActiveChanged_cb(active):
if not active:
return
for account in gajim.connections:
if not gajim.sleeper_state.has_key(account) or \
not gajim.sleeper_state[account]:
continue
if gajim.sleeper_state[account] == 'online':
# we save out online status
gajim.status_before_autoaway[account] = \
gajim.connections[account].status
# we go away (no auto status) [we pass True to auto param]
auto_message = gajim.config.get('autoaway_message')
if not auto_message:
auto_message = gajim.connections[account].status
self.roster.send_status(account, 'away', auto_message,
auto=True)
gajim.sleeper_state[account] = 'autoaway'
bus = dbus.SessionBus()
bus.add_signal_receiver(gnome_screensaver_ActiveChanged_cb,
'ActiveChanged', 'org.gnome.ScreenSaver')
self.show_vcard_when_connect = []
path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'gajim.png')
@ -2393,8 +2522,11 @@ class Interface:
self.last_ftwindow_update = 0
gobject.timeout_add(100, self.autoconnect)
gobject.timeout_add(200, self.process_connections)
gobject.timeout_add(500, self.read_sleepy)
if os.name == 'nt':
gobject.timeout_add(200, self.process_connections)
else:
gobject.timeout_add(2000, self.process_connections)
gobject.timeout_add(10000, self.read_sleepy)
if __name__ == '__main__':
def sigint_cb(num, stack):

View file

@ -50,6 +50,7 @@ class GajimThemesWindow:
self.italic_togglebutton = self.xml.get_widget('italic_togglebutton')
self.themes_tree = self.xml.get_widget('themes_treeview')
self.theme_options_vbox = self.xml.get_widget('theme_options_vbox')
self.theme_options_table = self.xml.get_widget('theme_options_table')
self.colorbuttons = {}
for chatstate in ('inactive', 'composing', 'paused', 'gone',
'muc_msg', 'muc_directed_msg'):
@ -67,6 +68,7 @@ class GajimThemesWindow:
self.current_theme = gajim.config.get('roster_theme')
self.no_update = False
self.fill_themes_treeview()
self.select_active_theme()
self.current_option = self.options[0]
self.set_theme_options(self.current_theme, self.current_option)
@ -81,6 +83,8 @@ class GajimThemesWindow:
return True # do NOT destroy the window
def on_close_button_clicked(self, widget):
if gajim.interface.instances.has_key('preferences'):
gajim.interface.instances['preferences'].update_theme_list()
self.window.hide()
def on_theme_cell_edited(self, cell, row, new_name):
@ -90,6 +94,11 @@ class GajimThemesWindow:
new_name = new_name.decode('utf-8')
if old_name == new_name:
return
if old_name == 'default':
dialogs.ErrorDialog(
_('You cannot make changes to the default theme'),
_('Please create a clean new theme with your desired name.'))
return
new_config_name = new_name.replace(' ', '_')
if new_config_name in gajim.config.get_per('themes'):
return
@ -110,28 +119,31 @@ class GajimThemesWindow:
self.current_theme = new_name
def fill_themes_treeview(self):
self.xml.get_widget('remove_button').set_sensitive(False)
self.theme_options_vbox.set_sensitive(False)
model = self.themes_tree.get_model()
model.clear()
for config_theme in gajim.config.get_per('themes'):
theme = config_theme.replace('_', ' ')
iter = model.append([theme])
if gajim.config.get('roster_theme') == config_theme:
self.themes_tree.get_selection().select_iter(iter)
self.xml.get_widget('remove_button').set_sensitive(True)
self.theme_options_vbox.set_sensitive(True)
def select_active_theme(self):
model = self.themes_tree.get_model()
iter = model.get_iter_root()
active_theme = gajim.config.get('roster_theme')
active_theme = gajim.config.get('roster_theme').replace('_', ' ')
while iter:
theme = model[iter][0]
if theme == active_theme:
self.themes_tree.get_selection().select_iter(iter)
self.xml.get_widget('remove_button').set_sensitive(True)
self.theme_options_vbox.set_sensitive(True)
self.theme_options_table.set_sensitive(True)
if active_theme == 'default':
self.xml.get_widget('remove_button').set_sensitive(False)
self.theme_options_vbox.set_sensitive(False)
self.theme_options_table.set_sensitive(False)
else:
self.xml.get_widget('remove_button').set_sensitive(True)
self.theme_options_vbox.set_sensitive(True)
self.theme_options_table.set_sensitive(True)
break
iter = model.iter_next(iter)
@ -140,12 +152,19 @@ class GajimThemesWindow:
selected = self.themes_tree.get_selection().get_selected_rows()
if not iter or selected[1] == []:
self.theme_options_vbox.set_sensitive(False)
self.theme_options_table.set_sensitive(False)
return
self.xml.get_widget('remove_button').set_sensitive(True)
self.theme_options_vbox.set_sensitive(True)
self.current_theme = model.get_value(iter, 0).decode('utf-8')
self.current_theme = self.current_theme.replace(' ', '_')
self.set_theme_options(self.current_theme)
if self.current_theme == 'default':
self.xml.get_widget('remove_button').set_sensitive(False)
self.theme_options_vbox.set_sensitive(False)
self.theme_options_table.set_sensitive(False)
else:
self.xml.get_widget('remove_button').set_sensitive(True)
self.theme_options_vbox.set_sensitive(True)
self.theme_options_table.set_sensitive(True)
def on_add_button_clicked(self, widget):
model = self.themes_tree.get_model()
@ -173,6 +192,8 @@ class GajimThemesWindow:
_('Please first choose another for your current theme.'))
return
self.theme_options_vbox.set_sensitive(False)
self.theme_options_table.set_sensitive(False)
self.xml.get_widget('remove_button').set_sensitive(False)
gajim.config.del_per('themes', self.current_theme)
model.remove(iter)

View file

@ -9,6 +9,8 @@
## Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -63,6 +65,12 @@ def tree_cell_data_func(column, renderer, model, iter, tv=None):
# cell data func is global, because we don't want it to keep
# reference to GroupchatControl instance (self)
theme = gajim.config.get('roster_theme')
# allocate space for avatar only if needed
if isinstance(renderer, gtk.CellRendererPixbuf):
if model[iter][C_AVATAR]:
renderer.set_property('visible', True)
else:
renderer.set_property('visible', False)
if model.iter_parent(iter):
bgcolor = gajim.config.get_per('themes', theme, 'contactbgcolor')
if bgcolor:
@ -97,12 +105,14 @@ def tree_cell_data_func(column, renderer, model, iter, tv=None):
class PrivateChatControl(ChatControl):
TYPE_ID = message_control.TYPE_PM
def __init__(self, parent_win, gc_contact, contact, acct):
def __init__(self, parent_win, gc_contact, contact, account):
room_jid = contact.jid.split('/')[0]
room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid, acct)
room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid, account)
if gajim.interface.minimized_controls[account].has_key(room_jid):
room_ctrl = gajim.interface.minimized_controls[account][room_jid]
self.room_name = room_ctrl.name
self.gc_contact = gc_contact
ChatControl.__init__(self, parent_win, contact, acct)
ChatControl.__init__(self, parent_win, contact, account)
self.TYPE_ID = 'pm'
def send_message(self, message):
@ -127,7 +137,7 @@ class PrivateChatControl(ChatControl):
return
ChatControl.send_message(self, message)
def update_ui(self):
if self.contact.show == 'offline':
self.got_disconnected()
@ -183,9 +193,8 @@ class GroupchatControl(ChatControlBase):
self.nick = contact.name
self.name = self.room_jid.split('@')[0]
hide_chat_buttons_always = gajim.config.get(
'always_hide_groupchat_buttons')
self.chat_buttons_set_visible(hide_chat_buttons_always)
compact_view = gajim.config.get('compact_view')
self.chat_buttons_set_visible(compact_view)
self.widget_set_visible(self.xml.get_widget('banner_eventbox'),
gajim.config.get('hide_groupchat_banner'))
self.widget_set_visible(self.xml.get_widget('list_scrolledwindow'),
@ -237,18 +246,14 @@ class GroupchatControl(ChatControlBase):
self._on_change_subject_menuitem_activate)
self.handlers[id] = self.change_subject_menuitem
self.compact_view_menuitem = xm.get_widget('compact_view_menuitem')
id = self.compact_view_menuitem.connect('activate',
self._on_compact_view_menuitem_activate)
self.handlers[id] = self.compact_view_menuitem
widget = xm.get_widget('history_menuitem')
id = widget.connect('activate', self._on_history_menuitem_activate)
self.handlers[id] = widget
widget = xm.get_widget('minimize_menuitem')
id = widget.connect('activate', self._on_minimize_menuitem_activate)
self.handlers[id] = widget
self.minimize_menuitem = xm.get_widget('minimize_menuitem')
id = self.minimize_menuitem.connect('toggled',
self.on_minimize_menuitem_toggled)
self.handlers[id] = self.minimize_menuitem
self.gc_popup_menu = xm.get_widget('gc_control_popup_menu')
@ -285,14 +290,8 @@ class GroupchatControl(ChatControlBase):
# first one img, second one text, third is sec pixbuf
column = gtk.TreeViewColumn()
renderer_pixbuf = gtk.CellRendererPixbuf() # avatar image
column.pack_start(renderer_pixbuf, expand = False)
column.add_attribute(renderer_pixbuf, 'pixbuf', C_AVATAR)
column.set_cell_data_func(renderer_pixbuf, tree_cell_data_func,
self.list_treeview)
renderer_pixbuf.set_property('xalign', 1) # align pixbuf to the right
renderer_image = cell_renderer_image.CellRendererImage(0, 0) # status img
renderer_image.set_property('width', 26)
column.pack_start(renderer_image, expand = False)
column.add_attribute(renderer_image, 'image', C_IMG)
column.set_cell_data_func(renderer_image, tree_cell_data_func,
@ -301,9 +300,17 @@ class GroupchatControl(ChatControlBase):
renderer_text = gtk.CellRendererText() # nickname
column.pack_start(renderer_text, expand = True)
column.add_attribute(renderer_text, 'markup', C_TEXT)
renderer_text.set_property("ellipsize", pango.ELLIPSIZE_END)
column.set_cell_data_func(renderer_text, tree_cell_data_func,
self.list_treeview)
renderer_pixbuf = gtk.CellRendererPixbuf() # avatar image
column.pack_start(renderer_pixbuf, expand = False)
column.add_attribute(renderer_pixbuf, 'pixbuf', C_AVATAR)
column.set_cell_data_func(renderer_pixbuf, tree_cell_data_func,
self.list_treeview)
renderer_pixbuf.set_property('xalign', 1) # align pixbuf to the right
self.list_treeview.append_column(column)
# workaround to avoid gtk arrows to be shown
@ -483,10 +490,10 @@ class GroupchatControl(ChatControlBase):
self.name_label.set_markup(text)
def prepare_context_menu(self):
'''sets compact view menuitem active state
sets sensitivity state for configure_room'''
# Check compact view menuitem
self.compact_view_menuitem.set_active(self.hide_chat_buttons_current)
'''sets sensitivity state for configure_room'''
if self.contact.jid in gajim.config.get_per('accounts', self.account,
'minimized_gc').split(' '):
self.minimize_menuitem.set_active(True)
if gajim.gc_connected[self.account][self.room_jid]:
c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
self.nick)
@ -515,11 +522,13 @@ class GroupchatControl(ChatControlBase):
else:
# message from someone
if has_timestamp:
self.print_old_conversation(msg, nick, tim, xhtml)
# don't print xhtml if it's an old message.
# Like that xhtml messages are grayed too.
self.print_old_conversation(msg, nick, tim, None)
else:
self.print_conversation(msg, nick, tim, xhtml)
def on_private_message(self, nick, msg, tim, xhtml):
def on_private_message(self, nick, msg, tim, xhtml, msg_id = None):
# Do we have a queue?
fjid = self.room_jid + '/' + nick
no_queue = len(gajim.events.get_events(self.account, fjid)) == 0
@ -531,7 +540,7 @@ class GroupchatControl(ChatControlBase):
return
event = gajim.events.create_event('pm', (msg, '', 'incoming', tim,
False, '', None, xhtml))
False, '', msg_id, xhtml))
gajim.events.add_event(self.account, fjid, event)
autopopup = gajim.config.get('autopopup')
@ -544,8 +553,8 @@ class GroupchatControl(ChatControlBase):
model = self.list_treeview.get_model()
state_images =\
gajim.interface.roster.get_appropriate_state_images(
self.room_jid, icon_name = 'message')
image = state_images['message']
self.room_jid, icon_name = 'event')
image = state_images['event']
model[iter][C_IMG] = image
if self.parent_win:
self.parent_win.show_title()
@ -556,7 +565,10 @@ class GroupchatControl(ChatControlBase):
self.list_treeview.expand_row(path[0:1], False)
self.list_treeview.scroll_to_cell(path)
self.list_treeview.set_cursor(path)
gajim.interface.roster.draw_contact(self.room_jid, self.account)
contact = gajim.contacts.get_contact_with_highest_priority(self.account, \
self.room_jid)
if contact:
gajim.interface.roster.draw_contact(self.room_jid, self.account)
def get_contact_iter(self, nick):
model = self.list_treeview.get_model()
@ -662,8 +674,7 @@ class GroupchatControl(ChatControlBase):
other_tags_for_text.append('gc_nickname_color_' + \
str(self.gc_custom_colors[contact]))
if self.parent_win:
self.check_and_possibly_add_focus_out_line()
self.check_and_possibly_add_focus_out_line()
ChatControlBase.print_conversation_line(self, text, kind, contact, tim,
other_tags_for_name, [], other_tags_for_text, xhtml = xhtml)
@ -697,7 +708,7 @@ class GroupchatControl(ChatControlBase):
if self.needs_visual_notification(text):
highlight = True
if gajim.config.get_per('soundevents', 'muc_message_highlight',
'enabled'):
'enabled'):
sound = 'highlight'
# Is it a history message? Don't want sound-floods when we join.
@ -712,7 +723,7 @@ class GroupchatControl(ChatControlBase):
it removes previous line first'''
win = gajim.interface.msg_win_mgr.get_window(self.room_jid, self.account)
if self.room_jid == win.get_active_jid() and\
if win and self.room_jid == win.get_active_jid() and\
win.window.get_property('has-toplevel-focus') and\
self.parent_win.get_active_control() == self:
# it's the current room and it's the focused window.
@ -780,7 +791,8 @@ class GroupchatControl(ChatControlBase):
gc_contact.show = 'offline'
gc_contact.status = ''
ctrl.update_ui()
ctrl.parent_win.redraw_tab(ctrl)
if ctrl.parent_win:
ctrl.parent_win.redraw_tab(ctrl)
gajim.contacts.remove_gc_contact(self.account, gc_contact)
gajim.gc_connected[self.account][self.room_jid] = False
ChatControlBase.got_disconnected(self)
@ -795,6 +807,8 @@ class GroupchatControl(ChatControlBase):
self.add_contact_to_roster(nick, gc_contact.show, gc_contact.role,
gc_contact.affiliation, gc_contact.status,
gc_contact.jid)
# Recalculate column width for ellipsizin
self.list_treeview.columns_autosize()
def on_send_pm(self, widget = None, model = None, iter = None, nick = None,
msg = None):
@ -809,6 +823,11 @@ class GroupchatControl(ChatControlBase):
gajim.interface.msg_win_mgr.get_control(fjid, self.account).\
send_message(msg)
def on_send_file(self, widget, gc_contact):
'''sends a file to a contact in the room'''
gajim.interface.instances['file_transfers'].show_file_send_request(
self.account, gc_contact)
def draw_contact(self, nick, selected=False, focus=False):
iter = self.get_contact_iter(nick)
if not iter:
@ -818,7 +837,7 @@ class GroupchatControl(ChatControlBase):
nick)
state_images = gajim.interface.roster.jabber_state_images['16']
if len(gajim.events.get_events(self.account, self.room_jid + '/' + nick)):
image = state_images['message']
image = state_images['event']
else:
image = state_images[gc_contact.show]
@ -867,66 +886,99 @@ class GroupchatControl(ChatControlBase):
affiliation = 'none'
fake_jid = self.room_jid + '/' + nick
newly_created = False
# statusCode
# http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes-init
if statusCode:
if '100' in statusCode:
# Can be a message (see handle_event_gc_config_change in gajim.py)
self.print_conversation(\
_('Any occupant is allowed to see your full JID'))
if '170' in statusCode:
# Can be a message (see handle_event_gc_config_change in gajim.py)
self.print_conversation(_('Room logging is enabled'))
if '201' in statusCode:
self.print_conversation(_('A new room has been created'))
if '210' in statusCode:
self.print_conversation(\
_('The server has assigned or modified your roomnick'))
if show in ('offline', 'error'):
if statusCode == '307':
if actor is None: # do not print 'kicked by None'
s = _('%(nick)s has been kicked: %(reason)s') % {
if statusCode:
if '307' in statusCode:
if actor is None: # do not print 'kicked by None'
s = _('%(nick)s has been kicked: %(reason)s') % {
'nick': nick,
'reason': reason }
else:
s = _('%(nick)s has been kicked by %(who)s: %(reason)s') % {
'nick': nick,
'who': actor,
'reason': reason }
self.print_conversation(s, 'info', tim = tim)
elif '301' in statusCode:
if actor is None: # do not print 'banned by None'
s = _('%(nick)s has been banned: %(reason)s') % {
'nick': nick,
'reason': reason }
else:
s = _('%(nick)s has been banned by %(who)s: %(reason)s') % {
'nick': nick,
'who': actor,
'reason': reason }
self.print_conversation(s, 'info', tim = tim)
elif '303' in statusCode: # Someone changed his or her nick
if new_nick == self.nick: # We changed our nick
s = _('You are now known as %s') % new_nick
else:
s = _('%s is now known as %s') % (nick, new_nick)
# We add new nick to muc roster here, so we don't see
# that "new_nick has joined the room" when he just changed nick.
# add_contact_to_roster will be called a second time
# after that, but that doesn't hurt
self.add_contact_to_roster(new_nick, show, role, affiliation,
status, jid)
if nick in self.attention_list:
self.attention_list.remove(nick)
# keep nickname color
if nick in self.gc_custom_colors:
self.gc_custom_colors[new_nick] = self.gc_custom_colors[nick]
# rename vcard / avatar
puny_jid = helpers.sanitize_filename(self.room_jid)
puny_nick = helpers.sanitize_filename(nick)
puny_new_nick = helpers.sanitize_filename(new_nick)
old_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
new_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_new_nick)
files = {old_path: new_path}
path = os.path.join(gajim.AVATAR_PATH, puny_jid)
# possible extensions
for ext in ('.png', '.jpeg', '_notif_size_bw.png',
'_notif_size_colored.png'):
files[os.path.join(path, puny_nick + ext)] = \
os.path.join(path, puny_new_nick + ext)
for old_file in files:
if os.path.exists(old_file):
if os.path.exists(files[old_file]):
# Windows require this
os.remove(files[old_file])
os.rename(old_file, files[old_file])
self.print_conversation(s, 'info', tim)
elif '321' in statusCode:
s = _('%(nick)s has been removed from the room (%(reason)s)') % {
'nick': nick, 'reason': _('affiliation changed') }
self.print_conversation(s, 'info', tim = tim)
elif '322' in statusCode:
s = _('%(nick)s has been removed from the room (%(reason)s)') % {
'nick': nick,
'reason': reason }
else:
s = _('%(nick)s has been kicked by %(who)s: %(reason)s') % {
'reason': _('room configuration changed to members-only') }
self.print_conversation(s, 'info', tim = tim)
elif '332' in statusCode:
s = _('%(nick)s has been removed from the room (%(reason)s)') % {
'nick': nick,
'who': actor,
'reason': reason }
self.print_conversation(s, 'info', tim = tim)
elif statusCode == '301':
if actor is None: # do not print 'banned by None'
s = _('%(nick)s has been banned: %(reason)s') % {
'nick': nick,
'reason': reason }
else:
s = _('%(nick)s has been banned by %(who)s: %(reason)s') % {
'nick': nick,
'who': actor,
'reason': reason }
self.print_conversation(s, 'info', tim = tim)
elif statusCode == '303': # Someone changed his or her nick
if nick == self.nick: # We changed our nick
self.nick = new_nick
s = _('You are now known as %s') % new_nick
else:
s = _('%s is now known as %s') % (nick, new_nick)
# We add new nick to muc roster here, so we don't see
# that "new_nick has joined the room" when he just changed nick.
# add_contact_to_roster will be called a second time
# after that, but that doesn't hurt
self.add_contact_to_roster(new_nick, show, role, affiliation,
status, jid)
# keep nickname color
if nick in self.gc_custom_colors:
self.gc_custom_colors[new_nick] = self.gc_custom_colors[nick]
# rename vcard / avatar
puny_jid = helpers.sanitize_filename(self.room_jid)
puny_nick = helpers.sanitize_filename(nick)
puny_new_nick = helpers.sanitize_filename(new_nick)
old_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
new_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_new_nick)
files = {old_path: new_path}
path = os.path.join(gajim.AVATAR_PATH, puny_jid)
# possible extensions
for ext in ('.png', '.jpeg', '_notif_size_bw.png',
'_notif_size_colored.png'):
files[os.path.join(path, puny_nick + ext)] = \
os.path.join(path, puny_new_nick + ext)
for old_file in files:
if os.path.exists(old_file):
if os.path.exists(files[old_file]):
# Windows require this
os.remove(files[old_file])
os.rename(old_file, files[old_file])
self.print_conversation(s, 'info', tim)
elif statusCode == 'destroyed': # Room has been destroyed
self.print_conversation(reason, 'info', tim)
'reason': _('system shutdown') }
self.print_conversation(s, 'info', tim = tim)
elif 'destroyed' in statusCode: # Room has been destroyed
self.print_conversation(reason, 'info', tim)
if len(gajim.events.get_events(self.account, fake_jid)) == 0:
self.remove_contact(nick)
@ -934,7 +986,8 @@ class GroupchatControl(ChatControlBase):
c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
c.show = show
c.status = status
if nick == self.nick and statusCode != '303': # We became offline
if nick == self.nick and (not statusCode or \
'303' not in statusCode): # We became offline
self.got_disconnected()
contact = gajim.contacts.\
get_contact_with_highest_priority(self.account, self.room_jid)
@ -948,7 +1001,7 @@ class GroupchatControl(ChatControlBase):
iter = self.add_contact_to_roster(nick, show, role, affiliation,
status, jid)
newly_created = True
if statusCode == '201': # We just created the room
if statusCode and '201' in statusCode: # We just created the room
gajim.connections[self.account].request_gc_config(self.room_jid)
else:
gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
@ -965,7 +1018,9 @@ class GroupchatControl(ChatControlBase):
real_jid = fake_jid
if con.vcard_shas.has_key(fake_jid):
if avatar_sha != con.vcard_shas[fake_jid]:
con.request_vcard(real_jid, fake_jid)
server = gajim.get_server_from_jid(self.room_jid)
if not server.startswith('irc'):
con.request_vcard(real_jid, fake_jid)
else:
cached_vcard = con.get_cached_vcard(fake_jid, True)
if cached_vcard and cached_vcard.has_key('PHOTO') and \
@ -976,7 +1031,9 @@ class GroupchatControl(ChatControlBase):
if cached_sha != avatar_sha:
# avatar has been updated
# sha in mem will be updated later
con.request_vcard(real_jid, fake_jid)
server = gajim.get_server_from_jid(self.room_jid)
if not server.startswith('irc'):
con.request_vcard(real_jid, fake_jid)
else:
# save sha in mem NOW
con.vcard_shas[fake_jid] = avatar_sha
@ -997,7 +1054,8 @@ class GroupchatControl(ChatControlBase):
if self.parent_win:
self.parent_win.redraw_tab(self)
if (time.time() - self.room_creation) > 30 and \
nick != self.nick and statusCode != '303':
nick != self.nick and (not statusCode or \
'303' not in statusCode):
st = ''
print_status = None
for bookmark in gajim.connections[self.account].bookmarks:
@ -1011,8 +1069,11 @@ class GroupchatControl(ChatControlBase):
# delete ressource
simple_jid = gajim.get_jid_without_resource(jid)
nick_jid += ' (%s)' % simple_jid
if show == 'offline':
if nick in self.attention_list:
self.attention_list.remove(nick)
if show == 'offline' and print_status in ('all', 'in_and_out') and \
statusCode != '307':
(not statusCode or '307' not in statusCode):
st = _('%s has left') % nick_jid
if reason:
st += ' [%s]' % reason
@ -1127,6 +1188,7 @@ class GroupchatControl(ChatControlBase):
nick = message_array[0]
nick = helpers.parse_resource(nick)
gajim.connections[self.account].join_gc(nick, self.room_jid, None)
self.nick = nick
self.clear(self.msg_textview)
else:
self.get_command_help(command)
@ -1408,9 +1470,31 @@ class GroupchatControl(ChatControlBase):
nick = instance.input_entry.get_text().decode('utf-8')
nick = helpers.parse_resource(nick)
gajim.connections[self.account].join_gc(nick, self.room_jid, None)
self.nick = nick
instance = dialogs.InputDialog(title, prompt, proposed_nick,
is_modal = False, ok_handler = on_ok)
def minimize(self, status='offline'):
# Minimize it
win = gajim.interface.msg_win_mgr.get_window(self.contact.jid,
self.account)
ctrl = win.get_control(self.contact.jid, self.account)
ctrl_page = win.notebook.page_num(ctrl.widget)
control = win.notebook.get_nth_page(ctrl_page)
win.notebook.remove_page(ctrl_page)
control.unparent()
ctrl.parent_win = None
gajim.interface.minimized_controls[self.account][self.contact.jid] = \
ctrl
del win._controls[self.account][self.contact.jid]
gajim.interface.roster.add_groupchat_to_roster(self.account,
self.contact.jid, status = self.subject)
def shutdown(self, status='offline'):
gajim.connections[self.account].send_gc_status(self.nick, self.room_jid,
show='offline', status=status)
@ -1441,13 +1525,15 @@ class GroupchatControl(ChatControlBase):
gajim.events.remove_events(self.account, self.room_jid)
def allow_shutdown(self, method):
'''If check_selection is True, '''
if self.contact.jid in gajim.config.get_per('accounts', self.account,
'minimized_gc').split(' '):
return 'minimize'
if method == self.parent_win.CLOSE_ESC:
model, iter = self.list_treeview.get_selection().get_selected()
if iter:
self.list_treeview.get_selection().unselect_all()
return False
retval = True
return 'no'
retval = 'yes'
includes = gajim.config.get('confirm_close_muc_rooms').split(' ')
excludes = gajim.config.get('noconfirm_close_muc_rooms').split(' ')
# whether to ask for comfirmation before closing muc
@ -1463,7 +1549,7 @@ class GroupchatControl(ChatControlBase):
_('Do _not ask me again'))
if dialog.get_response() != gtk.RESPONSE_OK:
retval = False
retval = 'no'
if dialog.is_checked(): # user does not want to be asked again
gajim.config.set('confirm_close_muc', False)
@ -1546,6 +1632,7 @@ class GroupchatControl(ChatControlBase):
'name': self.name,
'jid': self.room_jid,
'autojoin': '0',
'minimize': '0',
'password': '',
'nick': self.nick
}
@ -1566,8 +1653,25 @@ class GroupchatControl(ChatControlBase):
_('Bookmark has been added successfully'),
_('You can manage your bookmarks via Actions menu in your roster.'))
def _on_drag_data_received(self, widget, context, x, y, selection,
target_type, timestamp):
# Invite contact to groupchat
treeview = gajim.interface.roster.tree
model = treeview.get_model()
if not selection.data:
return
data = selection.data
path = treeview.get_selection().get_selected_rows()[1][0]
iter = model.get_iter(path)
type = model[iter][2]
account = model[iter][4].decode('utf-8')
if type != 'contact': # source is not a contact
return
contact_jid = data.decode('utf-8')
gajim.connections[self.account].send_invite(self.room_jid, contact_jid)
def handle_message_textview_mykey_press(self, widget, event_keyval,
event_keymod):
event_keymod):
# NOTE: handles mykeypress which is custom signal connected to this
# CB in new_room(). for this singal see message_textview.py
@ -1800,13 +1904,21 @@ class GroupchatControl(ChatControlBase):
item = xml.get_widget('add_to_roster_menuitem')
if not jid:
item.set_sensitive(False)
id = item.connect('activate', self.on_add_to_roster, jid)
self.handlers[id] = item
else:
id = item.connect('activate', self.on_add_to_roster, jid)
self.handlers[id] = item
item = xml.get_widget('send_private_message_menuitem')
id = item.connect('activate', self.on_send_pm, model, iter)
self.handlers[id] = item
item = xml.get_widget('send_file_menuitem')
if not c.resource:
item.set_sensitive(False)
else:
id = item.connect('activate', self.on_send_file, c)
self.handlers[id] = item
# show the popup now!
menu = xml.get_widget('gc_occupants_menu')
menu.show_all()
@ -1835,7 +1947,7 @@ class GroupchatControl(ChatControlBase):
else: # We want to send a private message
nick = model[path][C_NICK].decode('utf-8')
self._start_private_message(nick)
def on_list_treeview_row_activated(self, widget, path, col = 0):
'''When an iter is double clicked: open the chat window'''
if not gajim.single_click:

View file

@ -584,9 +584,8 @@ def make_python_month_gtk_month(month):
def make_color_string(color):
'''create #aabbcc color string from gtk color'''
col = '#'
for i in (color.red, color.green, color.blue):
# GTK sometime return a value > 256
h = hex(i%256)[2:4]
for i in ('red', 'green', 'blue'):
h = hex(getattr(color, i) / (16*16)).split('x')[1]
if len(h) == 1:
h = '0' + h
col += h

View file

@ -76,10 +76,7 @@ class HistoryManager:
self.search_results_scrolledwindow = xml.get_widget(
'search_results_scrolledwindow')
self.welcome_label = xml.get_widget('welcome_label')
self.logs_scrolledwindow.set_no_show_all(True)
self.search_results_scrolledwindow.set_no_show_all(True)
self.jids_already_in = [] # holds jids that we already have in DB
self.AT_LEAST_ONE_DELETION_DONE = False

View file

@ -34,7 +34,7 @@ class MessageControl:
self.widget_name = widget_name
self.contact = contact
self.account = account
self.hide_chat_buttons_current = False
self.hide_chat_buttons = False
self.resource = resource
gajim.last_message_time[self.account][self.get_full_jid()] = 0
@ -56,9 +56,9 @@ class MessageControl:
def allow_shutdown(self, method):
'''Called to check is a control is allowed to shutdown.
If a control is not in a suitable shutdown state this method
should return False'''
should return 'no', else 'yes' or 'minimize' '''
# NOTE: Derived classes MAY implement this
return True
return 'yes'
def shutdown(self):
# NOTE: Derived classes MUST implement this
@ -99,7 +99,7 @@ class MessageControl:
def chat_buttons_set_visible(self, state):
# NOTE: Derived classes MAY implement this
self.hide_chat_buttons_current = state
self.hide_chat_buttons = state
def got_connected(self):
pass
@ -111,7 +111,7 @@ class MessageControl:
return len(gajim.events.get_events(self.account, self.contact.jid))
def send_message(self, message, keyID = '', type = 'chat',
chatstate = None, msg_id = None, composing_jep = None, resource = None,
chatstate = None, msg_id = None, composing_xep = None, resource = None,
user_nick = None):
'''Send the given message to the active tab. Doesn't return None if error
'''
@ -119,5 +119,5 @@ class MessageControl:
# Send and update history
return gajim.connections[self.account].send_message(jid, message, keyID,
type = type, chatstate = chatstate, msg_id = msg_id,
composing_jep = composing_jep, resource = self.resource,
composing_xep = composing_xep, resource = self.resource,
user_nick = user_nick)

View file

@ -103,13 +103,13 @@ class MessageWindow:
self.notebook.set_show_tabs(False)
self.notebook.set_show_border(gajim.config.get('tabs_border'))
# set up DnD
self.hid = self.notebook.connect('drag_data_received',
self.on_tab_label_drag_data_received_cb)
self.handlers[self.hid] = self.notebook
self.notebook.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.DND_TARGETS,
gtk.gdk.ACTION_MOVE)
# if GTK+ version < 2.10, use OUR way to reorder tabs (set up DnD)
if gtk.pygtk_version < (2, 10, 0) or gtk.gtk_version < (2, 10, 0):
self.hid = self.notebook.connect('drag_data_received',
self.on_tab_label_drag_data_received_cb)
self.handlers[self.hid] = self.notebook
self.notebook.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.DND_TARGETS,
gtk.gdk.ACTION_MOVE)
def change_account_name(self, old_name, new_name):
if self._controls.has_key(old_name):
@ -146,9 +146,16 @@ class MessageWindow:
def _on_window_delete(self, win, event):
# Make sure all controls are okay with being deleted
ctrl_to_minimize = []
for ctrl in self.controls():
if not ctrl.allow_shutdown(self.CLOSE_CLOSE_BUTTON):
allow_shutdown = ctrl.allow_shutdown(self.CLOSE_CLOSE_BUTTON)
if allow_shutdown == 'no':
return True # halt the delete
elif allow_shutdown == 'minimize':
ctrl_to_minimize.append(ctrl)
# If all are ok, minimize the one that need to be minimized
for ctrl in ctrl_to_minimize:
ctrl.minimize()
return False
def _on_window_destroy(self, win):
@ -191,7 +198,11 @@ class MessageWindow:
control.handlers[id] = tab_label_box
self.notebook.append_page(control.widget, tab_label_box)
self.setup_tab_dnd(control.widget)
# If GTK+ version >= 2.10, use gtk native way to reorder tabs
if gtk.pygtk_version >= (2, 10, 0) and gtk.gtk_version >= (2, 10, 0):
self.notebook.set_tab_reorderable(control.widget, True)
else:
self.setup_tab_dnd(control.widget)
self.redraw_tab(control)
self.window.show_all()
@ -301,7 +312,12 @@ class MessageWindow:
'''reason is only for gc (offline status message)
if force is True, do not ask any confirmation'''
# Shutdown the MessageControl
if not force and not ctrl.allow_shutdown(method):
allow_shutdown = ctrl.allow_shutdown(method)
if not force and allow_shutdown == 'no':
return
if allow_shutdown == 'minimize' and method != self.CLOSE_COMMAND:
ctrl.minimize()
self.check_tabs()
return
if reason is not None: # We are leaving gc with a status message
ctrl.shutdown(reason)
@ -313,7 +329,10 @@ class MessageWindow:
types = ['printed_msg', 'chat', 'gc_msg'])
del gajim.last_message_time[ctrl.account][ctrl.get_full_jid()]
self.disconnect_tab_dnd(ctrl.widget)
# Disconnect tab DnD only if GTK version < 2.10
if gtk.pygtk_version < (2, 10, 0) or gtk.gtk_version < (2, 10, 0):
self.disconnect_tab_dnd(ctrl.widget)
self.notebook.remove_page(self.notebook.page_num(ctrl.widget))
fjid = ctrl.get_full_jid()
@ -533,7 +552,7 @@ class MessageWindow:
(event.state & gtk.gdk.MOD1_MASK): # ALT + 1,2,3..
self.notebook.set_current_page(st.index(event.string))
elif event.keyval == gtk.keysyms.c: # ALT + C toggles chat buttons
ctrl.chat_buttons_set_visible(not ctrl.hide_chat_buttons_current)
ctrl.chat_buttons_set_visible(not ctrl.hide_chat_buttons)
# Close tab bindings
elif event.keyval == gtk.keysyms.Escape and \
gajim.config.get('escape_key_closes'): # Escape

View file

@ -61,6 +61,8 @@ class MusicTrackListener(gobject.GObject):
'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Rhythmbox')
bus.add_signal_receiver(self._player_playing_changed_cb,
'playingChanged', 'org.gnome.Rhythmbox.Player')
bus.add_signal_receiver(self._player_playing_song_property_changed_cb,
'playingSongPropertyChanged', 'org.gnome.Rhythmbox.Player')
## Banshee
banshee_bus = dbus.SessionBus()
@ -77,7 +79,7 @@ class MusicTrackListener(gobject.GObject):
# Otherwise, it opens Banshee!
self.banshee_props ={}
gobject.timeout_add(1000, self._banshee_check_track_status)
def _check_if_banshee_bus(self):
if self.dubus_methods.NameHasOwner('org.gnome.Banshee'):
self._get_banshee_bus()
@ -86,7 +88,6 @@ class MusicTrackListener(gobject.GObject):
self.banshee_is_here = False
return True
def _get_banshee_bus(self):
bus = dbus.SessionBus()
banshee = bus.get_object('org.gnome.Banshee', '/org/gnome/Banshee/Player')
@ -106,6 +107,10 @@ class MusicTrackListener(gobject.GObject):
else:
self.emit('music-track-changed', None)
def _player_playing_song_property_changed_cb(self, a, b, c, d):
if b == 'rb:stream-song-title':
self.emit('music-track-changed', self._last_playing_music)
def _muine_properties_extract(self, song_string):
d = dict((x.strip() for x in s1.split(':', 1)) for s1 in \
song_string.split('\n'))

View file

@ -27,23 +27,25 @@ def device_no_longer_active(self, *args):
'listen_to_network_manager') and connection.connected > 1:
connection._disconnectedReconnCB()
supported = False
from common.dbus_support import system_bus
try:
from common.dbus_support import system_bus
import dbus
import dbus.glib
bus = system_bus.SystemBus()
bus = system_bus.SystemBus()
bus.add_signal_receiver(device_no_longer_active,
'DeviceNoLongerActive',
'org.freedesktop.NetworkManager',
'org.freedesktop.NetworkManager',
'/org/freedesktop/NetworkManager')
bus.add_signal_receiver(device_now_active,
'DeviceNowActive',
'org.freedesktop.NetworkManager',
'org.freedesktop.NetworkManager',
'/org/freedesktop/NetworkManager')
if 'org.freedesktop.NetworkManager' in bus.list_names():
supported = True
bus.add_signal_receiver(device_no_longer_active,
'DeviceNoLongerActive',
'org.freedesktop.NetworkManager',
'org.freedesktop.NetworkManager',
'/org/freedesktop/NetworkManager')
bus.add_signal_receiver(device_now_active,
'DeviceNowActive',
'org.freedesktop.NetworkManager',
'org.freedesktop.NetworkManager',
'/org/freedesktop/NetworkManager')
except:
pass

View file

@ -154,7 +154,11 @@ def notify(event, jid, account, parameters, advanced_notif_num = None):
message_type = parameters[0]
is_first_message = parameters[1]
nickname = parameters[2]
message = parameters[3]
if gajim.config.get('notification_preview_message'):
message = parameters[3]
else:
# We don't want message preview, do_preview = False
message = ''
if helpers.allow_showing_notification(account, 'notify_on_new_message',
advanced_notif_num, is_first_message):
do_popup = True
@ -235,8 +239,12 @@ def notify(event, jid, account, parameters, advanced_notif_num = None):
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'priv_msg_recv.png')
title = _('New Private Message from group chat %s') % room_name
text = _('%(nickname)s: %(message)s') % {'nickname': nickname,
'message': message}
if message:
text = _('%(nickname)s: %(message)s') % {'nickname': nickname,
'message': message}
else:
text = _('Messaged by %(nickname)s') % {'nickname': nickname}
else: # chat message
event_type = _('New Message')
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
@ -299,7 +307,8 @@ def popup(event_type, jid, account, msg_type = '', path_to_image = None,
gajim.log.debug(str(e))
# we failed to speak to notification daemon via D-Bus
if USER_HAS_PYNOTIFY: # try via libnotify
if not text:
if not text and event_type == 'new_message':
# empty text for new_message means do_preview = False
text = gajim.get_name_from_jid(account, jid) # default value of text
if not title:
title = event_type
@ -408,8 +417,9 @@ class DesktopNotification:
self.jid = jid
self.msg_type = msg_type
if not text:
# default value of text
# default value of text
if not text and event_type == 'new_message':
# empty text for new_message means do_preview = False
self.text = gajim.get_name_from_jid(account, jid)
if not title:

View file

@ -77,10 +77,6 @@ class ProfileWindow:
# Create Image for avatar button
image = gtk.Image()
self.xml.get_widget('PHOTO_button').set_image(image)
text_button = self.xml.get_widget('NOPHOTO_button')
# We use 2 buttons because some GTK theme don't show images in buttons
text_button.set_no_show_all(True)
text_button.hide()
self.xml.signal_autoconnect(self)
self.window.show_all()

View file

@ -4,6 +4,8 @@
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005-2006 Andrew Sayman <lorien420@myrealbox.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -223,6 +225,31 @@ class SignalObject(dbus.service.Object):
return connected_account, contact
def _get_account_for_groupchat(self, account, room_jid):
'''get the account which is connected to groupchat (if not given)
or check if the given account is connected to the groupchat'''
connected_account = None
accounts = gajim.contacts.get_accounts()
# if there is only one account in roster, take it as default
# if user did not ask for account
if not account and len(accounts) == 1:
account = accounts[0]
if account:
if gajim.connections[account].connected > 1 and \
room_jid in gajim.gc_connected[account] and \
gajim.gc_connected[account][room_jid]:
# account and groupchat are connected
connected_account = account
else:
for account in accounts:
if gajim.connections[account].connected > 1 and \
room_jid in gajim.gc_connected[account] and \
gajim.gc_connected[account][room_jid]:
# account and groupchat are connected
connected_account = account
break
return connected_account
@dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
def send_file(self, file_path, jid, account):
'''send file, located at 'file_path' to 'jid', using account
@ -269,6 +296,19 @@ class SignalObject(dbus.service.Object):
jid = self._get_real_jid(jid, account)
return self._send_message(jid, message, keyID, account, type, subject)
@dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
def send_groupchat_message(self, room_jid, message, account):
'''Send 'message' to groupchat 'room_jid',
using account (optional) 'account'.'''
if not room_jid or not message:
return DBUS_BOOLEAN(False)
connected_account = self._get_account_for_groupchat(account, room_jid)
if connected_account:
connection = gajim.connections[connected_account]
connection.send_gc_message(room_jid, message)
return DBUS_BOOLEAN(True)
return DBUS_BOOLEAN(False)
@dbus.service.method(INTERFACE, in_signature='ss', out_signature='b')
def open_chat(self, jid, account):
'''Shows the tabbed window for new message to 'jid', using account
@ -410,7 +450,7 @@ class SignalObject(dbus.service.Object):
if acct in accounts:
for jid in gajim.contacts.get_jid_list(acct):
item = self._contacts_as_dbus_structure(
gajim.contacts.get_contact(acct, jid))
gajim.contacts.get_contacts(acct, jid))
if item:
result.append(item)
return result
@ -503,7 +543,7 @@ class SignalObject(dbus.service.Object):
accounts = [account]
contact_exists = False
for account in accounts:
contacts = gajim.contacts.get_contact(account, jid)
contacts = gajim.contacts.get_contacts(account, jid)
if contacts:
gajim.connections[account].unsubscribe(jid)
for contact in contacts:
@ -531,12 +571,12 @@ class SignalObject(dbus.service.Object):
nick_in_roster = None # Is jid a nick ?
for account in accounts:
# Does jid exists in roster of one account ?
if gajim.contacts.get_contacts_from_jid(account, jid):
if gajim.contacts.get_contacts(account, jid):
return jid
if not nick_in_roster:
# look in all contact if one has jid as nick
for jid_ in gajim.contacts.get_jid_list(account):
c = gajim.contacts.get_contacts_from_jid(account, jid_)
c = gajim.contacts.get_contacts(account, jid_)
if c[0].name == jid:
nick_in_roster = jid_
break

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,8 @@ from common import xmpp, gajim, dataforms
import gtkgui_helpers
import dialogs
import vcard
import config
import dataforms_widget
class SearchWindow:
@ -32,12 +34,10 @@ class SearchWindow:
# retrieving widgets from xml
self.xml = gtkgui_helpers.get_glade('search_window.glade')
self.window = self.xml.get_widget('search_window')
for name in ('label', 'progressbar', 'search_vbox', 'search_button'):
for name in ('label', 'progressbar', 'search_vbox', 'search_button',
'add_contact_button', 'information_button'):
self.__dict__[name] = self.xml.get_widget(name)
self.data_form_widget = dataforms_widget.DataFormWidget()
self.table = None
# displaying the window
self.xml.signal_autoconnect(self)
self.window.show_all()
@ -45,10 +45,9 @@ class SearchWindow:
self.pulse_id = gobject.timeout_add(80, self.pulse_callback)
self.is_form = None
# for non-dataform forms
self.entries = {}
self.info = {}
# Is there a jid column in results ? if -1: no, else column number
self.jid_column = -1
def request_form(self):
gajim.connections[self.account].request_search_fields(self.jid)
@ -74,15 +73,14 @@ class SearchWindow:
self.data_form_widget.data_form.type = 'submit'
gajim.connections[self.account].send_search_form(self.jid,
self.data_form_widget.data_form, True)
self.search_vbox.remove(self.data_form_widget)
else:
for name in self.entries.keys():
self.infos[name] = self.entries[name].get_text().decode('utf-8')
if self.infos.has_key('instructions'):
del self.infos['instructions']
gajim.connections[self.account].send_search_form(self.jid, self.infos,
infos = self.data_form_widget.get_infos()
if infos.has_key('instructions'):
del infos['instructions']
gajim.connections[self.account].send_search_form(self.jid, infos,
False)
self.search_vbox.remove(self.table)
self.search_vbox.remove(self.data_form_widget)
self.progressbar.show()
self.label.set_text(_('Waiting for results'))
@ -90,58 +88,66 @@ class SearchWindow:
self.pulse_id = gobject.timeout_add(80, self.pulse_callback)
self.search_button.hide()
def on_add_contact_button_clicked(self, widget):
(model, iter) = self.result_treeview.get_selection().get_selected()
if not iter:
return
jid = model[iter][self.jid_column]
dialogs.AddNewContactWindow(self.account, jid)
def on_information_button_clicked(self, widget):
(model, iter) = self.result_treeview.get_selection().get_selected()
if not iter:
return
jid = model[iter][self.jid_column]
if gajim.interface.instances[self.account]['infos'].has_key(jid):
gajim.interface.instances[self.account]['infos'][jid].window.present()
else:
contact = gajim.contacts.create_contact(jid = jid, name='', groups=[],
show='', status='', sub='', ask='', resource='', priority=0,
keyID='', our_chatstate=None, chatstate=None)
gajim.interface.instances[self.account]['infos'][jid] = \
vcard.VcardWindow(contact, self.account)
def on_form_arrived(self, form, is_form):
if self.pulse_id:
gobject.source_remove(self.pulse_id)
self.progressbar.hide()
self.label.hide()
if not is_form:
self.is_form = False
self.infos = form
nbrow = 0
if self.infos.has_key('instructions'):
self.label.set_text(self.infos['instructions'])
if is_form:
self.is_form = True
self.data_form_widget = dataforms_widget.DataFormWidget()
self.dataform = dataforms.ExtendForm(node = form)
self.data_form_widget.set_sensitive(True)
try:
self.data_form_widget.data_form = self.dataform
except dataforms.Error:
self.label.set_text(_('Error in received dataform'))
self.label.show()
self.table = gtk.Table()
for name in self.infos.keys():
if not name:
continue
if name == 'instructions':
continue
nbrow = nbrow + 1
self.table.resize(rows = nbrow, columns = 2)
label = gtk.Label(name.capitalize() + ':')
self.table.attach(label, 0, 1, nbrow - 1, nbrow, 0, 0, 0, 0)
entry = gtk.Entry()
entry.set_activates_default(True)
if self.infos[name]:
entry.set_text(self.infos[name])
if name == 'password':
entry.set_visibility(False)
self.table.attach(entry, 1, 2, nbrow - 1, nbrow, 0, 0, 0, 0)
self.entries[name] = entry
self.table.show_all()
self.search_vbox.pack_start(self.table)
return
self.dataform = dataforms.ExtendForm(node = form)
self.data_form_widget.set_sensitive(True)
try:
self.data_form_widget.data_form = self.dataform
except dataforms.Error:
self.label.set_text(_('Error in received dataform'))
self.label.show()
return
self.is_form = True
return
if self.data_form_widget.title:
self.window.set_title('%s - Search - Gajim' % \
self.data_form_widget.title)
else:
self.is_form = False
self.data_form_widget = config.FakeDataForm(form)
self.data_form_widget.show_all()
self.search_vbox.pack_start(self.data_form_widget)
self.data_form_widget.show()
if self.data_form_widget.title:
self.window.set_title('%s - Search - Gajim' % \
self.data_form_widget.title)
def on_result_treeview_cursor_changed(self, treeview):
if self.jid_column == -1:
return
(model, iter) = treeview.get_selection().get_selected()
if not iter:
return
if model[iter][self.jid_column]:
self.add_contact_button.set_sensitive(True)
self.information_button.set_sensitive(True)
else:
self.add_contact_button.set_sensitive(False)
self.information_button.set_sensitive(False)
def on_result_arrived(self, form, is_form):
if self.pulse_id:
@ -157,8 +163,10 @@ class SearchWindow:
# We suppose all items have the same fields
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
treeview = gtk.TreeView()
sw.add(treeview)
self.result_treeview = gtk.TreeView()
self.result_treeview.connect('cursor-changed',
self.on_result_treeview_cursor_changed)
sw.add(self.result_treeview)
# Create model
fieldtypes = [str]*len(form[0])
model = gtk.ListStore(*fieldtypes)
@ -168,13 +176,18 @@ class SearchWindow:
# Create columns
counter = 0
for field in form[0].keys():
treeview.append_column(
self.result_treeview.append_column(
gtk.TreeViewColumn(field, gtk.CellRendererText(),
text = counter))
if field == 'jid':
self.jid_column = counter
counter += 1
treeview.set_model(model)
self.result_treeview.set_model(model)
sw.show_all()
self.search_vbox.pack_start(sw)
if self.jid_column > -1:
self.add_contact_button.show()
self.information_button.show()
return
self.dataform = dataforms.ExtendForm(node = form)
@ -187,8 +200,23 @@ class SearchWindow:
self.label.show()
return
self.result_treeview = self.data_form_widget.records_treeview
selection = self.result_treeview.get_selection()
selection.set_mode(gtk.SELECTION_SINGLE)
self.result_treeview.connect('cursor-changed',
self.on_result_treeview_cursor_changed)
counter = 0
for field in self.dataform.items[0].fields:
if field.var == 'jid':
self.jid_column = counter
break
counter += 1
self.search_vbox.pack_start(self.data_form_widget)
self.data_form_widget.show()
if self.jid_column > -1:
self.add_contact_button.show()
self.information_button.show()
if self.data_form_widget.title:
self.window.set_title('%s - Search - Gajim' % \
self.data_form_widget.title)

Some files were not shown because too many files have changed in this diff Show more