Merging changes from trunk (6561:6774)

This commit is contained in:
Tomasz Melcer 2006-09-13 16:47:58 +00:00
parent 5824d3b873
commit 9b29c4c8b8
75 changed files with 15939 additions and 7647 deletions

View file

@ -12,7 +12,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1241">
<widget class="GtkImage" id="image1235">
<property name="visible">True</property>
<property name="stock">gtk-network</property>
<property name="icon_size">1</property>
@ -32,7 +32,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1242">
<widget class="GtkImage" id="image1236">
<property name="visible">True</property>
<property name="stock">gtk-connect</property>
<property name="icon_size">1</property>
@ -45,6 +45,14 @@
</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>
@ -52,7 +60,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1243">
<widget class="GtkImage" id="image1237">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon_size">1</property>
@ -72,7 +80,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1244">
<widget class="GtkImage" id="image1238">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@ -92,7 +100,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1245">
<widget class="GtkImage" id="image1239">
<property name="visible">True</property>
<property name="stock">gtk-find</property>
<property name="icon_size">1</property>
@ -132,7 +140,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1247">
<widget class="GtkImage" id="image1240">
<property name="visible">True</property>
<property name="stock">gtk-preferences</property>
<property name="icon_size">1</property>

View file

@ -17,7 +17,9 @@
<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">
@ -51,185 +53,143 @@
</child>
<child>
<widget class="GtkTable" id="table21">
<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">7</property>
<property name="n_rows">4</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="GtkCheckButton" id="auto_authorize_checkbutton">
<widget class="GtkLabel" id="uid_label">
<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="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"></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">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">fill</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">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="jid_entry">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="editable">False</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="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">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="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">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label187">
<property name="visible">True</property>
<property name="label" translatable="yes">_Jabber 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="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="account_label">
<property name="visible">True</property>
<property name="label" translatable="yes">_Account:</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>
@ -239,6 +199,7 @@
<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>
@ -267,139 +228,129 @@
<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">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label185">
<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">2</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="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="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="GtkHBox" id="account_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<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="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="x_options">fill</property>
<property name="y_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="protocol_hbox">
<widget class="GtkLabel" id="label188">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</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="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>
<placeholder/>
</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">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>
<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="left_attach">1</property>
<property name="right_attach">2</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>
</widget>
@ -411,7 +362,7 @@
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow6">
<widget class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
@ -447,6 +398,91 @@
</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 to 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
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>
@ -468,78 +504,16 @@
</child>
<child>
<widget class="GtkButton" id="subscribe_button">
<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_subscribe_button_clicked" last_modification_time="Mon, 28 Feb 2005 22:46:16 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment39">
<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="hbox2915">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image280">
<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="label192">
<property name="visible">True</property>
<property name="label" translatable="yes">_Subscribe</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>
<signal name="clicked" handler="on_add_button_clicked" last_modification_time="Thu, 03 Aug 2006 14:27:55 GMT"/>
</widget>
</child>
</widget>

View file

@ -9,7 +9,7 @@
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">True</property>
<property name="default_height">260</property>
<property name="default_height">290</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
@ -18,6 +18,7 @@
<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>
<property name="has_separator">True</property>
<signal name="response" handler="on_edit_groups_dialog_response" last_modification_time="Fri, 22 Jul 2005 22:28:44 GMT"/>
@ -159,7 +160,7 @@
<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_NEVER</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>

View file

@ -18,6 +18,7 @@
<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_preferences_window_key_press_event" last_modification_time="Fri, 08 Apr 2005 01:08:08 GMT"/>
<signal name="destroy" handler="on_preferences_window_destroy" last_modification_time="Sun, 05 Mar 2006 11:50:52 GMT"/>
@ -899,6 +900,7 @@ Per type</property>
<widget class="GtkFontButton" id="conversation_fontbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="title" translatable="yes">Επιλογή μιας γραμματοσειράς</property>
<property name="show_style">True</property>
<property name="show_size">True</property>
<property name="use_font">False</property>
@ -950,6 +952,7 @@ Per type</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="use_alpha">False</property>
<property name="title" translatable="yes">Επιλογή χρώματος</property>
<property name="focus_on_click">True</property>
<signal name="color_set" handler="on_outgoing_msg_colorbutton_color_set" last_modification_time="Sun, 06 Mar 2005 14:07:56 GMT"/>
</widget>
@ -968,6 +971,7 @@ Per type</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="use_alpha">False</property>
<property name="title" translatable="yes">Επιλογή χρώματος</property>
<property name="focus_on_click">True</property>
<signal name="color_set" handler="on_url_msg_colorbutton_color_set" last_modification_time="Sun, 25 Dec 2005 15:22:17 GMT"/>
</widget>
@ -1095,6 +1099,7 @@ Per type</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="use_alpha">False</property>
<property name="title" translatable="yes">Επιλογή χρώματος</property>
<property name="focus_on_click">True</property>
<signal name="color_set" handler="on_incoming_msg_colorbutton_color_set" last_modification_time="Sun, 06 Mar 2005 14:07:44 GMT"/>
</widget>
@ -1113,6 +1118,7 @@ Per type</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="use_alpha">False</property>
<property name="title" translatable="yes">Επιλογή χρώματος</property>
<property name="focus_on_click">True</property>
<signal name="color_set" handler="on_status_msg_colorbutton_color_set" last_modification_time="Sun, 06 Mar 2005 14:08:04 GMT"/>
</widget>
@ -2537,6 +2543,78 @@ Disabled</property>
</packing>
</child>
<child>
<widget class="GtkEventBox" id="eventbox6">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">An example: If you have enabled status message for away, Gajim won't ask you anymore for a status message when you change your status to away; it will use the default one set here</property>
<property name="visible_window">True</property>
<property name="above_child">False</property>
<child>
<widget class="GtkExpander" id="expander1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="expanded">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow24">
<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_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="default_msg_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</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>
</child>
<child>
<widget class="GtkLabel" id="label384">
<property name="visible">True</property>
<property name="label" translatable="yes">Default Status Messages</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="type">label_item</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame22">
<property name="visible">True</property>
@ -3128,7 +3206,8 @@ Custom</property>
<child>
<widget class="GtkCheckButton" id="notify_gmail_extra_checkbutton">
<property name="visible">True</property>
<property name="visible">True</property>
<property name="tooltip" translatable="yes">If checked, Gajim will also include information about the sender of the new emails</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Display _extra email details</property>
<property name="use_underline">True</property>
@ -3137,7 +3216,7 @@ Custom</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_notify_gmail_extra_checkbutton_toggled" last_modification_time="Wed, 06 Apr 2005 14:43:56 GMT"/>
<signal name="toggled" handler="on_notify_gmail_extra_checkbutton_toggled" last_modification_time="Wed, 06 Apr 2005 14:43:56 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
@ -3179,7 +3258,7 @@ Custom</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame27">
<property name="visible">True</property>
@ -3245,7 +3324,6 @@ Custom</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1447">
<widget class="GtkImage" id="image1511">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property>
@ -32,7 +32,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1448">
<widget class="GtkImage" id="image1512">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon_size">1</property>
@ -45,13 +45,33 @@
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="invite_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">In_vite to</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1513">
<property name="visible">True</property>
<property name="stock">gtk-go-back</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="image1449">
<widget class="GtkImage" id="image1514">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon_size">1</property>
@ -84,7 +104,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1450">
<widget class="GtkImage" id="image1515">
<property name="visible">True</property>
<property name="stock">gtk-file</property>
<property name="icon_size">1</property>
@ -104,7 +124,7 @@
<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="image1451">
<widget class="GtkImage" id="image1516">
<property name="visible">True</property>
<property name="stock">gtk-dialog-authentication</property>
<property name="icon_size">1</property>
@ -124,7 +144,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1452">
<widget class="GtkImage" id="image1517">
<property name="visible">True</property>
<property name="stock">gtk-info</property>
<property name="icon_size">1</property>
@ -169,7 +189,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1453">
<widget class="GtkImage" id="image1518">
<property name="visible">True</property>
<property name="stock">gtk-dialog-question</property>
<property name="icon_size">1</property>
@ -190,7 +210,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1454">
<widget class="GtkImage" id="image1519">
<property name="visible">True</property>
<property name="stock">gtk-go-up</property>
<property name="icon_size">1</property>
@ -210,7 +230,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1455">
<widget class="GtkImage" id="image1520">
<property name="visible">True</property>
<property name="stock">gtk-go-down</property>
<property name="icon_size">1</property>
@ -230,7 +250,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1456">
<widget class="GtkImage" id="image1521">
<property name="visible">True</property>
<property name="stock">gtk-stop</property>
<property name="icon_size">1</property>
@ -253,7 +273,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1457">
<widget class="GtkImage" id="image1522">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@ -272,7 +292,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1458">
<widget class="GtkImage" id="image1523">
<property name="visible">True</property>
<property name="stock">gtk-remove</property>
<property name="icon_size">1</property>
@ -304,7 +324,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1459">
<widget class="GtkImage" id="image1524">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>

File diff suppressed because it is too large Load diff

View file

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 919 B

View file

Before

Width:  |  Height:  |  Size: 944 B

After

Width:  |  Height:  |  Size: 944 B

View file

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 919 B

View file

Before

Width:  |  Height:  |  Size: 944 B

After

Width:  |  Height:  |  Size: 944 B

View file

Before

Width:  |  Height:  |  Size: 944 B

After

Width:  |  Height:  |  Size: 944 B

View file

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 919 B

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
data/pixmaps/person.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

View file

@ -1,3 +1,3 @@
#!/bin/sh
#!/bin/bash
cd `dirname $0`/src
exec python -t gajim.py $@
exec -a gajim python -t gajim.py $@

View file

@ -9,6 +9,9 @@ all: $(LANGS_MO)
%.mo: %.po
msgfmt $< -o $@
%.glade.h: %.glade
intltool-extract --type=gettext/glade $<
gajim.pot: ../src/*py ../src/common/*py \
../data/glade/account_context_menu.glade.h \
../data/glade/account_creation_wizard_window.glade.h \
@ -16,6 +19,7 @@ gajim.pot: ../src/*py ../src/common/*py \
../data/glade/accounts_window.glade.h \
../data/glade/add_new_contact_window.glade.h \
../data/glade/advanced_configuration_window.glade.h \
../data/glade/advanced_notifications_window.glade.h \
../data/glade/advanced_menuitem_menu.glade.h \
../data/glade/change_password_dialog.glade.h \
../data/glade/change_status_message_dialog.glade.h \

View file

@ -4,50 +4,50 @@
[encoding: UTF-8]
gajim.desktop.in
data/glade/account_context_menu.glade
data/glade/account_creation_wizard_window.glade
data/glade/account_modification_window.glade
data/glade/accounts_window.glade
data/glade/add_new_contact_window.glade
data/glade/advanced_configuration_window.glade
data/glade/advanced_menuitem_menu.glade
data/glade/advanced_notifications_window.glade
data/glade/change_password_dialog.glade
data/glade/change_status_message_dialog.glade
data/glade/chat_context_menu.glade
data/glade/chat_control_popup_menu.glade
data/glade/choose_gpg_key_dialog.glade
data/glade/data_form_window.glade
data/glade/edit_groups_dialog.glade
data/glade/filetransfers.glade
data/glade/gajim_themes_window.glade
data/glade/gc_control_popup_menu.glade
data/glade/gc_occupants_menu.glade
data/glade/history_manager.glade
data/glade/history_window.glade
data/glade/input_dialog.glade
data/glade/invitation_received_dialog.glade
data/glade/join_groupchat_window.glade
data/glade/manage_accounts_window.glade
data/glade/manage_bookmarks_window.glade
data/glade/manage_proxies_window.glade
data/glade/message_window.glade
data/glade/passphrase_dialog.glade
data/glade/popup_notification_window.glade
data/glade/preferences_window.glade
data/glade/privacy_list_edit_window.glade
data/glade/privacy_lists_first_window.glade
data/glade/progress_dialog.glade
data/glade/remove_account_window.glade
data/glade/roster_contact_context_menu.glade
data/glade/roster_window.glade
data/glade/service_discovery_window.glade
data/glade/service_registration_window.glade
data/glade/single_message_window.glade
data/glade/subscription_request_window.glade
data/glade/systray_context_menu.glade
data/glade/vcard_information_window.glade
data/glade/xml_console_window.glade
data/glade/account_context_menu.glade.h
data/glade/account_creation_wizard_window.glade.h
data/glade/account_modification_window.glade.h
data/glade/accounts_window.glade.h
data/glade/add_new_contact_window.glade.h
data/glade/advanced_configuration_window.glade.h
data/glade/advanced_menuitem_menu.glade.h
data/glade/advanced_notifications_window.glade.h
data/glade/change_password_dialog.glade.h
data/glade/change_status_message_dialog.glade.h
data/glade/chat_context_menu.glade.h
data/glade/chat_control_popup_menu.glade.h
data/glade/choose_gpg_key_dialog.glade.h
data/glade/data_form_window.glade.h
data/glade/edit_groups_dialog.glade.h
data/glade/filetransfers.glade.h
data/glade/gajim_themes_window.glade.h
data/glade/gc_control_popup_menu.glade.h
data/glade/gc_occupants_menu.glade.h
data/glade/history_manager.glade.h
data/glade/history_window.glade.h
data/glade/input_dialog.glade.h
data/glade/invitation_received_dialog.glade.h
data/glade/join_groupchat_window.glade.h
data/glade/manage_accounts_window.glade.h
data/glade/manage_bookmarks_window.glade.h
data/glade/manage_proxies_window.glade.h
data/glade/message_window.glade.h
data/glade/passphrase_dialog.glade.h
data/glade/popup_notification_window.glade.h
data/glade/preferences_window.glade.h
data/glade/privacy_list_edit_window.glade.h
data/glade/privacy_lists_first_window.glade.h
data/glade/progress_dialog.glade.h
data/glade/remove_account_window.glade.h
data/glade/roster_contact_context_menu.glade.h
data/glade/roster_window.glade.h
data/glade/service_discovery_window.glade.h
data/glade/service_registration_window.glade.h
data/glade/single_message_window.glade.h
data/glade/subscription_request_window.glade.h
data/glade/systray_context_menu.glade.h
data/glade/vcard_information_window.glade.h
data/glade/xml_console_window.glade.h
src/advanced.py
src/cell_renderer_image.py
src/chat_control.py

2369
po/de.po

File diff suppressed because it is too large Load diff

View file

@ -2622,7 +2622,7 @@ msgstr "Fine ne malpli ol aliajn ni dankas ĉiujn pakaĵvartistojn."
#. here you write your name in the form Name FamilyName <someone@somewhere>
#: ../src/dialogs.py:610
msgid "translator-credits"
msgstr "tradukistoj"
msgstr "Segrio Ĥliutĉin <Sergey.Khlutchin@gmail.com>"
#: ../src/dialogs.py:873
#, python-format

1761
po/es.po

File diff suppressed because it is too large Load diff

1998
po/eu.po

File diff suppressed because it is too large Load diff

5267
po/hr.po Normal file

File diff suppressed because it is too large Load diff

View file

@ -1982,7 +1982,7 @@ msgstr "Extra adresa:"
#. Family Name
#: ../data/glade/vcard_information_window.glade.h:15
msgid "Family:"
msgstr "Priezvysko:"
msgstr "Priezvisko:"
#: ../data/glade/vcard_information_window.glade.h:16
msgid "Format: YYYY-MM-DD"

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
## gajim
##
## Contributors for this file:
@ -29,4 +29,4 @@ fi
cd PREFIX/share/gajim/src
export PYTHONPATH="$PYTHONPATH:PREFIXLIB/gajim"
exec PYTHON_EXEC -OO gajim.py $@
exec -a gajim PYTHON_EXEC -OO gajim.py $@

View file

@ -1,21 +1,24 @@
# Set the C flags to include the GTK+ and Python libraries
PYTHON ?= python
PYTHONVER = `$(PYTHON) -c 'import sys; print sys.version[:3]'`
CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -fPIC -I/usr/include/python$(PYTHONVER) -I.
LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0` -lpython$(PYTHONVER)
gtk_CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -fPIC -I/usr/include/python$(PYTHONVER) -I.
gtk_LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0` -lpython$(PYTHONVER)
all: trayicon.so gtkspell.so
# Build the shared objects
trayicon.so: trayicon.o eggtrayicon.o trayiconmodule.o
$(CC) -shared $^ -o $@ $(LDFLAGS)
$(CC) -shared $^ -o $@ $(LDFLAGS) $(gtk_LDFLAGS)
gtkspell.so:
$(CC) $(OPTFLAGS) $(CFLAGS) `pkg-config --cflags gtkspell-2.0` -shared gtkspellmodule.c $^ -o $@ $(LDFLAGS) `pkg-config --libs gtkspell-2.0`
$(CC) $(OPTFLAGS) $(CFLAGS) $(LDFLAGS) $(gtk_CFLAGS) $(gtk_LDFLAGS) `pkg-config --libs --cflags gtkspell-2.0` -shared gtkspellmodule.c $^ -o $@
# The path to the GTK+ python types
DEFS=`pkg-config --variable=defsdir pygtk-2.0`
%.o: %.c
$(CC) -o $@ -c $< $(CFLAGS) $(gtk_CFLAGS)
# Generate the C wrapper from the defs and our override file
trayicon.c: trayicon.defs trayicon.override
pygtk-codegen-2.0 --prefix trayicon \

View file

@ -46,6 +46,8 @@ class AdvancedConfigurationWindow:
def __init__(self):
self.xml = gtkgui_helpers.get_glade('advanced_configuration_window.glade')
self.window = self.xml.get_widget('advanced_configuration_window')
self.window.set_transient_for(
gajim.interface.instances['preferences'].window)
self.entry = self.xml.get_widget('advanced_entry')
self.desc_label = self.xml.get_widget('advanced_desc_label')
self.restart_label = self.xml.get_widget('restart_label')

View file

@ -24,6 +24,7 @@ import gtkgui_helpers
import message_control
import dialogs
import history_window
import notify
from common import gajim
from common import helpers
@ -41,6 +42,14 @@ except:
HAS_GTK_SPELL = False
# the next script, executed in the "po" directory,
# generates the following list.
##!/bin/sh
#LANG=$(for i in *.po; do j=${i/.po/}; echo -n "_('"$j"')":" '"$j"', " ; done)
#echo "{_('en'):'en'",$LANG"}"
langs = {_('English'): 'en', _('Bulgarian'): 'bg', _('Briton'): 'br', _('Czech'): 'cs', _('German'): 'de', _('Greek'): 'el', _('Esperanto'): 'eo', _('Spanish'): 'es', _('Basc'): 'eu', _('French'): 'fr', _('Croatian'): 'hr', _('Italian'): 'it', _('Norvegian b'): 'nb', _('Dutch'): 'nl', _('Norvegian'): 'no', _('Polish'): 'pl', _('Portuguese'): 'pt', _('Brazilian Portuguese'): 'pt_BR', _('Russian'): 'ru', _('Slovak'): 'sk', _('Swedish'): 'sv', _('Chinese (Ch)'): 'zh_CN'}
################################################################################
class ChatControlBase(MessageControl):
'''A base class containing a banner, ConversationTextview, MessageTextView
@ -50,7 +59,7 @@ class ChatControlBase(MessageControl):
theme = gajim.config.get('roster_theme')
bannerfont = gajim.config.get_per('themes', theme, 'bannerfont')
bannerfontattrs = gajim.config.get_per('themes', theme, 'bannerfontattrs')
if bannerfont:
font = pango.FontDescription(bannerfont)
else:
@ -61,16 +70,24 @@ class ChatControlBase(MessageControl):
font.set_weight(pango.WEIGHT_HEAVY)
if 'I' in bannerfontattrs:
font.set_style(pango.STYLE_ITALIC)
font_attrs = 'font_desc="%s"' % font.to_string()
# in case there is no font specified we use x-large font size
if font.get_size() == 0:
font_attrs = '%s size="x-large"' % font_attrs
font.set_weight(pango.WEIGHT_NORMAL)
font_attrs_small = 'font_desc="%s" size="small"' % font.to_string()
return (font_attrs, font_attrs_small)
def get_nb_unread(self):
jid = self.contact.jid
if self.resource:
jid += '/' + self.resource
type_ = self.type_id
return len(gajim.events.get_events(self.account, jid, ['printed_' + type_,
type_]))
def draw_banner(self):
self._paint_banner()
self._update_banner_state_image()
@ -99,7 +116,7 @@ class ChatControlBase(MessageControl):
widget = self.xml.get_widget('emoticons_button')
id = widget.connect('clicked', self.on_emoticons_button_clicked)
self.handlers[id] = widget
id = self.widget.connect('key_press_event', self._on_keypress_event)
self.handlers[id] = self.widget
@ -107,10 +124,10 @@ class ChatControlBase(MessageControl):
id = widget.connect('button-press-event',
self._on_banner_eventbox_button_press_event)
self.handlers[id] = widget
# Create textviews and connect signals
self.conv_textview = ConversationTextview(self.account)
self.conv_scrolledwindow = self.xml.get_widget(
'conversation_scrolledwindow')
self.conv_scrolledwindow.add(self.conv_textview.tv)
@ -122,20 +139,23 @@ class ChatControlBase(MessageControl):
self.msg_scrolledwindow = self.xml.get_widget('message_scrolledwindow')
self.msg_textview = MessageTextView()
id = self.msg_textview.connect('mykeypress',
self._on_message_textview_mykeypress_event)
self._on_message_textview_mykeypress_event)
self.handlers[id] = self.msg_textview
self.msg_scrolledwindow.add(self.msg_textview)
id = self.msg_textview.connect('key_press_event',
self._on_message_textview_key_press_event)
self._on_message_textview_key_press_event)
self.handlers[id] = self.msg_textview
id = self.msg_textview.connect('size-request', self.size_request)
self.handlers[id] = self.msg_textview
id = self.msg_textview.connect('populate_popup',
self.on_msg_textview_populate_popup)
self.handlers[id] = self.msg_textview
self.update_font()
# Hook up send button
widget = self.xml.get_widget('send_button')
id = widget.connect('clicked',
self._on_send_button_clicked)
id = widget.connect('clicked', self._on_send_button_clicked)
self.handlers[id] = widget
# the following vars are used to keep history of user's messages
@ -144,8 +164,6 @@ class ChatControlBase(MessageControl):
self.typing_new = False
self.orig_msg = ''
self.nb_unread = 0
# Emoticons menu
# set image no matter if user wants at this time emoticons or not
# (so toggle works ok)
@ -157,8 +175,27 @@ class ChatControlBase(MessageControl):
# Attach speller
if gajim.config.get('use_speller') and HAS_GTK_SPELL:
try:
gtkspell.Spell(self.msg_textview)
except gobject.GError, msg:
spell = gtkspell.Spell(self.msg_textview)
# loop removing non-existant dictionaries
# iterating on a copy
for lang in dict(langs):
try:
spell.set_language(langs[lang])
except:
del langs[lang]
# now set the one the user selected
per_type = 'contacts'
if self.type_id == message_control.TYPE_GC:
per_type = 'rooms'
lang = gajim.config.get_per(per_type, self.contact.jid,
'speller_language')
if not lang:
# use the default one
lang = gajim.config.get('speller_language')
if lang:
self.msg_textview.lang = lang
spell.set_language(lang)
except (gobject.GError, RuntimeError), msg:
#FIXME: add a ui for this use spell.set_language()
dialogs.ErrorDialog(unicode(msg), _('If that is not your language '
'for which you want to highlight misspelled words, then please '
@ -174,6 +211,44 @@ class ChatControlBase(MessageControl):
# For JEP-0172
self.user_nick = None
def on_msg_textview_populate_popup(self, textview, menu):
'''we override the default context menu and we prepend an option to switch languages'''
def _on_select_dictionary(widget, lang):
per_type = 'contacts'
if self.type_id == message_control.TYPE_GC:
per_type = 'rooms'
if not gajim.config.get_per(per_type, self.contact.jid):
gajim.config.add_per(per_type, self.contact.jid)
gajim.config.set_per(per_type, self.contact.jid, 'speller_language',
lang)
spell = gtkspell.get_from_text_view(self.msg_textview)
self.msg_textview.lang = lang
spell.set_language(lang)
widget.set_active(True)
item = gtk.SeparatorMenuItem()
menu.prepend(item)
if gajim.config.get('use_speller') and HAS_GTK_SPELL:
item = gtk.MenuItem(_('Spelling language'))
menu.prepend(item)
submenu = gtk.Menu()
item.set_submenu(submenu)
for lang in sorted(langs):
item = gtk.CheckMenuItem(lang)
if langs[lang] == self.msg_textview.lang:
item.set_active(True)
submenu.append(item)
id = item.connect('activate', _on_select_dictionary, langs[lang])
self.handlers[id] = item
item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
menu.prepend(item)
id = item.connect('activate', self.msg_textview.clear)
self.handlers[id] = item
menu.show_all()
# moved from ChatControl
def _on_banner_eventbox_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
@ -250,7 +325,7 @@ class ChatControlBase(MessageControl):
if event.state & gtk.gdk.CONTROL_MASK:
# CTRL + l|L: clear conv_textview
if event.keyval == gtk.keysyms.l or event.keyval == gtk.keysyms.L:
self.conv_textview.tv.get_buffer().set_text('')
self.conv_textview.clear()
return True
# CTRL + v: Paste into msg_textview
elif event.keyval == gtk.keysyms.v:
@ -477,12 +552,25 @@ class ChatControlBase(MessageControl):
gajim.last_message_time[self.account][full_jid] = time.time()
urgent = True
if (not self.parent_win.get_active_jid() or \
full_jid != self.parent_win.get_active_jid() or \
not self.parent_win.is_active() or not end) and \
kind in ('incoming', 'incoming_queue'):
self.nb_unread += 1
if gajim.interface.systray_enabled and self.notify_on_new_messages():
gajim.interface.systray.add_jid(full_jid, self.account, self.type_id)
full_jid != self.parent_win.get_active_jid() or \
not self.parent_win.is_active() or not end) and \
kind in ('incoming', 'incoming_queue'):
if self.notify_on_new_messages():
type_ = 'printed_' + self.type_id
if self.type_id == message_control.TYPE_GC:
type_ = 'printed_gc_msg'
show_in_roster = notify.get_show_in_roster('message_received',
self.account, self.contact)
show_in_systray = notify.get_show_in_systray('message_received',
self.account, self.contact)
event = gajim.events.create_event(type_, None,
show_in_roster = show_in_roster,
show_in_systray = show_in_systray)
gajim.events.add_event(self.account, full_jid, event)
# We need to redraw contact if we show in roster
if show_in_roster:
gajim.interface.roster.draw_contact(self.contact.jid,
self.account)
self.parent_win.redraw_tab(self)
if not self.parent_win.is_active():
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid,
@ -507,6 +595,7 @@ class ChatControlBase(MessageControl):
else: # we are the beginning of buffer
buffer.insert_at_cursor('%s ' % str_)
self.msg_textview.grab_focus()
def on_emoticons_button_clicked(self, widget):
'''popup emoticons menu'''
gajim.interface.emoticon_menuitem_clicked = self.append_emoticon
@ -551,15 +640,24 @@ class ChatControlBase(MessageControl):
if state:
jid = self.contact.jid
if self.conv_textview.at_the_end():
#we are at the end
if self.nb_unread > 0:
self.nb_unread = self.get_specific_unread()
# we are at the end
type_ = 'printed_' + self.type_id
if self.type_id == message_control.TYPE_GC:
type_ = 'printed_gc_msg'
if not gajim.events.remove_events(self.account, self.get_full_jid(),
types = [type_]):
# There were events to remove
self.parent_win.redraw_tab(self)
self.parent_win.show_title()
if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(self.get_full_jid(),
self.account,
self.type_id)
# redraw roster
if self.type_id == message_control.TYPE_PM:
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
groupchat_control = gajim.interface.msg_win_mgr.get_control(
room_jid, self.account)
groupchat_control.draw_contact(nick)
else:
gajim.interface.roster.draw_contact(jid, self.account)
gajim.interface.roster.show_title()
self.msg_textview.grab_focus()
# Note, we send None chatstate to preserve current
self.parent_win.redraw_tab(self)
@ -632,22 +730,28 @@ class ChatControlBase(MessageControl):
return True
def on_conversation_vadjustment_value_changed(self, widget):
if not self.nb_unread:
return
if self.resource:
jid = self.contact.get_full_jid()
else:
jid = self.contact.jid
type_ = self.type_id
if type_ == message_control.TYPE_GC:
type_ = 'gc_msg'
if not len(gajim.events.get_events(self.account, jid, ['printed_' + type_,
type_])):
return
if self.conv_textview.at_the_end() and \
self.parent_win.get_active_control() == self and \
self.parent_win.window.is_active():
#we are at the end
self.nb_unread = self.get_specific_unread()
self.parent_win.redraw_tab(self)
self.parent_win.show_title()
if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(jid, self.account,
self.type_id)
# we are at the end
type_ = self.type_id
if type_ == message_control.TYPE_GC:
type_ = 'gc_msg'
if not gajim.events.remove_events(self.account, self.get_full_jid(),
types = ['printed_' + type_, type_]):
# There were events to remove
self.parent_win.redraw_tab(self)
self.parent_win.show_title()
def sent_messages_scroll(self, direction, conv_buf):
size = len(self.sent_history)
@ -706,6 +810,7 @@ class ChatControlBase(MessageControl):
def got_disconnected(self):
self.msg_textview.set_sensitive(False)
self.msg_textview.set_editable(False)
self.conv_textview.tv.grab_focus()
self.xml.get_widget('send_button').set_sensitive(False)
################################################################################
@ -776,6 +881,8 @@ class ChatControl(ChatControlBase):
self.update_ui()
# restore previous conversation
self.restore_conversation()
# is account displayed after nick in banner ?
self.account_displayed= False
def notify_on_new_messages(self):
return gajim.config.get('trayicon_notification_on_new_messages')
@ -898,17 +1005,27 @@ class ChatControl(ChatControlBase):
if self.resource:
name += '/' + self.resource
avoid_showing_account_too = True
if self.TYPE_ID == message_control.TYPE_PM:
room_jid = self.contact.jid.split('/')[0]
room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid,
self.account)
name = _('%s from room %s') % (name, room_ctrl.name)
name = gtkgui_helpers.escape_for_pango_markup(name)
# We know our contacts nick, but if there are any other controls
# with the same nick we need to also display the account
# except if we are talking to two different resources of the same contact
acct_info = ''
self.account_displayed = False
for ctrl in self.parent_win.controls():
if ctrl == self:
continue
if self.contact.get_shown_name() == ctrl.contact.get_shown_name()\
and not avoid_showing_account_too:
self.account_displayed = True
if not ctrl.account_displayed:
# do that after this instance exists
gobject.idle_add(ctrl.draw_banner)
acct_info = ' (%s)' % \
gtkgui_helpers.escape_for_pango_markup(self.account)
break
@ -925,9 +1042,9 @@ class ChatControl(ChatControlBase):
if cs and st in ('composing_only', 'all'):
if contact.show == 'offline':
chatstate = ''
elif st == 'all' and contact.composing_jep == 'JEP-0085':
elif contact.composing_jep == 'JEP-0085':
chatstate = helpers.get_uf_chatstate(cs)
elif st == 'composing_only' or contact.composing_jep == 'JEP-0022':
elif contact.composing_jep == 'JEP-0022':
if cs in ('composing', 'paused'):
# only print composing, paused
chatstate = helpers.get_uf_chatstate(cs)
@ -935,16 +1052,16 @@ class ChatControl(ChatControlBase):
chatstate = ''
elif chatstate is None:
chatstate = helpers.get_uf_chatstate(cs)
label_text = '<span %s>%s</span><span %s>%s %s</span>' % \
(font_attrs, name, font_attrs_small, acct_info, chatstate)
(font_attrs, name, font_attrs_small, acct_info, chatstate)
else:
# weight="heavy" size="x-large"
label_text = '<span %s>%s</span><span %s>%s</span>' % \
(font_attrs, name, font_attrs_small, acct_info)
(font_attrs, name, font_attrs_small, acct_info)
if status_escaped:
label_text += '\n<span %s>%s</span>' %\
(font_attrs_small, status_escaped)
(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()
@ -952,7 +1069,7 @@ class ChatControl(ChatControlBase):
self.status_tooltip.disable()
# setup the label that holds name and jid
banner_name_label.set_markup(label_text)
def on_toggle_gpg_togglebutton(self, widget):
gajim.config.set_per('contacts', self.contact.get_full_jid(),
'gpg_enabled', widget.get_active())
@ -967,12 +1084,12 @@ class ChatControl(ChatControlBase):
tt = _('OpenPGP Encryption')
# restore gpg pref
gpg_pref = gajim.config.get_per('contacts',
self.contact.get_full_jid(), 'gpg_enabled')
gpg_pref = gajim.config.get_per('contacts', self.contact.jid,
'gpg_enabled')
if gpg_pref == None:
gajim.config.add_per('contacts', self.contact.get_full_jid())
gpg_pref = gajim.config.get_per('contacts',
self.contact.get_full_jid(), 'gpg_enabled')
gajim.config.add_per('contacts', self.contact.jid)
gpg_pref = gajim.config.get_per('contacts', self.contact.jid,
'gpg_enabled')
tb.set_active(gpg_pref)
else:
@ -1132,7 +1249,12 @@ class ChatControl(ChatControlBase):
def get_tab_label(self, chatstate):
unread = ''
num_unread = self.nb_unread
if self.resource:
jid = self.contact.get_full_jid()
else:
jid = self.contact.jid
num_unread = len(gajim.events.get_events(self.account, jid,
['printed_' + self.type_id, self.type_id]))
if num_unread == 1 and not gajim.config.get('show_unread_tab_icon'):
unread = '*'
elif num_unread > 1:
@ -1175,7 +1297,12 @@ class ChatControl(ChatControlBase):
return (label_str, color)
def get_tab_image(self):
num_unread = self.nb_unread
if self.resource:
jid = self.contact.get_full_jid()
else:
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
tab_img = None
@ -1184,8 +1311,8 @@ class ChatControl(ChatControlBase):
self.contact.jid, icon_name = 'message')
tab_img = img_16['message']
else:
contact = gajim.contacts.get_contact_with_highest_priority(self.account,
self.contact.jid)
contact = gajim.contacts.get_contact_with_highest_priority(
self.account, self.contact.jid)
if not contact or self.resource:
# For transient contacts
contact = self.contact
@ -1361,10 +1488,9 @@ class ChatControl(ChatControlBase):
# Remove bigger avatar window
if self.bigger_avatar_window:
self.bigger_avatar_window.destroy()
# Clean up systray
if gajim.interface.systray_enabled and self.nb_unread > 0:
gajim.interface.systray.remove_jid(self.contact.jid, self.account,
self.type_id)
# Clean events
gajim.events.remove_events(self.account, self.get_full_jid(),
types = ['printed_' + self.type_id, self.type_id])
# remove all register handlers on wigets, created by self.xml
# to prevent circular references among objects
for i in self.handlers.keys():
@ -1381,7 +1507,7 @@ class ChatControl(ChatControlBase):
# 2 seconds
dialog = dialogs.ConfirmationDialog(
#%s is being replaced in the code with JID
_('You just received a new message from "%s"' % self.contact.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:
@ -1466,14 +1592,11 @@ class ChatControl(ChatControlBase):
if restore_how_many <= 0:
return
timeout = gajim.config.get('restore_timeout') # in minutes
# number of messages that are in queue and are already logged
pending_how_many = 0 # we want to avoid duplication
if gajim.awaiting_events[self.account].has_key(jid):
events = gajim.awaiting_events[self.account][jid]
for event in events:
if event[0] == 'chat':
pending_how_many += 1
events = gajim.events.get_events(self.account, jid, ['chat'])
# number of messages that are in queue and are already logged, we want
# to avoid duplication
pending_how_many = len(events)
rows = gajim.logger.get_last_conversation_lines(jid, restore_how_many,
pending_how_many, timeout, self.account)
@ -1492,10 +1615,14 @@ class ChatControl(ChatControlBase):
tim = time.localtime(float(row[0]))
if gajim.config.get('restored_messages_small'):
small_attr = ['small']
else:
small_attr = []
ChatControlBase.print_conversation_line(self, row[2], kind, name, tim,
['small'],
['small', 'restored_message'],
['small', 'restored_message'],
small_attr,
small_attr + ['restored_message'],
small_attr + ['restored_message'],
False, old_kind = local_old_kind)
if row[2].startswith('/me ') or row[2].startswith('/me\n'):
local_old_kind = None
@ -1510,7 +1637,7 @@ class ChatControl(ChatControlBase):
jid_with_resource = jid
if self.resource:
jid_with_resource += '/' + self.resource
l = gajim.awaiting_events[self.account][jid_with_resource]
events = gajim.events.get_events(self.account, jid_with_resource)
# Is it a pm ?
is_pm = False
@ -1518,15 +1645,12 @@ class ChatControl(ChatControlBase):
control = gajim.interface.msg_win_mgr.get_control(room_jid, self.account)
if control and control.type_id == message_control.TYPE_GC:
is_pm = True
events_to_keep = []
# list of message ids which should be marked as read
message_ids = []
for event in l:
typ = event[0]
if typ != 'chat':
events_to_keep.append(event)
for event in events:
if event.type_ != self.type_id:
continue
data = event[1]
data = event.parameters
kind = data[2]
if kind == 'error':
kind = 'info'
@ -1536,33 +1660,31 @@ class ChatControl(ChatControlBase):
encrypted = data[4], subject = data[1])
if len(data) > 6 and isinstance(data[6], int):
message_ids.append(data[6])
# remove from gc nb_unread if it's pm or from roster
if is_pm:
control.nb_unread -= 1
else:
gajim.interface.roster.nb_unread -= 1
if message_ids:
gajim.logger.set_read_messages(message_ids)
if is_pm:
control.parent_win.show_title()
else:
gajim.interface.roster.show_title()
# Keep only non-messages events
if len(events_to_keep):
gajim.awaiting_events[self.account][jid_with_resource] = events_to_keep
else:
del gajim.awaiting_events[self.account][jid_with_resource]
gajim.events.remove_events(self.account, jid_with_resource,
types = [self.type_id])
self.parent_win.show_title()
self.parent_win.redraw_tab(self)
# redraw roster
gajim.interface.roster.show_title()
typ = 'chat' # Is it a normal chat or a pm ?
# reset to status image in gc if it is a pm
if is_pm:
control.update_ui()
typ = 'pm'
gajim.interface.roster.draw_contact(jid, self.account)
if is_pm:
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
groupchat_control = gajim.interface.msg_win_mgr.get_control(
room_jid, self.account)
groupchat_control.draw_contact(nick)
else:
gajim.interface.roster.draw_contact(jid, self.account)
# Redraw parent too
gajim.interface.roster.draw_parent_contact(jid, self.account)
if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(jid_with_resource, self.account, typ)
if (self.contact.show == 'offline' or self.contact.show == 'error'):
showOffline = gajim.config.get('showoffline')
if not showOffline and typ == 'chat' and \
@ -1592,7 +1714,7 @@ class ChatControl(ChatControlBase):
# It's why I set it transparent.
image = self.xml.get_widget('avatar_image')
pixbuf = image.get_pixbuf()
pixbuf.fill(0xffffff00) # RGBA
pixbuf.fill(0xffffff00L) # RGBA
image.queue_draw()
screen_w = gtk.gdk.screen_width()

View file

@ -138,7 +138,7 @@ else:
def verify(self, str, sign):
if not USE_GPG:
return str
if not str:
if str == None:
return ''
f = tmpfile()
fd = f.fileno()

View file

@ -6,19 +6,19 @@ HAVE_XSCRNSAVER = $(shell pkg-config --exists xscrnsaver && echo 'YES')
ifeq ($(HAVE_XSCRNSAVER),YES)
# We link with libXScrnsaver from modular X.Org X11
CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0 xscrnsaver` -fpic -I/usr/include/python$(PYTHONVER) -I.
LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0 xscrnsaver` -lpython$(PYTHONVER)
gtk_and_x_CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0 xscrnsaver` -fpic -I/usr/include/python$(PYTHONVER) -I.
gtk_and_x_LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0 xscrnsaver` -lpython$(PYTHONVER)
else
# # We link with libXScrnsaver from monolithic X.Org X11
CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -fpic -I/usr/include/python$(PYTHONVER) -I.
LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0` -L/usr/X11R6$(LIBDIR) -lX11 \
-lXss -lXext -lpython$(PYTHONVER)
gtk_and_x_CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -fpic -I/usr/include/python$(PYTHONVER) -I.
gtk_and_x_LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0` \
-L/usr/X11R6$(LIBDIR) -lX11 -lXss -lXext -lpython$(PYTHONVER)
endif
all: idle.so
idle.so:
$(CC) $(OPTFLAGS) $(CFLAGS) $(LDFLAGS) -shared idle.c $^ -o $@
$(CC) $(OPTFLAGS) $(CFLAGS) $(LDFLAGS) $(gtk_and_x_CFLAGS) $(gtk_and_x_LDFLAGS) -shared idle.c $^ -o $@
clean:
rm -f *.so

View file

@ -57,6 +57,11 @@ def create_log_db():
jid_id INTEGER
);
CREATE TABLE transports_cache (
transport TEXT UNIQUE,
type INTEGER
);
CREATE TABLE logs(
log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
jid_id INTEGER,

View file

@ -48,7 +48,7 @@ class Config:
'notify_on_signout': [ opt_bool, False ],
'notify_on_new_message': [ opt_bool, True ],
'autopopupaway': [ opt_bool, False ],
'use_notif_daemon': [ opt_bool, True , _('Use DBus and Notification-Daemon to show notifications') ],
'use_notif_daemon': [ opt_bool, True , _('Use D-Bus and Notification-Daemon to show notifications') ],
'ignore_unknown_contacts': [ opt_bool, False ],
'showoffline': [ opt_bool, False, '', True ],
'autoaway': [ opt_bool, True ],
@ -74,12 +74,13 @@ class Config:
'statusmsgcolor': [ opt_color, '#1eaa1e', '', True ],
'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 ],
'collapsed_rows': [ opt_str, '', _('List (space separated) of rows (accounts and groups) that are collapsed.'), True ],
'roster_theme': [ opt_str, 'gtk+', '', True ],
'saveposition': [ opt_bool, True ],
'mergeaccounts': [ opt_bool, False, '', True ],
'sort_by_show': [ opt_bool, True, '', True ],
'use_speller': [ opt_bool, False, ],
'speller_language': [ opt_str, '', _('Language used by speller')],
'print_time': [ opt_str, 'always', _('\'always\' - print time for every message.\n\'sometimes\' - print time every print_ichat_every_foo_minutes minute.\n\'never\' - never print time.')],
'print_time_fuzzy': [ opt_int, 0, _('Value of fuzziness from 1 to 4 or 0 to disable fuzzyclock. 1 is the most precise clock, 4 the less precise one.') ],
'emoticons_theme': [opt_str, 'static', '', True ],
@ -95,8 +96,8 @@ class Config:
'custommailapp': [ opt_str, 'mozilla-thunderbird -compose' ],
'custom_file_manager': [ opt_str, 'xffm' ],
'gc-hpaned-position': [opt_int, 430],
'gc_refer_to_nick_char': [opt_str, ',', _('Character to add after nickname when using nick completion (tab) in group chat')],
'gc_proposed_nick_char': [opt_str, '_', _('Character to propose to add after desired nickname when desired nickname is used by someone else in group chat')],
'gc_refer_to_nick_char': [opt_str, ',', _('Character to add after nickname when using nick completion (tab) in group chat.')],
'gc_proposed_nick_char': [opt_str, '_', _('Character to propose to add after desired nickname when desired nickname is used by someone else in group chat.')],
'msgwin-x-position': [opt_int, -1], # Default is to let the window manager decide
'msgwin-y-position': [opt_int, -1], # Default is to let the window manager decide
'msgwin-width': [opt_int, 500],
@ -134,7 +135,7 @@ class Config:
'send_on_ctrl_enter': [opt_bool, False, _('Send message on Ctrl+Enter and with Enter make new line (Mirabilis ICQ Client default behaviour).')],
'show_roster_on_startup': [opt_bool, True],
'key_up_lines': [opt_int, 25, _('How many lines to store for Ctrl+KeyUP.')],
'version': [ opt_str, '0.10.1.2' ], # which version created the config
'version': [ opt_str, '0.10.1.3' ], # which version created the config
'search_engine': [opt_str, 'http://www.google.com/search?&q=%s&sourceid=gajim'],
'dictionary_url': [opt_str, 'WIKTIONARY', _("Either custom url with %s in it where %s is the word/phrase or 'WIKTIONARY' which means use wiktionary.")],
'always_english_wikipedia': [opt_bool, False],
@ -142,7 +143,7 @@ class Config:
'remote_control': [opt_bool, True, _('If checked, Gajim can be controlled remotely using gajim-remote.'), True],
'chat_state_notifications': [opt_str, 'all'], # 'all', 'composing_only', 'disabled'
'autodetect_browser_mailer': [opt_bool, False, '', True],
'print_ichat_every_foo_minutes': [opt_int, 5, _('When not printing time for every message (print_time==sometimes), print it every x minutes')],
'print_ichat_every_foo_minutes': [opt_int, 5, _('When not printing time for every message (print_time==sometimes), print it every x minutes.')],
'confirm_close_muc': [opt_bool, True, _('Ask before closing a group chat tab/window.')],
'confirm_close_muc_rooms': [opt_str, '', _('Always ask before closing group chat tab/window in this space separated list of room jids.')],
'noconfirm_close_muc_rooms': [opt_str, '', _('Never ask before closing group chat tab/window in this space separated list of room jids.')],
@ -177,31 +178,33 @@ class Config:
'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.')],
'set_xmpp://_handler_everytime': [opt_bool, False, _('If True, Gajim registers for xmpp:// 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.')],
'show_status_msgs_in_roster': [opt_bool, True, _('If True, Gajim will display the status message, if not empty, for every contact under the contact name in roster window'), True],
'show_status_msgs_in_roster': [opt_bool, True, _('If True, Gajim will display the status message, if not empty, for every contact under the contact name in roster window.'), True],
'show_avatars_in_roster': [opt_bool, True, '', True],
'ask_avatars_on_startup': [opt_bool, True, _('If True, Gajim will ask for avatar each contact that did not have an avatar last time or has one cached that is too old.')],
'print_status_in_chats': [opt_bool, True, _('If False, Gajim will no longer print status line in chats when a contact changes his or her status and/or his or her status message.')],
'print_status_in_muc': [opt_str, 'in_and_out', _('can be "none", "all" or "in_and_out". If "none", Gajim will no longer print status line in groupchats when a member changes his or her status and/or his or her status message. If "all" Gajim will print all status messages. If "in_and_out", gajim will only print FOO enters/leaves room')],
'print_status_in_muc': [opt_str, 'in_and_out', _('can be "none", "all" or "in_and_out". If "none", Gajim will no longer print status line in groupchats when a member changes his or her status and/or his or her status message. If "all" Gajim will print all status messages. If "in_and_out", gajim will only print FOO enters/leaves room.')],
'log_contact_status_changes': [opt_bool, False],
'restored_messages_color': [opt_str, 'grey'],
'restored_messages_small': [opt_bool, True, _('If True, restored messages will use a smaller font than the default one.')],
'hide_avatar_of_transport': [opt_bool, False, _('Don\'t show avatar for the transport itself.')],
'roster_window_skip_taskbar': [opt_bool, False],
'use_urgency_hint': [opt_bool, True, _('If True and installed GTK+ and PyGTK versions are at least 2.8, make the window flash (the default behaviour in most Window Managers) when holding pending events.')],
'notification_timeout': [opt_int, 5],
'send_sha_in_gc_presence': [opt_bool, True, _('Jabberd1.4 does not like sha info when one join a password protected room. Turn this option to False to stop sending sha info in groupchat presences')],
'send_sha_in_gc_presence': [opt_bool, True, _('Jabberd1.4 does not like sha info when one join a password protected room. Turn this option to False to stop sending sha info in group chat presences.')],
'one_message_window': [opt_str, 'always',
#always, never, peracct, pertype should not be translated
_('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')],
_('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.')],
'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 room occupants list in groupchat window')],
'chat_merge_consecutive_nickname': [opt_bool, False, _('Merge consecutive nickname in chat window')],
'chat_merge_consecutive_nickname_indent': [opt_str, ' ', _('Indentation when using merge consecutive nickame')],
'gc_nicknames_colors': [ opt_str, '#a34526:#c000ff:#0012ff:#388a99:#38995d:#519938:#ff8a00:#94452d:#244b5a:#32645a', _('List of colors that will be used to color nicknames in groupchats'), True ],
'hide_groupchat_occupants_list': [opt_bool, False, _('Hides the room occupants list in group chat window.')],
'chat_merge_consecutive_nickname': [opt_bool, False, _('Merge consecutive nickname in chat window.')],
'chat_merge_consecutive_nickname_indent': [opt_str, ' ', _('Indentation when using merge consecutive nickame.')],
'gc_nicknames_colors': [ opt_str, '#a34526:#c000ff:#0012ff:#388a99:#38995d:#519938:#ff8a00:#94452d:#244b5a:#32645a', _('List of colors that will be used to color nicknames in group chats.'), True ],
'ctrl_tab_go_to_next_composing': [opt_bool, True, _('Ctrl-Tab go to next composing tab when none is unread.')],
}
__options_per_key = {
@ -247,6 +250,10 @@ class Config:
'statusmsg': ({
'message': [ opt_str, '' ],
}, {}),
'defaultstatusmsg': ({
'enabled': [ opt_bool, False ],
'message': [ opt_str, '' ],
}, {}),
'soundevents': ({
'enabled': [ opt_bool, True ],
'path': [ opt_str, '' ],
@ -289,7 +296,11 @@ class Config:
'state_muc_directed_msg_color': [ opt_color, 'red2' ],
}, {}),
'contacts': ({
'gpg_enabled': [ opt_bool, True ],
'gpg_enabled': [ opt_bool, True, _('Is OpenPGP enabled for this contact?')],
'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')],
}, {}),
'rooms': ({
'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')],
}, {}),
'notifications': ({
'event': [opt_str, ''],
@ -319,6 +330,16 @@ class Config:
_('Out'): _("I'm out enjoying life"),
}
defaultstatusmsg_default = {
'online': [ False, _("I'm available") ],
'chat': [ False, _("I'm free for chat") ],
'away': [ False, _('Be right back') ],
'xa': [ False, _("I'm not available") ],
'dnd': [ False, _('Do not disturb') ],
'invisible': [ False, _('Bye!') ],
'offline': [ False, _('Bye!') ],
}
soundevents_default = {
'first_message_received': [ True, '../data/sounds/message1.wav' ],
'next_message_received': [ True, '../data/sounds/message2.wav' ],
@ -532,3 +553,8 @@ class Config:
self.set_per('soundevents', event, 'enabled', default[0])
self.set_per('soundevents', event, 'path', default[1])
for status in self.defaultstatusmsg_default:
default = self.defaultstatusmsg_default[status]
self.add_per('defaultstatusmsg', status)
self.set_per('defaultstatusmsg', status, 'enabled', default[0])
self.set_per('defaultstatusmsg', status, 'message', default[1])

View file

@ -84,18 +84,12 @@ class Connection(ConnectionHandlers):
self.on_connect_failure = None
self.retrycount = 0
self.jids_for_auto_auth = [] # list of jid to auto-authorize
self.muc_jid = {} # jid of muc server for each transport type
self.available_transports = {} # list of available transports on this
# server {'icq': ['icq.server.com', 'icq2.server.com'], }
self.vcard_supported = True
# END __init__
def build_user_nick(self, user_nick):
df = common.xmpp.DataForm(typ = 'result')
field = df.setField('FORM_TYPE')
field.setType('hidden')
field.setValue(common.xmpp.NS_PROFILE)
field = df.setField('nickname')
field.delAttr('type')
field.setValue(user_nick)
return df
def put_event(self, ev):
if gajim.handlers.has_key(ev[0]):
gajim.handlers[ev[0]](self.name, ev[1])
@ -143,22 +137,21 @@ class Connection(ConnectionHandlers):
if not self.on_purpose:
self.disconnect()
if gajim.config.get_per('accounts', self.name, 'autoreconnect') \
and self.retrycount <= 10:
and self.retrycount <= 10:
self.connected = 1
self.dispatch('STATUS', 'connecting')
self.time_to_reconnect = 10
# this check has moved from _reconnect method
if self.retrycount > 5:
self.time_to_reconnect = 20
else:
self.time_to_reconnect = 10
gajim.idlequeue.set_alarm(self._reconnect_alarm,
self.time_to_reconnect)
gajim.idlequeue.set_alarm(self._reconnect_alarm,
self.time_to_reconnect)
elif self.on_connect_failure:
self.on_connect_failure()
self.on_connect_failure = None
else:
# show error dialog
# show error dialog
self._connection_lost()
else:
self.disconnect()
@ -168,9 +161,9 @@ class Connection(ConnectionHandlers):
def _connection_lost(self):
self.disconnect(on_purpose = False)
self.dispatch('STATUS', 'offline')
self.dispatch('ERROR',
(_('Connection with account "%s" has been lost') % self.name,
_('To continue sending and receiving messages, you will need to reconnect.')))
self.dispatch('CONNECTION_LOST',
(_('Connection with account "%s" has been lost') % self.name,
_('To continue sending and receiving messages, you will need to reconnect.')))
def _event_dispatcher(self, realm, event, data):
if realm == common.xmpp.NS_REGISTER:
@ -394,7 +387,8 @@ class Connection(ConnectionHandlers):
if not self.retrycount and self.connected != 0:
self.disconnect(on_purpose = True)
self.dispatch('STATUS', 'offline')
self.dispatch('ERROR', (_('Could not connect to "%s"') % self._hostname,
self.dispatch('CONNECTION_LOST',
(_('Could not connect to "%s"') % self._hostname,
_('Check your connection or try again later.')))
def _connect_success(self, con, con_type):
@ -430,7 +424,8 @@ class Connection(ConnectionHandlers):
if not con:
self.disconnect(on_purpose = True)
self.dispatch('STATUS', 'offline')
self.dispatch('ERROR', (_('Could not connect to "%s"') % self._hostname,
self.dispatch('CONNECTION_LOST',
(_('Could not connect to "%s"') % self._hostname,
_('Check your connection or try again later')))
if self.on_connect_auth:
self.on_connect_auth(None)
@ -712,8 +707,8 @@ class Connection(ConnectionHandlers):
# JEP-0172: user_nickname
if user_nick:
df = self.build_user_nick(user_nick)
msg_iq.addChild(node = df)
msg_iq.setTag('nick', namespace = common.xmpp.NS_NICK).setData(
user_nick)
# chatstates - if peer supports jep85 or jep22, send chatstates
# please note that the only valid tag inside a message containing a <body>
@ -788,12 +783,10 @@ class Connection(ConnectionHandlers):
p = common.xmpp.Presence(jid, 'subscribe')
if user_nick:
df = self.build_user_nick(user_nick)
p.addChild(node = df)
p.setTag('nick', namespace = common.xmpp.NS_NICK).setData(user_nick)
p = self.add_sha(p)
if not msg:
msg = _('I would like to add you to my roster.')
p.setStatus(msg)
if msg:
p.setStatus(msg)
self.connection.send(p)
def send_authorization(self, jid):
@ -876,6 +869,10 @@ class Connection(ConnectionHandlers):
def request_os_info(self, jid, resource):
if not self.connection:
return
# If we are invisible, do not request
if self.connected == gajim.SHOW_LIST.index('invisible'):
self.dispatch('OS_INFO', (jid, resource, _('Not fetched because of invisible status'), _('Not fetched because of invisible status')))
return
to_whom_jid = jid
if resource:
to_whom_jid += '/' + resource
@ -932,6 +929,9 @@ class Connection(ConnectionHandlers):
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2.addChild(name='storage', namespace='storage:metacontacts')
id = self.connection.getAnID()
iq.setID(id)
self.awaiting_answers[id] = (METACONTACTS_ARRIVED, )
self.connection.send(iq)
def store_metacontacts(self, tags_list):

View file

@ -24,6 +24,7 @@ import sha
import socket
import sys
from time import localtime, strftime, gmtime
from calendar import timegm
import socks5
@ -43,6 +44,7 @@ STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
VCARD_PUBLISHED = 'vcard_published'
VCARD_ARRIVED = 'vcard_arrived'
AGENT_REMOVED = 'agent_removed'
METACONTACTS_ARRIVED = 'metacontacts_arrived'
HAS_IDLE = True
try:
import common.idle as idle # when we launch gajim from sources
@ -171,7 +173,12 @@ class ConnectionBytestream:
file_props['sha_str'] = sha_str
if not ft_override_host_to_send:
ft_override_host_to_send = self.peerhost[0]
ft_override_host_to_send = socket.gethostbyname(ft_override_host_to_send)
try:
ft_override_host_to_send = socket.gethostbyname(
ft_override_host_to_send)
except socket.gaierror:
self.dispatch('ERROR', (_('Wrong host'), _('The host you configured as the ft_override_host_to_send advanced option is not valid, so ignored.')))
ft_override_host_to_send = self.peerhost[0]
listener = gajim.socks5queue.start_listener(self.peerhost[0], port,
sha_str, self._result_socks5_sid, file_props['sid'])
if listener == None:
@ -582,8 +589,8 @@ class ConnectionDisco:
iq.setID(id)
# Wait the answer during 30 secondes
self.awaiting_timeouts[gajim.idlequeue.current_time() + 30] = (id,
_('Registration information for transport %s has not arrived in time' % \
agent))
_('Registration information for transport %s has not arrived in time') % \
agent)
self.connection.SendAndCallForResponse(iq, self._ReceivedRegInfo,
{'agent': agent})
@ -746,17 +753,28 @@ class ConnectionDisco:
qc = iq_obj.getQueryChildren()
if not qc:
qc = []
is_muc = False
transport_type = ''
for i in qc:
if i.getName() == 'identity':
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'):
transport_type = attr['type']
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':
features.append(i.getAttr('var'))
elif i.getName() == 'x' and i.getAttr('xmlns') == common.xmpp.NS_DATA:
elif i.getName() == 'x' and i.getNamespace() == common.xmpp.NS_DATA:
data.append(common.xmpp.DataForm(node=i))
jid = helpers.get_full_jid_from_iq(iq_obj)
if transport_type and jid not in gajim.transport_type:
gajim.transport_type[jid] = transport_type
gajim.logger.save_transport_type(jid, transport_type)
id = iq_obj.getID()
if not identities: # ejabberd doesn't send identities when we browse online users
#FIXME: see http://www.jabber.ru/bugzilla/show_bug.cgi?id=225
@ -764,6 +782,14 @@ class ConnectionDisco:
if id[0] == 'p':
if features.__contains__(common.xmpp.NS_BYTESTREAM):
gajim.proxy65_manager.resolve(jid, self.connection, self.name)
if features.__contains__(common.xmpp.NS_MUC) and is_muc:
type_ = transport_type or 'jabber'
self.muc_jid[type_] = jid
if transport_type:
if self.available_transports.has_key(transport_type):
self.available_transports[transport_type].append(jid)
else:
self.available_transports[transport_type] = [jid]
self.dispatch('AGENT_INFO_INFO', (jid, node, identities,
features, data))
@ -871,7 +897,10 @@ class ConnectionVcard:
id = self.connection.getAnID()
iq.setID(id)
self.awaiting_answers[id] = (VCARD_ARRIVED, jid)
j = jid
if not j:
j = gajim.get_jid_from_account(self.name)
self.awaiting_answers[id] = (VCARD_ARRIVED, j)
if is_fake_jid:
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
if not room_jid in self.room_jids:
@ -959,9 +988,12 @@ class ConnectionVcard:
elif self.awaiting_answers[id][0] == VCARD_ARRIVED:
# If vcard is empty, we send to the interface an empty vcard so that
# it knows it arrived
if not iq_obj.getTag('vCard'):
jid = self.awaiting_answers[id][1]
our_jid = gajim.get_jid_from_account(self.name)
jid = self.awaiting_answers[id][1]
our_jid = gajim.get_jid_from_account(self.name)
if iq_obj.getType() == 'error' and jid == our_jid:
# our server doesn't support vcard
self.vcard_supported = False
if not iq_obj.getTag('vCard') or iq_obj.getType() == 'error':
if jid and jid != our_jid:
# Write an empty file
self.save_vcard_to_hd(jid, '')
@ -971,6 +1003,29 @@ class ConnectionVcard:
elif self.awaiting_answers[id][0] == AGENT_REMOVED:
jid = self.awaiting_answers[id][1]
self.dispatch('AGENT_REMOVED', jid)
elif self.awaiting_answers[id][0] == METACONTACTS_ARRIVED:
if iq_obj.getType() == 'result':
# Metacontact tags
# http://www.jabber.org/jeps/jep-XXXX.html
meta_list = {}
query = iq_obj.getTag('query')
storage = query.getTag('storage')
metas = storage.getTags('meta')
for meta in metas:
jid = meta.getAttr('jid')
tag = meta.getAttr('tag')
data = {'jid': jid}
order = meta.getAttr('order')
if order != None:
data['order'] = order
if meta_list.has_key(tag):
meta_list[tag].append(data)
else:
meta_list[tag] = [data]
self.dispatch('METACONTACTS', meta_list)
# We can now continue connection by requesting the roster
self.connection.initRoster()
del self.awaiting_answers[id]
def _vCardCB(self, con, vc):
@ -1072,6 +1127,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# keep the jids we auto added (transports contacts) to not send the
# SUBSCRIBED event to gui
self.automatically_added = []
# keep the latest subscribed event for each jid to prevent loop when we
# acknoledge presences
self.subscribed_events = {}
try:
idle.init()
except:
@ -1134,26 +1192,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.bookmarks.append(bm)
self.dispatch('BOOKMARKS', self.bookmarks)
elif ns == 'storage:metacontacts':
# Metacontact tags
# http://www.jabber.org/jeps/jep-XXXX.html
meta_list = {}
metas = storage.getTags('meta')
for meta in metas:
jid = meta.getAttr('jid')
tag = meta.getAttr('tag')
data = {'jid': jid}
order = meta.getAttr('order')
if order != None:
data['order'] = order
if meta_list.has_key(tag):
meta_list[tag].append(data)
else:
meta_list[tag] = [data]
self.dispatch('METACONTACTS', meta_list)
# We can now continue connection by requesting the roster
self.connection.initRoster()
elif ns == 'gajim:prefs':
# Preferences data
# http://www.jabber.org/jeps/jep-0049.html
@ -1236,6 +1274,16 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
self.dispatch('OS_INFO', (jid_stripped, resource, client_info, os_info))
def _TimeCB(self, con, iq_obj):
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()))
self.connection.send(iq_obj)
raise common.xmpp.NodeProcessed
def _gMailNewMailCB(self, con, gm):
'''Called when we get notified of new mail messages in gmail account'''
@ -1332,16 +1380,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if not msgtxt and chatstate_child.getTag('composing'):
chatstate = 'composing'
# JEP-0172 User Nickname
user_nick = ''
xtags = msg.getTags('x', attrs = {'type': 'result'},
namespace = common.xmpp.NS_DATA)
for xtag in xtags:
df = common.xmpp.DataForm(node = xtag)
field = df.getField('FORM_TYPE')
if not field or field.getValue() != common.xmpp.NS_PROFILE:
continue
user_nick = df.getField('nickname').getValue()
user_nick = msg.getTagData('nick')
if not user_nick:
user_nick = ''
if encTag and GnuPG.USE_GPG:
#decrypt
encmsg = encTag.getData()
@ -1372,8 +1414,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if not self.last_history_line.has_key(jid):
return
self.dispatch('GC_MSG', (frm, msgtxt, tim))
if self.name not in no_log_for and jid in self.last_history_line \
and not int(float(time.mktime(tim))) <= \
if self.name not in no_log_for and not int(float(time.mktime(tim))) <= \
self.last_history_line[jid] and msgtxt:
gajim.logger.write('gc_msg', frm, msgtxt, tim = tim)
elif mtype == 'chat': # it's type 'chat'
@ -1433,13 +1474,26 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if ptype == 'available':
ptype = None
gajim.log.debug('PresenceCB: %s' % ptype)
who = helpers.get_full_jid_from_iq(prs)
try:
who = helpers.get_full_jid_from_iq(prs)
except:
if prs.getTag('error').getTag('jid-malformed'):
# wrong jid, we probably tried to change our nick in a room to a non valid
# one
who = str(prs.getFrom())
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
self.dispatch('GC_MSG', (jid_stripped, _('Nickname not allowed: %s') % \
resource, None))
return
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
timestamp = None
is_gc = False # is it a GC presence ?
sigTag = None
avatar_sha = None
user_nick = '' # for JEP-0172
# JEP-0172 User Nickname
user_nick = prs.getTagData('nick')
if not user_nick:
user_nick = ''
transport_auto_auth = False
xtags = prs.getTags('x')
for x in xtags:
@ -1460,19 +1514,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
agent = gajim.get_server_from_jid(jid_stripped)
if self.connection.getRoster().getItem(agent): # to be sure it's a transport contact
transport_auto_auth = True
if namespace == common.xmpp.NS_DATA:
# JEP-0172
df = common.xmpp.DataForm(node = x)
if df.getType() != 'result':
continue
field = df.getField('FORM_TYPE')
if not field or field.getValue() != common.xmpp.NS_PROFILE:
continue
user_nick = df.getField('nickname').getValue()
no_log_for = gajim.config.get_per('accounts', self.name,
'no_log_for').split()
status = prs.getStatus()
status = prs.getStatus() or ''
show = prs.getShow()
if not show in STATUS_LIST:
show = '' # We ignore unknown show
@ -1531,7 +1576,16 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
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('gcstatus', who, status, show)
gc_c = gajim.contacts.get_gc_contact(self.name, jid_stripped, resource)
st = status or ''
if gc_c:
jid = gc_c.jid
else:
jid = prs.getJid()
if jid:
# we know real jid, save it in db
st += ' (%s)' % jid
gajim.logger.write('gcstatus', who, st, show)
if avatar_sha:
if self.vcard_shas.has_key(who):
if avatar_sha != self.vcard_shas[who]:
@ -1567,14 +1621,40 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if jid_stripped in self.automatically_added:
self.automatically_added.remove(jid_stripped)
else:
self.dispatch('SUBSCRIBED', (jid_stripped, resource))
# 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())
block = False
if len(self.subscribed_events[jid_stripped]) > 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:
gajim.config.set_per('account', self.name,
'dont_ack_subscription', True)
else:
self.dispatch('SUBSCRIBED', (jid_stripped, resource))
# BE CAREFUL: no con.updateRosterItem() in a callback
gajim.log.debug(_('we are now subscribed to %s') % who)
elif ptype == 'unsubscribe':
gajim.log.debug(_('unsubscribe request from %s') % who)
elif ptype == 'unsubscribed':
gajim.log.debug(_('we are now unsubscribed from %s') % who)
self.dispatch('UNSUBSCRIBED', jid_stripped)
# 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())
block = False
if len(self.subscribed_events[jid_stripped]) > 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:
gajim.config.set_per('account', self.name, 'dont_ack_subscription',
True)
else:
self.dispatch('UNSUBSCRIBED', jid_stripped)
elif ptype == 'error':
errmsg = prs.getError()
errcode = prs.getErrorCode()
@ -1733,13 +1813,18 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
print >> sys.stderr, _('JID %s is not RFC compliant. It will not be added to your roster. Use roster management tools such as http://jru.jabberstudio.org/ to remove it') % jid
else:
infos = raw_roster[jid]
if jid != our_jid and (not infos['subscription'] or infos['subscription'] == \
'none') and (not infos['ask'] or infos['ask'] == 'none') and not infos['name'] \
and not infos['groups']:
if jid != our_jid and (not infos['subscription'] or \
infos['subscription'] == 'none') and (not infos['ask'] or \
infos['ask'] == 'none') and not infos['name'] and \
not infos['groups']:
# remove this useless item, it won't be shown in roster anyway
self.connection.getRoster().delItem(jid)
elif jid != our_jid: # don't add our jid
roster[j] = raw_roster[jid]
if gajim.jid_is_transport(jid) and \
not gajim.get_transport_name_from_jid(jid):
# we can't determine which iconset to use
self.discoverInfo(jid)
self.dispatch('ROSTER', roster)
@ -1831,6 +1916,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
common.xmpp.NS_DISCO_INFO)
con.RegisterHandler('iq', self._VersionCB, 'get',
common.xmpp.NS_VERSION)
con.RegisterHandler('iq', self._TimeCB, 'get',
common.xmpp.NS_TIME)
con.RegisterHandler('iq', self._LastCB, 'get',
common.xmpp.NS_LAST)
con.RegisterHandler('iq', self._LastResultCB, 'result',
@ -1845,8 +1932,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
common.xmpp.NS_ROSTER)
con.RegisterHandler('iq', self._PrivateCB, 'result',
common.xmpp.NS_PRIVATE)
con.RegisterHandler('iq', self._PrivateErrorCB, 'error',
common.xmpp.NS_PRIVATE)
con.RegisterHandler('iq', self._HttpAuthCB, 'get',
common.xmpp.NS_HTTP_AUTH)
con.RegisterHandler('iq', self._CommandExecuteCB, 'set',

View file

@ -210,7 +210,7 @@ class Contacts:
contacts = self.get_contacts_from_jid(account, jid)
if not contacts and '/' in jid:
# jid may be a fake jid, try it
room, nick = jid.split('/')
room, nick = jid.split('/', 1)
contact = self.get_gc_contact(account, room, nick)
return contact
return self.get_highest_prio_contact_from_contacts(contacts)

233
src/common/events.py Normal file
View file

@ -0,0 +1,233 @@
## common/events.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
##
## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <nkour@jabber.org>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
##
## 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 time
import gajim
class Event:
'''Information concerning each event'''
def __init__(self, type_, time_, parameters, show_in_roster = False,
show_in_systray = True):
''' type_ in chat, normal, file-request, file-error, file-completed,
file-request-error, file-send-error, file-stopped, gc_msg, pm,
printed_chat, printed_gc_msg, printed_pm
parameters is (per type_):
chat, normal: [message, subject, kind, time, encrypted, resource,
msg_id]
where kind in error, incoming
file-*: file_props
gc_msg: None
printed_*: None
messages that are already printed in chat, but not read'''
self.type_ = type_
self.time_ = time_
self.parameters = parameters
self.show_in_roster = show_in_roster
self.show_in_systray = show_in_systray
class Events:
'''Information concerning all events'''
def __init__(self):
self._events = {} # list of events {acct: {jid1: [E1, E2]}, }
def change_account_name(self, old_name, new_name):
if self._events.has_key(old_name):
self._events[new_name] = self._events[old_name]
del self._events[old_name]
def add_account(self, account):
self._events[account] = {}
def get_accounts(self):
return self._events.keys()
def remove_account(self, account):
del self._events[account]
def create_event(self, type_, parameters, time_ = time.time(),
show_in_roster = False, show_in_systray = True):
return Event(type_, time_, parameters, show_in_roster,
show_in_systray)
def add_event(self, account, jid, event):
# No such account before ?
if not self._events.has_key(account):
self._events[account] = {jid: [event]}
# no such jid before ?
elif not self._events[account].has_key(jid):
self._events[account][jid] = [event]
else:
self._events[account][jid].append(event)
if event.show_in_systray and gajim.interface.systray_capabilities:
gajim.interface.systray.set_img()
def remove_events(self, account, jid, event = None, types = []):
'''if event is not speficied, remove all events from this jid,
optionnaly only from given type
return True if no such event found'''
if not self._events.has_key(account):
return True
if not self._events[account].has_key(jid):
return True
if event: # remove only one event
if event in self._events[account][jid]:
if len(self._events[account][jid]) == 1:
del self._events[account][jid]
else:
self._events[account][jid].remove(event)
if event.show_in_systray and gajim.interface.systray_capabilities:
gajim.interface.systray.set_img()
return
else:
return True
if types:
new_list = [] # list of events to keep
for ev in self._events[account][jid]:
if ev.type_ not in types:
new_list.append(ev)
if len(new_list) == len(self._events[account][jid]):
return True
if new_list:
self._events[account][jid] = new_list
else:
del self._events[account][jid]
if gajim.interface.systray_capabilities:
gajim.interface.systray.set_img()
return
# no event nor type given, remove them all
del self._events[account][jid]
if gajim.interface.systray_capabilities:
gajim.interface.systray.set_img()
def get_nb_events(self, types = []):
return self._get_nb_events(types = types)
def get_events(self, account, jid = None, types = []):
'''if event is not speficied, remove all events from this jid,
optionnaly only from given type'''
if not self._events.has_key(account):
return []
if not jid:
return self._events[account]
if not self._events[account].has_key(jid):
return []
events_list = [] # list of events
for ev in self._events[account][jid]:
if not types or ev.type_ in types:
events_list.append(ev)
return events_list
def get_first_event(self, account, jid = None, type_ = None):
'''Return the first event of type type_ if given'''
events_list = self.get_events(account, jid, type_)
# be sure it's bigger than latest event
first_event_time = time.time() + 1
first_event = None
for event in events_list:
if event.time_ < first_event_time:
first_event_time = event.time_
first_event = event
return first_event
def _get_nb_events(self, account = None, jid = None, attribute = None, types = []):
'''return the number of events'''
nb = 0
if account:
accounts = [account]
else:
accounts = self._events.keys()
for acct in accounts:
if not self._events.has_key(acct):
continue
if jid:
jids = [jid]
else:
jids = self._events[acct].keys()
for j in jids:
if not self._events[acct].has_key(j):
continue
for event in self._events[acct][j]:
if types and event.type_ not in types:
continue
if not attribute or \
attribute == 'systray' and event.show_in_systray or \
attribute == 'roster' and event.show_in_roster:
nb += 1
return nb
def _get_some_events(self, attribute):
'''attribute in systray, roster'''
events = {}
for account in self._events:
events[account] = {}
for jid in self._events[account]:
events[account][jid] = []
for event in self._events[account][jid]:
if attribute == 'systray' and event.show_in_systray or \
attribute == 'roster' and event.show_in_roster:
events[account][jid].append(event)
if not events[account][jid]:
del events[account][jid]
if not events[account]:
del events[account]
return events
def _get_first_event_with_attribute(self, events):
'''get the first event
events is in the form {account1: {jid1: [ev1, ev2], },. }'''
# be sure it's bigger than latest event
first_event_time = time.time() + 1
first_account = None
first_jid = None
first_event = None
for account in events:
for jid in events[account]:
for event in events[account][jid]:
if event.time_ < first_event_time:
first_event_time = event.time_
first_account = account
first_jid = jid
first_event = event
return first_account, first_jid, first_event
def get_nb_systray_events(self, types = []):
'''returns the number of events displayedin roster'''
return self._get_nb_events(attribute = 'systray', types = types)
def get_systray_events(self):
'''return all events that must be displayed in systray:
{account1: {jid1: [ev1, ev2], },. }'''
return self._get_some_events('systray')
def get_first_systray_event(self):
events = self.get_systray_events()
return self._get_first_event_with_attribute(events)
def get_nb_roster_events(self, account = None, jid = None, types = []):
'''returns the number of events displayedin roster'''
return self._get_nb_events(attribute = 'roster', account = account,
jid = jid, types = types)
def get_roster_events(self):
'''return all events that must be displayed in roster:
{account1: {jid1: [ev1, ev2], },. }'''
return self._get_some_events('roster')

View file

@ -23,6 +23,7 @@ import locale
import config
from contacts import Contacts
from events import Events
interface = None # The actual interface (the gtk one for the moment)
config = config.Config()
@ -83,6 +84,8 @@ else:
gmail_domains = ['gmail.com', 'googlemail.com']
transport_type = {} # list the type of transport
last_message_time = {} # list of time of the latest incomming message
# {acct1: {jid1: time1, jid2: time2}, }
encrypted_chats = {} # list of encrypted chats {acct1: [jid1, jid2], ..}
@ -90,19 +93,14 @@ encrypted_chats = {} # list of encrypted chats {acct1: [jid1, jid2], ..}
contacts = Contacts()
gc_connected = {} # tell if we are connected to the room or not {acct: {room_jid: True}}
gc_passwords = {} # list of the pass required to enter a room {room_jid: password}
automatic_rooms = {} # list of rooms that must be automaticaly configured and for which we have a list of invities {account: {room_jid: {'invities': []}}}
groups = {} # list of groups
newly_added = {} # list of contacts that has just signed in
to_be_removed = {} # list of contacts that has just signed out
awaiting_events = {} # list of messages/FT reveived but not printed
# awaiting_events[jid] = (type, (data1, data2, ...))
# if type in ('chat', 'normal'): data = (message, subject, kind, time,
# encrypted, resource)
# kind can be (incoming, error)
# if type in file-request, file-request-error, file-send-error, file-error,
# file-completed, file-stopped:
# data = file_props
events = Events()
nicks = {} # list of our nick names in each account
# should we block 'contact signed in' notifications for this account?
# this is only for the first 30 seconds after we change our show
@ -222,8 +220,11 @@ def get_transport_name_from_jid(jid, use_config_setting = True):
# jid was None. Yann why?
if not jid or (use_config_setting and not config.get('use_transports_iconsets')):
return
host = get_server_from_jid(jid)
if host in transport_type:
return transport_type[host]
# host is now f.e. icq.foo.org or just icq (sometimes on hacky transports)
host_splitted = host.split('.')
if len(host_splitted) != 0:
@ -233,7 +234,7 @@ def get_transport_name_from_jid(jid, use_config_setting = True):
if host == 'aim':
return 'aim'
elif host == 'gg':
return 'gadugadu'
return 'gadu-gadu'
elif host == 'irc':
return 'irc'
elif host == 'icq':
@ -281,18 +282,6 @@ def get_hostname_from_account(account_name, use_srv = False):
return config.get_per('accounts', account_name, 'custom_host')
return config.get_per('accounts', account_name, 'hostname')
def get_first_event(account, jid, typ = None):
'''returns the first event of the given type from the awaiting_events queue'''
if not awaiting_events[account].has_key(jid):
return None
q = awaiting_events[account][jid]
if not typ:
return q[0]
for ev in q:
if ev[0] == typ:
return ev
return None
def get_notification_image_prefix(jid):
'''returns the prefix for the notification images'''
transport_name = get_transport_name_from_jid(jid)

View file

@ -18,6 +18,7 @@
import sre
import os
import subprocess
import urllib
import errno
import select
@ -360,6 +361,11 @@ def is_in_path(name_of_command, return_abs_path = False):
else:
return is_in_dir
def exec_command(command):
'''command is a string that contain arguments'''
# os.system(command)
subprocess.Popen(command.split())
def launch_browser_mailer(kind, uri):
#kind = 'url' or 'mail'
if os.name == 'nt':
@ -383,11 +389,9 @@ def launch_browser_mailer(kind, uri):
command = gajim.config.get('custommailapp')
if command == '': # if no app is configured
return
# we add the uri in "" so we have good parsing from shell
uri = uri.replace('"', '\\"') # escape "
command = command + ' "' + uri + '" &'
try: #FIXME: when we require python2.4+ use subprocess module
os.system(command)
command = command + ' ' + uri
try:
exec_command(command)
except:
pass
@ -406,11 +410,9 @@ def launch_file_manager(path_to_open):
command = gajim.config.get('custom_file_manager')
if command == '': # if no app is configured
return
# we add the path in "" so we have good parsing from shell
path_to_open = path_to_open.replace('"', '\\"') # escape "
command = command + ' "' + path_to_open + '" &'
try: #FIXME: when we require python2.4+ use subprocess module
os.system(command)
command = command + ' ' + path_to_open
try:
exec_command(command)
except:
pass
@ -436,11 +438,8 @@ def play_sound_file(path_to_soundfile):
if gajim.config.get('soundplayer') == '':
return
player = gajim.config.get('soundplayer')
# we add the path in "" so we have good parsing from shell
path_to_soundfile = path_to_soundfile.replace('"', '\\"') # escape "
command = player + ' "' + path_to_soundfile + '" &'
#FIXME: when we require 2.4+ use subprocess module
os.system(command)
command = player + ' ' + path_to_soundfile
exec_command(command)
def get_file_path_from_dnd_dropped_uri(uri):
path = urllib.url2pathname(uri) # escape special chars
@ -464,14 +463,6 @@ def from_xs_boolean_to_python_boolean(value):
return val
def ensure_unicode_string(s):
# py23 u'abc'.decode('utf-8') raises
# python24 does not. if python23 is ooold we can remove this func
# FIXME: remove this when we abandon py23
if isinstance(s, str):
s = s.decode('utf-8')
return s
def get_xmpp_show(show):
if show in ('online', 'offline'):
return None
@ -514,9 +505,9 @@ 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.awaiting_events[account].has_key(contact.jid):
if account and gajim.events.get_nb_roster_events(account, contact.jid):
return 'message'
if account and gajim.awaiting_events[account].has_key(
if account and gajim.events.get_nb_roster_events(account,
contact.get_full_jid()):
return 'message'
if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
@ -543,6 +534,14 @@ def decode_string(string):
return string
def ensure_utf8_string(string):
'''make sure string is in UTF-8'''
try:
string = decode_string(string).encode('utf-8')
except:
pass
return string
def get_windows_reg_env(varname, default=''):
'''asks for paths commonly used but not exposed as ENVs
in english Windows 2003 those are:
@ -727,20 +726,73 @@ def sanitize_filename(filename):
return filename
def allow_showing_notification(account):
def allow_showing_notification(account, type = None, advanced_notif_num = None,
first = True):
'''is it allowed to show nofication?
check OUR status and if we allow notifications for that status'''
check OUR status and if we allow notifications for that status
type is the option that need to be True ex: notify_on_signing
first: set it to false when it's not the first message'''
if advanced_notif_num != None:
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
'popup')
if popup == 'yes':
return True
if popup == 'no':
return False
if type and (not gajim.config.get(type) or not first):
return False
if type and gajim.config.get(type) and first:
return True
if gajim.config.get('autopopupaway'): # always show notification
return True
if gajim.connections[account].connected in (2, 3): # we're online or chat
return True
return False
def allow_popup_window(account):
def allow_popup_window(account, advanced_notif_num = None):
'''is it allowed to popup windows?'''
if advanced_notif_num != None:
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
'auto_open')
if popup == 'yes':
return True
if popup == 'no':
return False
autopopup = gajim.config.get('autopopup')
autopopupaway = gajim.config.get('autopopupaway')
if autopopup and (autopopupaway or \
gajim.connections[account].connected in (2, 3)): # we're online or chat
return True
return False
def allow_sound_notification(sound_event, advanced_notif_num = None):
if advanced_notif_num != None:
sound = gajim.config.get_per('notifications', str(advanced_notif_num),
'sound')
if sound == 'yes':
return True
if sound == 'no':
return False
if gajim.config.get_per('soundevents', sound_event, 'enabled'):
return True
return False
def get_chat_control(account, contact):
full_jid_with_resource = contact.jid
if contact.resource:
full_jid_with_resource += '/' + contact.resource
highest_contact = gajim.contacts.get_contact_with_highest_priority(
account, contact.jid)
# Look for a chat control that has the given resource, or default to
# one without resource
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid_with_resource,
account)
if ctrl:
return ctrl
elif not highest_contact or not highest_contact.resource:
# unknow contact or offline message
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
elif highest_contact and contact.resource != \
highest_contact.resource:
return None
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)

View file

@ -78,30 +78,50 @@ class Constants:
self.SHOW_OFFLINE
) = range(6)
(
self.TYPE_AIM,
self.TYPE_GG,
self.TYPE_HTTP_WS,
self.TYPE_ICQ,
self.TYPE_MSN,
self.TYPE_QQ,
self.TYPE_SMS,
self.TYPE_SMTP,
self.TYPE_TLEN,
self.TYPE_YAHOO,
self.TYPE_NEWMAIL,
self.TYPE_RSS,
self.TYPE_WEATHER,
) = range(13)
constants = Constants()
class Logger:
def __init__(self):
self.jids_already_in = [] # holds jids that we already have in DB
self.con = None
if not os.path.exists(LOG_DB_PATH):
# this can happen only the first time (the time we create the db)
# db is not created here but in src/common/checks_paths.py
return
self.init_vars()
def init_vars(self):
# if locked, wait up to 20 sec to unlock
# before raise (hopefully should be enough)
if self.con:
self.con.close()
self.con = sqlite.connect(LOG_DB_PATH, timeout = 20.0,
isolation_level = 'IMMEDIATE')
self.cur = self.con.cursor()
self.get_jids_already_in_db()
def get_jids_already_in_db(self):
self.cur.execute('SELECT jid FROM jids')
rows = self.cur.fetchall() # list of tupples: [(u'aaa@bbb',), (u'cc@dd',)]
self.jids_already_in = []
for row in rows:
# row[0] is first item of row (the only result here, the jid)
self.jids_already_in.append(row[0])
@ -192,7 +212,66 @@ class Logger:
show_col = 'UNKNOWN'
return kind_col, show_col
def convert_human_transport_type_to_db_api_values(self, type_):
'''converts from string style to constant ints for db'''
if type_ == 'aim':
return constants.TYPE_AIM
if type_ == 'gadu-gadu':
return constants.TYPE_GG
if type_ == 'http-ws':
return constants.TYPE_HTTP_WS
if type_ == 'icq':
return constants.TYPE_ICQ
if type_ == 'msn':
return constants.TYPE_MSN
if type_ == 'qq':
return constants.TYPE_QQ
if type_ == 'sms':
return constants.TYPE_SMS
if type_ == 'smtp':
return constants.TYPE_SMTP
if type_ == 'tlen':
return constants.TYPE_TLEN
if type_ == 'yahoo':
return constants.TYPE_YAHOO
if type_ == 'newmail':
return constants.TYPE_NEWMAIL
if type_ == 'rss':
return constants.TYPE_RSS
if type_ == 'weather':
return constants.TYPE_WEATHER
return None
def convert_api_values_to_human_transport_type(self, type_id):
'''converts from constant ints for db to string style'''
if type_id == constants.TYPE_AIM:
return 'aim'
if type_id == constants.TYPE_GG:
return 'gadu-gadu'
if type_id == constants.TYPE_HTTP_WS:
return 'http-ws'
if type_id == constants.TYPE_ICQ:
return 'icq'
if type_id == constants.TYPE_MSN:
return 'msn'
if type_id == constants.TYPE_QQ:
return 'qq'
if type_id == constants.TYPE_SMS:
return 'sms'
if type_id == constants.TYPE_SMTP:
return 'smtp'
if type_id == constants.TYPE_TLEN:
return 'tlen'
if type_id == constants.TYPE_YAHOO:
return 'yahoo'
if type_id == constants.TYPE_NEWMAIL:
return 'newmail'
if type_id == constants.TYPE_RSS:
return 'rss'
if type_id == constants.TYPE_WEATHER:
return 'weather'
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 (?, ?, ?, ?, ?, ?, ?)'
@ -238,25 +317,8 @@ class Logger:
self.cur.execute(
'SELECT message_id from unread_messages WHERE jid_id = %d' % jid_id)
results = self.cur.fetchall()
# Remove before 0.10
except:
try:
self.cur.executescript('DROP TABLE unread_messages;')
self.con.commit()
except:
pass
try:
self.cur.executescript('''CREATE TABLE unread_messages(
message_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
jid_id INTEGER
);''')
self.con.commit()
except:
pass
self.con.close()
self.jids_already_in = []
self.init_vars()
return []
pass
for message in results:
msg_id = message[0]
@ -515,3 +577,43 @@ class Logger:
jid_id = self.get_jid_id(jid)
where_sql = 'jid_id = %s' % jid_id
return where_sql
def save_transport_type(self, jid, type_):
'''save the type of the transport in DB'''
type_id = self.convert_human_transport_type_to_db_api_values(type_)
if not type_id:
# unknown type
return
self.cur.execute(
'SELECT type from transports_cache WHERE transport = "%s"' % jid)
results = self.cur.fetchall()
if results:
result = results[0][0]
if result == type_id:
return
self.cur.execute(
'UPDATE transports_cache SET type = %d WHERE transport = "%s"' % (type_id,
jid))
try:
self.con.commit()
except sqlite.OperationalError, e:
print >> sys.stderr, str(e)
return
self.cur.execute(
'INSERT INTO transports_cache VALUES ("%s", %d)' % (jid, type_id))
try:
self.con.commit()
except sqlite.OperationalError, e:
print >> sys.stderr, str(e)
def get_transports_type(self):
'''return all the type of the transports in DB'''
self.cur.execute(
'SELECT * from transports_cache')
results = self.cur.fetchall()
if not results:
return {}
answer = {}
for result in results:
answer[result[0]] = self.convert_api_values_to_human_transport_type(result[1])
return answer

View file

@ -144,7 +144,10 @@ class OptionsParser:
self.update_config_to_01011()
if old < [0, 10, 1, 2] and new >= [0, 10, 1, 2]:
self.update_config_to_01012()
if old < [0, 10, 1, 3] and new >= [0, 10, 1, 3]:
self.update_config_to_01013()
gajim.logger.init_vars()
gajim.config.set('version', new_version)
def update_config_x_to_09(self):
@ -272,3 +275,29 @@ class OptionsParser:
self.old_values['emoticons_theme'] == 'Disabled':
gajim.config.set('emoticons_theme', '')
gajim.config.set('version', '0.10.1.2')
def update_config_to_01013(self):
'''create table transports_cache if there is no such table'''
import exceptions
try:
from pysqlite2 import dbapi2 as sqlite
except ImportError:
raise exceptions.PysqliteNotAvailable
import logger
con = sqlite.connect(logger.LOG_DB_PATH)
cur = con.cursor()
try:
cur.executescript(
'''
CREATE TABLE transports_cache (
transport TEXT UNIQUE,
type INTEGER
);
'''
)
con.commit()
except sqlite.OperationalError, e:
pass
con.close()
gajim.config.set('version', '0.10.1.3')

View file

@ -126,12 +126,11 @@ class NBCommonClient(CommonClient):
def _on_connected(self):
self.connected = 'tcp'
if (self._Ssl is None and self.Connection.getPort() in (5223, 443)) or self._Ssl:
try:
transports_nb.NonBlockingTLS().PlugIn(self, now=1)
self.connected = 'ssl'
except socket.sslerror:
if self._Ssl:
transports_nb.NonBlockingTLS().PlugIn(self, now=1)
if not self.Connection: # ssl error, stream is closed
return
self.connected = 'ssl'
self.onreceive(self._on_receive_document_attrs)
dispatcher_nb.Dispatcher().PlugIn(self)
@ -194,6 +193,8 @@ class NonBlockingClient(NBCommonClient):
self.isplugged = True
self.onreceive(None)
transports_nb.NonBlockingTLS().PlugIn(self)
if not self.Connection: # ssl error, stream is closed
return True
if not self.Dispatcher.Stream._document_attrs.has_key('version') or \
not self.Dispatcher.Stream._document_attrs['version']=='1.0':
self._is_connected()

View file

@ -134,6 +134,7 @@ class Dispatcher(PlugIn):
return 0
except ExpatError:
sys.exc_clear()
self.DEBUG('Invalid XML received from server. Forcing disconnect.')
self._owner.Connection.pollend()
return 0
if len(self._pendingExceptions) > 0:

View file

@ -62,6 +62,7 @@ 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_PRESENCE ='presence' # Jabberd2
@ -83,7 +84,7 @@ NS_SIGNED ='jabber:x:signed' # JEP-00
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'
NS_TIME ='jabber:iq:time' # JEP-0900
NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls'
NS_VACATION ='http://jabber.org/protocol/vacation'
NS_VCARD ='vcard-temp'

View file

@ -371,8 +371,12 @@ class NonBlockingTLS(PlugIn):
PlugIn.PlugIn(self, owner)
DBG_LINE='NonBlockingTLS'
self.on_tls_start = on_tls_start
if now:
res = self._startSSL()
if now:
try:
res = self._startSSL()
except Exception, e:
self._owner.socket.pollend()
return
self.tls_start()
return res
if self._owner.Dispatcher.Stream.features:
@ -434,7 +438,11 @@ class NonBlockingTLS(PlugIn):
self.DEBUG('Got starttls response: ' + self.starttls,'error')
return
self.DEBUG('Got starttls proceed response. Switching to TLS/SSL...','ok')
self._startSSL()
try:
self._startSSL()
except Exception, e:
self._owner.socket.pollend()
return
self._owner.Dispatcher.PlugOut()
dispatcher_nb.Dispatcher().PlugIn(self._owner)

View file

@ -53,6 +53,7 @@ class PreferencesWindow:
'''Initialize Preferences window'''
self.xml = gtkgui_helpers.get_glade('preferences_window.glade')
self.window = self.xml.get_widget('preferences_window')
self.window.set_transient_for(gajim.interface.roster.window)
self.iconset_combobox = self.xml.get_widget('iconset_combobox')
self.notify_on_new_message_radiobutton = self.xml.get_widget(
'notify_on_new_message_radiobutton')
@ -375,6 +376,32 @@ class PreferencesWindow:
self.xml.get_widget('prompt_offline_status_message_checkbutton').\
set_active(st)
# Default Status messages
self.default_msg_tree = self.xml.get_widget('default_msg_treeview')
# (status, translated_status, message, enabled)
model = gtk.ListStore(str, str, str, bool)
self.default_msg_tree.set_model(model)
col = gtk.TreeViewColumn('Status')
self.default_msg_tree.append_column(col)
renderer = gtk.CellRendererText()
col.pack_start(renderer, False)
col.set_attributes(renderer, text = 1)
col = gtk.TreeViewColumn('Message')
self.default_msg_tree.append_column(col)
renderer = gtk.CellRendererText()
col.pack_start(renderer, True)
col.set_attributes(renderer, text = 2)
renderer.connect('edited', self.on_default_msg_cell_edited)
renderer.set_property('editable', True)
col = gtk.TreeViewColumn('Enabled')
self.default_msg_tree.append_column(col)
renderer = gtk.CellRendererToggle()
col.pack_start(renderer, False)
col.set_attributes(renderer, active = 3)
renderer.set_property('activatable', True)
renderer.connect('toggled', self.default_msg_toggled_cb)
self.fill_default_msg_treeview()
#Status messages
self.msg_tree = self.xml.get_widget('msg_treeview')
model = gtk.ListStore(str, str)
@ -455,11 +482,14 @@ class PreferencesWindow:
self.on_msg_treemodel_row_changed)
self.msg_tree.get_model().connect('row-deleted',
self.on_msg_treemodel_row_deleted)
self.default_msg_tree.get_model().connect('row-changed',
self.on_default_msg_treemodel_row_changed)
self.theme_preferences = None
self.notebook.set_current_page(0)
self.window.show_all()
gtkgui_helpers.possibly_move_window_in_current_desktop(self.window)
def on_preferences_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
@ -590,7 +620,23 @@ class PreferencesWindow:
gajim.config.set('use_speller', active)
gajim.interface.save_config()
if active:
self.apply_speller()
lang = gajim.config.get('speller_language')
if not lang:
lang = gajim.LANG
tv = gtk.TextView()
try:
spell = gtkspell.Spell(tv, lang)
except:
dialogs.ErrorDialog(
_('Dictionary for lang %s not available') % lang,
_('You have to install %s dictionary to use spellchecking, or '
'choose another language by setting the speller_language option.'
) % lang)
gajim.config.set('use_speller', False)
widget.set_active(False)
else:
gajim.config.set('speller_language', lang)
self.apply_speller()
else:
self.remove_speller()
@ -787,6 +833,36 @@ class PreferencesWindow:
def on_auto_xa_message_entry_changed(self, widget):
gajim.config.set('autoxa_message', widget.get_text().decode('utf-8'))
def fill_default_msg_treeview(self):
model = self.default_msg_tree.get_model()
model.clear()
status = []
for status_ in gajim.config.get_per('defaultstatusmsg'):
status.append(status_)
status.sort()
for status_ in status:
msg = gajim.config.get_per('defaultstatusmsg', status_, 'message')
enabled = gajim.config.get_per('defaultstatusmsg', status_, 'enabled')
iter = model.append()
uf_show = helpers.get_uf_show(status_)
model.set(iter, 0, status_, 1, uf_show, 2, msg, 3, enabled)
def on_default_msg_cell_edited(self, cell, row, new_text):
model = self.default_msg_tree.get_model()
iter = model.get_iter_from_string(row)
model.set_value(iter, 2, new_text)
def default_msg_toggled_cb(self, cell, path):
model = self.default_msg_tree.get_model()
model[path][3] = not model[path][3]
def on_default_msg_treemodel_row_changed(self, model, path, iter):
status = model[iter][0]
message = model[iter][2].decode('utf-8')
gajim.config.set_per('defaultstatusmsg', status, 'enabled',
model[iter][3])
gajim.config.set_per('defaultstatusmsg', status, 'message', message)
def save_status_messages(self, model):
for msg in gajim.config.get_per('statusmsg'):
gajim.config.del_per('statusmsg', msg)
@ -1005,6 +1081,7 @@ class AccountModificationWindow:
def __init__(self, account):
self.xml = gtkgui_helpers.get_glade('account_modification_window.glade')
self.window = self.xml.get_widget('account_modification_window')
self.window.set_transient_for(gajim.interface.roster.window)
self.account = account
# init proxy list
@ -1153,7 +1230,7 @@ class AccountModificationWindow:
_('You are currently connected to the server'),
_('To change the account name, you must be disconnected.'))
return
if len(gajim.awaiting_events[self.account]):
if len(gajim.events.get_events(self.account)):
dialogs.ErrorDialog(_('Unread events'),
_('To change the account name, you must read all pending '
'events.'))
@ -1262,12 +1339,12 @@ class AccountModificationWindow:
if name != self.account:
#update variables
gajim.interface.instances[name] = gajim.interface.instances[self.account]
gajim.awaiting_events[name] = gajim.awaiting_events[self.account]
gajim.nicks[name] = gajim.nicks[self.account]
gajim.block_signed_in_notifications[name] = \
gajim.block_signed_in_notifications[self.account]
gajim.groups[name] = gajim.groups[self.account]
gajim.gc_connected[name] = gajim.gc_connected[self.account]
gajim.automatic_rooms[name] = gajim.automatic_rooms[self.account]
gajim.newly_added[name] = gajim.newly_added[self.account]
gajim.to_be_removed[name] = gajim.to_be_removed[self.account]
gajim.sleeper_state[name] = gajim.sleeper_state[self.account]
@ -1278,27 +1355,25 @@ class AccountModificationWindow:
gajim.status_before_autoaway[self.account]
gajim.contacts.change_account_name(self.account, name)
gajim.events.change_account_name(self.account, name)
#upgrade account variable in opened windows
for kind in ('infos', 'disco', 'chats', 'gc', 'gc_config'):
# change account variable for chat / gc controls
for ctrl in gajim.interface.msg_win_mgr.get_controls():
ctrl.account = name
# upgrade account variable in opened windows
for kind in ('infos', 'disco', 'gc_config'):
for j in gajim.interface.instances[name][kind]:
gajim.interface.instances[name][kind][j].account = name
#upgrade account in systray
if gajim.interface.systray_enabled:
for list in gajim.interface.systray.jids:
if list[0] == self.account:
list[0] = name
# ServiceCache object keep old property account
if hasattr(gajim.connections[self.account], 'services_cache'):
gajim.connections[self.account].services_cache.account = name
del gajim.interface.instances[self.account]
del gajim.awaiting_events[self.account]
del gajim.nicks[self.account]
del gajim.block_signed_in_notifications[self.account]
del gajim.groups[self.account]
del gajim.gc_connected[self.account]
del gajim.automatic_rooms[self.account]
del gajim.newly_added[self.account]
del gajim.to_be_removed[self.account]
del gajim.sleeper_state[self.account]
@ -1411,6 +1486,11 @@ class AccountModificationWindow:
_('Without a connection, you can not edit your personal information.'))
return
if not gajim.connections[self.account].vcard_supported:
dialogs.ErrorDialog(_("Your server doesn't support Vcard"),
_("Your server can't save your personal information."))
return
gajim.interface.edit_own_details(self.account)
def on_manage_proxies_button_clicked(self, widget):
@ -1435,7 +1515,7 @@ class AccountModificationWindow:
dialogs.ErrorDialog(_('Failed to get secret keys'),
_('There was a problem retrieving your OpenPGP secret keys.'))
return
secret_keys['None'] = 'None'
secret_keys[_('None')] = _('None')
instance = dialogs.ChooseGPGKeyDialog(_('OpenPGP Key Selection'),
_('Choose your OpenPGP key'), secret_keys)
keyID = instance.run()
@ -1444,7 +1524,7 @@ class AccountModificationWindow:
checkbutton = self.xml.get_widget('gpg_save_password_checkbutton')
gpg_key_label = self.xml.get_widget('gpg_key_label')
gpg_name_label = self.xml.get_widget('gpg_name_label')
if keyID[0] == 'None':
if keyID[0] == _('None'):
gpg_key_label.set_text(_('No key selected'))
gpg_name_label.set_text('')
checkbutton.set_sensitive(False)
@ -1492,6 +1572,7 @@ class ManageProxiesWindow:
def __init__(self):
self.xml = gtkgui_helpers.get_glade('manage_proxies_window.glade')
self.window = self.xml.get_widget('manage_proxies_window')
self.window.set_transient_for(gajim.interface.roster.window)
self.proxies_treeview = self.xml.get_widget('proxies_treeview')
self.proxyname_entry = self.xml.get_widget('proxyname_entry')
self.init_list()
@ -1656,6 +1737,7 @@ class AccountsWindow:
def __init__(self):
self.xml = gtkgui_helpers.get_glade('accounts_window.glade')
self.window = self.xml.get_widget('accounts_window')
self.window.set_transient_for(gajim.interface.roster.window)
self.accounts_treeview = self.xml.get_widget('accounts_treeview')
self.modify_button = self.xml.get_widget('modify_button')
self.remove_button = self.xml.get_widget('remove_button')
@ -1711,7 +1793,7 @@ class AccountsWindow:
if not iter:
return
account = model.get_value(iter, 0).decode('utf-8')
if len(gajim.awaiting_events[account]):
if len(gajim.events.get_events(account)):
dialogs.ErrorDialog(_('Unread events'),
_('Read all pending events before removing this account.'))
return
@ -1758,6 +1840,7 @@ class DataFormWindow:
self.config = config
self.xml = gtkgui_helpers.get_glade('data_form_window.glade')
self.window = self.xml.get_widget('data_form_window')
self.window.set_transient_for(gajim.interface.roster.window)
self.config_vbox = self.xml.get_widget('config_vbox')
if config:
self.fill_vbox()
@ -1907,10 +1990,11 @@ class ServiceRegistrationWindow(DataFormWindow):
else:
self.xml = gtkgui_helpers.get_glade('service_registration_window.glade')
self.window = self.xml.get_widget('service_registration_window')
self.window.set_transient_for(gajim.interface.roster.window)
if infos.has_key('registered'):
self.window.set_title(_('Edit %s' % service))
self.window.set_title(_('Edit %s') % service)
else:
self.window.set_title(_('Register to %s' % service))
self.window.set_title(_('Register to %s') % service)
self.xml.get_widget('label').set_text(infos['instructions'])
self.entries = {}
self.draw_table()
@ -1980,7 +2064,7 @@ class GroupchatConfigWindow(DataFormWindow):
self.room_jid = room_jid
self.remove_button = {}
self.affiliation_treeview = {}
self.removed_jid = {}
self.list_init = {} # list at the begining
ui_list = {'outcast': _('Ban List'),
'member': _('Member List'),
'owner': _('Owner List'),
@ -1990,7 +2074,7 @@ class GroupchatConfigWindow(DataFormWindow):
add_on_vbox = self.xml.get_widget('add_on_vbox')
for affiliation in ('outcast', 'member', 'owner', 'admin'):
self.removed_jid[affiliation] = []
self.list_init[affiliation] = {}
hbox = gtk.HBox(spacing = 5)
add_on_vbox.pack_start(hbox, False)
@ -2083,8 +2167,6 @@ class GroupchatConfigWindow(DataFormWindow):
return
model = self.affiliation_treeview[affiliation].get_model()
model.append((jid,'', '', ''))
if jid in self.removed_jid[affiliation]:
self.removed_jid[affiliation].remove(jid)
def on_remove_button_clicked(self, widget, affiliation):
selection = self.affiliation_treeview[affiliation].get_selection()
@ -2097,7 +2179,6 @@ class GroupchatConfigWindow(DataFormWindow):
iter = model.get_iter(path)
jid = model[iter][0]
model.remove(iter)
self.removed_jid[affiliation].append(jid)
self.remove_button[affiliation].set_sensitive(False)
def on_affiliation_treeview_cursor_changed(self, widget, affiliation):
@ -2105,6 +2186,7 @@ class GroupchatConfigWindow(DataFormWindow):
def affiliation_list_received(self, affiliation, list):
'''Fill the affiliation treeview'''
self.list_init[affiliation] = list
if not affiliation:
return
tv = self.affiliation_treeview[affiliation]
@ -2131,18 +2213,28 @@ class GroupchatConfigWindow(DataFormWindow):
self.config)
for affiliation in ('outcast', 'member', 'owner', 'admin'):
list = {}
actual_jid_list = []
model = self.affiliation_treeview[affiliation].get_model()
iter = model.get_iter_first()
# add new jid
while iter:
jid = model[iter][0].decode('utf-8')
list[jid] = {'affiliation': affiliation}
if affiliation == 'outcast':
list[jid]['reason'] = model[iter][1].decode('utf-8')
actual_jid_list.append(jid)
if jid not in self.list_init[affiliation] or \
(affiliation == 'outcast' and self.list_init[affiliation]\
[jid].has_key('reason') and self.list_init[affiliation][jid]\
['reason'] != model[iter][1].decode('utf-8')):
list[jid] = {'affiliation': affiliation}
if affiliation == 'outcast':
list[jid]['reason'] = model[iter][1].decode('utf-8')
iter = model.iter_next(iter)
for jid in self.removed_jid[affiliation]:
list[jid] = {'affiliation': 'none'}
gajim.connections[self.account].send_gc_affiliation_list(self.room_jid,
list)
# remove removed one
for jid in self.list_init[affiliation]:
if jid not in actual_jid_list:
list[jid] = {'affiliation': 'none'}
if list:
gajim.connections[self.account].send_gc_affiliation_list(
self.room_jid, list)
self.window.destroy()
#---------- RemoveAccountWindow class -------------#
@ -2161,8 +2253,9 @@ class RemoveAccountWindow:
self.account = account
xml = gtkgui_helpers.get_glade('remove_account_window.glade')
self.window = xml.get_widget('remove_account_window')
self.window.set_transient_for(gajim.interface.roster.window)
self.remove_and_unregister_radiobutton = xml.get_widget(
'remove_and_unregister_radiobutton')
'remove_and_unregister_radiobutton')
self.window.set_title(_('Removing %s account') % self.account)
xml.signal_autoconnect(self)
self.window.show_all()
@ -2195,7 +2288,7 @@ class RemoveAccountWindow:
self.dialog = None
if gajim.connections[self.account].connected:
self.dialog = dialogs.ConfirmationDialog(
_('Account "%s" is connected to the server' % self.account),
_('Account "%s" is connected to the server') % self.account,
_('If you remove it, the connection will be lost.'),
on_response_ok = remove)
else:
@ -2207,18 +2300,18 @@ class RemoveAccountWindow:
if not res:
return
# Close all opened windows
gajim.interface.roster.close_all(gajim.interface.instances[self.account])
gajim.interface.roster.close_all(self.account)
gajim.connections[self.account].disconnect(on_purpose = True)
del gajim.connections[self.account]
gajim.config.del_per('accounts', self.account)
gajim.interface.save_config()
del gajim.interface.instances[self.account]
del gajim.awaiting_events[self.account]
del gajim.nicks[self.account]
del gajim.block_signed_in_notifications[self.account]
del gajim.groups[self.account]
gajim.contacts.remove_account(self.account)
del gajim.gc_connected[self.account]
del gajim.automatic_rooms[self.account]
del gajim.to_be_removed[self.account]
del gajim.newly_added[self.account]
del gajim.sleeper_state[self.account]
@ -2240,6 +2333,7 @@ class ManageBookmarksWindow:
def __init__(self):
self.xml = gtkgui_helpers.get_glade('manage_bookmarks_window.glade')
self.window = self.xml.get_widget('manage_bookmarks_window')
self.window.set_transient_for(gajim.interface.roster.window)
#Account-JID, RoomName, Room-JID, Autojoin, Passowrd, Nick, Show_Status
self.treestore = gtk.TreeStore(str, str, str, bool, str, str, str)
@ -2538,8 +2632,11 @@ class AccountCreationWizardWindow:
# Connect events from comboboxentry.child
server_comboboxentry = self.xml.get_widget('server_comboboxentry')
server_comboboxentry.child.connect('key_press_event',
entry = server_comboboxentry.child
entry.connect('key_press_event',
self.on_server_comboboxentry_key_press_event)
completion = gtk.EntryCompletion()
entry.set_completion(completion)
# parse servers.xml
servers_xml = os.path.join(gajim.DATA_DIR, 'other', 'servers.xml')
@ -2548,6 +2645,9 @@ class AccountCreationWizardWindow:
for server in servers:
servers_model.append((str(server[0]), int(server[1])))
completion.set_model(servers_model)
completion.set_text_column(0)
# Put servers into comboboxentries
server_comboboxentry.set_model(servers_model)
server_comboboxentry.set_text_column(0)
@ -2836,12 +2936,12 @@ _('You can set advanced account options by pressing Advanced button, or later by
# update variables
gajim.interface.instances[self.account] = {'infos': {}, 'disco': {},
'chats': {}, 'gc': {}, 'gc_config': {}}
gajim.awaiting_events[self.account] = {}
'gc_config': {}}
gajim.connections[self.account].connected = 0
gajim.groups[self.account] = {}
gajim.contacts.add_account(self.account)
gajim.gc_connected[self.account] = {}
gajim.automatic_rooms[self.account] = {}
gajim.newly_added[self.account] = []
gajim.to_be_removed[self.account] = []
gajim.nicks[self.account] = config['name']

View file

@ -28,6 +28,7 @@ import pango
import gobject
import time
import sys
import os
import tooltips
import dialogs
import locale
@ -132,6 +133,10 @@ class ConversationTextview:
buffer.create_tag('focus-out-line', justification = gtk.JUSTIFY_CENTER)
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
self.line_tooltip = tooltips.BaseTooltip()
def del_handlers(self):
@ -187,6 +192,68 @@ class ConversationTextview:
self.tv.scroll_to_iter(end_iter, 0, False, 1, 1)
return False # when called in an idle_add, just do it once
def show_focus_out_line(self):
if not self.allow_focus_out_line:
# if room did not receive focus-in from the last time we added
# --- line then do not readd
return
print_focus_out_line = False
buffer = self.tv.get_buffer()
if self.focus_out_end_iter_offset 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():
# 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
# ---- lines)
print_focus_out_line = True
if print_focus_out_line and buffer.get_char_count() > 0:
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)
begin_iter_for_previous_line = end_iter_for_previous_line.copy()
# img_char+1 (the '\n')
begin_iter_for_previous_line.backward_chars(2)
# remove focus out line
buffer.delete(begin_iter_for_previous_line,
end_iter_for_previous_line)
# add the new focus out line
# FIXME: Why is this loaded from disk everytime
path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'muc_separator.png')
focus_out_line_pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
end_iter = buffer.get_end_iter()
buffer.insert(end_iter, '\n')
buffer.insert_pixbuf(end_iter, focus_out_line_pixbuf)
end_iter = buffer.get_end_iter()
before_img_iter = end_iter.copy()
before_img_iter.backward_char() # one char back (an image also takes one char)
buffer.apply_tag_by_name('focus-out-line', before_img_iter, end_iter)
#FIXME: remove this workaround when bug is fixed
# c http://bugzilla.gnome.org/show_bug.cgi?id=318569
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()
buffer.end_user_action()
# scroll to the end (via idle in case the scrollbar has appeared)
gobject.idle_add(self.scroll_to_end)
def show_line_tooltip(self):
pointer = self.tv.get_pointer()
x, y = self.tv.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, pointer[0],
@ -241,6 +308,7 @@ class ConversationTextview:
buffer = self.tv.get_buffer()
start, end = buffer.get_bounds()
buffer.delete(start, end)
self.focus_out_end_iter_offset = None
def visit_url_from_menuitem(self, widget, link):
'''basically it filters out the widget instance'''
@ -613,9 +681,11 @@ class ConversationTextview:
if day_str:
format += day_str + ' '
format += '%X' + after_str
# format comes as unicode, because of day_str.
# we convert it to the encoding that we want
tim_format = time.strftime(format, tim).encode('utf-8')
tim_format = time.strftime(format, tim)
# if tim_format comes as unicode because of day_str.
# we convert it to the encoding that we want (and that is utf-8)
tim_format = helpers.ensure_utf8_string(tim_format)
tim_format = tim_format.encode('utf-8')
buffer.insert_with_tags_by_name(end_iter, tim_format + ' ',
*other_tags_for_time)
elif current_print_time == 'sometimes' and kind != 'info':
@ -627,7 +697,7 @@ class ConversationTextview:
end_iter = buffer.get_end_iter()
if gajim.config.get('print_time_fuzzy') > 0:
fc = FuzzyClock()
fc.setTime(time.strftime('%H:%M'))
fc.setTime(time.strftime('%H:%M', tim))
ft = fc.getFuzzyTime(gajim.config.get('print_time_fuzzy'))
tim_format = ft.decode(locale.getpreferredencoding())
else:
@ -642,6 +712,7 @@ class ConversationTextview:
other_text_tag = self.detect_other_text_tag(text, kind)
text_tags = other_tags_for_text[:] # create a new list
if other_text_tag:
# note that color of /me may be overwritten in gc_control
text_tags.append(other_text_tag)
else: # not status nor /me
if gajim.config.get(

View file

@ -21,7 +21,6 @@
import gtk
import gobject
import os
import sys
import gtkgui_helpers
import vcard
@ -44,85 +43,97 @@ from common import helpers
class EditGroupsDialog:
'''Class for the edit group dialog window'''
def __init__(self, user, account):
def __init__(self, list_):
'''list_ is a list of (contact, account) tuples'''
self.xml = gtkgui_helpers.get_glade('edit_groups_dialog.glade')
self.dialog = self.xml.get_widget('edit_groups_dialog')
self.account = account
self.user = user
self.dialog.set_transient_for(gajim.interface.roster.window)
self.list_ = list_
self.changes_made = False
self.list = self.xml.get_widget('groups_treeview')
self.xml.get_widget('nickname_label').set_markup(
_("Contact's name: <i>%s</i>") % user.get_shown_name())
self.xml.get_widget('jid_label').set_markup(
_('JID: <i>%s</i>') % user.jid)
if len(list_) == 1:
contact = list_[0][0]
self.xml.get_widget('nickname_label').set_markup(
_("Contact name: <i>%s</i>") % contact.get_shown_name())
self.xml.get_widget('jid_label').set_markup(
_('JID: <i>%s</i>') % contact.jid)
else:
self.xml.get_widget('nickname_label').set_no_show_all(True)
self.xml.get_widget('nickname_label').hide()
self.xml.get_widget('jid_label').set_no_show_all(True)
self.xml.get_widget('jid_label').hide()
self.xml.signal_autoconnect(self)
self.init_list()
def run(self):
self.dialog.show_all()
if self.changes_made:
gajim.connections[self.account].update_contact(self.user.jid,
self.user.name, self.user.groups)
for (contact, account) in self.list_:
gajim.connections[account].update_contact(contact.jid, contact.name,
contact.groups)
def on_edit_groups_dialog_response(self, widget, response_id):
if response_id == gtk.RESPONSE_CLOSE:
self.dialog.destroy()
def update_contact(self):
tag = gajim.contacts.get_metacontacts_tag(self.account, self.user.jid)
if not tag:
gajim.interface.roster.remove_contact(self.user, self.account)
gajim.interface.roster.add_contact_to_roster(self.user.jid,
self.account)
gajim.connections[self.account].update_contact(self.user.jid,
self.user.name, self.user.groups)
return
all_jid = gajim.contacts.get_metacontacts_jids(tag)
for _account in all_jid:
if not gajim.interface.roster.regroup and _account != self.account:
for (contact, account) in self.list_:
tag = gajim.contacts.get_metacontacts_tag(account, contact.jid)
if not tag:
gajim.interface.roster.remove_contact(contact, account)
gajim.interface.roster.add_contact_to_roster(contact.jid, account)
gajim.connections[account].update_contact(contact.jid, contact.name,
contact.groups)
continue
for _jid in all_jid[_account]:
c = gajim.contacts.get_first_contact_from_jid(_account, _jid)
if not c:
all_jid = gajim.contacts.get_metacontacts_jids(tag)
for _account in all_jid:
if not gajim.interface.roster.regroup and _account != account:
continue
gajim.interface.roster.remove_contact(c, _account)
gajim.interface.roster.add_contact_to_roster(_jid, _account)
gajim.connections[_account].update_contact(_jid, c.name, c.groups)
for _jid in all_jid[_account]:
c = gajim.contacts.get_first_contact_from_jid(_account, _jid)
if not c:
continue
gajim.interface.roster.remove_contact(c, _account)
gajim.interface.roster.add_contact_to_roster(_jid, _account)
gajim.connections[_account].update_contact(_jid, c.name,
c.groups)
def remove_group(self, group):
'''add group group to self.user and all his brothers'''
tag = gajim.contacts.get_metacontacts_tag(self.account, self.user.jid)
if not tag:
if group in self.user.groups:
self.user.groups.remove(group)
return
all_jid = gajim.contacts.get_metacontacts_jids(tag)
for _account in all_jid:
if not gajim.interface.roster.regroup and _account != self.account:
'''remove group group from all contacts and all their brothers'''
for (contact, account) in self.list_:
tag = gajim.contacts.get_metacontacts_tag(account, contact.jid)
if not tag:
if group in contact.groups:
contact.groups.remove(group)
continue
for _jid in all_jid[_account]:
contacts = gajim.contacts.get_contact(_account, _jid)
for contact in contacts:
if group in contact.groups:
contact.groups.remove(group)
all_jid = gajim.contacts.get_metacontacts_jids(tag)
for _account in all_jid:
if not gajim.interface.roster.regroup and _account != account:
continue
for _jid in all_jid[_account]:
contacts = gajim.contacts.get_contact(_account, _jid)
for c in contacts:
if group in c.groups:
c.groups.remove(group)
def add_group(self, group):
'''add group group to self.user and all his brothers'''
tag = gajim.contacts.get_metacontacts_tag(self.account, self.user.jid)
if not tag:
if group not in self.user.groups:
self.user.groups.append(group)
return
all_jid = gajim.contacts.get_metacontacts_jids(tag)
for _account in all_jid:
if not gajim.interface.roster.regroup and _account != self.account:
'''add group group to all contacts and all their brothers'''
for (contact, account) in self.list_:
tag = gajim.contacts.get_metacontacts_tag(account, contact.jid)
if not tag:
if group not in contact.groups:
contact.groups.append(group)
continue
for _jid in all_jid[_account]:
contacts = gajim.contacts.get_contact(_account, _jid)
for contact in contacts:
if not group in contact.groups:
contact.groups.append(group)
all_jid = gajim.contacts.get_metacontacts_jids(tag)
for _account in all_jid:
if not gajim.interface.roster.regroup and _account != account:
continue
for _jid in all_jid[_account]:
contacts = gajim.contacts.get_contact(_account, _jid)
for c in contacts:
if not group in c.groups:
c.groups.append(group)
def on_add_button_clicked(self, widget):
group = self.xml.get_widget('group_entry').get_text().decode('utf-8')
@ -136,7 +147,7 @@ class EditGroupsDialog:
return
iter = model.iter_next(iter)
self.changes_made = True
model.append((group, True))
model.append((group, True, False))
self.add_group(group)
self.update_contact()
self.init_list() # Re-draw list to sort new item
@ -144,7 +155,11 @@ class EditGroupsDialog:
def group_toggled_cb(self, cell, path):
self.changes_made = True
model = self.list.get_model()
model[path][1] = not model[path][1]
if model[path][2]:
model[path][2] = False
model[path][1] = True
else:
model[path][1] = not model[path][1]
group = model[path][0].decode('utf-8')
if model[path][1]:
self.add_group(group)
@ -153,23 +168,39 @@ class EditGroupsDialog:
self.update_contact()
def init_list(self):
store = gtk.ListStore(str, bool)
store = gtk.ListStore(str, bool, bool)
self.list.set_model(store)
for column in self.list.get_columns(): # Clear treeview when re-drawing
self.list.remove_column(column)
groups = [] # Store accounts in a list so we can sort them
for g in gajim.groups[self.account].keys():
if g in helpers.special_groups:
continue
in_group = False
if g in self.user.groups:
in_group = True
groups.append([g, in_group])
groups.sort()
for group in groups:
accounts = []
# Store groups in a list so we can sort them and the number of contacts in
# it
groups = {}
for (contact, account) in self.list_:
if account not in accounts:
accounts.append(account)
for g in gajim.groups[account].keys():
if g in helpers.special_groups:
continue
if g in groups:
continue
groups[g] = 0
for g in contact.groups:
groups[g] += 1
group_list = groups.keys()
group_list.sort()
for group in group_list:
iter = store.append()
store.set(iter, 0, group[0]) # Group name
store.set(iter, 1, group[1]) # In group boolean
store.set(iter, 0, group) # Group name
if groups[group] == 0:
store.set(iter, 1, False)
else:
store.set(iter, 1, True)
if groups[group] == len(self.list_):
# all contacts are in this group
store.set(iter, 2, False)
else:
store.set(iter, 2, True)
column = gtk.TreeViewColumn(_('Group'))
column.set_expand(True)
self.list.append_column(column)
@ -184,7 +215,7 @@ class EditGroupsDialog:
column.pack_start(renderer)
renderer.set_property('activatable', True)
renderer.connect('toggled', self.group_toggled_cb)
column.set_attributes(renderer, active = 1)
column.set_attributes(renderer, active = 1, inconsistent = 2)
class PassphraseDialog:
'''Class for Passphrase dialog'''
@ -223,6 +254,7 @@ class ChooseGPGKeyDialog:
prompt_label = xml.get_widget('prompt_label')
prompt_label.set_text(prompt_text)
model = gtk.ListStore(str, str)
model.set_sort_func(1, self.sort_keys)
model.set_sort_column_id(1, gtk.SORT_ASCENDING)
self.keys_treeview.set_model(model)
#columns
@ -235,6 +267,17 @@ class ChooseGPGKeyDialog:
self.fill_tree(secret_keys, selected)
self.window.show_all()
def sort_keys(self, model, iter1, iter2):
value1 = model[iter1][1]
value2 = model[iter2][1]
if value1 == _('None'):
return -1
elif value2 == _('None'):
return 1
elif value1 < value2:
return -1
return 1
def run(self):
rep = self.window.run()
if rep == gtk.RESPONSE_OK:
@ -261,6 +304,7 @@ class ChangeStatusMessageDialog:
self.show = show
self.xml = gtkgui_helpers.get_glade('change_status_message_dialog.glade')
self.window = self.xml.get_widget('change_status_message_dialog')
self.window.set_transient_for(gajim.interface.roster.window)
if show:
uf_show = helpers.get_uf_show(show)
title_text = _('%s Status Message') % uf_show
@ -361,6 +405,12 @@ class ChangeStatusMessageDialog:
class AddNewContactWindow:
'''Class for AddNewContactWindow'''
uid_labels = {'jabber': _('Jabber ID'),
'aim': _('AIM Address'),
'gadu-gadu': _('GG Number'),
'icq': _('ICQ Number'),
'msn': _('MSN Address'),
'yahoo': _('Yahoo! Address')}
def __init__(self, account = None, jid = None, user_nick = None,
group = None):
self.account = account
@ -376,87 +426,123 @@ class AddNewContactWindow:
self.account = account
else:
accounts = [self.account]
if self.account:
location = gajim.interface.instances[self.account]
else:
location = gajim.interface.instances
if location.has_key('add_contact'):
location['add_contact'].window.present()
# An instance is already opened
return
location['add_contact'] = self
self.xml = gtkgui_helpers.get_glade('add_new_contact_window.glade')
self.account_combobox = self.xml.get_widget('account_combobox')
self.account_hbox = self.xml.get_widget('account_hbox')
self.account_label = self.xml.get_widget('account_label')
self.window = self.xml.get_widget('add_new_contact_window')
self.uid_entry = self.xml.get_widget('uid_entry')
self.protocol_combobox = self.xml.get_widget('protocol_combobox')
self.protocol_hbox = self.xml.get_widget('protocol_hbox')
self.jid_entry = self.xml.get_widget('jid_entry')
self.nickname_entry = self.xml.get_widget('nickname_entry')
for w in ('account_combobox', 'account_hbox', 'account_label',
'uid_label', 'uid_entry', 'protocol_combobox', 'protocol_jid_combobox',
'protocol_hbox', 'nickname_entry', 'message_scrolledwindow',
'register_hbox', 'subscription_table', 'add_button',
'message_textview', 'connected_label', 'group_comboboxentry'):
self.__dict__[w] = self.xml.get_widget(w)
if account and len(gajim.connections) >= 2:
prompt_text =\
_('Please fill in the data of the contact you want to add in account %s') %account
else:
prompt_text = _('Please fill in the data of the contact you want to add')
self.xml.get_widget('prompt_label').set_text(prompt_text)
self.old_uid_value = ''
liststore = gtk.ListStore(str, str)
liststore.append(['Jabber', ''])
self.agents = ['Jabber']
jid_agents = []
self.agents = {'jabber': []}
# types to which we are not subscribed but account has an agent for it
self.available_types = []
for acct in accounts:
for j in gajim.contacts.get_jid_list(acct):
contact = gajim.contacts.get_first_contact_from_jid(acct, j)
if _('Transports') in contact.groups and contact.show != 'offline' and\
contact.show != 'error':
jid_agents.append(j)
for a in jid_agents:
if a.find('aim') > -1:
name = 'AIM'
elif a.find('icq') > -1:
name = 'ICQ'
elif a.find('msn') > -1:
name = 'MSN'
elif a.find('yahoo') > -1:
name = 'Yahoo!'
else:
name = a
liststore.append([name, a])
self.agents.append(name)
self.protocol_combobox.set_model(liststore)
self.protocol_combobox.set_active(0)
self.fill_jid()
if jid:
self.jid_entry.set_text(jid)
self.uid_entry.set_sensitive(False)
jid_splited = jid.split('@')
if jid_splited[1] in jid_agents:
uid = jid_splited[0].replace('%', '@')
self.uid_entry.set_text(uid)
self.protocol_combobox.set_active(jid_agents.index(jid_splited[1])\
+ 1)
else:
self.uid_entry.set_text(jid)
self.protocol_combobox.set_active(0)
if user_nick:
self.nickname_entry.set_text(user_nick)
else:
self.set_nickname()
self.nickname_entry.grab_focus()
self.group_comboboxentry = self.xml.get_widget('group_comboboxentry')
if _('Transports') in contact.groups:
type_ = gajim.get_transport_name_from_jid(j)
if self.agents.has_key(type_):
self.agents[type_].append(j)
else:
self.agents[type_] = [j]
# Now add the one to which we can register
for acct in accounts:
for type_ in gajim.connections[account].available_transports:
if type_ in self.agents:
continue
self.agents[type_] = []
for jid_ in gajim.connections[account].available_transports[type_]:
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)
uf_type = {'jabber': 'Jabber', 'aim': 'AIM', 'gadu-gadu': 'Gadu Gadu',
'icq': 'ICQ', 'msn': 'MSN', 'yahoo': 'Yahoo'}
# Jabber as first
liststore.append(['Jabber', 'jabber'])
for type_ in self.agents:
if type_ == 'jabber':
continue
if type_ in uf_type:
liststore.append([uf_type[type_], type_])
else:
liststore.append([type_, type_])
self.protocol_combobox.set_model(liststore)
self.protocol_combobox.set_active(0)
self.protocol_jid_combobox.set_sensitive(False)
self.subscription_table.set_no_show_all(True)
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)
if jid:
type_ = gajim.get_transport_name_from_jid(jid) or 'jabber'
if type_ == 'jabber':
self.uid_entry.set_text(jid)
else:
uid, transport = gajim.get_room_name_and_server_from_room_jid(jid)
self.uid_entry.set_text(uid.replace('%', '@', 1))
#set protocol_combobox
model = self.protocol_combobox.get_model()
iter = model.get_iter_first()
i = 0
while iter:
if model[iter][1] == type_:
self.protocol_combobox.set_active(i)
break
iter = model.iter_next(iter)
i += 1
# set protocol_jid_combobox
self.protocol_combobox.set_active(0)
model = self.protocol_jid_combobox.get_model()
iter = model.get_iter_first()
i = 0
while iter:
if model[iter][0] == transport:
self.protocol_combobox.set_active(i)
break
iter = model.iter_next(iter)
i += 1
if user_nick:
self.nickname_entry.set_text(user_nick)
self.nickname_entry.grab_focus()
else:
self.uid_entry.grab_focus()
group_names = []
i = 0
for acct in accounts:
for g in gajim.groups[acct].keys():
if g not in helpers.special_groups and g not in group_names:
group_names.append(g)
self.group_comboboxentry.append_text(g)
if group == g:
self.group_comboboxentry.set_active(i)
i += 1
group_names.sort()
i = 0
for g in group_names:
self.group_comboboxentry.append_text(g)
if group == g:
self.group_comboboxentry.set_active(i)
i += 1
if not jid_agents:
# There are no transports, so hide the protocol combobox and label
self.protocol_hbox.hide()
self.protocol_hbox.set_no_show_all(True)
protocol_label = self.xml.get_widget('protocol_label')
protocol_label.hide()
protocol_label.set_no_show_all(True)
if self.account:
self.account_label.hide()
self.account_hbox.hide()
@ -468,9 +554,19 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
liststore.append([acct, acct])
self.account_combobox.set_model(liststore)
self.account_combobox.set_active(0)
self.xml.signal_autoconnect(self)
self.window.show_all()
def on_add_new_contact_window_destroy(self, widget):
if self.account:
location = gajim.interface.instances[self.account]
else:
location = gajim.interface.instances
del location['add_contact']
def on_register_button_clicked(self, widget):
jid = self.protocol_jid_combobox.get_active_text().decode('utf-8')
gajim.connections[self.account].request_register_agent_info(jid)
def on_add_new_contact_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape: # ESCAPE
self.window.destroy()
@ -479,13 +575,20 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
'''When Cancel button is clicked'''
self.window.destroy()
def on_subscribe_button_clicked(self, widget):
def on_add_button_clicked(self, widget):
'''When Subscribe button is clicked'''
jid = self.jid_entry.get_text().decode('utf-8')
nickname = self.nickname_entry.get_text().decode('utf-8')
jid = self.uid_entry.get_text().decode('utf-8')
if not jid:
return
model = self.protocol_combobox.get_model()
iter = self.protocol_combobox.get_active_iter()
type_ = model[iter][1]
if type_ != 'jabber':
transport = self.protocol_jid_combobox.get_active_text().decode(
'utf-8')
jid = jid.replace('@', '%') + '@' + transport
# check if jid is conform to RFC and stringprep it
try:
jid = helpers.parse_jid(jid)
@ -500,6 +603,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
ErrorDialog(pritext, _('The user ID must not contain a resource.'))
return
nickname = self.nickname_entry.get_text().decode('utf-8') or ''
# get value of account combobox, if account was not specified
if not self.account:
model = self.account_combobox.get_model()
@ -514,57 +618,81 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
_('This contact is already listed in your roster.'))
return
message_buffer = self.xml.get_widget('message_textview').get_buffer()
start_iter = message_buffer.get_start_iter()
end_iter = message_buffer.get_end_iter()
message = message_buffer.get_text(start_iter, end_iter).decode('utf-8')
if type_ == 'jabber':
message_buffer = self.message_textview.get_buffer()
start_iter = message_buffer.get_start_iter()
end_iter = message_buffer.get_end_iter()
message = message_buffer.get_text(start_iter, end_iter).decode('utf-8')
else:
message= ''
group = self.group_comboboxentry.child.get_text().decode('utf-8')
auto_auth = self.xml.get_widget('auto_authorize_checkbutton').get_active()
gajim.interface.roster.req_sub(self, jid, message, self.account,
group = group, pseudo = nickname, auto_auth = auto_auth)
self.window.destroy()
def fill_jid(self):
model = self.protocol_combobox.get_model()
index = self.protocol_combobox.get_active()
jid = self.uid_entry.get_text().decode('utf-8').strip()
if index > 0: # it's not jabber but a transport
jid = jid.replace('@', '%')
agent = model[index][1].decode('utf-8')
if agent:
jid += '@' + agent
self.jid_entry.set_text(jid)
def on_protocol_combobox_changed(self, widget):
self.fill_jid()
model = widget.get_model()
iter = widget.get_active_iter()
type_ = model[iter][1]
model = self.protocol_jid_combobox.get_model()
model.clear()
if len(self.agents[type_]):
for jid_ in self.agents[type_]:
model.append([jid_])
self.protocol_jid_combobox.set_active(0)
self.protocol_jid_combobox.set_sensitive(True)
else:
self.protocol_jid_combobox.set_sensitive(False)
if type_ in self.uid_labels:
self.uid_label.set_text(self.uid_labels[type_])
else:
self.uid_label.set_text(_('User ID'))
if type_ == 'jabber':
self.message_scrolledwindow.show()
else:
self.message_scrolledwindow.hide()
if type_ in self.available_types:
self.register_hbox.set_no_show_all(False)
self.register_hbox.show_all()
self.connected_label.hide()
self.subscription_table.hide()
self.add_button.set_sensitive(False)
else:
self.register_hbox.hide()
if type_ != 'jabber':
jid = self.protocol_jid_combobox.get_active_text()
contact = gajim.contacts.get_first_contact_from_jid(self.account,
jid)
if contact.show in ('offline', 'error'):
self.subscription_table.hide()
self.connected_label.show()
self.add_button.set_sensitive(False)
return
self.subscription_table.set_no_show_all(False)
self.subscription_table.show_all()
self.connected_label.hide()
self.add_button.set_sensitive(True)
def guess_agent(self):
uid = self.uid_entry.get_text().decode('utf-8')
model = self.protocol_combobox.get_model()
#If login contains only numbers, it's probably an ICQ number
if uid.isdigit():
if 'ICQ' in self.agents:
self.protocol_combobox.set_active(self.agents.index('ICQ'))
return
def transport_signed_in(self, jid):
if self.protocol_jid_combobox.get_active_text() == jid:
self.register_hbox.hide()
self.connected_label.hide()
self.subscription_table.set_no_show_all(False)
self.subscription_table.show_all()
self.add_button.set_sensitive(True)
def set_nickname(self):
uid = self.uid_entry.get_text().decode('utf-8')
nickname = self.nickname_entry.get_text().decode('utf-8')
if nickname == self.old_uid_value:
self.nickname_entry.set_text(uid.split('@')[0])
def on_uid_entry_changed(self, widget):
uid = self.uid_entry.get_text().decode('utf-8')
self.guess_agent()
self.set_nickname()
self.fill_jid()
self.old_uid_value = uid.split('@')[0]
def transport_signed_out(self, jid):
if self.protocol_jid_combobox.get_active_text() == jid:
self.subscription_table.hide()
self.connected_label.show()
self.add_button.set_sensitive(False)
class AboutDialog:
'''Class for about dialog'''
def __init__(self):
dlg = gtk.AboutDialog()
dlg.set_transient_for(gajim.interface.roster.window)
dlg.set_name('Gajim')
dlg.set_version(gajim.version)
s = u'Copyright © 2003-2006 Gajim Team'
@ -576,7 +704,7 @@ class AboutDialog:
% (_('A GTK+ jabber client'), \
_('GTK+ Version:'), self.tuple2str(gtk.gtk_version), \
_('PyGTK Version:'), self.tuple2str(gtk.pygtk_version)))
dlg.set_website('http://www.gajim.org')
dlg.set_website('http://www.gajim.org/')
authors = []
authors_file = open('../AUTHORS').read()
@ -585,7 +713,7 @@ class AboutDialog:
if author == 'CURRENT DEVELOPERS:':
authors.append(_('Current Developers:'))
elif author == 'PAST DEVELOPERS:':
authors.append('\n' +_('Past Developers:'))
authors.append('\n' + _('Past Developers:'))
elif author != '': # Real author line
authors.append(author)
@ -735,7 +863,7 @@ class FileChooserDialog(gtk.FileChooserDialog):
class BindPortError(HigDialog):
def __init__(self, port):
ErrorDialog(_('Unable to bind to port %s.' % port),
ErrorDialog(_('Unable to bind to port %s.') % port,
_('Maybe you have another running instance of Gajim. '
'File Transfer will be canceled.'))
@ -917,8 +1045,18 @@ class SubscriptionRequestWindow:
self.window.destroy()
class JoinGroupchatWindow:
def __init__(self, account, server = '', room = '', nick = ''):
def __init__(self, account, server = '', room = '', nick = '',
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'''
if server and room:
jid = room + '@' + server
if jid in gajim.gc_connected[account] and gajim.gc_connected[account][jid]:
ErrorDialog(_('You are already in room %s') % jid)
raise RuntimeError, 'You are already in this room'
self.account = account
self.automatic = automatic
if nick == '':
nick = gajim.nicks[self.account]
if gajim.connections[account].connected < 2:
@ -1019,10 +1157,12 @@ _('You can not join a group chat unless you are connected.'))
def on_join_button_clicked(self, widget):
'''When Join button is clicked'''
nickname = self.xml.get_widget('nickname_entry').get_text().decode('utf-8')
nickname = self.xml.get_widget('nickname_entry').get_text().decode(
'utf-8')
room = self.xml.get_widget('room_entry').get_text().decode('utf-8')
server = self.xml.get_widget('server_entry').get_text().decode('utf-8')
password = self.xml.get_widget('password_entry').get_text().decode('utf-8')
password = self.xml.get_widget('password_entry').get_text().decode(
'utf-8')
jid = '%s@%s' % (room, server)
try:
jid = helpers.parse_jid(jid)
@ -1037,7 +1177,9 @@ _('You can not join a group chat unless you are connected.'))
if len(self.recently_groupchat) > 10:
self.recently_groupchat = self.recently_groupchat[0:10]
gajim.config.set('recently_groupchat', ' '.join(self.recently_groupchat))
if self.automatic:
gajim.automatic_rooms[self.account][jid] = self.automatic
gajim.interface.roster.join_gc_room(self.account, jid, nickname, password)
self.window.destroy()
@ -1076,7 +1218,7 @@ class NewChatDialog(InputDialog):
if gajim.connections[self.account].connected <= 1:
#if offline or connecting
ErrorDialog(_('Connection not available'),
_('Please make sure you are connected with "%s".' % self.account))
_('Please make sure you are connected with "%s".') % self.account)
return
if self.completion_dict.has_key(jid):
@ -1088,7 +1230,7 @@ class NewChatDialog(InputDialog):
ErrorDialog(_('Invalid JID'), e[0])
return
except:
ErrorDialog(_('Invalid JID'), _('Unable to parse "%s".' % jid))
ErrorDialog(_('Invalid JID'), _('Unable to parse "%s".') % jid)
return
gajim.interface.roster.new_chat_from_jid(self.account, jid)
@ -1284,10 +1426,13 @@ class SingleMessageWindow:
if gajim.config.get('use_speller') and HAS_GTK_SPELL and action == 'send':
try:
gtkspell.Spell(self.conversation_textview.tv)
gtkspell.Spell(self.message_textview)
spell1 = gtkspell.Spell(self.conversation_textview.tv)
spell2 = gtkspell.Spell(self.message_textview)
lang = gajim.config.get('speller_language')
if lang:
spell1.set_language(lang)
spell2.set_language(lang)
except gobject.GError, msg:
#FIXME: add a ui for this use spell.set_language()
ErrorDialog(unicode(msg), _('If that is not your language for which you want to highlight misspelled words, then please set your $LANG as appropriate. Eg. for French do export LANG=fr_FR or export LANG=fr_FR.UTF-8 in ~/.bash_profile or to make it global in /etc/profile.\n\nHighlighting misspelled words feature will not be used'))
gajim.config.set('use_speller', False)
@ -1355,8 +1500,10 @@ class SingleMessageWindow:
def prepare_widgets_for(self, action):
if len(gajim.connections) > 1:
#FIXME: for Received with should become 'in'
title = _('Single Message with account %s') % self.account
if action == 'send':
title = _('Single Message using account %s') % self.account
else:
title = _('Single Message in account %s') % self.account
else:
title = _('Single Message')
@ -1425,7 +1572,7 @@ class SingleMessageWindow:
if gajim.connections[self.account].connected <= 1:
# if offline or connecting
ErrorDialog(_('Connection not available'),
_('Please make sure you are connected with "%s".' % self.account))
_('Please make sure you are connected with "%s".') % self.account)
return
to_whom_jid = self.to_entry.get_text().decode('utf-8')
if self.completion_dict.has_key(to_whom_jid):
@ -1452,7 +1599,7 @@ class SingleMessageWindow:
def on_reply_button_clicked(self, widget):
# we create a new blank window to send and we preset RE: and to jid
self.subject = _('RE: %s') % self.subject
self.message = _('%s wrote:\n' % self.from_whom) + self.message
self.message = _('%s wrote:\n') % self.from_whom + self.message
# add > at the begining of each line
self.message = self.message.replace('\n', '\n> ') + '\n\n'
self.window.destroy()
@ -1549,7 +1696,7 @@ class XMLConsoleWindow:
if gajim.connections[self.account].connected <= 1:
#if offline or connecting
ErrorDialog(_('Connection not available'),
_('Please make sure you are connected with "%s".' % self.account))
_('Please make sure you are connected with "%s".') % self.account)
return
begin_iter, end_iter = self.input_tv_buffer.get_bounds()
stanza = self.input_tv_buffer.get_text(begin_iter, end_iter).decode('utf-8')
@ -1889,6 +2036,7 @@ class PrivacyListsWindow:
'privacy_lists_refresh_button', 'close_privacy_lists_window_button']:
self.__dict__[widget_to_add] = self.xml.get_widget(widget_to_add)
self.draw_privacy_lists_in_combobox()
self.privacy_lists_refresh()
self.enabled = True
@ -2008,7 +2156,10 @@ class InvitationReceivedDialog:
def on_accept_button_clicked(self, widget):
self.dialog.destroy()
room, server = gajim.get_room_name_and_server_from_room_jid(self.room_jid)
JoinGroupchatWindow(self.account, server = server, room = room)
try:
JoinGroupchatWindow(self.account, server = server, room = room)
except RuntimeError:
pass
class ProgressDialog:
def __init__(self, title_text, during_text, messages_queue):
@ -2081,6 +2232,8 @@ class ImageChooserDialog(FileChooserDialog):
def on_ok(widget, callback):
'''check if file exists and call callback'''
path_to_file = self.get_filename()
if not path_to_file:
return
path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
(path_to_file,))[0]
if os.path.exists(path_to_file):
@ -2254,8 +2407,6 @@ class AdvancedNotificationsWindow:
# No rule selected at init time
self.conditions_treeview.get_selection().unselect_all()
#TODO
# self.conditions_treeview.set_cursor(None)
self.active_num = -1
self.config_vbox.set_sensitive(False)
self.delete_button.set_sensitive(False)
@ -2277,8 +2428,7 @@ class AdvancedNotificationsWindow:
if value:
self.event_combobox.set_active(self.events_list.index(value))
else:
#TODO: unselect all
pass
self.event_combobox.set_active(-1)
# recipient_type
value = gajim.config.get_per('notifications', str(self.active_num),
'recipient_type')
@ -2286,8 +2436,7 @@ class AdvancedNotificationsWindow:
self.recipient_type_combobox.set_active(
self.recipient_types_list.index(value))
else:
#TODO: unselect all
pass
self.recipient_type_combobox.set_active(-1)
# recipient
value = gajim.config.get_per('notifications', str(self.active_num),
'recipients')
@ -2457,7 +2606,11 @@ class AdvancedNotificationsWindow:
def on_event_combobox_changed(self, widget):
if self.active_num < 0:
return
event = self.events_list[self.event_combobox.get_active()]
active = self.event_combobox.get_active()
if active == -1:
event = ''
else:
event = self.events_list[active]
gajim.config.set_per('notifications', str(self.active_num), 'event',
event)
self.set_treeview_string()

View file

@ -87,7 +87,7 @@ def _gen_agent_type_info():
('proxy', 'bytestreams'): (None, 'bytestreams.png'), # Socks5 FT proxy
# Transports
('conference', 'irc'): (False, 'irc.png'),
('conference', 'irc'): (ToplevelAgentBrowser, 'irc.png'),
('_jid', 'irc'): (False, 'irc.png'),
('gateway', 'aim'): (False, 'aim.png'),
('_jid', 'aim'): (False, 'aim.png'),
@ -135,7 +135,8 @@ class CacheDictionary:
def _expire_timeout(self, key):
'''The timeout has expired, remove the object.'''
del self.cache[key]
if key in self.cache:
del self.cache[key]
return False
def _refresh_timeout(self, key):
@ -274,7 +275,7 @@ class ServicesCache:
except KeyError:
continue
browser = info[0]
if browser is not None:
if browser:
break
# Note: possible outcome here is browser=False
if browser is None:
@ -457,7 +458,6 @@ _('Without a connection, you can not browse available services'))
self.address_comboboxentry.set_text_column(0)
self.latest_addresses = gajim.config.get(
'latest_disco_addresses').split()
jid = gajim.get_hostname_from_account(self.account)
if jid in self.latest_addresses:
self.latest_addresses.remove(jid)
self.latest_addresses.insert(0, jid)
@ -1202,7 +1202,10 @@ class ToplevelAgentBrowser(AgentBrowser):
else:
room = ''
if not gajim.interface.instances[self.account].has_key('join_gc'):
dialogs.JoinGroupchatWindow(self.account, service, room)
try:
dialogs.JoinGroupchatWindow(self.account, service, room)
except RuntimeError:
pass
else:
gajim.interface.instances[self.account]['join_gc'].window.present()
self.window.destroy(chain = True)
@ -1532,7 +1535,10 @@ class MucBrowser(AgentBrowser):
else:
room = model[iter][1].decode('utf-8')
if not gajim.interface.instances[self.account].has_key('join_gc'):
dialogs.JoinGroupchatWindow(self.account, service, room)
try:
dialogs.JoinGroupchatWindow(self.account, service, room)
except RuntimeError:
pass
else:
gajim.interface.instances[self.account]['join_gc'].window.present()
self.window.destroy(chain = True)

View file

@ -221,7 +221,7 @@ _('Connection with peer cannot be established.'))
else:
file_name = file_props['name']
sectext = '\t' + _('Filename: %s') % file_name
sectext += '\n\t' + _('Sender: %s') % jid
sectext += '\n\t' + _('Recipient: %s') % jid
dialogs.ErrorDialog(_('File transfer stopped by the contact of the other side'), \
sectext)
self.tree.get_selection().unselect_all()
@ -328,7 +328,7 @@ _('Connection with peer cannot be established.'))
else:
dirname = os.path.dirname(file_path)
if not os.access(dirname, os.W_OK):
dialogs.ErrorDialog(_('Directory "%s" is not writable' % dirname), _('You do not have permission to create files in this directory.'))
dialogs.ErrorDialog(_('Directory "%s" is not writable') % dirname, _('You do not have permission to create files in this directory.'))
return
dialog2.destroy()
self._start_receive(file_path, account, contact, file_props)
@ -445,12 +445,11 @@ _('Connection with peer cannot be established.'))
jid = gajim.get_jid_without_resource(other)
else: # It's a Contact instance
jid = other.jid
if gajim.awaiting_events[account].has_key(jid):
for event in gajim.awaiting_events[account][jid]:
if event[0] in ('file-error', 'file-completed',
'file-request-error', 'file-send-error', 'file-stopped') and \
event[1]['sid'] == file_props['sid']:
gajim.interface.remove_event(account, jid, event)
for ev_type in ('file-error', 'file-completed', 'file-request-error',
'file-send-error', 'file-stopped'):
for event in gajim.events.get_events(account, jid, [ev_type]):
if event.parameters[1]['sid'] == file_props['sid']:
gajim.events.remove_events(account, jid, event)
del(self.files_props[sid[0]][sid[1:]])
del(file_props)

View file

@ -66,7 +66,7 @@ BASENAME = 'gajim-remote'
class GajimRemote:
def __init__(self):
self.argv_len = len(sys.argv)
# define commands dict. Prototype :
@ -79,7 +79,7 @@ class GajimRemote:
#
self.commands = {
'help':[
_('shows a help on specific command'),
_('Shows a help on specific command'),
[
#User gets help for the command, specified by this parameter
(_('command'),
@ -99,7 +99,7 @@ class GajimRemote:
[
(_('account'), _('show only contacts of the given account'), False)
]
],
'list_accounts': [
_('Prints a list of registered accounts'),
@ -201,7 +201,7 @@ class GajimRemote:
('jid', _('JID of the contact'), True),
(_('account'), _('if specified, contact is taken from the '
'contact list of this account'), False)
]
],
'add_contact': [
@ -211,14 +211,14 @@ class GajimRemote:
(_('account'), _('Adds new contact to this account'), False)
]
],
'get_status': [
_('Returns current status (the global one unless account is specified)'),
[
(_('account'), _(''), False)
]
],
'get_status_message': [
_('Returns current status message(the global one unless account is specified)'),
[
@ -231,11 +231,20 @@ class GajimRemote:
[ ]
],
'start_chat': [
_('Open \'Start Chat\' dialog'),
_('Opens \'Start Chat\' dialog'),
[
(_('account'), _('Starts chat, using this account'), True)
]
],
'send_xml': [
_('Sends custom XML'),
[
('xml', _('XML to send'), True),
('account', _('Account in which the xml will be sent; '
'if not specified, xml will be sent to all accounts'),
False)
]
],
}
if self.argv_len < 2 or \
sys.argv[1] not in self.commands.keys(): # no args or bad args
@ -247,14 +256,14 @@ class GajimRemote:
else:
print self.compose_help().encode(PREFERRED_ENCODING)
sys.exit(0)
self.init_connection()
self.check_arguments()
if self.command == 'contact_info':
if self.argv_len < 3:
send_error(_('Missing argument "contact_jid"'))
try:
res = self.call_remote_method()
except exceptions.ServiceNotAvailable:
@ -262,14 +271,14 @@ class GajimRemote:
sys.exit(1)
else:
self.print_result(res)
def print_result(self, res):
''' Print retrieved result to the output '''
if res is not None:
if self.command in ('open_chat', 'send_message', 'send_single_message', 'start_chat'):
if self.command in ('send_message', 'send_single_message'):
self.argv_len -= 2
if res is False:
if self.argv_len < 4:
send_error(_('\'%s\' is not in your roster.\n'
@ -302,7 +311,7 @@ class GajimRemote:
print self.print_info(0, res, True)
elif res:
print unicode(res).encode(PREFERRED_ENCODING)
def init_connection(self):
''' create the onnection to the session dbus,
or exit if it is not possible '''
@ -310,7 +319,7 @@ class GajimRemote:
self.sbus = dbus.SessionBus()
except:
raise exceptions.SessionBusNotPresent
if _version[1] >= 30:
obj = self.sbus.get_object(SERVICE, OBJ_PATH)
interface = dbus.Interface(obj, INTERFACE)
@ -319,10 +328,10 @@ class GajimRemote:
interface = self.service.get_object(OBJ_PATH, INTERFACE)
else:
send_error(_('Unknown D-Bus version: %s') % _version[1])
# get the function asked
self.method = interface.__getattr__(self.command)
def make_arguments_row(self, args):
''' return arguments list. Mandatory arguments are enclosed with:
'<', '>', optional arguments - with '[', ']' '''
@ -339,7 +348,7 @@ class GajimRemote:
else:
str += ']'
return str
def help_on_command(self, command):
''' return help message for a given command '''
if command in self.commands:
@ -353,7 +362,7 @@ class GajimRemote:
str += ' ' + argument[0] + ' - ' + argument[1] + '\n'
return str
send_error(_('%s not found') % command)
def compose_help(self):
''' print usage, and list available commands '''
str = _('Usage: %s command [arguments]\nCommand is one of:\n' ) % BASENAME
@ -374,7 +383,7 @@ class GajimRemote:
str += ']'
str += '\n'
return str
def print_info(self, level, prop_dict, encode_return = False):
''' return formated string from data structure '''
if prop_dict is None or not isinstance(prop_dict, (dict, list, tuple)):
@ -423,7 +432,7 @@ class GajimRemote:
except:
pass
return ret_str
def check_arguments(self):
''' Make check if all necessary arguments are given '''
argv_len = self.argv_len - 2
@ -433,7 +442,7 @@ class GajimRemote:
send_error(_('Argument "%s" is not specified. \n'
'Type "%s help %s" for more info') %
(args[argv_len][0], BASENAME, self.command))
def call_remote_method(self):
''' calls self.method with arguments from sys.argv[2:] '''
args = sys.argv[2:]

View file

@ -42,6 +42,14 @@ from atom_window import AtomWindow
from common import exceptions
if os.name == 'posix': # dl module is Unix Only
try: # rename the process name to gajim
import dl
libc = dl.open('/lib/libc.so.6')
libc.call('prctl', 15, 'gajim\0', 0, 0, 0)
except:
pass
try:
import gtk
except RuntimeError, msg:
@ -71,6 +79,15 @@ except exceptions.PysqliteNotAvailable, e:
pritext = _('Gajim needs PySQLite2 to run')
sectext = str(e)
if os.name == 'nt':
try:
import winsound # windows-only built-in module for playing wav
import win32api
import win32con
except:
pritext = _('Gajim needs pywin32 to run')
sectext = _('Please make sure that Pywin32 is installed on your system. You can get it at %s') % 'http://sourceforge.net/project/showfiles.php?group_id=78018'
if pritext:
dlg = gtk.MessageDialog(None,
gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
@ -178,23 +195,9 @@ parser = optparser.OptionsParser(config_filename)
import roster_window
import systray
import vcard
import profile_window
import config
class MigrateCommand(nslookup.IdleCommand):
def __init__(self, on_result):
nslookup.IdleCommand.__init__(self, on_result)
self.commandtimeout = 10
def _compose_command_args(self):
return ['python', 'migrate_logs_to_dot9_db.py', 'dont_wait']
def _return_result(self):
print self.result
if self.result_handler:
self.result_handler(self.result)
self.result_handler = None
class GlibIdleQueue(idlequeue.IdleQueue):
'''
Extends IdleQueue to use glib io_add_wath, instead of select/poll
@ -307,6 +310,14 @@ class Interface:
gajim.con_types[account] = con_type
self.roster.draw_account(account)
def handle_event_connection_lost(self, account, array):
# ('CONNECTION_LOST', account, [title, text])
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'connection_lost.png')
path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
notify.popup(_('Connection Failed'), account, account,
'connection_failed', path, array[0], array[1])
def unblock_signed_in_notifications(self, account):
gajim.block_signed_in_notifications[account] = False
@ -346,9 +357,9 @@ class Interface:
def edit_own_details(self, account):
jid = gajim.get_jid_from_account(account)
if not self.instances[account]['infos'].has_key(jid):
self.instances[account]['infos'][jid] = \
vcard.VcardWindow(jid, account, True)
if not self.instances[account].has_key('profile'):
self.instances[account]['profile'] = \
profile_window.ProfileWindow(account)
gajim.connections[account].request_vcard(jid)
def handle_event_notify(self, account, array):
@ -396,7 +407,9 @@ class Interface:
else:
contact1 = gajim.contacts.get_first_contact_from_jid(account, ji)
if not contact1:
# presence of another resource of out jid
# presence of another resource of our jid
if resource == gajim.connections[account].server_resource:
return
contact1 = gajim.contacts.create_contact(jid = ji,
name = gajim.nicks[account], groups = [],
show = array[1], status = status_message, sub = 'both',
@ -452,6 +465,15 @@ class Interface:
gajim.block_signed_in_notifications[account_ji] = True
gobject.timeout_add(30000, self.unblock_signed_in_notifications,
account_ji)
locations = (self.instances, self.instances[account])
for location in locations:
if location.has_key('add_contact'):
if old_show == 0 and new_show > 1:
location['add_contact'].transport_signed_in(jid)
break
elif old_show > 1 and new_show == 0:
location['add_contact'].transport_signed_out(jid)
break
elif ji in jid_list:
# It isn't an agent
# reset chatstate if needed:
@ -495,6 +517,7 @@ class Interface:
message = array[1]
msg_type = array[4]
subject = array[5]
chatstate = array[6]
msg_id = array[7]
composing_jep = array[8]
@ -560,10 +583,13 @@ class Interface:
not gajim.contacts.get_contact(account, jid) and not pm:
return
advanced_notif_num = notify.get_advanced_notification('message_received',
account, contact)
# Is it a first or next message received ?
first = False
if not chat_control and not gajim.awaiting_events[account].has_key(
jid_of_control):
if not chat_control and not gajim.events.get_events(account,
jid_of_control, ['chat']):
# It's a first message and not a Private Message
first = True
@ -574,11 +600,14 @@ class Interface:
else:
# array: (jid, msg, time, encrypted, msg_type, subject)
self.roster.on_message(jid, message, array[2], account, array[3],
msg_type, array[5], resource, msg_id, array[9])
msg_type, subject, resource, msg_id, array[9], advanced_notif_num)
nickname = gajim.get_name_from_jid(account, jid)
# Check and do wanted notifications
msg = message
if subject:
msg = _('Subject: %s') % subject + '\n' + msg
notify.notify('new_message', jid, account, [msg_type, first, nickname,
message])
msg], advanced_notif_num)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('NewMessage', (account, array))
@ -712,8 +741,8 @@ class Interface:
config.ServiceRegistrationWindow(array[0], array[1], account,
array[2])
else:
dialogs.ErrorDialog(_('Contact with "%s" cannot be established'\
% array[0]), _('Check your connection or try again later.'))
dialogs.ErrorDialog(_('Contact with "%s" cannot be established')\
% array[0], _('Check your connection or try again later.'))
def handle_event_agent_info_items(self, account, array):
#('AGENT_INFO_ITEMS', account, (agent, node, items))
@ -753,8 +782,8 @@ class Interface:
nick = array['NICKNAME']
if nick:
gajim.nicks[account] = nick
if self.instances[account]['infos'].has_key(array['jid']):
win = self.instances[account]['infos'][array['jid']]
if self.instances[account].has_key('profile'):
win = self.instances[account]['profile']
win.set_values(array)
if account in self.show_vcard_when_connect:
self.show_vcard_when_connect.remove(account)
@ -859,7 +888,6 @@ class Interface:
uf_show = helpers.get_uf_show(show)
ctrl.print_conversation(_('%s is now %s (%s)') % (nick, uf_show, status),
'status')
ctrl.draw_banner()
ctrl.parent_win.redraw_tab(ctrl)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('GCPresence', (account, array))
@ -899,10 +927,18 @@ class Interface:
def handle_event_gc_config(self, account, array):
#('GC_CONFIG', account, (jid, config)) config is a dict
jid = array[0].split('/')[0]
if not self.instances[account]['gc_config'].has_key(jid):
self.instances[account]['gc_config'][jid] = \
config.GroupchatConfigWindow(account, jid, array[1])
room_jid = array[0].split('/')[0]
if room_jid in gajim.automatic_rooms[account]:
# use default configuration
gajim.connections[account].send_gc_config(room_jid, array[1])
# invite contacts
if gajim.automatic_rooms[account][room_jid].has_key('invities'):
for jid in gajim.automatic_rooms[account][room_jid]['invities']:
gajim.connections[account].send_invite(room_jid, jid)
del gajim.automatic_rooms[account][room_jid]
elif not self.instances[account]['gc_config'].has_key(room_jid):
self.instances[account]['gc_config'][room_jid] = \
config.GroupchatConfigWindow(account, room_jid, array[1])
def handle_event_gc_affiliation(self, account, array):
#('GC_AFFILIATION', account, (room_jid, affiliation, list)) list is list
@ -923,8 +959,7 @@ class Interface:
self.add_event(account, jid, 'gc-invitation', (room_jid, array[2],
array[3]))
if gajim.config.get('notify_on_new_message') and \
helpers.allow_showing_notification(account):
if helpers.allow_showing_notification(account, 'notify_on_new_message'):
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'gc_invitation.png')
path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
@ -956,7 +991,7 @@ class Interface:
c = contacts[0]
self.roster.remove_contact(c, account)
gajim.contacts.remove_jid(account, jid)
if gajim.awaiting_events[account].has_key(c.jid):
if gajim.events.get_events(account, c.jid):
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
@ -1091,60 +1126,48 @@ class Interface:
path_to_bw_file = path_to_file + '_notif_size_bw.png'
bwbuf.save(path_to_bw_file, 'png')
def add_event(self, account, jid, typ, args):
'''add an event to the awaiting_events var'''
# We add it to the awaiting_events queue
def add_event(self, account, jid, type_, args):
'''add an event to the gajim.events var'''
# We add it to the gajim.events queue
# Do we have a queue?
jid = gajim.get_jid_without_resource(jid)
qs = gajim.awaiting_events[account]
no_queue = False
if not qs.has_key(jid):
no_queue = True
qs[jid] = []
qs[jid].append((typ, args))
self.roster.nb_unread += 1
no_queue = len(gajim.events.get_events(account, jid)) == 0
event_type = None
# type_ can be gc-invitation file-send-error file-error file-request-error
# file-request file-completed file-stopped
# event_type can be in advancedNotificationWindow.events_list
event_types = {'file-request': 'ft_request',
'file-completed': 'ft_finished'}
if type_ in event_types:
event_type = event_types[type_]
show_in_roster = notify.get_show_in_roster(event_type, account, jid)
show_in_systray = notify.get_show_in_systray(event_type, account, jid)
event = gajim.events.create_event(type_, args,
show_in_roster = show_in_roster,
show_in_systray = show_in_systray)
gajim.events.add_event(account, jid, event)
self.roster.show_title()
if no_queue: # We didn't have a queue: we change icons
self.roster.draw_contact(jid, account)
if self.systray_enabled:
self.systray.add_jid(jid, account, typ)
def redraw_roster_systray(self, account, jid, typ = None):
self.roster.nb_unread -= 1
self.roster.show_title()
self.roster.draw_contact(jid, account)
if self.systray_enabled:
self.systray.remove_jid(jid, account, typ)
def remove_first_event(self, account, jid, type_ = None):
event = gajim.events.get_first_event(account, jid, type_)
self.remove_event(account, jid, event)
def remove_first_event(self, account, jid, typ = None):
qs = gajim.awaiting_events[account]
event = gajim.get_first_event(account, jid, typ)
qs[jid].remove(event)
# Is it the last event?
if not len(qs[jid]):
del qs[jid]
def remove_event(self, account, jid, event):
if gajim.events.remove_events(account, jid, event):
# No such event found
return
# no other event?
if not len(gajim.events.get_events(account, jid)):
if not gajim.config.get('showoffline'):
contact = gajim.contacts.get_contact_with_highest_priority(account,
jid)
if contact:
self.roster.really_remove_contact(contact, account)
self.redraw_roster_systray(account, jid, typ)
def remove_event(self, account, jid, event):
qs = gajim.awaiting_events[account]
if not event in qs[jid]:
return
qs[jid].remove(event)
# Is it the last event?
if not len(qs[jid]):
del qs[jid]
if not gajim.config.get('showoffline'):
contact = gajim.contacts.get_contact_with_highest_priority(account,
jid)
if contact:
self.roster.really_remove_contact(contact, account)
self.redraw_roster_systray(account, jid, event[0])
self.roster.show_title()
self.roster.draw_contact(jid, account)
def handle_event_file_request_error(self, account, array):
jid = array[0]
@ -1170,7 +1193,7 @@ class Interface:
if helpers.allow_showing_notification(account):
# check if we should be notified
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', 'ft_error.png')
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
event_type = _('File Transfer Error')
notify.popup(event_type, jid, account, msg_type, path,
@ -1193,7 +1216,8 @@ class Interface:
if helpers.allow_showing_notification(account):
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'ft_request.png')
txt = _('%s wants to send you a file.') % gajim.get_name_from_jid(account, jid)
txt = _('%s wants to send you a file.') % gajim.get_name_from_jid(
account, jid)
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
event_type = _('File Transfer Request')
notify.popup(event_type, jid, account, 'file-request',
@ -1202,7 +1226,7 @@ class Interface:
def handle_event_file_progress(self, account, file_props):
self.instances['file_transfers'].set_progress(file_props['type'],
file_props['sid'], file_props['received-len'])
def handle_event_file_rcv_completed(self, account, file_props):
ft = self.instances['file_transfers']
if file_props['error'] == 0:
@ -1228,13 +1252,14 @@ class Interface:
msg_type = ''
event_type = ''
if file_props['error'] == 0 and gajim.config.get('notify_on_file_complete'):
if file_props['error'] == 0 and gajim.config.get(
'notify_on_file_complete'):
msg_type = 'file-completed'
event_type = _('File Transfer Completed')
elif file_props['error'] == -1:
msg_type = 'file-stopped'
event_type = _('File Transfer Stopped')
if event_type == '':
# FIXME: ugly workaround (this can happen Gajim sent, Gaim recvs)
# this should never happen but it does. see process_result() in socks5.py
@ -1244,7 +1269,7 @@ class Interface:
if msg_type:
self.add_event(account, jid, msg_type, file_props)
if file_props is not None:
if file_props['type'] == 'r':
# get the name of the sender, as it is in the roster
@ -1429,12 +1454,12 @@ class Interface:
return False
def show_systray(self):
self.systray.show_icon()
self.systray_enabled = True
self.systray.show_icon()
def hide_systray(self):
self.systray.hide_icon()
self.systray_enabled = False
self.systray.hide_icon()
def image_is_ok(self, image):
if not os.path.exists(image):
@ -1664,6 +1689,7 @@ class Interface:
'ROSTER_INFO': self.handle_event_roster_info,
'BOOKMARKS': self.handle_event_bookmarks,
'CON_TYPE': self.handle_event_con_type,
'CONNECTION_LOST': self.handle_event_connection_lost,
'FILE_REQUEST': self.handle_event_file_request,
'GMAIL_NOTIFY': self.handle_event_gmail_notify,
'FILE_REQUEST_ERROR': self.handle_event_file_request_error,
@ -1700,14 +1726,14 @@ class Interface:
err_str)
sys.exit()
def handle_event(self, account, jid, typ):
def handle_event(self, account, jid, type_):
w = None
fjid = jid
resource = gajim.get_resource_from_jid(jid)
jid = gajim.get_jid_without_resource(jid)
if typ == message_control.TYPE_GC:
if type_ in ('printed_gc_msg', 'gc_msg'):
w = self.msg_win_mgr.get_window(jid, account)
elif typ == message_control.TYPE_CHAT:
elif type_ in ('printed_chat', 'chat'):
if self.msg_win_mgr.has_window(fjid, account):
w = self.msg_win_mgr.get_window(fjid, account)
else:
@ -1717,30 +1743,30 @@ class Interface:
self.roster.new_chat(contact, account, resource = resource)
w = self.msg_win_mgr.get_window(fjid, account)
gajim.last_message_time[account][jid] = 0 # long time ago
elif typ == message_control.TYPE_PM:
elif type_ in ('printed_pm', 'pm'):
if self.msg_win_mgr.has_window(fjid, account):
w = self.msg_win_mgr.get_window(fjid, account)
else:
room_jid = jid
nick = resource
gc_contact = gajim.contacts.get_gc_contact(account, room_jid,
nick)
nick)
if gc_contact:
show = gc_contact.show
else:
show = 'offline'
gc_contact = gajim.contacts.create_gc_contact(room_jid = room_jid,
name = nick, show = show)
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)
w = self.msg_win_mgr.get_window(fjid, account)
elif typ in ('normal', 'file-request', 'file-request-error',
'file-send-error', 'file-error', 'file-stopped', 'file-completed'):
elif type_ in ('normal', 'file-request', 'file-request-error',
'file-send-error', 'file-error', 'file-stopped', 'file-completed'):
# Get the first single message event
ev = gajim.get_first_event(account, jid, typ)
event = gajim.events.get_first_event(account, jid, type_)
# Open the window
self.roster.open_event(account, jid, ev)
elif typ == 'gmail':
self.roster.open_event(account, jid, event)
elif type_ == 'gmail':
if gajim.config.get_per('accounts', account, 'savepass'):
url = ('http://www.google.com/accounts/ServiceLoginAuth?service=mail&Email=%s&Passwd=%s&continue=https://mail.google.com/mail') %\
(urllib.quote(gajim.config.get_per('accounts', account, 'name')),
@ -1748,12 +1774,12 @@ class Interface:
else:
url = ('http://mail.google.com/')
helpers.launch_browser_mailer('url', url)
elif typ == 'gc-invitation':
ev = gajim.get_first_event(account, jid, typ)
data = ev[1]
elif type_ == 'gc-invitation':
event = gajim.events.get_first_event(account, jid, type_)
data = event.parameters
dialogs.InvitationReceivedDialog(account, data[0], jid, data[2],
data[1])
self.remove_first_event(account, jid, typ)
gajim.events.remove_events(account, jid, event)
if w:
w.set_active_tab(fjid, account)
w.window.present()
@ -1840,14 +1866,13 @@ class Interface:
self.instances = {'logs': {}}
for a in gajim.connections:
self.instances[a] = {'infos': {}, 'disco': {}, 'chats': {},
'gc': {}, 'gc_config': {}}
self.instances[a] = {'infos': {}, 'disco': {}, 'gc_config': {}}
gajim.contacts.add_account(a)
gajim.groups[a] = {}
gajim.gc_connected[a] = {}
gajim.automatic_rooms[a] = {}
gajim.newly_added[a] = []
gajim.to_be_removed[a] = []
gajim.awaiting_events[a] = {}
gajim.nicks[a] = gajim.config.get_per('accounts', a, 'name')
gajim.block_signed_in_notifications[a] = True
gajim.sleeper_state[a] = 0
@ -1901,6 +1926,25 @@ class Interface:
# get instances for windows/dialogs that will show_all()/hide()
self.instances['file_transfers'] = dialogs.FileTransfersWindow()
# get transports type from DB
gajim.transport_type = gajim.logger.get_transports_type()
# test is dictionnary is present for speller
if gajim.config.get('use_speller'):
lang = gajim.config.get('speller_language')
if not lang:
lang = gajim.LANG
tv = gtk.TextView()
try:
import gtkspell
spell = gtkspell.Spell(tv, lang)
except:
dialogs.ErrorDialog(
_('Dictionary for lang %s not available') % lang,
_('You have to install %s dictionary to use spellchecking, or '
'choose another language by setting the speller_language option.'
) % lang)
gajim.config.set('use_speller', False)
gobject.timeout_add(100, self.autoconnect)
gobject.timeout_add(200, self.process_connections)
gobject.timeout_add(500, self.read_sleepy)
@ -1938,30 +1982,6 @@ if __name__ == '__main__':
gtkgui_helpers.possibly_set_gajim_as_xmpp_handler()
# Migrate old logs if we have such olds logs
from common import logger
from migrate_logs_to_dot9_db import PATH_TO_LOGS_BASE_DIR
LOG_DB_PATH = logger.LOG_DB_PATH
if not os.path.exists(LOG_DB_PATH) and os.path.isdir(PATH_TO_LOGS_BASE_DIR):
import Queue
q = Queue.Queue(100)
dialog = dialogs.ProgressDialog(_('Migrating Logs...'),
_('Please wait while logs are being migrated...'), q)
if os.name == 'nt' and gtk.pygtk_version > (2, 8, 0):
idlequeue = idlequeue.SelectIdleQueue()
else:
idlequeue = GlibIdleQueue()
def on_result(*arg):
dialog.dialog.destroy()
dialog.dialog = None
gobject.source_remove(dialog.update_progressbar_timeout_id)
gajim.logger.init_vars()
check_paths.check_and_possibly_create_paths()
Interface()
m = MigrateCommand(on_result)
m.set_idlequeue(idlequeue)
m.start()
else:
check_paths.check_and_possibly_create_paths()
Interface()
check_paths.check_and_possibly_create_paths()
Interface()
gtk.main()

View file

@ -36,6 +36,7 @@ class GajimThemesWindow:
def __init__(self):
self.xml = gtkgui_helpers.get_glade('gajim_themes_window.glade')
self.window = self.xml.get_widget('gajim_themes_window')
self.window.set_transient_for(gajim.interface.roster.window)
self.options = ['account', 'group', 'contact', 'banner']
self.options_combobox = self.xml.get_widget('options_combobox')

View file

@ -119,6 +119,13 @@ class PrivateChatControl(ChatControl):
return
ChatControl.send_message(self, message)
def update_ui(self):
if self.contact.show == 'offline':
self.got_disconnected()
else:
self.got_connected()
ChatControl.update_ui(self)
class GroupchatControl(ChatControlBase):
@ -189,10 +196,6 @@ class GroupchatControl(ChatControlBase):
self.tooltip = tooltips.GCTooltip()
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
# connect the menuitems to their respective functions
xm = gtkgui_helpers.get_glade('gc_control_popup_menu.glade')
@ -284,12 +287,9 @@ class GroupchatControl(ChatControlBase):
column.set_visible(False)
self.list_treeview.set_expander_column(column)
id = self.msg_textview.connect('populate_popup',
self.on_msg_textview_populate_popup)
self.handlers[id] = self.msg_textview
# set an empty subject to show the room_jid
self.set_subject('')
self.got_disconnected() #init some variables
self.got_disconnected() # init some variables
self.update_ui()
self.conv_textview.tv.grab_focus()
@ -298,20 +298,18 @@ class GroupchatControl(ChatControlBase):
def on_msg_textview_populate_popup(self, textview, menu):
'''we override the default context menu and we prepend Clear
and the ability to insert a nick'''
ChatControlBase.on_msg_textview_populate_popup(self, textview, menu)
item = gtk.SeparatorMenuItem()
menu.prepend(item)
item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
menu.prepend(item)
id = item.connect('activate', self.msg_textview.clear)
self.handlers[id] = item
item = gtk.MenuItem(_('Insert Nickname'))
menu.prepend(item)
submenu = gtk.Menu()
item.set_submenu(submenu)
for nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
item = gtk.MenuItem(nick)
for nick in sorted(gajim.contacts.get_nick_list(self.account,
self.room_jid)):
item = gtk.MenuItem(nick, use_underline = False)
submenu.append(item)
id = item.connect('activate', self.append_nick_in_msg_textview, nick)
self.handlers[id] = item
@ -325,7 +323,7 @@ class GroupchatControl(ChatControlBase):
def _on_window_focus_in_event(self, widget, event):
'''When window gets focus'''
if self.parent_win.get_active_jid() == self.room_jid:
self.allow_focus_out_line = True
self.conv_textview.allow_focus_out_line = True
def on_treeview_size_allocate(self, widget, allocation):
'''The MUC treeview has resized. Move the hpaned in all tabs to match'''
@ -443,10 +441,7 @@ class GroupchatControl(ChatControlBase):
def on_private_message(self, nick, msg, tim):
# Do we have a queue?
fjid = self.room_jid + '/' + nick
qs = gajim.awaiting_events[self.account]
no_queue = True
if qs.has_key(fjid):
no_queue = False
no_queue = len(gajim.events.get_events(self.account, fjid)) == 0
# We print if window is opened
pm_control = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
@ -454,9 +449,9 @@ class GroupchatControl(ChatControlBase):
pm_control.print_conversation(msg, tim = tim)
return
if no_queue:
qs[fjid] = []
qs[fjid].append(('chat', (msg, '', 'incoming', tim, False, '')))
event = gajim.events.create_event('pm', (msg, '', 'incoming', tim,
False, '', None))
gajim.events.add_event(self.account, fjid, event)
autopopup = gajim.config.get('autopopup')
autopopupaway = gajim.config.get('autopopupaway')
@ -471,8 +466,6 @@ class GroupchatControl(ChatControlBase):
self.room_jid, icon_name = 'message')
image = state_images['message']
model[iter][C_IMG] = image
if gajim.interface.systray_enabled:
gajim.interface.systray.add_jid(fjid, self.account, 'pm')
self.parent_win.show_title()
else:
self._start_private_message(nick)
@ -531,18 +524,19 @@ class GroupchatControl(ChatControlBase):
if kind == 'incoming': # it's a message NOT from us
# highlighting and sounds
(highlight, sound) = self.highlighting_for_message(text, tim)
gc_class=self.__class__
if gc_class.gc_custom_colors.has_key(contact):
if self.gc_custom_colors.has_key(contact):
other_tags_for_name.append('gc_nickname_color_' + \
str(gc_class.gc_custom_colors[contact]))
str(self.gc_custom_colors[contact]))
else:
gc_class.gc_count_nicknames_colors += 1
gc_class.gc_custom_colors[contact] = gc_class.gc_count_nicknames_colors
self.gc_count_nicknames_colors += 1
number_of_colors = len(gajim.config.get('gc_nicknames_colors').\
split(':'))
if self.gc_count_nicknames_colors == number_of_colors:
self.gc_count_nicknames_colors = 0
self.gc_custom_colors[contact] = \
self.gc_count_nicknames_colors
other_tags_for_name.append('gc_nickname_color_' + \
str(gc_class.gc_count_nicknames_colors))
number_of_colors = len(gajim.config.get('gc_nicknames_colors').split(':'))
if gc_class.gc_count_nicknames_colors == number_of_colors:
gc_class.gc_count_nicknames_colors = 0
str(self.gc_count_nicknames_colors))
if highlight:
# muc-specific chatstate
self.parent_win.redraw_tab(self, 'attention')
@ -552,12 +546,23 @@ class GroupchatControl(ChatControlBase):
helpers.play_sound('muc_message_received')
elif sound == 'highlight':
helpers.play_sound('muc_message_highlight')
if text.startswith('/me ') or text.startswith('/me\n'):
other_tags_for_text.append('gc_nickname_color_' + \
str(self.gc_custom_colors[contact]))
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)
def get_nb_unread(self):
nb = len(gajim.events.get_events(self.account, self.room_jid,
['printed_gc_msg']))
for nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
nb += len(gajim.events.get_events(self.account, self.room_jid + '/' + \
nick, ['pm']))
return nb
def highlighting_for_message(self, text, tim):
'''Returns a 2-Tuple. The first says whether or not to highlight the
text, the second, what sound to play.'''
@ -594,64 +599,7 @@ class GroupchatControl(ChatControlBase):
# we have full focus (we are reading it!)
return
if not self.allow_focus_out_line:
# if room did not receive focus-in from the last time we added
# --- line then do not readd
return
print_focus_out_line = False
buffer = self.conv_textview.tv.get_buffer()
if self.focus_out_end_iter_offset 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():
# 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
# ---- lines)
print_focus_out_line = True
if print_focus_out_line and buffer.get_char_count() > 0:
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)
begin_iter_for_previous_line = end_iter_for_previous_line.copy()
begin_iter_for_previous_line.backward_chars(2) # img_char+1 (the '\n')
# remove focus out line
buffer.delete(begin_iter_for_previous_line,
end_iter_for_previous_line)
# add the new focus out line
# FIXME: Why is this loaded from disk everytime
path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'muc_separator.png')
focus_out_line_pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
end_iter = buffer.get_end_iter()
buffer.insert(end_iter, '\n')
buffer.insert_pixbuf(end_iter, focus_out_line_pixbuf)
end_iter = buffer.get_end_iter()
before_img_iter = end_iter.copy()
before_img_iter.backward_char() # one char back (an image also takes one char)
buffer.apply_tag_by_name('focus-out-line', before_img_iter, end_iter)
#FIXME: remove this workaround when bug is fixed
# c http://bugzilla.gnome.org/show_bug.cgi?id=318569
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()
buffer.end_user_action()
# scroll to the end (via idle in case the scrollbar has appeared)
gobject.idle_add(self.conv_textview.scroll_to_end)
self.conv_textview.show_focus_out_line()
def needs_visual_notification(self, text):
'''checks text to see whether any of the words in (muc_highlight_words
@ -747,7 +695,7 @@ class GroupchatControl(ChatControlBase):
model = self.list_treeview.get_model()
gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
state_images = gajim.interface.roster.jabber_state_images['16']
if gajim.awaiting_events[self.account].has_key(self.room_jid + '/' + nick):
if len(gajim.events.get_events(self.account, self.room_jid + '/' + nick)):
image = state_images['message']
else:
image = state_images[gc_contact.show]
@ -830,6 +778,9 @@ class GroupchatControl(ChatControlBase):
# 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)
@ -848,7 +799,8 @@ class GroupchatControl(ChatControlBase):
os.rename(old_file, files[old_file])
self.print_conversation(s, 'info')
if not gajim.awaiting_events[self.account].has_key(self.room_jid + '/' + nick):
if len(gajim.events.get_events(self.account,
self.room_jid + '/' + nick)) == 0:
self.remove_contact(nick)
else:
c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
@ -893,15 +845,18 @@ class GroupchatControl(ChatControlBase):
break
if print_status is None:
print_status = gajim.config.get('print_status_in_muc')
nick_jid = nick
if jid:
nick_jid += ' (%s)' % jid
if show == 'offline' and print_status in ('all', 'in_and_out'):
st = _('%s has left') % nick
st = _('%s has left') % nick_jid
if reason:
st += ' [%s]' % reason
else:
if newly_created and print_status in ('all', 'in_and_out'):
st = _('%s has joined the room') % nick
st = _('%s has joined the room') % nick_jid
elif print_status == 'all':
st = _('%s is now %s') % (nick, helpers.get_uf_show(show))
st = _('%s is now %s') % (nick_jid, helpers.get_uf_show(show))
if st:
if status:
st += ' (' + status + ')'
@ -1030,7 +985,8 @@ class GroupchatControl(ChatControlBase):
if len(message_array):
message_array = message_array[0].split()
nick = message_array.pop(0)
room_nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
room_nicks = gajim.contacts.get_nick_list(self.account,
self.room_jid)
if nick in room_nicks:
privmsg = ' '.join(message_array)
self.on_send_pm(nick=nick, msg=privmsg)
@ -1334,9 +1290,8 @@ class GroupchatControl(ChatControlBase):
nb = 0
for nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
fjid = self.room_jid + '/' + nick
if gajim.awaiting_events[self.account].has_key(fjid):
# gc can only have messages as event
nb += len(gajim.awaiting_events[self.account][fjid])
nb += len(gajim.events.get_events(self.account, fjid))
# gc can only have messages as event
return nb
def _on_change_subject_menuitem_activate(self, widget):

View file

@ -126,8 +126,8 @@ def get_default_font():
# in try because daemon may not be there
client = gconf.client_get_default()
return helpers.ensure_unicode_string(
client.get_string('/desktop/gnome/interface/font_name'))
return client.get_string('/desktop/gnome/interface/font_name'
).decode('utf-8')
except:
pass
@ -147,8 +147,7 @@ def get_default_font():
for line in file(xfce_config_file):
if line.find('name="Gtk/FontName"') != -1:
start = line.find('value="') + 7
return helpers.ensure_unicode_string(
line[start:line.find('"', start)])
return line[start:line.find('"', start)].decode('utf-8')
except:
#we talk about file
print >> sys.stderr, _('Error: cannot open %s for reading') % xfce_config_file
@ -163,7 +162,7 @@ def get_default_font():
font_name = values[0]
font_size = values[1]
font_string = '%s %s' % (font_name, font_size) # Verdana 9
return helpers.ensure_unicode_string(font_string)
return font_string.decode('utf-8')
except:
#we talk about file
print >> sys.stderr, _('Error: cannot open %s for reading') % kde_config_file
@ -406,7 +405,7 @@ def possibly_move_window_in_current_desktop(window):
if current_virtual_desktop_no != window_virtual_desktop:
# we are in another VD that the window was
# so show it in current VD
window.show()
window.present()
def file_is_locked(path_to_file):
'''returns True if file is locked (WINDOWS ONLY)'''
@ -458,7 +457,7 @@ def _get_fade_color(treeview, selected, focused):
def get_scaled_pixbuf(pixbuf, kind):
'''returns scaled pixbuf, keeping ratio etc or None
kind is either "chat" or "roster" or "notification" or "tooltip"'''
kind is either "chat", "roster", "notification", "tooltip", "vcard"'''
# resize to a width / height for the avatar not to have distortion
# (keep aspect ratio)

View file

@ -464,8 +464,8 @@ class HistoryManager:
except ValueError:
pass
file_.write(_('%(who)s on %(time)s said: %(message)s\n' % {'who': who,
'time': time_, 'message': message}))
file_.write(_('%(who)s on %(time)s said: %(message)s\n') % {'who': who,
'time': time_, 'message': message})
def _delete_jid_logs(self, liststore, list_of_paths):
paths_len = len(list_of_paths)

View file

@ -39,7 +39,6 @@ class MessageControl:
self.account = account
self.hide_chat_buttons_always = False
self.hide_chat_buttons_current = False
self.nb_unread = 0
self.resource = resource
gajim.last_message_time[self.account][self.get_full_jid()] = 0
@ -117,10 +116,7 @@ class MessageControl:
pass
def get_specific_unread(self):
n = 0
if gajim.awaiting_events[self.account].has_key(self.contact.jid):
n = len(gajim.awaiting_events[self.account][self.contact.jid])
return n
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,

View file

@ -50,6 +50,8 @@ class MessageTextView(gtk.TextView):
self.set_pixels_above_lines(2)
self.set_pixels_below_lines(2)
self.lang = None # Lang used for spell checking
def destroy(self):
import gc
gobject.idle_add(lambda:gc.collect())

View file

@ -22,6 +22,7 @@
##
import gtk
import gobject
import common
import gtkgui_helpers
@ -149,8 +150,17 @@ class MessageWindow:
fjid = control.get_full_jid()
self._controls[control.account][fjid] = control
if self.get_num_controls() > 1:
if self.get_num_controls() == 2:
# is first conversation_textview scrolled down ?
scrolled = False
first_widget = self.notebook.get_nth_page(0)
ctrl = self._widget_to_control(first_widget)
conv_textview = ctrl.conv_textview
if conv_textview.at_the_end():
scrolled = True
self.notebook.set_show_tabs(True)
if scrolled:
gobject.idle_add(conv_textview.scroll_to_end_iter)
self.alignment.set_property('top-padding', 2)
# Add notebook page and connect up to the tab's close button
@ -214,7 +224,7 @@ class MessageWindow:
gajim.config.get('notify_on_all_muc_messages') and not \
ctrl.attention_flag:
continue
unread += ctrl.nb_unread
unread += ctrl.get_nb_unread()
unread_str = ''
if unread > 1:
@ -270,9 +280,8 @@ class MessageWindow:
ctrl.shutdown()
# Update external state
if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(ctrl.get_full_jid(), ctrl.account,
ctrl.type_id)
gajim.events.remove_events(ctrl.account, ctrl.get_full_jid,
types = ['printed_msg', 'chat', 'gc_msg'])
del gajim.last_message_time[ctrl.account][ctrl.get_full_jid()]
self.disconnect_tab_dnd(ctrl.widget)
@ -422,10 +431,10 @@ class MessageWindow:
if ind < 0:
ind = self.notebook.get_n_pages() - 1
ctrl = self.get_control(ind, None)
if ctrl.nb_unread > 0:
if ctrl.get_nb_unread() > 0:
found = True
break # found
else: # Search for a composing contact
elif gajim.config.get('ctrl_tab_go_to_next_composing') : # Search for a composing contact
contact = ctrl.contact
if first_composing_ind == -1 and contact.chatstate == 'composing':
# If no composing contact found yet, check if this one is composing

View file

@ -1,267 +0,0 @@
#!/bin/sh
''':'
exec python -OOt "$0" ${1+"$@"}
' '''
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Nikos Kouremenos <kourem@gmail.com>
##
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <nkour@jabber.org>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
##
## 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 sre
import sys
import time
from common import logger
from common import i18n
try:
PREFERRED_ENCODING = sys.getpreferredencoding()
except:
PREFERRED_ENCODING = 'utf-8'
from common.helpers import from_one_line, decode_string
from pysqlite2 import dbapi2 as sqlite
if os.name == 'nt':
try:
PATH_TO_LOGS_BASE_DIR = os.path.join(os.environ['appdata'], 'Gajim', 'Logs')
PATH_TO_DB = os.path.join(os.environ['appdata'], 'Gajim', 'logs.db') # database is called logs.db
except KeyError:
# win9x
PATH_TO_LOGS_BASE_DIR = '../src/Logs'
PATH_TO_DB = '../src/logs.db'
else:
PATH_TO_LOGS_BASE_DIR = os.path.expanduser('~/.gajim/logs')
PATH_TO_DB = os.path.expanduser('~/.gajim/logs.db') # database is called logs.db
class Migration:
def __init__(self):
self.constants = logger.Constants()
self.DONE = False
self.PROCESSING = False
if os.path.exists(PATH_TO_DB):
print '%s already exists. Exiting..' % PATH_TO_DB
sys.exit()
self.jids_already_in = [] # jid we already put in DB
def get_jid(self, dirname, filename):
# jids.jid text column will be JID if TC-related, room_jid if GC-related,
# ROOM_JID/nick if pm-related. Here I get names from filenames
if dirname.endswith('logs') or dirname.endswith('Logs'):
# we have file (not dir) in logs base dir, so it's TC
jid = filename # file is JID
else:
# we are in a room folder (so it can be either pm or message in room)
if filename == os.path.basename(dirname): # room/room
jid = dirname # filename is ROOM_JID
else: #room/nick it's pm
jid = dirname + '/' + filename
if jid.startswith('/'):
p = len(PATH_TO_LOGS_BASE_DIR)
jid = jid[p+1:]
jid = jid.lower()
return jid
def decode_jid(self, string):
'''try to decode (to make it Unicode instance) given jid'''
string = decode_string(string)
if isinstance(string, str):
return None # decode failed
return string
def visit(self, arg, dirname, filenames):
s = _('Visiting %s') % dirname
if self.queue:
self.queue.put(s)
else:
print s
for filename in filenames:
# Don't take this file into account, this is dup info
# notifications are also in contact log file
if filename in ('notify.log', 'README'):
continue
path_to_text_file = os.path.join(dirname, filename)
if os.path.isdir(path_to_text_file):
continue
jid = self.get_jid(dirname, filename)
jid = self.decode_jid(jid)
if not jid:
continue
if filename == os.path.basename(dirname): # gajim@conf/gajim@conf then gajim@conf is type room
jid_type = self.constants.JID_ROOM_TYPE
#Type of log
typ = 'room'
else:
jid_type = self.constants.JID_NORMAL_TYPE
#Type of log
typ = _('normal')
s = _('Processing %s of type %s') % (jid, typ)
if self.queue:
self.queue.put(s.encode(PREFERRED_ENCODING))
else:
print s.encode(PREFERRED_ENCODING)
JID_ID = None
f = open(path_to_text_file, 'r')
lines = f.readlines()
for line in lines:
line = from_one_line(line)
splitted_line = line.split(':')
if len(splitted_line) > 2:
# type in logs is one of
# 'gc', 'gcstatus', 'recv', 'sent' and if nothing of those
# it is status
# new db has:
# status, gcstatus, gc_msg, (we only recv those 3),
# single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent
# to meet all our needs
# here I convert
# gc ==> gc_msg, gcstatus ==> gcstatus, recv ==> chat_msg_recv
# sent ==> chat_msg_sent, status ==> status
typ = splitted_line[1] # line[1] has type of logged message
message_data = splitted_line[2:] # line[2:] has message data
# line[0] is date,
# some lines can be fucked up, just drop them
try:
tim = int(float(splitted_line[0]))
except:
continue
contact_name = None
show = None
if typ == 'gc':
contact_name = message_data[0]
message = ':'.join(message_data[1:])
kind = self.constants.KIND_GC_MSG
elif typ == 'gcstatus':
contact_name = message_data[0]
show = message_data[1]
message = ':'.join(message_data[2:]) # status msg
kind = self.constants.KIND_GCSTATUS
elif typ == 'recv':
message = ':'.join(message_data[0:])
kind = self.constants.KIND_CHAT_MSG_RECV
elif typ == 'sent':
message = ':'.join(message_data[0:])
kind = self.constants.KIND_CHAT_MSG_SENT
else: # status
kind = self.constants.KIND_STATUS
show = message_data[0]
message = ':'.join(message_data[1:]) # status msg
message = message[:-1] # remove last \n
if not message:
continue
# jid is already in the DB, don't create a new row, just get his jid_id
if not JID_ID:
if jid in self.jids_already_in:
self.cur.execute('SELECT jid_id FROM jids WHERE jid = "%s"' % jid)
JID_ID = self.cur.fetchone()[0]
else:
self.jids_already_in.append(jid)
self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)',
(jid, jid_type))
self.con.commit()
JID_ID = self.cur.lastrowid
sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message) '\
'VALUES (?, ?, ?, ?, ?, ?)'
values = (JID_ID, contact_name, tim, kind, show, message)
self.cur.execute(sql, values)
self.con.commit()
def migrate(self, queue = None):
self.queue = queue
self.con = sqlite.connect(PATH_TO_DB)
os.chmod(PATH_TO_DB, 0600) # rw only for us
self.cur = self.con.cursor()
# create the tables
# kind can be
# status, gcstatus, gc_msg, (we only recv for those 3),
# single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent
# to meet all our needs
# logs.jid_id --> jids.jid_id but Sqlite doesn't do FK etc so it's done in python code
self.cur.executescript(
'''
CREATE TABLE jids(
jid_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
jid TEXT UNIQUE,
type INTEGER
);
CREATE TABLE unread_messages(
message_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
jid_id INTEGER
);
CREATE TABLE logs(
log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
jid_id INTEGER,
contact_name TEXT,
time INTEGER,
kind INTEGER,
show INTEGER,
message TEXT,
subject TEXT
);
'''
)
self.con.commit()
self.PROCESSING = True
os.path.walk(PATH_TO_LOGS_BASE_DIR, self.visit, None)
s = '''
We do not use plain-text files anymore, because they do not meet our needs.
Those files here are logs for Gajim up until 0.8.2
We now use an sqlite database called logs.db found in %s
You can now safely remove your %s folder
Thank you''' % (os.path.dirname(PATH_TO_LOGS_BASE_DIR), PATH_TO_LOGS_BASE_DIR)
f = open(os.path.join(PATH_TO_LOGS_BASE_DIR, 'README'), 'w')
f.write(s)
f.close()
if queue:
queue.put(s)
self.DONE = True
if __name__ == '__main__':
# magic argumen 'dont_wait' tells us that script is run from Gajim
if len(sys.argv) < 2 or sys.argv[1] != 'dont_wait':
print 'IMPORTNANT: PLEASE READ http://trac.gajim.org/wiki/MigrateLogToDot9DB'
print 'Migration will start in 40 seconds unless you press Ctrl+C'
time.sleep(40) # give the user time to act
print
print 'Starting Logs Migration'
print '======================='
print 'Please do NOT run Gajim until this script is over'
m = Migration()
m.migrate()

View file

@ -32,12 +32,89 @@ if dbus_support.supported:
import dbus.glib
import dbus.service
def notify(event, jid, account, parameters):
def get_show_in_roster(event, account, contact):
'''Return True if this event must be shown in roster, else False'''
num = get_advanced_notification(event, account, contact)
if num != None:
if gajim.config.get_per('notifications', str(num), 'roster') == 'yes':
return True
if gajim.config.get_per('notifications', str(num), 'roster') == 'no':
return False
if event == 'message_received':
chat_control = helpers.get_chat_control(account, contact)
if not chat_control:
return True
elif event == 'ft_request':
return True
return False
def get_show_in_systray(event, account, contact):
'''Return True if this event must be shown in roster, else False'''
num = get_advanced_notification(event, account, contact)
if num != None:
if gajim.config.get_per('notifications', str(num), 'systray') == 'yes':
return True
if gajim.config.get_per('notifications', str(num), 'systray') == 'no':
return False
if event in ('message_received', 'ft_request', 'gc_msg_highlight',
'ft_request'):
return True
return False
def get_advanced_notification(event, account, contact):
'''Returns the number of the first advanced notification or None'''
num = 0
notif = gajim.config.get_per('notifications', str(num))
while notif:
recipient_ok = False
status_ok = False
tab_opened_ok = False
# test event
if gajim.config.get_per('notifications', str(num), 'event') == event:
# test recipient
recipient_type = gajim.config.get_per('notifications', str(num),
'recipient_type')
recipients = gajim.config.get_per('notifications', str(num),
'recipients').split()
if recipient_type == 'all':
recipient_ok = True
elif recipient_type == 'contact' and contact.jid in recipients:
recipient_ok = True
elif recipient_type == 'group':
for group in contact.groups:
if group in contact.groups:
recipient_ok = True
break
if recipient_ok:
# test status
our_status = gajim.SHOW_LIST[gajim.connections[account].connected]
status = gajim.config.get_per('notifications', str(num), 'status')
if status == 'all' or our_status in status.split():
status_ok = True
if status_ok:
# test window_opened
tab_opened = gajim.config.get_per('notifications', str(num),
'tab_opened')
if tab_opened == 'both':
tab_opened_ok = True
else:
chat_control = helper.get_chat_control(account, contact)
if (chat_control and tab_opened == 'yes') or (not chat_control and \
tab_opened == 'no'):
tab_opened_ok = True
if tab_opened_ok:
return num
num += 1
notif = gajim.config.get_per('notifications', str(num))
def notify(event, jid, account, parameters, advanced_notif_num = None):
'''Check what type of notifications we want, depending on basic configuration
of notifications and advanced one and do these notifications'''
# First, find what notifications we want
do_popup = False
do_sound = False
do_cmd = False
if (event == 'status_change'):
new_show = parameters[0]
status_message = parameters[1]
@ -51,9 +128,8 @@ def notify(event, jid, account, parameters):
if account_server in gajim.block_signed_in_notifications and \
gajim.block_signed_in_notifications[account_server]:
block_transport = True
if gajim.config.get('notify_on_signin') and \
not gajim.block_signed_in_notifications[account] and not block_transport \
and helpers.allow_showing_notification(account):
if helpers.allow_showing_notification(account, 'notify_on_signin') and \
not gajim.block_signed_in_notifications[account] and not block_transport:
do_popup = True
if gajim.config.get_per('soundevents', 'contact_connected',
'enabled') and not gajim.block_signed_in_notifications[account] and \
@ -61,8 +137,7 @@ def notify(event, jid, account, parameters):
do_sound = True
elif (event == 'contact_disconnected'):
status_message = parameters
if gajim.config.get('notify_on_signout') \
and helpers.allow_showing_notification(account):
if helpers.allow_showing_notification(account, 'notify_on_signout'):
do_popup = True
if gajim.config.get_per('soundevents', 'contact_disconnected',
'enabled'):
@ -72,17 +147,21 @@ def notify(event, jid, account, parameters):
first = parameters[1]
nickname = parameters[2]
message = parameters[3]
if gajim.config.get('notify_on_new_message') and \
helpers.allow_showing_notification(account) and first:
if helpers.allow_showing_notification(account, 'notify_on_new_message',
advanced_notif_num, first):
do_popup = True
if first and gajim.config.get_per('soundevents', 'first_message_received',
'enabled'):
if first and helpers.allow_sound_notification('first_message_received',
advanced_notif_num):
do_sound = True
elif not first and gajim.config.get_per('soundevents', 'next_message_received',
'enabled'):
elif not first and helpers.allow_sound_notification(
'next_message_received', advanced_notif_num):
do_sound = True
else:
print '*Event not implemeted yet*'
if advanced_notif_num != None and gajim.config.get_per('notifications',
str(advanced_notif_num), 'run_command'):
do_cmd = True
# Do the wanted notifications
if (do_popup):
@ -161,14 +240,34 @@ def notify(event, jid, account, parameters):
path_to_image = path, title = title, text = text)
if (do_sound):
snd_file = None
snd_event = None # If not snd_file, play the event
if (event == 'new_message'):
if first:
helpers.play_sound('first_message_received')
if advanced_notif_num != None and gajim.config.get_per('notifications',
str(advanced_notif_num), 'sound') == 'yes':
snd_file = gajim.config.get_per('notifications',
str(advanced_notif_num), 'sound_file')
elif advanced_notif_num != None and gajim.config.get_per(
'notifications', str(advanced_notif_num), 'sound') == 'no':
pass # do not set snd_event
elif first:
snd_event = 'first_message_received'
else:
helpers.play_sound('next_message_received')
snd_event = 'next_message_received'
elif event in ('contact_connected', 'contact_disconnected'):
helpers.play_sound(event)
snd_event = event
if snd_file:
helpers.play_sound_file(snd_file)
if snd_event:
helpers.play_sound(snd_event)
if do_cmd:
command = gajim.config.get_per('notifications', str(advanced_notif_num),
'command')
try:
helpers.exec_command(command)
except:
pass
def popup(event_type, jid, account, msg_type = '', path_to_image = None,
title = None, text = None):
@ -283,6 +382,8 @@ class DesktopNotification:
ntype = 'im.invitation'
elif event_type == _('Contact Changed Status'):
ntype = 'presence.status'
elif event_type == _('Connection Failed'):
ntype = 'connection.failed'
else:
# default failsafe values
self.path_to_image = os.path.abspath(

283
src/profile_window.py Normal file
View file

@ -0,0 +1,283 @@
## profile_window.py
##
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@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 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 gtk
import gobject
import base64
import mimetypes
import os
import time
import locale
import gtkgui_helpers
import dialogs
from common import helpers
from common import gajim
from common.i18n import Q_
def get_avatar_pixbuf_encoded_mime(photo):
'''return the pixbuf of the image
photo is a dictionary containing PHOTO information'''
if not isinstance(photo, dict):
return None, None, None
img_decoded = None
avatar_encoded = None
avatar_mime_type = None
if photo.has_key('BINVAL'):
img_encoded = photo['BINVAL']
avatar_encoded = img_encoded
try:
img_decoded = base64.decodestring(img_encoded)
except:
pass
if img_decoded:
if photo.has_key('TYPE'):
avatar_mime_type = photo['TYPE']
pixbuf = gtkgui_helpers.get_pixbuf_from_data(img_decoded)
else:
pixbuf, avatar_mime_type = gtkgui_helpers.get_pixbuf_from_data(
img_decoded, want_type=True)
else:
pixbuf = None
return pixbuf, avatar_encoded, avatar_mime_type
class ProfileWindow:
'''Class for our information window'''
def __init__(self, account):
self.xml = gtkgui_helpers.get_glade('profile_window.glade')
self.window = self.xml.get_widget('profile_window')
self.account = account
self.jid = gajim.get_jid_from_account(account)
self.avatar_mime_type = None
self.avatar_encoded = None
self.xml.signal_autoconnect(self)
self.window.show_all()
def on_profile_window_destroy(self, widget):
del gajim.interface.instances[self.account]['profile']
def on_profile_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
self.window.destroy()
def on_clear_button_clicked(self, widget):
# empty the image
self.xml.get_widget('PHOTO_image').set_from_icon_name('stock_person',
gtk.ICON_SIZE_DIALOG)
self.avatar_encoded = None
self.avatar_mime_type = None
def on_set_avatar_button_clicked(self, widget):
f = None
def on_ok(widget, path_to_file):
filesize = os.path.getsize(path_to_file) # in bytes
#FIXME: use messages for invalid file for 0.11
invalid_file = False
msg = ''
if os.path.isfile(path_to_file):
stat = os.stat(path_to_file)
if stat[6] == 0:
invalid_file = True
else:
invalid_file = True
if not invalid_file and filesize > 16384: # 16 kb
try:
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
# get the image at 'notification size'
# and use that user did not specify in ACE crazy size
scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf,
'tooltip')
except gobject.GError, msg: # unknown format
# msg should be string, not object instance
msg = str(msg)
invalid_file = True
if invalid_file:
if True: # keep identation
dialogs.ErrorDialog(_('Could not load image'), msg)
return
if filesize > 16384:
if scaled_pixbuf:
path_to_file = os.path.join(gajim.TMP,
'avatar_scaled.png')
scaled_pixbuf.save(path_to_file, 'png')
self.dialog.destroy()
fd = open(path_to_file, 'rb')
data = fd.read()
pixbuf = gtkgui_helpers.get_pixbuf_from_data(data)
# rescale it
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
image = self.xml.get_widget('PHOTO_image')
image.set_from_pixbuf(pixbuf)
self.avatar_encoded = base64.encodestring(data)
# returns None if unknown type
self.avatar_mime_type = mimetypes.guess_type(path_to_file)[0]
self.dialog = dialogs.ImageChooserDialog(on_response_ok = on_ok)
def on_PHOTO_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3 and self.avatar_encoded: # right click
menu = gtk.Menu()
nick = gajim.config.get_per('accounts', self.account, 'name')
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
menuitem.connect('activate',
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
self.jid, None, nick + '.jpeg')
menu.append(menuitem)
# show clear
menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
menuitem.connect('activate', self.on_clear_button_clicked)
menu.append(menuitem)
menu.connect('selection-done', lambda w:w.destroy())
# show the menu
menu.show_all()
menu.popup(None, None, None, event.button, event.time)
elif event.button == 1: # left click
self.on_set_avatar_button_clicked(widget)
def set_value(self, entry_name, value):
try:
self.xml.get_widget(entry_name).set_text(value)
except AttributeError:
pass
def set_values(self, vcard):
if not 'PHOTO' in vcard:
# set default image
image = self.xml.get_widget('PHOTO_image')
image.set_from_icon_name('stock_person', gtk.ICON_SIZE_DIALOG)
for i in vcard.keys():
if i == 'PHOTO':
pixbuf, self.avatar_encoded, self.avatar_mime_type = \
get_avatar_pixbuf_encoded_mime(vcard[i])
image = self.xml.get_widget('PHOTO_image')
if not pixbuf:
image.set_from_icon_name('stock_person', gtk.ICON_SIZE_DIALOG)
continue
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
image.set_from_pixbuf(pixbuf)
continue
if i == 'ADR' or i == 'TEL' or i == 'EMAIL':
for entry in vcard[i]:
add_on = '_HOME'
if 'WORK' in entry:
add_on = '_WORK'
for j in entry.keys():
self.set_value(i + add_on + '_' + j + '_entry', entry[j])
if isinstance(vcard[i], dict):
for j in vcard[i].keys():
self.set_value(i + '_' + j + '_entry', vcard[i][j])
else:
if i == 'DESC':
self.xml.get_widget('DESC_textview').get_buffer().set_text(
vcard[i], 0)
else:
self.set_value(i + '_entry', vcard[i])
def add_to_vcard(self, vcard, entry, txt):
'''Add an information to the vCard dictionary'''
entries = entry.split('_')
loc = vcard
if len(entries) == 3: # We need to use lists
if not loc.has_key(entries[0]):
loc[entries[0]] = []
found = False
for e in loc[entries[0]]:
if entries[1] in e:
found = True
break
if found:
e[entries[2]] = txt
else:
loc[entries[0]].append({entries[1]: '', entries[2]: txt})
return vcard
while len(entries) > 1:
if not loc.has_key(entries[0]):
loc[entries[0]] = {}
loc = loc[entries[0]]
del entries[0]
loc[entries[0]] = txt
return vcard
def make_vcard(self):
'''make the vCard dictionary'''
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
vcard = {}
for e in entries:
txt = self.xml.get_widget(e + '_entry').get_text().decode('utf-8')
if txt != '':
vcard = self.add_to_vcard(vcard, e, txt)
# DESC textview
buff = self.xml.get_widget('DESC_textview').get_buffer()
start_iter = buff.get_start_iter()
end_iter = buff.get_end_iter()
txt = buff.get_text(start_iter, end_iter, 0)
if txt != '':
vcard['DESC'] = txt.decode('utf-8')
# Avatar
if self.avatar_encoded:
vcard['PHOTO'] = {'BINVAL': self.avatar_encoded}
if self.avatar_mime_type:
vcard['PHOTO']['TYPE'] = self.avatar_mime_type
return vcard
def on_publish_button_clicked(self, widget):
if gajim.connections[self.account].connected < 2:
dialogs.ErrorDialog(_('You are not connected to the server'),
_('Without a connection you can not publish your contact '
'information.'))
return
vcard = self.make_vcard()
nick = ''
if vcard.has_key('NICKNAME'):
nick = vcard['NICKNAME']
if nick == '':
nick = gajim.config.get_per('accounts', self.account, 'name')
gajim.nicks[self.account] = nick
gajim.connections[self.account].send_vcard(vcard)
def on_retrieve_button_clicked(self, widget):
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'ADR_WORK_STREET', 'ADR_WORK_EXTADR',
'ADR_WORK_LOCALITY', 'ADR_WORK_REGION', 'ADR_WORK_PCODE',
'ADR_WORK_CTRY']
if gajim.connections[self.account].connected > 1:
# clear all entries
for e in entries:
self.xml.get_widget(e + '_entry').set_text('')
self.xml.get_widget('DESC_textview').get_buffer().set_text('')
self.xml.get_widget('PHOTO_image').set_from_icon_name('stock_person',
gtk.ICON_SIZE_DIALOG)
gajim.connections[self.account].request_vcard(self.jid)
else:
dialogs.ErrorDialog(_('You are not connected to the server'),
_('Without a connection, you can not get your contact information.'))

View file

@ -54,7 +54,7 @@ ident = lambda e: e
if dbus_support.version[1] >= 43:
# in most cases it is a utf-8 string
DBUS_STRING = dbus.String
# general type (for use in dicts,
# where all values should have the same type)
DBUS_VARIANT = dbus.Variant
@ -67,7 +67,7 @@ if dbus_support.version[1] >= 43:
DBUS_DICT_SS = lambda : dbus.Dictionary({}, signature="ss")
# empty type
DBUS_NONE = lambda : dbus.Variant(0)
else: # 33, 35, 36
DBUS_DICT_SV = lambda : {}
DBUS_DICT_SS = lambda : {}
@ -122,7 +122,7 @@ class Remote:
def __init__(self):
self.signal_object = None
session_bus = dbus_support.session_bus.SessionBus()
if dbus_support.version[1] >= 41:
service = dbus.service.BusName(SERVICE, bus=session_bus)
self.signal_object = SignalObject(service)
@ -139,7 +139,7 @@ class Remote:
class SignalObject(DbusPrototype):
''' Local object definition for /org/gajim/dbus/RemoteObject. This doc must
not be visible, because the clients can access only the remote object. '''
def __init__(self, service):
self.first_show = True
self.vcard_account = None
@ -171,6 +171,7 @@ class SignalObject(DbusPrototype):
self.get_status,
self.get_status_message,
self.start_chat,
self.send_xml,
])
def raise_signal(self, signal, arg):
@ -180,7 +181,7 @@ class SignalObject(DbusPrototype):
i = message.get_iter(True)
i.append(arg)
self._connection.send(message)
def get_status(self, *args):
'''get_status(account = None)
returns status (show to be exact) which is the global one
@ -193,7 +194,7 @@ class SignalObject(DbusPrototype):
# return show for the given account
index = gajim.connections[account].connected
return DBUS_STRING(STATUS_LIST[index])
def get_status_message(self, *args):
'''get_status(account = None)
returns status which is the global one
@ -206,7 +207,7 @@ class SignalObject(DbusPrototype):
# return show for the given account
status = gajim.connections[account].status
return DBUS_STRING(status)
def get_account_and_contact(self, account, jid):
''' get the account (if not given) and contact instance from jid'''
@ -232,7 +233,7 @@ class SignalObject(DbusPrototype):
break
if not contact:
contact = jid
return connected_account, contact
def send_file(self, *args):
@ -240,6 +241,7 @@ class SignalObject(DbusPrototype):
send file, located at 'file_path' to 'jid', using account
(optional) 'account' '''
file_path, jid, account = self._get_real_arguments(args, 3)
jid = self._get_real_jid(jid, account)
connected_account, contact = self.get_account_and_contact(account, jid)
if connected_account:
@ -250,7 +252,7 @@ class SignalObject(DbusPrototype):
connected_account, contact, file_path)
return True
return False
def _send_message(self, jid, message, keyID, account, type = 'chat', subject = None):
''' can be called from send_chat_message (default when send_message)
or send_single_message'''
@ -258,20 +260,21 @@ class SignalObject(DbusPrototype):
return None # or raise error
if not keyID:
keyID = ''
connected_account, contact = self.get_account_and_contact(account, jid)
if connected_account:
connection = gajim.connections[connected_account]
res = connection.send_message(jid, message, keyID, type, subject)
return True
return False
def send_chat_message(self, *args):
''' send_message(jid, message, keyID=None, account=None)
send chat 'message' to 'jid', using account (optional) 'account'.
if keyID is specified, encrypt the message with the pgp key '''
jid, message, keyID, account = self._get_real_arguments(args, 4)
jid = self._get_real_jid(jid, account)
return self._send_message(jid, message, keyID, account)
def send_single_message(self, *args):
@ -279,6 +282,7 @@ class SignalObject(DbusPrototype):
send single 'message' to 'jid', using account (optional) 'account'.
if keyID is specified, encrypt the message with the pgp key '''
jid, subject, message, keyID, account = self._get_real_arguments(args, 5)
jid = self._get_real_jid(jid, account)
return self._send_message(jid, message, keyID, account, type, subject)
def open_chat(self, *args):
@ -288,9 +292,8 @@ class SignalObject(DbusPrototype):
if not jid:
# FIXME: raise exception for missing argument (dbus0.35+)
return None
if jid.startswith('xmpp:'):
jid = jid[5:] # len('xmpp:') = 5
jid = self._get_real_jid(jid, account)
if account:
accounts = [account]
else:
@ -315,11 +318,11 @@ class SignalObject(DbusPrototype):
connected_account = acct
elif first_connected_acct is None:
first_connected_acct = acct
# if jid is not a conntact, open-chat with first connected account
if connected_account is None and first_connected_acct:
connected_account = first_connected_acct
if connected_account:
gajim.interface.roster.new_chat_from_jid(connected_account, jid)
# preserve the 'steal focus preservation'
@ -328,7 +331,7 @@ class SignalObject(DbusPrototype):
win.window.focus()
return True
return False
def change_status(self, *args, **keywords):
''' change_status(status, message, account). account is optional -
if not specified status is changed for all accounts. '''
@ -346,13 +349,12 @@ class SignalObject(DbusPrototype):
gobject.idle_add(gajim.interface.roster.send_status, acc,
status, message)
return None
def show_next_unread(self, *args):
''' Show the window(s) with next waiting messages in tabbed/group chats. '''
#FIXME: when systray is disabled this method does nothing.
if len(gajim.interface.systray.jids) != 0:
if gajim.events.get_nb_events():
gajim.interface.systray.handle_first_event()
def contact_info(self, *args):
''' get vcard info for a contact. Return cached value of the vcard.
'''
@ -362,14 +364,15 @@ class SignalObject(DbusPrototype):
if not jid:
# FIXME: raise exception for missing argument (0.3+)
return None
jid = self._get_real_jid(jid, account)
cached_vcard = gajim.connections.values()[0].get_cached_vcard(jid)
if cached_vcard:
return get_dbus_struct(cached_vcard)
# return empty dict
return DBUS_DICT_SV()
def list_accounts(self, *args):
''' list register accounts '''
result = gajim.contacts.get_accounts()
@ -379,7 +382,7 @@ class SignalObject(DbusPrototype):
result_array.append(DBUS_STRING(account))
return result_array
return None
def account_info(self, *args):
''' show info on account: resource, jid, nick, prio, message '''
[for_account] = self._get_real_arguments(args, 1)
@ -398,7 +401,7 @@ class SignalObject(DbusPrototype):
result['resource'] = DBUS_STRING(unicode(gajim.config.get_per('accounts',
account.name, 'resource')))
return result
def list_contacts(self, *args):
''' list all contacts in the roster. If the first argument is specified,
then return the contacts for the specified account '''
@ -422,7 +425,7 @@ class SignalObject(DbusPrototype):
if result == []:
return None
return result
def toggle_roster_appearance(self, *args):
''' shows/hides the roster window '''
win = gajim.interface.roster.window
@ -449,14 +452,14 @@ class SignalObject(DbusPrototype):
prefs_dict[DBUS_STRING(key)] = DBUS_STRING(value[1])
gajim.config.foreach(get_prefs)
return prefs_dict
def prefs_store(self, *args):
try:
gajim.interface.save_config()
except Exception, e:
return False
return True
def prefs_del(self, *args):
[key] = self._get_real_arguments(args, 1)
if not key:
@ -469,7 +472,7 @@ class SignalObject(DbusPrototype):
else:
gajim.config.del_per(key_path[0], key_path[1], key_path[2])
return True
def prefs_put(self, *args):
[key] = self._get_real_arguments(args, 1)
if not key:
@ -482,7 +485,7 @@ class SignalObject(DbusPrototype):
subname, value = key_path[2].split('=', 1)
gajim.config.set_per(key_path[0], key_path[1], subname, value)
return True
def add_contact(self, *args):
[jid, account] = self._get_real_arguments(args, 2)
if account:
@ -497,11 +500,12 @@ class SignalObject(DbusPrototype):
# if account is not given, show account combobox
AddNewContactWindow(account = None, jid = jid)
return True
def remove_contact(self, *args):
[jid, account] = self._get_real_arguments(args, 2)
jid = self._get_real_jid(jid, account)
accounts = gajim.contacts.get_accounts()
# if there is only one account in roster, take it as default
if account:
accounts = [account]
@ -515,7 +519,7 @@ class SignalObject(DbusPrototype):
gajim.contacts.remove_jid(account, jid)
contact_exists = True
return contact_exists
def _is_first(self):
if self.first_show:
self.first_show = False
@ -535,7 +539,35 @@ class SignalObject(DbusPrototype):
args.extend([None] * (desired_length - len(args)))
args = args[:desired_length]
return args
def _get_real_jid(self, jid, account = None):
'''get the real jid from the given one: removes xmpp: or get jid from nick
if account is specified, search only in this account
'''
if account:
accounts = [account]
else:
accounts = gajim.connections.keys()
if jid.startswith('xmpp:'):
return jid[5:] # len('xmpp:') = 5
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):
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_)
if c[0].name == jid:
nick_in_roster = jid_
break
if nick_in_roster:
# We have not found jid in roster, but we found is as a nick
return nick_in_roster
# We have not found it as jid nor as nick, probably a not in roster jid
return jid
def _contacts_as_dbus_structure(self, contacts):
''' get info from list of Contact objects and create dbus dict '''
if not contacts:
@ -564,7 +596,7 @@ class SignalObject(DbusPrototype):
return contact_dict
def get_unread_msgs_number(self, *args):
return str(gajim.interface.roster.nb_unread)
return str(gajim.events.get_nb_events)
def start_chat(self, *args):
[account] = self._get_real_arguments(args, 1)
@ -574,13 +606,21 @@ class SignalObject(DbusPrototype):
NewChatDialog(account)
return True
def send_xml(self, *args):
xml, account = self._get_real_arguments(args, 2)
if account:
gajim.connections[account[0]].send_stanza(xml)
else:
for acc in gajim.contacts.get_accounts():
gajim.connections[acc].send_stanza(xml)
if dbus_support.version[1] >= 30 and dbus_support.version[1] <= 40:
method = dbus.method
signal = dbus.signal
elif dbus_support.version[1] >= 41:
method = dbus.service.method
signal = dbus.service.signal
# prevent using decorators, because they are not supported
# on python < 2.4
# FIXME: use decorators when python2.3 (and dbus 0.23) is OOOOOOLD
@ -605,3 +645,4 @@ class SignalObject(DbusPrototype):
account_info = method(INTERFACE)(account_info)
get_unread_msgs_number = method(INTERFACE)(get_unread_msgs_number)
start_chat = method(INTERFACE)(start_chat)
send_xml = method(INTERFACE)(send_xml)

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
##
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <nkour@jabber.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
@ -45,9 +45,8 @@ class Systray:
'''Class for icon in the notification area
This class is both base class (for systraywin32.py) and normal class
for trayicon in GNU/Linux'''
def __init__(self):
self.jids = [] # Contain things like [account, jid, type_of_msg]
self.single_message_handler_id = None
self.new_chat_handler_id = None
self.t = None
@ -59,7 +58,9 @@ class Systray:
self.popup_menus = []
def set_img(self):
if len(self.jids) > 0:
if not gajim.interface.systray_enabled:
return
if gajim.events.get_nb_systray_events():
state = 'message'
else:
state = self.status
@ -69,26 +70,13 @@ class Systray:
elif image.get_storage_type() == gtk.IMAGE_PIXBUF:
self.img_tray.set_from_pixbuf(image.get_pixbuf())
def add_jid(self, jid, account, typ):
l = [account, jid, typ]
# We can keep several single message 'cause we open them one by one
if not l in self.jids or typ == 'normal':
self.jids.append(l)
self.set_img()
def remove_jid(self, jid, account, typ):
l = [account, jid, typ]
if l in self.jids:
self.jids.remove(l)
self.set_img()
def change_status(self, global_status):
''' set tray image to 'global_status' '''
# change image and status, only if it is different
if global_status is not None and self.status != global_status:
self.status = global_status
self.set_img()
def start_chat(self, widget, account, jid):
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
if gajim.interface.msg_win_mgr.has_window(jid, account):
@ -99,13 +87,13 @@ class Systray:
gajim.interface.roster.new_chat(contact, account)
gajim.interface.msg_win_mgr.get_window(jid, account).set_active_tab(
jid, account)
def on_single_message_menuitem_activate(self, widget, account):
dialogs.SingleMessageWindow(account, action = 'send')
def on_new_chat(self, widget, account):
dialogs.NewChatDialog(account)
def make_menu(self, event = None):
'''create chat with and new message (sub) menus/menuitems
event is None when we're in Windows
@ -118,7 +106,7 @@ class Systray:
single_message_menuitem = self.xml.get_widget('single_message_menuitem')
status_menuitem = self.xml.get_widget('status_menu')
join_gc_menuitem = self.xml.get_widget('join_gc_menuitem')
if self.single_message_handler_id:
single_message_menuitem.handler_disconnect(
self.single_message_handler_id)
@ -130,7 +118,7 @@ class Systray:
sub_menu = gtk.Menu()
self.popup_menus.append(sub_menu)
status_menuitem.set_submenu(sub_menu)
gc_sub_menu = gtk.Menu() # gc is always a submenu
join_gc_menuitem.set_submenu(gc_sub_menu)
@ -175,7 +163,7 @@ class Systray:
chat_with_menuitem.set_sensitive(iskey)
single_message_menuitem.set_sensitive(iskey)
join_gc_menuitem.set_sensitive(iskey)
if connected_accounts >= 2: # 2 or more connections? make submenus
account_menu_for_chat_with = gtk.Menu()
chat_with_menuitem.set_submenu(account_menu_for_chat_with)
@ -184,7 +172,7 @@ class Systray:
account_menu_for_single_message = gtk.Menu()
single_message_menuitem.set_submenu(account_menu_for_single_message)
self.popup_menus.append(account_menu_for_single_message)
accounts_list = gajim.contacts.get_accounts()
accounts_list.sort()
for account in accounts_list:
@ -208,7 +196,7 @@ class Systray:
gc_item.add(label)
gc_sub_menu.append(gc_item)
gajim.interface.roster.add_bookmarks_list(gc_sub_menu, account)
elif connected_accounts == 1: # one account
# one account connected, no need to show 'as jid'
for account in gajim.connections:
@ -223,7 +211,7 @@ class Systray:
# join gc
gajim.interface.roster.add_bookmarks_list(gc_sub_menu, account)
break # No other connected account
if event is None:
# None means windows (we explicitly popup in systraywin32.py)
if self.added_hide_menuitem is False:
@ -231,14 +219,18 @@ class Systray:
item = gtk.MenuItem(_('Hide this menu'))
self.systray_context_menu.prepend(item)
self.added_hide_menuitem = True
else: # GNU and Unices
self.systray_context_menu.popup(None, None, None, event.button, event.time)
self.systray_context_menu.popup(None, None, None, event.button,
event.time)
self.systray_context_menu.show_all()
def on_show_all_events_menuitem_activate(self, widget):
for i in range(len(self.jids)):
self.handle_first_event()
events = gajim.events.get_systray_events()
for account in events:
for jid in events[account]:
for event in events[account][jid]:
gajim.interface.handle_event(account, jid, event.type_)
def on_show_roster_menuitem_activate(self, widget):
win = gajim.interface.roster.window
@ -255,11 +247,11 @@ class Systray:
def on_left_click(self):
win = gajim.interface.roster.window
if len(self.jids) == 0:
if len(gajim.events.get_systray_events()) == 0:
# no pending events, so toggle visible/hidden for roster window
if win.get_property('visible'): # visible in ANY virtual desktop?
win.hide() # we hide it from VD that was visible in
# but we could be in another VD right now. eg vd2
# and we want not only to hide it in vd1 but also show it in vd2
gtkgui_helpers.possibly_move_window_in_current_desktop(win)
@ -269,10 +261,8 @@ class Systray:
self.handle_first_event()
def handle_first_event(self):
account = self.jids[0][0]
jid = self.jids[0][1]
typ = self.jids[0][2]
gajim.interface.handle_event(account, jid, typ)
account, jid, event = gajim.events.get_first_systray_event()
gajim.interface.handle_event(account, jid, event.type_)
def on_middle_click(self):
'''middle click raises window to have complete focus (fe. get kbd events)
@ -285,13 +275,13 @@ class Systray:
def on_clicked(self, widget, event):
self.on_tray_leave_notify_event(widget, None)
if event.button == 1: # Left click
if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1: # Left click
self.on_left_click()
elif event.button == 2: # middle click
self.on_middle_click()
elif event.button == 3: # right click
self.make_menu(event)
def on_show_menuitem_activate(self, widget, show):
# we all add some fake (we cannot select those nor have them as show)
# but this helps to align with roster's status_combobox index positions
@ -320,7 +310,7 @@ class Systray:
if self.tooltip.id == position:
size = widget.window.get_size()
self.tooltip.show_tooltip('', size[1], position[1])
def on_tray_motion_notify_event(self, widget, event):
wireq=widget.size_request()
position = widget.window.get_origin()
@ -332,16 +322,23 @@ class Systray:
self.tooltip.id = position
self.tooltip.timeout = gobject.timeout_add(500,
self.show_tooltip, widget)
def on_tray_leave_notify_event(self, widget, event):
position = widget.window.get_origin()
if self.tooltip.timeout > 0 and \
self.tooltip.id == position:
self.tooltip.hide_tooltip()
def on_tray_destroyed(self, widget):
'''re-add trayicon when systray is destroyed'''
self.t = None
if gajim.interface.systray_enabled:
self.show_icon()
def show_icon(self):
if not self.t:
self.t = trayicon.TrayIcon('Gajim')
self.t.connect('destroy', self.on_tray_destroyed)
eb = gtk.EventBox()
# avoid draw seperate bg color in some gtk themes
eb.set_visible_window(False)
@ -356,7 +353,7 @@ class Systray:
self.t.add(eb)
self.set_img()
self.t.show_all()
def hide_icon(self):
if self.t:
self.t.destroy()

View file

@ -245,36 +245,25 @@ class SystrayWin32(systray.Systray):
elif lparam == win32con.WM_LBUTTONUP: # Left click
self.on_left_click()
def add_jid(self, jid, account, typ):
systray.Systray.add_jid(self, jid, account, typ)
def set_img(self):
self.tray_ico_imgs = self.load_icos() #FIXME: do not do this here
# see gajim.interface.roster.reload_jabber_state_images() to merge
nb = gajim.interface.roster.nb_unread
for acct in gajim.connections:
# in chat / groupchat windows
for kind in ('chats', 'gc'):
jids = gajim.interface.instances[acct][kind]
for jid in jids:
if jid != 'tabbed':
nb += jids[jid].nb_unread[jid]
text = i18n.ngettext(
'Gajim - %d unread message',
'Gajim - %d unread messages',
nb, nb, nb)
if len(self.jids) > 0:
state = 'message'
else:
state = self.status
hicon = self.tray_ico_imgs[state]
if hicon is None:
return
self.systray_winapi.notify_icon.set_tooltip(text)
self.systray_winapi.remove_notify_icon()
self.systray_winapi.add_notify_icon(self.systray_context_menu, hicon,
'Gajim')
self.systray_winapi.notify_icon.menu = self.systray_context_menu
def remove_jid(self, jid, account, typ):
systray.Systray.remove_jid(self, jid, account, typ)
nb = gajim.events.get_nb_systray_events()
nb = gajim.interface.roster.nb_unread
for acct in gajim.connections:
# in chat / groupchat windows
for kind in ('chats', 'gc'):
for jid in gajim.interface.instances[acct][kind]:
if jid != 'tabbed':
nb += gajim.interface.instances[acct][kind][jid].nb_unread[jid]
if nb > 0:
text = i18n.ngettext(
'Gajim - %d unread message',
@ -284,23 +273,6 @@ class SystrayWin32(systray.Systray):
text = 'Gajim'
self.systray_winapi.notify_icon.set_tooltip(text)
def set_img(self):
self.tray_ico_imgs = self.load_icos() #FIXME: do not do this here
# see gajim.interface.roster.reload_jabber_state_images() to merge
if len(self.jids) > 0:
state = 'message'
else:
state = self.status
hicon = self.tray_ico_imgs[state]
if hicon is None:
return
self.systray_winapi.remove_notify_icon()
self.systray_winapi.add_notify_icon(self.systray_context_menu, hicon,
'Gajim')
self.systray_winapi.notify_icon.menu = self.systray_context_menu
def load_icos(self):
'''load .ico files and return them to a dic of SHOW --> img_obj'''
iconset = str(gajim.config.get('iconset'))

View file

@ -282,34 +282,14 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable):
self.table.set_property('column-spacing', 1)
text, single_line = '', ''
unread_chat = gajim.interface.roster.nb_unread
unread_single_chat = 0
unread_gc = 0
unread_pm = 0
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',
'gc_msg'])
unread_pm = gajim.events.get_nb_events(types = ['printed_pm', 'pm'])
accounts = self.get_accounts_info()
for acct in gajim.connections:
# Count unread chat messages
chat_t = message_control.TYPE_CHAT
for ctrl in gajim.interface.msg_win_mgr.get_controls(chat_t, acct):
unread_chat += ctrl.nb_unread
# Count unread PM messages for which we have a control
chat_t = message_control.TYPE_PM
for ctrl in gajim.interface.msg_win_mgr.get_controls(chat_t, acct):
unread_pm += ctrl.nb_unread
# we count unread gc/pm messages
chat_t = message_control.TYPE_GC
for ctrl in gajim.interface.msg_win_mgr.get_controls(chat_t, acct):
# These are PMs for which the PrivateChatControl has not yet been
# created
pm_msgs = ctrl.get_specific_unread()
unread_gc += ctrl.nb_unread
unread_gc -= pm_msgs
unread_pm += pm_msgs
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
@ -508,8 +488,9 @@ class RosterTooltip(NotificationAreaTooltip):
properties = []
jid_markup = '<span weight="bold">' + prim_contact.jid + '</span>'
properties.append((jid_markup, None))
properties.append((_('Name: '), gtkgui_helpers.escape_for_pango_markup(
prim_contact.get_shown_name())))
prim_contact.get_shown_name())))
if prim_contact.sub:
properties.append(( _('Subscription: '),
gtkgui_helpers.escape_for_pango_markup(helpers.get_uf_sub(prim_contact.sub))))
@ -532,10 +513,11 @@ class RosterTooltip(NotificationAreaTooltip):
contacts_dict[contact.priority].append(contact)
else:
contacts_dict[contact.priority] = [contact]
if num_resources== 1 and contact.resource:
properties.append((_('Resource: '), gtkgui_helpers.escape_for_pango_markup(
contact.resource) + ' (' + unicode(contact.priority) + ')'))
if num_resources == 1 and contact.resource:
properties.append((_('Resource: '),
gtkgui_helpers.escape_for_pango_markup(contact.resource) + ' (' + \
unicode(contact.priority) + ')'))
if num_resources > 1:
properties.append((_('Status: '), ' '))
contact_keys = contacts_dict.keys()

View file

@ -57,51 +57,25 @@ def get_avatar_pixbuf_encoded_mime(photo):
class VcardWindow:
'''Class for contact's information window'''
def __init__(self, contact, account, vcard = False, is_fake = False):
def __init__(self, contact, account, is_fake = False):
# the contact variable is the jid if vcard is true
self.xml = gtkgui_helpers.get_glade('vcard_information_window.glade')
self.window = self.xml.get_widget('vcard_information_window')
self.publish_button = self.xml.get_widget('publish_button')
self.retrieve_button = self.xml.get_widget('retrieve_button')
self.nickname_entry = self.xml.get_widget('nickname_entry')
if not vcard: # Maybe gc_vcard ?
self.nickname_entry.set_property('editable', False)
self.publish_button.set_no_show_all(True)
self.retrieve_button.set_no_show_all(True)
self.xml.get_widget('photo_vbuttonbox').set_no_show_all(True)
self.contact = contact # don't use it if vcard is true
self.contact = contact
self.account = account
self.vcard = vcard
self.is_fake = is_fake
self.avatar_mime_type = None
self.avatar_encoded = None
self.avatar_save_as_id = None
if vcard: # we view/edit our own vcard
self.jid = contact
# remove Jabber tab & show publish/retrieve/close/set_avatar buttons
# and make entries and textview editable
self.change_to_vcard()
else: # we see someone else's vcard
self.publish_button.hide()
self.retrieve_button.hide()
self.jid = contact.jid
self.fill_jabber_page()
# if we are editing our own vcard publish button should publish
# vcard data we have typed including nickname, it's why we connect only
# here (when we see someone else's vcard)
self.nickname_entry.connect('focus-out-event',
self.on_nickname_entry_focus_out_event)
self.fill_jabber_page()
self.xml.signal_autoconnect(self)
self.window.show_all()
def on_vcard_information_window_destroy(self, widget):
del gajim.interface.instances[self.account]['infos'][self.jid]
del gajim.interface.instances[self.account]['infos'][self.contact.jid]
def on_vcard_information_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
@ -122,108 +96,16 @@ class VcardWindow:
if oldlog != log:
gajim.config.set_per('accounts', self.account, 'no_log_for',
' '.join(no_log_for))
def on_nickname_entry_focus_out_event(self, widget, event):
'''Save contact information and update
the roster item on the Jabber server'''
new_name = self.nickname_entry.get_text().decode('utf-8')
# update contact.name with new nickname if that is not ''
if new_name != self.contact.name and new_name != '':
self.contact.name = new_name
# update roster model
model = gajim.interface.roster.tree.get_model()
for iter_ in gajim.interface.roster.get_contact_iter(self.contact.jid,
self.account):
model[iter_][1] = new_name
gajim.connections[self.account].update_contact(self.contact.jid,
self.contact.name, self.contact.groups)
# update opened chat window
ctrl = gajim.interface.msg_win_mgr.get_control(self.contact.jid,
self.account)
if ctrl:
ctrl.update_ui()
win = gajim.interface.msg_win_mgr.get_window(self.contact.jid,
self.account)
win.redraw_tab(ctrl)
win.show_title()
def on_close_button_clicked(self, widget):
self.window.destroy()
def on_clear_button_clicked(self, widget):
# empty the image
self.xml.get_widget('PHOTO_image').set_from_pixbuf(None)
self.avatar_encoded = None
if self.avatar_save_as_id:
self.xml.get_widget('PHOTO_eventbox').disconnect(
self.avatar_save_as_id)
self.avatar_save_as_id = None
def on_set_avatar_button_clicked(self, widget):
f = None
def on_ok(widget, path_to_file):
filesize = os.path.getsize(path_to_file) # in bytes
#FIXME: use messages for invalid file for 0.11
invalid_file = False
msg = ''
if os.path.isfile(path_to_file):
stat = os.stat(path_to_file)
if stat[6] == 0:
invalid_file = True
else:
invalid_file = True
if not invalid_file and filesize > 16384: # 16 kb
try:
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
# get the image at 'notification size'
# and use that user did not specify in ACE crazy size
scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf,
'tooltip')
except gobject.GError, msg: # unknown format
# msg should be string, not object instance
msg = str(msg)
invalid_file = True
if invalid_file:
if True: # keep identation
dialogs.ErrorDialog(_('Could not load image'), msg)
return
if filesize > 16384:
if scaled_pixbuf:
path_to_file = os.path.join(gajim.TMP,
'avatar_scaled.png')
scaled_pixbuf.save(path_to_file, 'png')
self.dialog.destroy()
fd = open(path_to_file, 'rb')
data = fd.read()
pixbuf = gtkgui_helpers.get_pixbuf_from_data(data)
# rescale it
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
image = self.xml.get_widget('PHOTO_image')
image.set_from_pixbuf(pixbuf)
self.avatar_encoded = base64.encodestring(data)
# returns None if unknown type
self.avatar_mime_type = mimetypes.guess_type(path_to_file)[0]
self.dialog = dialogs.ImageChooserDialog(on_response_ok = on_ok)
def on_PHOTO_eventbox_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3: # right click
if self.vcard:
# our own avatar
account = None
nick = gajim.config.get_per('accounts', self.account, 'name')
else:
account = self.account
nick = self.contact.name
menu = gtk.Menu()
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
menuitem.connect('activate',
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
self.jid, account, nick + '.jpeg')
self.contact.jid, self.account, self.contact.name + '.jpeg')
menu.append(menuitem)
menu.show_all()
menu.connect('selection-done', lambda w:w.destroy())
# show the menu
menu.show_all()
@ -240,14 +122,13 @@ class VcardWindow:
if i == 'PHOTO':
pixbuf, self.avatar_encoded, self.avatar_mime_type = \
get_avatar_pixbuf_encoded_mime(vcard[i])
if not pixbuf:
continue
image = self.xml.get_widget('PHOTO_image')
if not pixbuf:
image.set_from_icon_name('stock_person',
gtk.ICON_SIZE_DIALOG)
continue
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
image.set_from_pixbuf(pixbuf)
eventbox = self.xml.get_widget('PHOTO_eventbox')
self.avatar_save_as_id = eventbox.connect('button-press-event',
self.on_PHOTO_eventbox_button_press_event)
continue
if i == 'ADR' or i == 'TEL' or i == 'EMAIL':
for entry in vcard[i]:
@ -255,22 +136,22 @@ class VcardWindow:
if 'WORK' in entry:
add_on = '_WORK'
for j in entry.keys():
self.set_value(i + add_on + '_' + j + '_entry', entry[j])
self.set_value(i + add_on + '_' + j + '_label', entry[j])
if isinstance(vcard[i], dict):
for j in vcard[i].keys():
self.set_value(i + '_' + j + '_entry', vcard[i][j])
self.set_value(i + '_' + j + '_label', vcard[i][j])
else:
if i == 'DESC':
self.xml.get_widget('DESC_textview').get_buffer().set_text(
vcard[i], 0)
else:
self.set_value(i + '_entry', vcard[i])
self.set_value(i + '_label', vcard[i])
def set_last_status_time(self):
self.fill_status_label()
def set_os_info(self, resource, client_info, os_info):
if self.xml.get_widget('information_notebook').get_n_pages() < 5:
if self.xml.get_widget('information_notebook').get_n_pages() < 4:
return
i = 0
client = ''
@ -295,7 +176,7 @@ class VcardWindow:
self.xml.get_widget('os_label').set_text(os)
def fill_status_label(self):
if self.xml.get_widget('information_notebook').get_n_pages() < 5:
if self.xml.get_widget('information_notebook').get_n_pages() < 4:
return
contact_list = gajim.contacts.get_contact(self.account, self.contact.jid)
# stats holds show and status message
@ -312,7 +193,7 @@ class VcardWindow:
stats += '\n' + _('since %s') % time.strftime('%c',
c.last_status_time).decode(locale.getpreferredencoding())
one = False
elif not self.vcard: # Maybe gc_vcard ?
else: # Maybe gc_vcard ?
stats = helpers.get_uf_show(self.contact.show)
if self.contact.status:
stats += ': ' + self.contact.status
@ -326,8 +207,10 @@ class VcardWindow:
def fill_jabber_page(self):
tooltips = gtk.Tooltips()
self.xml.get_widget('nickname_label').set_text(
self.contact.get_shown_name())
self.xml.get_widget('nickname_label').set_markup(
'<b><span size="x-large">' +
self.contact.get_shown_name() +
'</span></b>')
self.xml.get_widget('jid_label').set_text(self.contact.jid)
uf_sub = helpers.get_uf_sub(self.contact.sub)
self.xml.get_widget('subscription_label').set_text(uf_sub)
@ -349,7 +232,6 @@ class VcardWindow:
if self.contact.ask == 'subscribe':
tooltips.set_tip(eb,
_("You are waiting contact's answer about your subscription request"))
self.nickname_entry.set_text(self.contact.name)
log = True
if self.contact.jid in gajim.config.get_per('accounts', self.account,
'no_log_for').split(' '):
@ -371,8 +253,8 @@ class VcardWindow:
# Request os info in contact is connected
if self.contact.show not in ('offline', 'error'):
gajim.connections[self.account].request_os_info(self.contact.jid,
self.contact.resource)
gobject.idle_add(gajim.connections[self.account].request_os_info,
self.contact.jid, self.contact.resource)
self.os_info = {0: {'resource': self.contact.resource, 'client': '',
'os': ''}}
i = 1
@ -385,7 +267,8 @@ class VcardWindow:
uf_resources += '\n' + c.resource + \
_(' resource with priority ') + unicode(c.priority)
if c.show not in ('offline', 'error'):
gajim.connections[self.account].request_os_info(c.jid,
gobject.idle_add(
gajim.connections[self.account].request_os_info, c.jid,
c.resource)
gajim.connections[self.account].request_last_status_time(c.jid,
c.resource)
@ -400,121 +283,3 @@ class VcardWindow:
self.fill_status_label()
gajim.connections[self.account].request_vcard(self.contact.jid, self.is_fake)
def add_to_vcard(self, vcard, entry, txt):
'''Add an information to the vCard dictionary'''
entries = entry.split('_')
loc = vcard
if len(entries) == 3: # We need to use lists
if not loc.has_key(entries[0]):
loc[entries[0]] = []
found = False
for e in loc[entries[0]]:
if entries[1] in e:
found = True
break
if found:
e[entries[2]] = txt
else:
loc[entries[0]].append({entries[1]: '', entries[2]: txt})
return vcard
while len(entries) > 1:
if not loc.has_key(entries[0]):
loc[entries[0]] = {}
loc = loc[entries[0]]
del entries[0]
loc[entries[0]] = txt
return vcard
def make_vcard(self):
'''make the vCard dictionary'''
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
vcard = {}
for e in entries:
txt = self.xml.get_widget(e + '_entry').get_text().decode('utf-8')
if txt != '':
vcard = self.add_to_vcard(vcard, e, txt)
# DESC textview
buff = self.xml.get_widget('DESC_textview').get_buffer()
start_iter = buff.get_start_iter()
end_iter = buff.get_end_iter()
txt = buff.get_text(start_iter, end_iter, 0)
if txt != '':
vcard['DESC'] = txt.decode('utf-8')
# Avatar
if self.avatar_encoded:
vcard['PHOTO'] = {'BINVAL': self.avatar_encoded}
if self.avatar_mime_type:
vcard['PHOTO']['TYPE'] = self.avatar_mime_type
return vcard
def on_publish_button_clicked(self, widget):
if gajim.connections[self.account].connected < 2:
dialogs.ErrorDialog(_('You are not connected to the server'),
_('Without a connection you can not publish your contact '
'information.'))
return
vcard = self.make_vcard()
nick = ''
if vcard.has_key('NICKNAME'):
nick = vcard['NICKNAME']
if nick == '':
nick = gajim.config.get_per('accounts', self.account, 'name')
gajim.nicks[self.account] = nick
gajim.connections[self.account].send_vcard(vcard)
def on_retrieve_button_clicked(self, widget):
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'ADR_WORK_STREET', 'ADR_WORK_EXTADR',
'ADR_WORK_LOCALITY', 'ADR_WORK_REGION', 'ADR_WORK_PCODE',
'ADR_WORK_CTRY']
if gajim.connections[self.account].connected > 1:
# clear all entries
for e in entries:
self.xml.get_widget(e + '_entry').set_text('')
self.xml.get_widget('DESC_textview').get_buffer().set_text('')
self.xml.get_widget('PHOTO_image').set_from_pixbuf(None)
if self.avatar_save_as_id:
self.xml.get_widget('PHOTO_eventbox').disconnect(
self.avatar_save_as_id)
self.avatar_save_as_id = None
gajim.connections[self.account].request_vcard(self.jid)
else:
dialogs.ErrorDialog(_('You are not connected to the server'),
_('Without a connection, you can not get your contact information.'))
def change_to_vcard(self):
self.xml.get_widget('information_notebook').remove_page(0)
self.xml.get_widget('nickname_label').set_text(_('Personal details'))
self.publish_button.show()
self.retrieve_button.show()
#photo_vbuttonbox visible
self.xml.get_widget('photo_vbuttonbox').show()
#make all entries editable
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
for e in entries:
self.xml.get_widget(e + '_entry').set_property('editable', True)
description_textview = self.xml.get_widget('DESC_textview')
description_textview.set_editable(True)
description_textview.set_cursor_visible(True)