merged trunk into session_centric branch

This commit is contained in:
Brendan Taylor 2008-02-05 03:09:31 +00:00
parent b8882ba48e
commit 0b48b05218
68 changed files with 13704 additions and 7863 deletions

13
TODO.pep Normal file
View file

@ -0,0 +1,13 @@
• configure access model when changing it in the combobox
• PEP in status change
Tune use cases:
• on disconnection of an account set Tune to None
Tooltips use cases:
• Show PEP in GC tooltips
Mood/Activity use cases
• on connection of an account set them to None
• on disconnection of an account set them to None
• on explicit set publish them

View file

@ -9,7 +9,7 @@
echo "[encoding: UTF-8]" > po/POTFILES.in \
&& ls -1 -U data/gajim.desktop.in.in data/glade/*.glade \
src/*py src/common/*py src/common/zeroconf/*.py >> \
src/*py src/common/*py src/common/zeroconf/*.py src/osx/*.py >> \
po/POTFILES.in || exit 1
if test -z `which pkg-config 2>/dev/null`;then
echo "***Error: pkg-config not found***"

View file

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

View file

@ -14,7 +14,13 @@ sounds_DATA = $(srcdir)/sounds/*.wav
otherdir = $(pkgdatadir)/data/other
other_DATA = other/servers.xml other/cacerts.pem
man_MANS = gajim.1 gajim-remote.1
if BUILD_REMOTE_CONTROL
OPTIONAL_MAN = gajim-remote.1
else
OPTIONAL_MAN =
endif
man_MANS = gajim.1 $(OPTIONAL_MAN)
EXTRA_DIST = $(desktop_in_files) \

View file

@ -17,6 +17,20 @@
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="pep_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Personal Events</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="menu-item-image7">
<property name="stock">gtk-home</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>

View file

@ -117,7 +117,7 @@ to the Jabber network.</property>
<widget class="GtkLabel" id="label270">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Please fill in the data for your new account&lt;/b&gt;</property>
<property name="label" translatable="yes">&lt;b&gt;Please fill in the data for your existing account&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,158 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkDialog" id="change_activity_dialog">
<property name="border_width">6</property>
<property name="title" translatable="yes"></property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">270</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<property name="has_separator">True</property>
<signal name="key_press_event" handler="on_change_status_message_dialog_key_press_event" last_modification_time="Wed, 16 Mar 2005 00:53:06 GMT"/>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox5">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-6</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Sat, 31 Mar 2007 07:58:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok_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-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
<signal name="clicked" handler="on_ok_button_clicked" last_modification_time="Sat, 31 Mar 2007 07:57:55 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame38">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="label_yalign">0.5</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="alignment107">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">1</property>
<property name="yscale">1</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">12</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkVBox" id="vbox112">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkComboBox" id="combobox1">
<property name="visible">True</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>
<widget class="GtkComboBox" id="combobox2">
<property name="visible">True</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>
<widget class="GtkEntry" id="entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">●</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -0,0 +1,145 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkDialog" id="change_mood_dialog">
<property name="border_width">6</property>
<property name="title" translatable="yes"></property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">270</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<property name="has_separator">True</property>
<signal name="key_press_event" handler="on_change_status_message_dialog_key_press_event" last_modification_time="Wed, 16 Mar 2005 00:53:06 GMT"/>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox5">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-6</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Sat, 31 Mar 2007 07:58:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok_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-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
<signal name="clicked" handler="on_ok_button_clicked" last_modification_time="Sat, 31 Mar 2007 07:57:55 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame38">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="label_yalign">0.5</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="alignment107">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">1</property>
<property name="yscale">1</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">12</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkVBox" id="vbox112">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkComboBox" id="combobox">
<property name="visible">True</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>
<widget class="GtkEntry" id="entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">●</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -43,7 +43,7 @@
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkVBox" id="vbox2">
<widget class="GtkVBox" id="welcome_vbox">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>

View file

@ -0,0 +1,135 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="manage_pep_services_window">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="title" translatable="yes"></property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">350</property>
<property name="default_height">150</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="destroy" handler="on_manage_pep_services_window_destroy"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<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_NONE</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="services_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</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>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_DEFAULT_STYLE</property>
<property name="spacing">0</property>
<child>
<widget class="GtkButton" id="add_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
<signal name="clicked" handler="on_add_button_clicked" last_modification_time="Sat, 31 Mar 2007 07:57:55 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="delete_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-delete</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
<signal name="clicked" handler="on_delete_button_clicked" last_modification_time="Sat, 31 Mar 2007 07:57:55 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-6</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Sat, 31 Mar 2007 07:58:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok_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-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
<signal name="clicked" handler="on_ok_button_clicked" last_modification_time="Sat, 31 Mar 2007 07:57:55 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View file

@ -108,15 +108,34 @@
</widget>
</child>
<child>
<widget class="GtkScrolledWindow" id="message_scrolledwindow">
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">3</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<placeholder/>
<widget class="GtkImage" id="lock_image">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="no_show_all">True</property>
<property name="stock">gtk-dialog-authentication</property>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="message_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">3</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
@ -136,38 +155,6 @@
<widget class="GtkHBox" id="hbox3006">
<property name="visible">True</property>
<property name="spacing">1</property>
<child>
<widget class="GtkEventBox" id="gpg_eventbox">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">OpenPGP Encryption</property>
<child>
<widget class="GtkToggleButton" id="gpg_togglebutton">
<property name="visible">True</property>
<property name="relief">GTK_RELIEF_NONE</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
<child>
<widget class="GtkImage" id="image1333">
<property name="visible">True</property>
<property name="stock">gtk-dialog-authentication</property>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkVSeparator" id="vseparator4">
<property name="visible">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
<child>
@ -399,6 +386,7 @@
</child>
<child>
<widget class="GtkVBox" id="muc_child_vbox">
<property name="can_focus">True</property>
<property name="border_width">3</property>
<child>
<widget class="GtkAlignment" id="alignment103">

File diff suppressed because it is too large Load diff

View file

@ -193,6 +193,14 @@
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="pep_services_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Services</property>
<property name="use_underline">True</property>
</widget>
</child>
</widget>
</child>
</widget>
@ -222,6 +230,17 @@
<signal name="activate" handler="on_show_transports_menuitem_activate"/>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="show_roster_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Show _roster</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<signal name="toggled" handler="on_show_roster_menuitem_toggled"/>
<accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator3">
<property name="visible">True</property>
@ -344,45 +363,65 @@
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow">
<widget class="GtkHPaned" id="roster_hpaned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">2</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkTreeView" id="roster_treeview">
<widget class="GtkVBox" id="roster_vbox2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="reorderable">True</property>
<signal name="leave_notify_event" handler="on_roster_treeview_leave_notify_event"/>
<signal name="button_press_event" handler="on_roster_treeview_button_press_event"/>
<signal name="motion_notify_event" handler="on_roster_treeview_motion_notify_event"/>
<signal name="row_collapsed" handler="on_roster_treeview_row_collapsed"/>
<signal name="row_expanded" handler="on_roster_treeview_row_expanded"/>
<signal name="key_press_event" handler="on_roster_treeview_key_press_event"/>
<signal name="row_activated" handler="on_roster_treeview_row_activated"/>
<signal name="button_release_event" handler="on_roster_treeview_button_release_event"/>
<signal name="scroll_event" handler="on_roster_treeview_scroll_event"/>
<signal name="style_set" handler="on_roster_treeview_style_set"/>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">2</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<child>
<widget class="GtkTreeView" id="roster_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="reorderable">True</property>
<signal name="leave_notify_event" handler="on_roster_treeview_leave_notify_event"/>
<signal name="button_press_event" handler="on_roster_treeview_button_press_event"/>
<signal name="motion_notify_event" handler="on_roster_treeview_motion_notify_event"/>
<signal name="row_collapsed" handler="on_roster_treeview_row_collapsed"/>
<signal name="row_expanded" handler="on_roster_treeview_row_expanded"/>
<signal name="key_press_event" handler="on_roster_treeview_key_press_event"/>
<signal name="row_activated" handler="on_roster_treeview_row_activated"/>
<signal name="button_release_event" handler="on_roster_treeview_button_release_event"/>
<signal name="scroll_event" handler="on_roster_treeview_scroll_event"/>
<signal name="style_set" handler="on_roster_treeview_style_set"/>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkComboBox" id="status_combobox">
<property name="visible">True</property>
<signal name="changed" handler="on_status_combobox_changed"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="status_combobox">
<property name="visible">True</property>
<signal name="changed" handler="on_status_combobox_changed"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>

View file

@ -54,6 +54,7 @@
<property name="xpad">5</property>
<property name="ypad">5</property>
<property name="selectable">True</property>
<property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
</child>
</widget>
@ -94,6 +95,7 @@
<property name="xpad">5</property>
<property name="ypad">5</property>
<property name="selectable">True</property>
<property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
<property name="left_attach">1</property>
@ -218,8 +220,8 @@
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label59">
<property name="visible">True</property>
<widget class="GtkLabel" id="user_avatar_label">
<property name="no_show_all">True</property>
<property name="label" translatable="yes">User avatar:</property>
</widget>
<packing>
@ -227,17 +229,6 @@
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="no_user_avatar_label">
<property name="no_show_all">True</property>
<property name="label" translatable="yes">None</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkEventBox" id="PHOTO_eventbox">
<property name="visible">True</property>
@ -254,7 +245,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
<property name="position">1</property>
</packing>
</child>
<child>
@ -265,7 +256,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
<property name="position">2</property>
</packing>
</child>
<child>
@ -277,11 +268,12 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
<property name="position">3</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
@ -384,9 +376,6 @@
</packing>
</child>
</widget>
<packing>
<property name="tab_expand">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label3">
@ -397,7 +386,6 @@
</widget>
<packing>
<property name="type">tab</property>
<property name="tab_expand">False</property>
<property name="tab_fill">False</property>
</packing>
</child>
@ -1014,7 +1002,6 @@
</widget>
<packing>
<property name="position">1</property>
<property name="tab_expand">False</property>
</packing>
</child>
<child>
@ -1027,7 +1014,6 @@
<packing>
<property name="type">tab</property>
<property name="position">1</property>
<property name="tab_expand">False</property>
<property name="tab_fill">False</property>
</packing>
</child>
@ -1459,7 +1445,6 @@
</widget>
<packing>
<property name="position">2</property>
<property name="tab_expand">False</property>
</packing>
</child>
<child>
@ -1472,7 +1457,6 @@
<packing>
<property name="type">tab</property>
<property name="position">2</property>
<property name="tab_expand">False</property>
<property name="tab_fill">False</property>
</packing>
</child>
@ -1497,7 +1481,6 @@
</widget>
<packing>
<property name="position">3</property>
<property name="tab_expand">False</property>
</packing>
</child>
<child>
@ -1510,7 +1493,6 @@
<packing>
<property name="type">tab</property>
<property name="position">3</property>
<property name="tab_expand">False</property>
<property name="tab_fill">False</property>
</packing>
</child>
@ -1533,7 +1515,6 @@
</widget>
<packing>
<property name="position">4</property>
<property name="tab_expand">False</property>
</packing>
</child>
<child>
@ -1544,7 +1525,6 @@
<packing>
<property name="type">tab</property>
<property name="position">4</property>
<property name="tab_expand">False</property>
<property name="tab_fill">False</property>
</packing>
</child>
@ -1588,6 +1568,7 @@
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>

View file

@ -1 +1,2 @@
data/gajim.desktop.in
src/eggtrayicon.c

3412
po/de.po

File diff suppressed because it is too large Load diff

402
po/fr.po
View file

@ -3570,7 +3570,7 @@ msgstr "Fichier"
#: ../src/filetransfers_window.py:87
msgid "Time"
msgstr "Moment"
msgstr "Durée"
#: ../src/filetransfers_window.py:99
msgid "Progress"
@ -6537,6 +6537,406 @@ msgstr "Connecté"
msgid "Disconnected"
msgstr "Déconnecté"
#pep
msgid "afraid"
msgstr "appeuré"
msgid "amazed"
msgstr "surpris, amusé"
msgid "angry"
msgstr "en colère"
msgid "annoyed"
msgstr "dérangé"
msgid "anxious"
msgstr "anxieux"
msgid "aroused"
msgstr "excité"
msgid "ashamed"
msgstr "honteux/éhonté"
msgid "bored"
msgstr "ennuyé"
msgid "brave"
msgstr "courageux"
msgid "calm"
msgstr "calme"
msgid "cold"
msgstr "froid"
msgid "confused"
msgstr "confus"
msgid "contented"
msgstr "contenté"
msgid "cranky"
msgstr "excentrique"
msgid "curious"
msgstr "curieux"
msgid "depressed"
msgstr "déprimé"
msgid "disappointed"
msgstr "décu"
msgid "disgusted"
msgstr "dégouté"
msgid "distracted"
msgstr "distrait"
msgid "embarrassed"
msgstr "embarssé"
msgid "excited"
msgstr "excité"
msgid "flirtatious"
msgstr "coquet"
msgid "frustrated"
msgstr "frustré"
msgid "grumpy"
msgstr "grognon"
msgid "guilty"
msgstr "coupable"
msgid "happy"
msgstr "joyeux"
msgid "hot"
msgstr "chaud"
msgid "humbled"
msgstr "humilié"
msgid "humiliated"
msgstr "humilié"
msgid "hungry"
msgstr "affamé"
msgid "hurt"
msgstr "blessé"
msgid "impressed"
msgstr "impressionné"
msgid "in_awe"
msgstr "dans la crainte"
msgid "in_love"
msgstr "amoureux"
msgid "indignant"
msgstr "indigné"
msgid "interested"
msgstr "interessé"
msgid "intoxicated"
msgstr "intoxiqué"
msgid "invincible"
msgstr "invincible"
msgid "jealous"
msgstr "jaloux"
msgid "lonely"
msgstr "seul/esseulé"
msgid "mean"
msgstr "méchant"
msgid "moody"
msgstr "déprimé"
msgid "nervous"
msgstr "nerveux"
msgid "neutral"
msgstr "neutre"
msgid "offended"
msgstr "offensé"
msgid "playful"
msgstr "joueur"
msgid "proud"
msgstr "fier"
msgid "relieved"
msgstr "soulagé"
msgid "remorseful"
msgstr "plein de remord"
msgid "restless"
msgstr "infatiguable"
msgid "sad"
msgstr "triste"
msgid "sarcastic"
msgstr "sarcastique"
msgid "serious"
msgstr "serieux"
msgid "shocked"
msgstr "choqué"
msgid "shy"
msgstr "timide"
msgid "sick"
msgstr "malade"
msgid "sleepy"
msgstr "endormi"
msgid "stressed"
msgstr "stressé"
msgid "surprised"
msgstr "surpris"
msgid "thirsty"
msgstr "assoiffé"
msgid "worried"
msgstr "inquiet"
msgid "_Personnal Events"
msgstr "Évènements _Personnels"
msgid "Activity"
msgstr "Activité"
msgid "doing_chores"
msgstr "fait des corvées"
msgid "buying_groceries"
msgstr "achète des épiceries"
msgid "cleaning"
msgstr "nettoie"
msgid "cooking"
msgstr "cuisine"
msgid "doing_maintenance"
msgstr "fait de la maintenance"
msgid "doing_the_dishes"
msgstr "fait la vaiselle"
msgid "doing_the_laundry"
msgstr "fait la blanchisserie"
msgid "gardening"
msgstr "jardine"
msgid "running_an_errand"
msgstr "fait une course"
msgid "walking_the_dog"
msgstr "promène le chien"
msgid "drinking"
msgstr "boit"
msgid "having_a_beer"
msgstr "prend une bière"
msgid "having_coffee"
msgstr "prend un café"
msgid "having_tea"
msgstr "prend un thé"
msgid "eating"
msgstr "mange"
msgid "having_a_snack"
msgstr "prend un snack"
msgid "having_breakfast"
msgstr "prend le petit-déjeuner"
msgid "having_dinner"
msgstr "soupe"
msgid "having_lunch"
msgstr "dîne"
msgid "exercising"
msgstr "fait de l'exercice"
msgid "cycling"
msgstr "fait du vélo"
msgid "hiking"
msgstr "fait de la randonnée"
msgid "jogging"
msgstr "fait un jogging"
msgid "playing_sports"
msgstr "fait du sport"
msgid "running"
msgstr "court"
msgid "skiing"
msgstr "skie"
msgid "swimming"
msgstr "nage"
msgid "working_out"
msgstr "élabore"
msgid "grooming"
msgstr "se toilette"
msgid "at_the_spa"
msgstr "à la station thermale"
msgid "brushing_teeth"
msgstr "se brosse les dents"
msgid "getting_a_haircut"
msgstr "se fait couper les cheveux"
msgid "shaving"
msgstr "se rase"
msgid "taking_a_bath"
msgstr "prend un bain"
msgid "taking_a_shower"
msgstr "prend une douche"
msgid "having_appointment"
msgstr "à un rendez-vous"
msgid "inactive"
msgstr "inactif"
msgid "day_off"
msgstr "en congé"
msgid "hanging_out"
msgstr "traîne"
msgid "on_vacation"
msgstr "en vacances"
msgid "scheduled_holiday"
msgstr "en vacances organisées"
msgid "sleeping"
msgstr "dort"
msgid "relaxing"
msgstr "se relaxe"
msgid "gaming"
msgstr "joue"
msgid "going_out"
msgstr "sort"
msgid "partying"
msgstr "fait la fête"
msgid "reading"
msgstr "lit"
msgid "rehearsing"
msgstr "se prépare"
msgid "shopping"
msgstr "fait les magasins"
msgid "socializing"
msgstr "se socialise"
msgid "sunbathing"
msgstr "prend un bain de soleil"
msgid "watching_tv"
msgstr "regarde la TV"
msgid "watching_a_movie"
msgstr "regarde un film"
msgid "talking"
msgstr "discute"
msgid "in_real_life"
msgstr "dans la vraie vie"
msgid "on_the_phone"
msgstr "au téléphone"
msgid "traveling"
msgstr "voyage"
msgid "commuting"
msgstr "permute"
msgid "driving"
msgstr "conduit"
msgid "in_a_car"
msgstr "en voiture"
msgid "on_a_bus"
msgstr "en bus"
msgid "on_a_plane"
msgstr "en avion"
msgid "on_a_train"
msgstr "en train"
msgid "on_a_trip"
msgstr "en séjour"
msgid "walking"
msgstr "marche"
msgid "working"
msgstr "travaille"
msgid "coding"
msgstr "programme"
msgid "in_a_meeting"
msgstr "en réunion"
msgid "studying"
msgstr "étudie"
msgid "writing"
msgstr "écrit"
#~ msgid "2003-12-13T18:30:02Z"
#~ msgstr "2003-12-13T18:30:02Z"
#~ msgid "<small>Romeo and Juliet</small>"

3036
po/ru.po

File diff suppressed because it is too large Load diff

7123
po/sk.po

File diff suppressed because it is too large Load diff

View file

@ -44,26 +44,26 @@ trayicon.c:
$(srcdir)/trayicon.defs > $@
endif
gajimsrcdir = $(pkgdatadir)/src
gajimsrc_DATA = $(srcdir)/*.py
gajimsrc_PYTHON = $(srcdir)/*.py
gajimsrc1dir = $(pkgdatadir)/src/common
gajimsrc1_DATA = \
gajimsrc1_PYTHON = \
$(srcdir)/common/*.py
gajimsrc2dir = $(pkgdatadir)/src/common/xmpp
gajimsrc2_DATA = \
gajimsrc2_PYTHON = \
$(srcdir)/common/xmpp/*.py
gajimsrc3dir = $(pkgdatadir)/src/common/zeroconf
gajimsrc3_DATA = \
gajimsrc3_PYTHON = \
$(srcdir)/common/zeroconf/*.py
DISTCLEANFILES =
EXTRA_DIST = $(gajimsrc_DATA) \
$(gajimsrc1_DATA) \
$(gajimsrc2_DATA) \
$(gajimsrc3_DATA) \
EXTRA_DIST = $(gajimsrc_PYTHON) \
$(gajimsrc1_PYTHON) \
$(gajimsrc2_PYTHON) \
$(gajimsrc3_PYTHON) \
gtkspellmodule.c \
eggtrayicon.c \
trayiconmodule.c \

View file

@ -151,7 +151,7 @@ class ChatControlBase(MessageControl):
self._on_banner_eventbox_button_press_event)
self.handlers[id] = widget
self.urlfinder = re.compile("(https?://|www|ftp)[^ ]+")
self.urlfinder = re.compile(r"(www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'\"]+[^!,\.\s<>\)'\"\]]")
if gajim.HAVE_PYSEXY:
import sexy
@ -643,7 +643,7 @@ class ChatControlBase(MessageControl):
self.orig_msg = None
def print_conversation_line(self, text, kind, name, tim,
other_tags_for_name = [], other_tags_for_time = [],
other_tags_for_name = [], other_tags_for_time = [],
other_tags_for_text = [], count_as_new = True,
subject = None, old_kind = None, xhtml = None):
'''prints 'chat' type messages'''
@ -1017,7 +1017,11 @@ class ChatControl(ChatControlBase):
self.chat_buttons_set_visible(compact_view)
self.widget_set_visible(self.xml.get_widget('banner_eventbox'),
gajim.config.get('hide_chat_banner'))
# Add lock image to show chat encryption
self.lock_image = self.xml.get_widget('lock_image')
self.lock_tooltip = gtk.Tooltips()
# keep timeout id and window obj for possible big avatar
# it is on enter-notify and leave-notify so no need to be per jid
self.show_bigger_avatar_timeout_id = None
@ -1050,15 +1054,25 @@ class ChatControl(ChatControlBase):
self.on_avatar_eventbox_button_press_event)
self.handlers[id] = widget
widget = self.xml.get_widget('gpg_togglebutton')
id = widget.connect('clicked', self.on_toggle_gpg_togglebutton)
self.handlers[id] = widget
if self.contact.jid in gajim.encrypted_chats[self.account]:
self.xml.get_widget('gpg_togglebutton').set_active(True)
self.set_session(session)
# Enable ecryption if needed
e2e_is_active = hasattr(self, 'session') and self.session and self.session.enable_encryption
self.gpg_is_active = False
gpg_pref = gajim.config.get_per('contacts', contact.jid, 'gpg_enabled')
if not e2e_is_active and gpg_pref and gajim.config.get_per('accounts', self.account, 'keyid') and\
gajim.connections[self.account].USE_GPG:
self.gpg_is_active = True
gajim.encrypted_chats[self.account].append(contact.jid)
msg = _('GPG encryption enabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if self.session:
self.session.loggable = gajim.config.get('log_encrypted_sessions')
self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \
self.session.is_loggable())
self.status_tooltip = gtk.Tooltips()
self.update_ui()
# restore previous conversation
@ -1164,8 +1178,6 @@ class ChatControl(ChatControlBase):
gtk.gdk.INTERP_BILINEAR)
banner_status_img.set_from_pixbuf(scaled_pix)
self._update_gpg()
def draw_banner_text(self):
'''Draw the text in the fat line at the top of the window that
houses the name, jid.
@ -1254,34 +1266,52 @@ class ChatControl(ChatControlBase):
# 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.jid, 'gpg_enabled',
widget.get_active())
def _update_gpg(self):
tb = self.xml.get_widget('gpg_togglebutton')
# we can do gpg
# if self.contact is our own contact info (transports),
# don't enable pgp
if self.contact.keyID and not gajim.jid_is_transport(self.contact.jid):
tb.set_sensitive(True)
tt = _('OpenPGP Encryption')
# restore gpg pref
gpg_pref = gajim.config.get_per('contacts', self.contact.jid,
'gpg_enabled')
if gpg_pref == None:
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)
def _toggle_gpg(self):
ec = gajim.encrypted_chats[self.account]
if self.gpg_is_active:
# Disable encryption
ec.remove(self.contact.jid)
self.gpg_is_active = False
msg = _('GPG encryption disabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if self.session:
self.session.loggable = True
else:
tb.set_sensitive(False)
#we talk about a contact here
tt = _('%s has not broadcast an OpenPGP key, nor has one been assigned') %\
self.contact.get_shown_name()
gtk.Tooltips().set_tip(self.xml.get_widget('gpg_eventbox'), tt)
# Enable encryption
ec.append(self.contact.jid)
self.gpg_is_active = True
msg = _('GPG encryption enabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if self.session:
self.session.loggable = gajim.config.get('log_encrypted_sessions');
if self.session and not self.session.is_loggable():
msg = _('Session WILL NOT be logged')
else:
msg = _('Session WILL be logged')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
gpg_pref = gajim.config.get_per('contacts', self.contact.jid,
'gpg_enabled')
if gpg_pref is None:
gajim.config.add_per('contacts', self.contact.jid)
gajim.config.set_per('contacts', self.contact.jid, 'gpg_enabled',
self.gpg_is_active)
self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \
self.session.is_loggable())
def _show_lock_image(self, visible, enc_type = '', enc_enabled = False, chat_logged = False):
'''Set lock icon visibiity and create tooltip'''
status_string = enc_enabled and 'is' or 'is NOT'
logged_string = chat_logged and 'will' or 'will NOT'
tooltip = '%s Encryption %s active. \nYour chat session %s be logged.' %\
(enc_type, status_string, logged_string)
self.lock_tooltip.set_tip(self.lock_image, tooltip)
self.widget_set_visible(self.lock_image, not visible)
self.lock_image.set_sensitive(enc_enabled)
def _process_command(self, message):
if message[0] != '/':
@ -1369,9 +1399,11 @@ class ChatControl(ChatControlBase):
encrypted = bool(self.session) and self.session.enable_encryption
keyID = ''
if self.xml.get_widget('gpg_togglebutton').get_active():
if self.gpg_is_active:
keyID = contact.keyID
encrypted = True
if not keyID:
keyID = 'UNKNOWN'
chatstates_on = gajim.config.get('outgoing_chat_state_notifications') != \
'disabled'
@ -1401,7 +1433,7 @@ class ChatControl(ChatControlBase):
gobject.source_remove(self.possible_paused_timeout_id)
gobject.source_remove(self.possible_inactive_timeout_id)
self._schedule_activity_timers()
if not ChatControlBase.send_message(self, message, keyID, type = 'chat',
chatstate = chatstate_to_send, composing_xep = composing_xep,
process_command = process_command):
@ -1468,13 +1500,14 @@ class ChatControl(ChatControlBase):
msg = _('Session negotiation cancelled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
# print esession settings to textview
def print_esession_details(self):
if self.session and self.session.enable_encryption:
'''print esession settings to textview'''
e2e_is_active = self.session and self.session.enable_encryption
if e2e_is_active:
msg = _('E2E encryption enabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if self.session.loggable:
if self.session.is_loggable():
msg = _('Session WILL be logged')
else:
msg = _('Session WILL NOT be logged')
@ -1483,6 +1516,8 @@ class ChatControl(ChatControlBase):
else:
msg = _('E2E encryption disabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
self._show_lock_image(e2e_is_active, 'E2E', e2e_is_active, self.session and \
self.session.is_loggable())
def print_conversation(self, text, frm = '', tim = None,
encrypted = False, subject = None, xhtml = None):
@ -1510,19 +1545,15 @@ class ChatControl(ChatControlBase):
'status', '', tim)
else:
# GPG encryption
ec = gajim.encrypted_chats[self.account]
if encrypted and jid not in ec:
msg = _('OpenPGP Encryption enabled')
if encrypted and not self.gpg_is_active:
msg = _('The following message was encrypted')
ChatControlBase.print_conversation_line(self, msg,
'status', '', tim)
ec.append(jid)
elif not encrypted and jid in ec:
msg = _('OpenPGP Encryption disabled')
self._toggle_gpg()
elif not encrypted and self.gpg_is_active:
msg = _('The following message was NOT encrypted')
ChatControlBase.print_conversation_line(self, msg,
'status', '', tim)
ec.remove(jid)
self.xml.get_widget('gpg_togglebutton').set_active(encrypted)
if not frm:
kind = 'incoming'
name = contact.get_shown_name()
@ -1649,13 +1680,16 @@ class ChatControl(ChatControlBase):
contact = self.parent_win.get_active_contact()
jid = contact.jid
# check if gpg capabitlies or else make gpg toggle insensitive
gpg_btn = self.xml.get_widget('gpg_togglebutton')
isactive = gpg_btn.get_active()
is_sensitive = gpg_btn.get_property('sensitive')
toggle_gpg_menuitem.set_active(isactive)
toggle_gpg_menuitem.set_property('sensitive', is_sensitive)
# check if we support and use gpg
if not gajim.config.get_per('accounts', self.account, 'keyid') or\
not gajim.connections[self.account].USE_GPG or\
gajim.jid_is_transport(jid):
toggle_gpg_menuitem.set_sensitive(False)
else:
e2e_is_active = int(self.session != None and self.session.enable_encryption)
toggle_gpg_menuitem.set_sensitive(not e2e_is_active)
toggle_gpg_menuitem.set_active(self.gpg_is_active)
# TODO: check that the remote client supports e2e
if not gajim.HAVE_PYCRYPTO:
@ -1663,6 +1697,7 @@ class ChatControl(ChatControlBase):
else:
isactive = int(self.session != None and self.session.enable_encryption)
toggle_e2e_menuitem.set_active(isactive)
toggle_e2e_menuitem.set_sensitive(not self.gpg_is_active)
# If we don't have resource, we can't do file transfer
# in transports, contact holds our info we need to disable it too
@ -1694,14 +1729,15 @@ class ChatControl(ChatControlBase):
id = send_file_menuitem.connect('activate',
self._on_send_file_menuitem_activate)
self.handlers[id] = send_file_menuitem
id = add_to_roster_menuitem.connect('activate',
id = add_to_roster_menuitem.connect('activate',
self._on_add_to_roster_menuitem_activate)
self.handlers[id] = add_to_roster_menuitem
id = toggle_gpg_menuitem.connect('activate',
self.handlers[id] = add_to_roster_menuitem
id = toggle_gpg_menuitem.connect('activate',
self._on_toggle_gpg_menuitem_activate)
self.handlers[id] = toggle_gpg_menuitem
id = toggle_e2e_menuitem.connect('activate',
self._on_toggle_e2e_menuitem_activate)
self.handlers[id] = toggle_gpg_menuitem
self.handlers[id] = toggle_e2e_menuitem
id = information_menuitem.connect('activate',
self._on_contact_information_menuitem_activate)
self.handlers[id] = information_menuitem
@ -2154,10 +2190,7 @@ class ChatControl(ChatControlBase):
gajim.interface.roster.on_info(widget, self.contact, self.account)
def _on_toggle_gpg_menuitem_activate(self, widget):
# update the button
# this is reverse logic, as we are on 'activate' (before change happens)
tb = self.xml.get_widget('gpg_togglebutton')
tb.set_active(not tb.get_active())
self._toggle_gpg()
def _on_convert_to_gc_menuitem_activate(self, widget):
'''user want to invite some friends to chat'''

View file

@ -28,24 +28,12 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import os
import gajim
from os import tmpfile
from common import helpers
USE_GPG = True
try:
import GnuPGInterface # Debian package doesn't distribute 'our' file
except ImportError:
try:
from common import GnuPGInterface # use 'our' file
except ImportError:
USE_GPG = False # user can't do OpenGPG only if he or she removed the file!
else:
status = os.system('gpg -h >/dev/null 2>&1')
if status != 0:
USE_GPG = False
if gajim.HAVE_GPG:
import GnuPGInterface
class GnuPG(GnuPGInterface.GnuPG):
def __init__(self, use_agent = False):
@ -57,8 +45,6 @@ else:
self.options.armor = 1
self.options.meta_interactive = 0
self.options.extra_args.append('--no-secmem-warning')
# Nolith's patch - prevent crashs on non fully-trusted keys
self.options.extra_args.append('--always-trust')
if self.use_agent:
self.options.extra_args.append('--use-agent')
@ -88,8 +74,6 @@ else:
return resp
def encrypt(self, str, recipients):
if not USE_GPG:
return str, 'GnuPG not usable'
self.options.recipients = recipients # a list!
proc = self.run(['--encrypt'], create_fhs=['stdin', 'stdout', 'status',
@ -125,8 +109,6 @@ else:
return self._stripHeaderFooter(output), error
def decrypt(self, str, keyID):
if not USE_GPG:
return str
proc = self.run(['--decrypt', '-q', '-u %s'%keyID], create_fhs=['stdin', 'stdout'])
enc = self._addHeaderFooter(str, 'MESSAGE')
proc.handles['stdin'].write(enc)
@ -140,8 +122,6 @@ else:
return output
def sign(self, str, keyID):
if not USE_GPG:
return str
proc = self.run(['-b', '-u %s'%keyID], create_fhs=['stdin', 'stdout', 'status', 'stderr'])
proc.handles['stdin'].write(str)
try:
@ -170,8 +150,6 @@ else:
return 'BAD_PASSPHRASE'
def verify(self, str, sign):
if not USE_GPG:
return str
if str == None:
return ''
f = tmpfile()
@ -193,15 +171,13 @@ else:
try: proc.wait()
except IOError: pass
keyid = ''
if resp.has_key('GOODSIG'):
keyid = resp['GOODSIG'].split()[0]
return keyid
def get_keys(self, secret = False):
if not USE_GPG:
return {}
if secret:
opt = '--list-secret-keys'
else:
@ -217,8 +193,10 @@ else:
sline = line.split(':')
if (sline[0] == 'sec' and secret) or \
(sline[0] == 'pub' and not secret):
# decode escaped chars
name = eval('"' + sline[9].replace('"', '\\"') + '"')
# make it unicode instance
keys[sline[4][8:]] = helpers.decode_string(sline[9])
keys[sline[4][8:]] = helpers.decode_string(name)
return keys
try: proc.wait()
except IOError: pass

View file

@ -45,7 +45,7 @@ opt_int = [ 'integer', 0 ]
opt_str = [ 'string', 0 ]
opt_bool = [ 'boolean', 0 ]
opt_color = [ 'color', '^(#[0-9a-fA-F]{6})|()$' ]
opt_one_window_types = ['never', 'always', 'peracct', 'pertype']
opt_one_window_types = ['never', 'always', 'always_with_roster', 'peracct', 'pertype']
opt_treat_incoming_messages = ['', 'chat', 'normal']
class Config:
@ -100,7 +100,6 @@ class Config:
'urlmsgcolor': [ opt_color, '#0000ff', '', True ],
'collapsed_rows': [ opt_str, '', _('List (space separated) of rows (accounts and groups) that are collapsed.'), True ],
'roster_theme': [ opt_str, _('default'), '', True ],
'saveposition': [ opt_bool, True ],
'mergeaccounts': [ opt_bool, False, '', True ],
'sort_by_show': [ opt_bool, True, '', True ],
'enable_zeroconf': [opt_bool, False, _('Enable link-local/zeroconf messaging')],
@ -214,6 +213,7 @@ class Config:
'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_avatars_in_roster': [opt_bool, True, '', True],
'avatar_position_in_roster': [opt_str, 'right', _('Define the position of the avatar in roster. Can be left or right'), 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 group chat.')],
@ -229,7 +229,7 @@ class Config:
'send_sha_in_gc_presence': [opt_bool, True, _('Jabberd1.4 does not like sha info when one join a password protected group chat. 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.')],
_('Controls the window where new messages are placed.\n\'always\' - All messages are sent to a single window.\n\'always_with_roster\' - Like \'always\' but the messages are in a single window along with the roster.\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.')],
'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.')],
'compact_view': [opt_bool, False, _('Hides the buttons in chat windows.')],
@ -250,7 +250,14 @@ class Config:
'use_latex': [opt_bool, False, _('If True, Gajim will convert string between $$ and $$ to an image using dvips and convert before insterting it in chat window.')],
'change_status_window_timeout': [opt_int, 15, _('Time of inactivity needed before the change status window closes down.')],
'max_conversation_lines': [opt_int, 500, _('Maximum number of lines that are printed in conversations. Oldest lines are cleared.')],
'publish_mood': [opt_bool, False],
'publish_activity': [opt_bool, False],
'publish_tune': [opt_bool, False],
'subscribe_mood': [opt_bool, True],
'subscribe_activity': [opt_bool, True],
'subscribe_tune': [opt_bool, True],
'attach_notifications_to_systray': [opt_bool, False, _('If True, notification windows from notification-daemon will be attached to systray icon.')],
'use_pep': [opt_bool, False, 'temporary variable to enable pep support'],
}
__options_per_key = {
@ -275,7 +282,8 @@ class Config:
'keyid': [ opt_str, '', '', True ],
'gpg_sign_presence': [ opt_bool, True, _('If disabled, don\'t sign presences with GPG key, even if GPG is configured.') ],
'keyname': [ opt_str, '', '', True ],
'usessl': [ opt_bool, False, '', True ],
'connection_types': [ opt_str, 'tls ssl plain', _('Ordered list (space separated) of connection type to try. Can contain tls, ssl or plain')],
'warn_when_insecure_connection': [ opt_bool, True, _('Show a warning dialog before sending password on an insecure connection.') ],
'ssl_fingerprint_sha1': [ opt_str, '', '', True ],
'use_srv': [ opt_bool, True, '', True ],
'use_custom_host': [ opt_bool, False, '', True ],
@ -306,6 +314,7 @@ class Config:
'zeroconf_last_name': [ opt_str, '', '', True ],
'zeroconf_jabber_id': [ opt_str, '', '', True ],
'zeroconf_email': [ opt_str, '', '', True ],
'use_env_http_proxy' : [opt_bool, False],
}, {}),
'statusmsg': ({
'message': [ opt_str, '' ],
@ -354,7 +363,7 @@ class Config:
'state_muc_directed_msg_color': [ opt_color, 'red2' ],
}, {}),
'contacts': ({
'gpg_enabled': [ opt_bool, True, _('Is OpenPGP enabled for this contact?')],
'gpg_enabled': [ opt_bool, False, _('Is OpenPGP enabled for this contact?')],
'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')],
}, {}),
'rooms': ({
@ -400,7 +409,8 @@ class Config:
soundevents_default = {
'first_message_received': [ True, '../data/sounds/message1.wav' ],
'next_message_received': [ True, '../data/sounds/message2.wav' ],
'next_message_received_focused': [ True, '../data/sounds/message2.wav' ],
'next_message_received_unfocused': [ True, '../data/sounds/message2.wav' ],
'contact_connected': [ True, '../data/sounds/connected.wav' ],
'contact_disconnected': [ True, '../data/sounds/disconnected.wav' ],
'message_sent': [ True, '../data/sounds/sent.wav' ],

View file

@ -48,7 +48,6 @@ from common import passwords
from common import exceptions
from connection_handlers import *
USE_GPG = GnuPG.USE_GPG
from common.rst_xhtml_generator import create_xhtml
@ -56,8 +55,6 @@ from string import Template
import logging
log = logging.getLogger('gajim.c.connection')
import gtkgui_helpers
ssl_error = {
2: _("Unable to get issuer certificate"),
3: _("Unable to get certificate CRL"),
@ -75,9 +72,9 @@ ssl_error = {
15: _("Format error in CRL's lastUpdate field"),
16: _("Format error in CRL's nextUpdate field"),
17: _("Out of memory"),
18: _("Self signed certificate in certificate chain"),
19: _("Unable to get local issuer certificate"),
20: _("Unable to verify the first certificate"),
18: _("Self signed certificate"),
19: _("Self signed certificate in certificate chain"),
20: _("Unable to get local issuer certificate"),
21: _("Unable to verify the first certificate"),
22: _("Certificate chain too long"),
23: _("Certificate revoked"),
@ -107,7 +104,9 @@ class Connection(ConnectionHandlers):
self.last_connection = None # last ClientCommon instance
self.is_zeroconf = False
self.gpg = None
if USE_GPG:
self.USE_GPG = False
if gajim.HAVE_GPG:
self.USE_GPG = True
self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
self.status = ''
self.priority = gajim.get_priority(name, 'offline')
@ -142,6 +141,9 @@ class Connection(ConnectionHandlers):
self.blocked_contacts = []
self.blocked_groups = []
self.pep_supported = False
self.mood = {}
self.tune = {}
self.activity = {}
# Do we continue connection when we get roster (send presence,get vcard..)
self.continue_connect_info = None
# To know the groupchat jid associated with a sranza ID. Useful to
@ -178,7 +180,7 @@ class Connection(ConnectionHandlers):
self.dispatch('STATUS', 'connecting')
self.retrycount += 1
self.on_connect_auth = self._init_roster
self.connect_and_init(self.old_show, self.status, self.gpg != None)
self.connect_and_init(self.old_show, self.status, self.USE_GPG)
else:
# reconnect succeeded
self.time_to_reconnect = None
@ -186,6 +188,8 @@ class Connection(ConnectionHandlers):
# We are doing disconnect at so many places, better use one function in all
def disconnect(self, on_purpose=False):
#FIXME: set the Tune to None before disconnection per account
#gajim.interface.roster._music_track_changed(None, None)
self.on_purpose = on_purpose
self.connected = 0
self.time_to_reconnect = None
@ -265,7 +269,8 @@ class Connection(ConnectionHandlers):
if not common.xmpp.isResultNode(result):
self.dispatch('ACC_NOT_OK', (result.getError()))
return
if USE_GPG:
if gajim.HAVE_GPG:
self.USE_GPG = True
self.gpg = GnuPG.GnuPG(gajim.config.get(
'use_gpg_agent'))
self.dispatch('ACC_OK', (self.new_account_info))
@ -279,7 +284,7 @@ class Connection(ConnectionHandlers):
# typed, so send them
if is_form:
#TODO: Check if form has changed
iq = Iq('set', NS_REGISTER, to=self._hostname)
iq = common.xmpp.Iq('set', common.xmpp.NS_REGISTER, to=self._hostname)
iq.setTag('query').addChild(node=self.new_account_form)
self.connection.SendAndCallForResponse(iq,
_on_register_result)
@ -313,7 +318,7 @@ class Connection(ConnectionHandlers):
ssl_fingerprint = \
self.connection.Connection.ssl_fingerprint_sha1
self.dispatch('NEW_ACC_CONNECTED', (conf, is_form, ssl_msg,
ssl_cert, ssl_fingerprint))
errnum, ssl_cert, ssl_fingerprint))
self.connection.UnregisterDisconnectHandler(
self._on_new_account)
self.disconnect(on_purpose=True)
@ -423,27 +428,56 @@ class Connection(ConnectionHandlers):
proxy['user'] = gajim.config.get_per('proxies', p, 'user')
proxy['password'] = gajim.config.get_per('proxies', p, 'pass')
proxy['type'] = gajim.config.get_per('proxies', p, 'type')
elif gajim.config.get_per('accounts', self.name, 'use_env_http_proxy'):
try:
try:
env_http_proxy = os.environ['HTTP_PROXY']
except:
env_http_proxy = os.environ['http_proxy']
env_http_proxy = env_http_proxy.strip('"')
# Dispose of the http:// prefix
env_http_proxy = env_http_proxy.split('://')
env_http_proxy = env_http_proxy[len(env_http_proxy)-1]
env_http_proxy = env_http_proxy.split('@')
if len(env_http_proxy) == 2:
login = env_http_proxy[0].split(':')
addr = env_http_proxy[1].split(':')
else:
login = ['', '']
addr = env_http_proxy[0].split(':')
proxy = {'host': addr[0], 'type' : u'http', 'user':login[0]}
if len(addr) == 2:
proxy['port'] = addr[1]
else:
proxy['port'] = 3128
if len(login) == 2:
proxy['password'] = login[1]
else:
proxy['password'] = u''
except:
proxy = None
else:
proxy = None
h = hostname
p = 5222
# autodetect [for SSL in 5223/443 and for TLS if broadcasted]
secur = None
if usessl:
p = 5223
secur = 1 # 1 means force SSL no matter what the port will be
use_srv = False # wants ssl? disable srv lookup
ssl_p = 5223
# use_srv = False # wants ssl? disable srv lookup
if use_custom:
h = custom_h
p = custom_p
ssl_p = custom_p
use_srv = False
hosts = []
# SRV resolver
self._proxy = proxy
self._secure = secur
self._hosts = [ {'host': h, 'port': p, 'prio': 10, 'weight': 10} ]
self._hosts = [ {'host': h, 'port': p, 'ssl_port': ssl_p, 'prio': 10,
'weight': 10} ]
self._hostname = hostname
if use_srv:
# add request for srv query to the resolve, on result '_on_resolve'
@ -457,6 +491,12 @@ class Connection(ConnectionHandlers):
# SRV query returned at least one valid result, we put it in hosts dict
if len(result_array) != 0:
self._hosts = [i for i in result_array]
# Add ssl port
ssl_p = 5223
if gajim.config.get_per('accounts', self.name, 'use_custom_host'):
ssl_p = gajim.config.get_per('accounts', self.name, 'custom_port')
for i in self._hosts:
i['ssl_port'] = ssl_p
self.connect_to_next_host()
def on_proxy_failure(self, reason):
@ -468,8 +508,9 @@ class Connection(ConnectionHandlers):
self.dispatch('CONNECTION_LOST',
(_('Connection to proxy failed'), reason))
def connect_to_next_host(self, retry = False):
if len(self._hosts):
def connect_to_next_type(self, retry=False):
if len(self._connection_types):
self._current_type = self._connection_types.pop(0)
if self.last_connection:
self.last_connection.socket.disconnect()
self.last_connection = None
@ -478,27 +519,49 @@ class Connection(ConnectionHandlers):
con = common.xmpp.NonBlockingClient(self._hostname, caller = self,
on_connect = self.on_connect_success,
on_proxy_failure = self.on_proxy_failure,
on_connect_failure = self.connect_to_next_host)
on_connect_failure = self.connect_to_next_type)
else:
con = common.xmpp.NonBlockingClient(self._hostname, debug = [], caller = self,
on_connect = self.on_connect_success,
con = common.xmpp.NonBlockingClient(self._hostname, debug = [],
caller = self, on_connect = self.on_connect_success,
on_proxy_failure = self.on_proxy_failure,
on_connect_failure = self.connect_to_next_host)
on_connect_failure = self.connect_to_next_type)
self.last_connection = con
# increase default timeout for server responses
common.xmpp.dispatcher_nb.DEFAULT_TIMEOUT_SECONDS = self.try_connecting_for_foo_secs
con.set_idlequeue(gajim.idlequeue)
host = self.select_next_host(self._hosts)
self._current_host = host
self._hosts.remove(host)
# FIXME: this is a hack; need a better way
if self.on_connect_success == self._on_new_account:
con.RegisterDisconnectHandler(self._on_new_account)
log.info("Connecting to %s: [%s:%d]", self.name, host['host'], host['port'])
con.connect((host['host'], host['port']), proxy = self._proxy,
secure = self._secure)
if self._current_type == 'ssl':
port = self._current_host['ssl_port']
secur = 1
else:
port = self._current_host['port']
if self._current_type == 'plain':
secur = 0
else:
secur = None
log.info('Connecting to %s: [%s:%d]', self.name,
self._current_host['host'], port)
con.connect((self._current_host['host'], port), proxy=self._proxy,
secure = secur)
else:
self.connect_to_next_host(retry)
def connect_to_next_host(self, retry = False):
if len(self._hosts):
# No config option exist when creating a new account
if self.name in gajim.config.get_per('accounts'):
self._connection_types = gajim.config.get_per('accounts', self.name,
'connection_types').split()
else:
self._connection_types = ['tls', 'ssl', 'plain']
host = self.select_next_host(self._hosts)
self._current_host = host
self._hosts.remove(host)
self.connect_to_next_type()
else:
if not retry and self.retrycount == 0:
log.debug("Out of hosts, giving up connecting to %s", self.name)
@ -527,15 +590,26 @@ class Connection(ConnectionHandlers):
if not self.connected: # We went offline during connecting process
# FIXME - not possible, maybe it was when we used threads
return
_con_type = con_type
# xmpp returns 'tcp', but we set 'plain' in connection_types in config
if _con_type == 'tcp':
_con_type = 'plain'
if _con_type != self._current_type:
self.connect_to_next_type()
return
if _con_type == 'plain' and gajim.config.get_per('accounts', self.name,
'warn_when_insecure_connection'):
self.dispatch('PLAIN_CONNECTION', (con,))
return True
return self.connection_accepted(con, con_type)
def connection_accepted(self, con, con_type):
self.hosts = []
if not con_type:
log.debug('Could not connect to %s:%s' % (self._current_host['host'],
self._current_host['port']))
self.connected_hostname = self._current_host['host']
self.on_connect_failure = None
con.RegisterDisconnectHandler(self._disconnectedReconnCB)
log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'],
self._current_host['port'], con_type))
log.debug('Connected to server %s:%s with %s' % (
self._current_host['host'], self._current_host['port'], con_type))
name = gajim.config.get_per('accounts', self.name, 'name')
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
@ -548,10 +622,10 @@ class Connection(ConnectionHandlers):
text = _('The authenticity of the %s certificate could be invalid.') %\
hostname
if errnum in ssl_error:
text += _('\nSSL Error: %s') % ssl_error[errnum]
text += _('\nSSL Error: <b>%s</b>') % ssl_error[errnum]
else:
text += _('\nUnknown SSL error: %d') % errnum
self.dispatch('SSL_ERROR', (text, con.Connection.ssl_cert_pem,
self.dispatch('SSL_ERROR', (text, errnum, con.Connection.ssl_cert_pem,
con.Connection.ssl_fingerprint_sha1))
return True
if hasattr(con.Connection, 'ssl_fingerprint_sha1'):
@ -565,11 +639,17 @@ class Connection(ConnectionHandlers):
self._register_handlers(con, con_type)
con.auth(name, self.password, self.server_resource, 1, self.__on_auth)
def ssl_certificate_accepted(self):
name = gajim.config.get_per('accounts', self.name, 'name')
self._register_handlers(self.connection, 'ssl')
self.connection.auth(name, self.password, self.server_resource, 1, self.__on_auth)
self.connection.auth(name, self.password, self.server_resource, 1,
self.__on_auth)
def plain_connection_accepted(self):
name = gajim.config.get_per('accounts', self.name, 'name')
self._register_handlers(self.connection, 'tcp')
self.connection.auth(name, self.password, self.server_resource, 1,
self.__on_auth)
def _register_handlers(self, con, con_type):
self.peerhost = con.get_peerhost()
@ -699,6 +779,17 @@ class Connection(ConnectionHandlers):
def send_invisible_presence(self, msg, signed, initial = False):
if not self.connection:
return
# If we are already connected, and privacy rules are supported, send
# offline presence first as it's required by XEP-0126
if self.connected > 1 and self.privacy_rules_supported:
self.on_purpose = True
p = common.xmpp.Presence(typ = 'unavailable')
p = self.add_sha(p, False)
if msg:
p.setStatus(msg)
self.remove_all_transfers()
self.connection.send(p)
# try to set the privacy rule
iq = self.build_privacy_rule('invisible', 'deny')
self.connection.SendAndCallForResponse(iq, self._continue_invisible,
@ -707,8 +798,7 @@ class Connection(ConnectionHandlers):
def _continue_invisible(self, con, iq_obj, msg, signed, initial):
ptype = ''
show = ''
# FIXME: JEP 126 need some modifications (see http://lists.jabber.ru/pipermail/ejabberd/2005-July/001252.html). So I disable it for the moment
if 1 or iq_obj.getType() == 'error': #server doesn't support privacy lists
if iq_obj.getType() == 'error': # server doesn't support privacy lists
# We use the old way which is not xmpp complient
ptype = 'invisible'
show = 'invisible'
@ -760,7 +850,7 @@ class Connection(ConnectionHandlers):
callback is the function to call when user give the passphrase'''
signed = ''
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID and self.gpg:
if keyID and self.USE_GPG:
use_gpg_agent = gajim.config.get('use_gpg_agent')
if self.gpg.passphrase is None and not use_gpg_agent:
# We didn't set a passphrase
@ -768,7 +858,7 @@ class Connection(ConnectionHandlers):
if self.gpg.passphrase is not None or use_gpg_agent:
signed = self.gpg.sign(msg, keyID)
if signed == 'BAD_PASSPHRASE':
self.gpg = None
self.USE_GPG = False
signed = ''
self.dispatch('BAD_PASSPHRASE', ())
return signed
@ -845,7 +935,8 @@ class Connection(ConnectionHandlers):
safe_substitute({
'hostname': socket.gethostname()
})
if USE_GPG:
if gajim.HAVE_GPG:
self.USE_GPG = True
self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
self.connect_and_init(show, msg, sign_msg)
@ -922,9 +1013,15 @@ class Connection(ConnectionHandlers):
fjid += '/' + resource
msgtxt = msg
msgenc = ''
if keyID and self.gpg:
#encrypt
msgenc, error = self.gpg.encrypt(msg, [keyID])
if keyID and self.USE_GPG:
if keyID == 'UNKNOWN':
error = _('Neither the remote presence is signed, nor a key was assigned.')
elif keyID[8:] == 'MISMATCH':
error = _('The contact\'s key (%s) does not match the key assigned in Gajim.' % keyID[:8])
else:
#encrypt
msgenc, error = self.gpg.encrypt(msg, [keyID])
if msgenc and not error:
msgtxt = '[This message is encrypted]'
lang = os.getenv('LANG')
@ -1109,7 +1206,7 @@ class Connection(ConnectionHandlers):
def send_new_account_infos(self, form, is_form):
if is_form:
# Get username and password and put them in new_account_info
for field in self._data_form.iter_fields():
for field in form.iter_fields():
if field.var == 'username':
self.new_account_info['name'] = field.value
if field.var == 'password':

View file

@ -35,15 +35,20 @@ from calendar import timegm
import socks5
import common.xmpp
from common import GnuPG
from common import helpers
from common import gajim
from common import atom
from common import pep
from common import exceptions
from common.commands import ConnectionCommands
from common.pubsub import ConnectionPubSub
from common.caps import ConnectionCaps
from common import dbus_support
if dbus_support.supported:
import dbus
from music_track_listener import MusicTrackListener
from common.stanza_session import EncryptedStanzaSession
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
@ -54,6 +59,7 @@ VCARD_ARRIVED = 'vcard_arrived'
AGENT_REMOVED = 'agent_removed'
METACONTACTS_ARRIVED = 'metacontacts_arrived'
PRIVACY_ARRIVED = 'privacy_arrived'
PEP_ACCESS_MODEL = 'pep_access_model'
HAS_IDLE = True
try:
import idle
@ -182,7 +188,7 @@ class ConnectionBytestream:
ft_add_hosts_to_send = map(lambda e:e.strip(),
ft_add_hosts_to_send.split(','))
for ft_host in ft_add_hosts_to_send:
ft_add_hosts.append(ft_host)
ft_add_hosts.append(ft_host)
listener = gajim.socks5queue.start_listener(port,
sha_str, self._result_socks5_sid, file_props['sid'])
if listener == None:
@ -750,6 +756,13 @@ class ConnectionDisco:
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC})
q.addChild('feature', attrs = {'var': common.xmpp.NS_COMMANDS})
q.addChild('feature', attrs = {'var': common.xmpp.NS_DISCO_INFO})
if gajim.config.get('use_pep'):
q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY})
q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY + '+notify'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE})
q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE + '+notify'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD + '+notify'})
q.addChild('feature', attrs = {'var': common.xmpp.NS_ESESSION_INIT})
if (node is None or extension == 'cstates') and gajim.config.get('outgoing_chat_state_notifactions') != 'disabled':
@ -821,6 +834,12 @@ class ConnectionDisco:
if identity['category'] == 'pubsub' and identity['type'] == \
'pep':
self.pep_supported = True
if dbus_support.supported:
listener = MusicTrackListener.get()
track = listener.get_playing_track()
if gajim.config.get('publish_tune'):
gajim.interface.roster._music_track_changed(listener,
track, self.name)
break
if features.__contains__(common.xmpp.NS_BYTESTREAM):
gajim.proxy65_manager.resolve(jid, self.connection, self.name)
@ -860,7 +879,7 @@ class ConnectionVcard:
ext.append('xhtml')
if gajim.config.get('outgoing_chat_state_notifactions') != 'disabled':
ext.append('cstates')
if len(ext):
c.setAttr('ext', ' '.join(ext))
c.setAttr('ver', gajim.version.split('-', 1)[0])
@ -1099,6 +1118,16 @@ class ConnectionVcard:
self.get_privacy_list('block')
# Ask metacontacts before roster
self.get_metacontacts()
elif self.awaiting_answers[id][0] == PEP_ACCESS_MODEL:
conf = iq_obj.getTag('pubsub').getTag('configure')
node = conf.getAttr('node')
form_tag = conf.getTag('x', namespace=common.xmpp.NS_DATA)
if form_tag:
form = common.dataforms.ExtendForm(node=form_tag)
for field in form.iter_fields():
if field.var == 'pubsub#access_model':
self.dispatch('PEP_ACCESS_MODEL', (node, field.value))
break
del self.awaiting_answers[id]
@ -1251,7 +1280,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
reply.setType('error')
reply.addChild(feature)
reply.addChild(node=xmpp.ErrorNode('service-unavailable', typ='cancel'))
reply.addChild(node=common.xmpp.ErrorNode('service-unavailable', typ='cancel'))
con.send(reply)
@ -1419,7 +1448,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
iq_obj = iq_obj.buildReply('result')
qp = iq_obj.getTag('query')
qp.setTagData('utc', strftime('%Y%m%dT%T', gmtime()))
qp.setTagData('tz', tzname[daylight])
qp.setTagData('tz', helpers.decode_string(tzname[daylight]))
qp.setTagData('display', helpers.decode_string(strftime('%c',
localtime())))
self.connection.send(iq_obj)
@ -1599,7 +1628,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if not user_nick:
user_nick = ''
if encTag and GnuPG.USE_GPG:
if encTag and self.USE_GPG:
#decrypt
encmsg = encTag.getData()
@ -1770,8 +1799,22 @@ returns the session that we last sent a message to.'''
''' Called when we receive <message/> with pubsub event. '''
# TODO: Logging? (actually services where logging would be useful, should
# TODO: allow to access archives remotely...)
jid = helpers.get_full_jid_from_iq(msg)
event = msg.getTag('event')
# XEP-0107: User Mood
items = event.getTag('items', {'node': common.xmpp.NS_MOOD})
if items: pep.user_mood(items, self.name, jid)
# XEP-0118: User Tune
items = event.getTag('items', {'node': common.xmpp.NS_TUNE})
if items: pep.user_tune(items, self.name, jid)
# XEP-0080: User Geolocation
items = event.getTag('items', {'node': common.xmpp.NS_GEOLOC})
if items: pep.user_geoloc(items, self.name, jid)
# XEP-0108: User Activity
items = event.getTag('items', {'node': common.xmpp.NS_ACTIVITY})
if items: pep.user_activity(items, self.name, jid)
items = event.getTag('items')
if items is None: return
@ -1857,7 +1900,7 @@ returns the session that we last sent a message to.'''
except:
prio = 0
keyID = ''
if sigTag and self.gpg:
if sigTag and self.USE_GPG:
# verify
sigmsg = sigTag.getData()
keyID = self.gpg.verify(status, sigmsg)
@ -2109,6 +2152,21 @@ returns the session that we last sent a message to.'''
raw_roster = roster.getRaw()
roster = {}
our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name))
if self.connected > 1 and self.continue_connect_info:
msg = self.continue_connect_info[1]
sign_msg = self.continue_connect_info[2]
signed = ''
send_first_presence = True
if sign_msg:
signed = self.get_signed_presence(msg, self._send_first_presence)
if signed is None:
self.dispatch('GPG_PASSWORD_REQUIRED',
(self._send_first_presence,))
# _send_first_presence will be called when user enter passphrase
send_first_presence = False
if send_first_presence:
self._send_first_presence(signed)
for jid in raw_roster:
try:
j = helpers.parse_jid(jid)
@ -2131,20 +2189,6 @@ returns the session that we last sent a message to.'''
self.dispatch('ROSTER', roster)
# continue connection
if self.connected > 1 and self.continue_connect_info:
msg = self.continue_connect_info[1]
sign_msg = self.continue_connect_info[2]
signed = ''
if sign_msg:
signed = self.get_signed_presence(msg, self._send_first_presence)
if signed is None:
self.dispatch('GPG_PASSWORD_REQUIRED',
(self._send_first_presence,))
# _send_first_presence will be called when user enter passphrase
return
self._send_first_presence(signed)
def _send_first_presence(self, signed = ''):
show = self.continue_connect_info[0]
msg = self.continue_connect_info[1]
@ -2155,6 +2199,7 @@ returns the session that we last sent a message to.'''
self.dispatch('ERROR', (_('OpenPGP passphrase was not given'),
#%s is the account name here
_('You will be connected to %s without OpenPGP.') % self.name))
self.USE_GPG = False
signed = ''
self.connected = STATUS_LIST.index(show)
sshow = helpers.get_xmpp_show(show)

View file

@ -27,7 +27,8 @@ class Contact:
'''Information concerning each contact'''
def __init__(self, jid='', name='', groups=[], show='', status='', sub='',
ask='', resource='', priority=0, keyID='', our_chatstate=None,
chatstate=None, last_status_time=None, msg_id = None, composing_xep = None):
chatstate=None, last_status_time=None, msg_id = None, composing_xep = None,
mood={}, tune={}, activity={}):
self.jid = jid
self.name = name
self.contact_name = '' # nick choosen by contact
@ -63,6 +64,10 @@ class Contact:
self.chatstate = chatstate
self.last_status_time = last_status_time
self.mood = mood.copy()
self.tune = tune.copy()
self.activity = activity.copy()
def get_full_jid(self):
if self.resource:
return self.jid + '/' + self.resource
@ -162,15 +167,16 @@ class Contacts:
def create_contact(self, jid='', name='', groups=[], show='', status='',
sub='', ask='', resource='', priority=0, keyID='', our_chatstate=None,
chatstate=None, last_status_time=None, composing_xep=None):
chatstate=None, last_status_time=None, composing_xep=None,
mood={}, tune={}, activity={}):
return Contact(jid, name, groups, show, status, sub, ask, resource,
priority, keyID, our_chatstate, chatstate, last_status_time,
composing_xep)
None, composing_xep, mood, tune, activity)
def copy_contact(self, contact):
return self.create_contact(jid = contact.jid, name = contact.name,
groups = contact.groups, show = contact.show, status = contact.status,
sub = contact.sub, ask = contact.ask, resource = contact.resource,
groups = contact.groups, show = contact.show, status =
contact.status, sub = contact.sub, ask = contact.ask, resource = contact.resource,
priority = contact.priority, keyID = contact.keyID,
our_chatstate = contact.our_chatstate, chatstate = contact.chatstate,
last_status_time = contact.last_status_time)
@ -469,6 +475,15 @@ class Contacts:
return 1
if show2 > show1:
return -1
server1 = common.gajim.get_server_from_jid(jid1)
server2 = common.gajim.get_server_from_jid(jid2)
myserver1 = common.gajim.config.get_per('accounts', account1, 'hostname')
myserver2 = common.gajim.config.get_per('accounts', account2, 'hostname')
if server1 == myserver1:
if server2 != myserver2:
return 1
elif server2 == myserver2:
return -1
if jid1 > jid2:
return 1
if jid2 > jid1:

View file

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

View file

@ -138,9 +138,6 @@ SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
# zeroconf account name
ZEROCONF_ACC_NAME = 'Local'
priority_dict = {}
for status in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
priority_dict[status] = config.get('autopriority' + status)
HAVE_PYCRYPTO = True
try:
@ -154,6 +151,17 @@ try:
except ImportError:
HAVE_PYSEXY = False
HAVE_GPG = True
try:
import GnuPGInterface
except ImportError:
HAVE_GPG = False
else:
import os
status = os.system('gpg -h >/dev/null 2>&1')
if status != 0:
HAVE_GPG = False
def get_nick_from_jid(jid):
pos = jid.find('@')
return jid[:pos]
@ -260,13 +268,14 @@ def account_is_disconnected(account):
def get_number_of_securely_connected_accounts():
'''returns the number of the accounts that are SSL/TLS connected'''
num_of_secured = 0
for account in connections:
for account in connections.keys():
if account_is_securely_connected(account):
num_of_secured += 1
return num_of_secured
def account_is_securely_connected(account):
if account in con_types and con_types[account] in ('tls', 'ssl'):
if account_is_connected(account) and \
account in con_types and con_types[account] in ('tls', 'ssl'):
return True
else:
return False

View file

@ -429,8 +429,8 @@ def launch_browser_mailer(kind, uri):
command = 'kfmclient exec'
elif gajim.config.get('openwith') == 'exo-open':
command = 'exo-open'
elif ((sys.platform == 'darwin') and
(gajim.config.get('openwith') == 'open')):
elif ((sys.platform == 'darwin') and\
(gajim.config.get('openwith') == 'open')):
command = 'open'
elif gajim.config.get('openwith') == 'custom':
if kind == 'url':
@ -459,8 +459,8 @@ def launch_file_manager(path_to_open):
command = 'kfmclient exec'
elif gajim.config.get('openwith') == 'exo-open':
command = 'exo-open'
elif ((sys.platform == 'darwin') and
(gajim.config.get('openwith') == 'open')):
elif ((sys.platform == 'darwin') and\
(gajim.config.get('openwith') == 'open')):
command = 'open'
elif gajim.config.get('openwith') == 'custom':
command = gajim.config.get('custom_file_manager')
@ -784,7 +784,8 @@ def get_os_info():
'sourcemage') or not\
os.path.basename(path_to_file).startswith('slackware'):
text = distro_name + ' ' + text
elif path_to_file.endswith('aurox-release'):
elif path_to_file.endswith('aurox-release') or \
path_to_file.endswith('arch-release'):
# file doesn't have version
text = distro_name
elif path_to_file.endswith('lfs-release'): # file just has version
@ -792,7 +793,7 @@ def get_os_info():
return text
# our last chance, ask uname and strip it
uname_output = get_output_of_command('uname -a | cut -d" " -f1,3')
uname_output = get_output_of_command('uname -sr')
if uname_output is not None:
return uname_output[0] # only first line
return 'N/A'
@ -1092,3 +1093,38 @@ def get_transport_path(transport):
elif os.path.isdir(os.path.join(gajim.MY_ICONSETS_PATH, 'transports',
transport)):
return os.path.join(gajim.MY_ICONSETS_PATH, 'transports', transport)
# No transport folder found, use default jabber one
return get_iconset_path(gajim.config.get('iconset'))
def prepare_and_validate_gpg_keyID(account, jid, keyID):
'''Returns an eight char long keyID that can be used with for GPG encryption with this contact.
If the given keyID is None, return UNKNOWN; if the key does not match the assigned key
XXXXXXXXMISMATCH is returned. If the key is trusted and not yet assigned, assign it'''
if gajim.connections[account].USE_GPG:
if keyID and len(keyID) == 16:
keyID = keyID[8:]
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if jid in attached_keys and keyID:
attachedkeyID = attached_keys[attached_keys.index(jid) + 1]
if attachedkeyID != keyID:
# Mismatch! Another gpg key was expected
keyID += 'MISMATCH'
elif jid in attached_keys:
# An unsigned presence, just use the assigned key
keyID = attached_keys[attached_keys.index(jid) + 1]
elif keyID:
public_keys = gajim.connections[account].ask_gpg_keys()
# Assign the corresponding key, if we have it in our keyring
if public_keys.has_key(keyID):
for u in gajim.contacts.get_contacts(account, jid):
u.keyID = keyID
keys_str = gajim.config.get_per('accounts', account, 'attached_gpg_keys')
keys_str += jid + ' ' + keyID + ' '
gajim.config.set_per('accounts', account, 'attached_gpg_keys', keys_str)
elif keyID is None:
keyID = 'UNKNOWN'
return keyID

View file

@ -174,8 +174,10 @@ class OptionsParser:
self.update_config_to_01115()
if old < [0, 11, 2, 1] and new >= [0, 11, 2, 1]:
self.update_config_to_01121()
if old < [0, 11, 2, 2] and new >= [0, 11, 2, 2]:
self.update_config_to_01122()
if old < [0, 11, 4, 1] and new >= [0, 11, 4, 1]:
self.update_config_to_01141()
if old < [0, 11, 4, 2] and new >= [0, 11, 4, 2]:
self.update_config_to_01142()
gajim.logger.init_vars()
gajim.config.set('version', new_version)
@ -503,7 +505,7 @@ class OptionsParser:
gajim.config.set('version', '0.11.2.1')
def update_config_to_01122(self):
def update_config_to_01141(self):
back = os.getcwd()
os.chdir(logger.LOG_DB_FOLDER)
con = sqlite.connect(logger.LOG_DB_FILE)
@ -524,5 +526,20 @@ class OptionsParser:
except sqlite.OperationalError, e:
pass
con.close()
gajim.config.set('version', '0.11.2.2')
gajim.config.set('version', '0.11.4.1')
def update_config_to_01142(self):
'''next_message_received sound event is splittedin 2 events'''
gajim.config.add_per('soundevents', 'next_message_received_focused')
gajim.config.add_per('soundevents', 'next_message_received_unfocused')
if gajim.config.get_per('soundevents', 'next_message_received'):
enabled = gajim.config.get_per('soundevents', 'next_message_received',
'enabled')
path = gajim.config.get_per('soundevents', 'next_message_received',
'path')
gajim.config.del_per('soundevents', 'next_message_received')
gajim.config.set_per('soundevents', 'next_message_received_focused',
'enabled', enabled)
gajim.config.set_per('soundevents', 'next_message_received_focused',
'path', path)
gajim.config.set('version', '0.11.1.2')

220
src/common/pep.py Normal file
View file

@ -0,0 +1,220 @@
from common import gajim, xmpp
def user_mood(items, name, jid):
has_child = False
mood = None
text = None
for item in items.getTags('item'):
child = item.getTag('mood')
if child is not None:
has_child = True
for ch in child.getChildren():
if ch.getName() != 'text':
mood = ch.getName()
else:
text = ch.getData()
if jid == gajim.get_jid_from_account(name):
acc = gajim.connections[name]
if has_child:
if acc.mood.has_key('mood'):
del acc.mood['mood']
if acc.mood.has_key('text'):
del acc.mood['text']
if mood != None:
acc.mood['mood'] = mood
if text != None:
acc.mood['text'] = text
(user, resource) = gajim.get_room_and_nick_from_fjid(jid)
contact = gajim.contacts.get_contact(name, user, resource=resource)
if not contact:
return
if has_child:
if contact.mood.has_key('mood'):
del contact.mood['mood']
if contact.mood.has_key('text'):
del contact.mood['text']
if mood != None:
contact.mood['mood'] = mood
if text != None:
contact.mood['text'] = text
def user_tune(items, name, jid):
has_child = False
artist = None
title = None
source = None
track = None
length = None
for item in items.getTags('item'):
child = item.getTag('tune')
if child is not None:
has_child = True
for ch in child.getChildren():
if ch.getName() == 'artist':
artist = ch.getData()
elif ch.getName() == 'title':
title = ch.getData()
elif ch.getName() == 'source':
source = ch.getData()
elif ch.getName() == 'track':
track = ch.getData()
elif ch.getName() == 'length':
length = ch.getData()
if jid == gajim.get_jid_from_account(name):
acc = gajim.connections[name]
if has_child:
if acc.tune.has_key('artist'):
del acc.tune['artist']
if acc.tune.has_key('title'):
del acc.tune['title']
if acc.tune.has_key('source'):
del acc.tune['source']
if acc.tune.has_key('track'):
del acc.tune['track']
if acc.tune.has_key('length'):
del acc.tune['length']
if artist != None:
acc.tune['artist'] = artist
if title != None:
acc.tune['title'] = title
if source != None:
acc.tune['source'] = source
if track != None:
acc.tune['track'] = track
if length != None:
acc.tune['length'] = length
(user, resource) = gajim.get_room_and_nick_from_fjid(jid)
contact = gajim.contacts.get_contact(name, user, resource=resource)
if not contact:
return
if has_child:
if contact.tune.has_key('artist'):
del contact.tune['artist']
if contact.tune.has_key('title'):
del contact.tune['title']
if contact.tune.has_key('source'):
del contact.tune['source']
if contact.tune.has_key('track'):
del contact.tune['track']
if contact.tune.has_key('length'):
del contact.tune['length']
if artist != None:
contact.tune['artist'] = artist
if title != None:
contact.tune['title'] = title
if source != None:
contact.tune['source'] = source
if track != None:
contact.tune['track'] = track
if length != None:
contact.tune['length'] = length
def user_geoloc(items, name, jid):
pass
def user_activity(items, name, jid):
has_child = False
activity = None
subactivity = None
text = None
for item in items.getTags('item'):
child = item.getTag('activity')
if child is not None:
has_child = True
for ch in child.getChildren():
if ch.getName() != 'text':
activity = ch.getName()
for chi in ch.getChildren():
subactivity = chi.getName()
else:
text = ch.getData()
if jid == gajim.get_jid_from_account(name):
acc = gajim.connections[name]
if has_child:
if acc.activity.has_key('activity'):
del acc.activity['activity']
if acc.activity.has_key('subactivity'):
del acc.activity['subactivity']
if acc.activity.has_key('text'):
del acc.activity['text']
if activity != None:
acc.activity['activity'] = activity
if subactivity != None:
acc.activity['subactivity'] = subactivity
if text != None:
acc.activity['text'] = text
(user, resource) = gajim.get_room_and_nick_from_fjid(jid)
contact = gajim.contacts.get_contact(name, user, resource=resource)
if not contact:
return
if has_child:
if contact.activity.has_key('activity'):
del contact.activity['activity']
if contact.activity.has_key('subactivity'):
del contact.activity['subactivity']
if contact.activity.has_key('text'):
del contact.activity['text']
if activity != None:
contact.activity['activity'] = activity
if subactivity != None:
contact.activity['subactivity'] = subactivity
if text != None:
contact.activity['text'] = text
def user_send_mood(account, mood, message = ''):
if gajim.config.get('publish_mood') == False:
return
item = xmpp.Node('mood', {'xmlns': xmpp.NS_MOOD})
if mood != '':
item.addChild(mood)
if message != '':
i = item.addChild('text')
i.addData(message)
gajim.connections[account].send_pb_publish('', xmpp.NS_MOOD, item, '0')
def user_send_activity(account, activity, subactivity = '', message = ''):
if gajim.config.get('publish_activity') == False:
return
item = xmpp.Node('activity', {'xmlns': xmpp.NS_ACTIVITY})
if activity != '':
i = item.addChild(activity)
if subactivity != '':
i.addChild(subactivity)
if message != '':
i = item.addChild('text')
i.addData(message)
gajim.connections[account].send_pb_publish('', xmpp.NS_ACTIVITY, item, '0')
def user_send_tune(account, artist = '', title = '', source = '', track = 0,length = 0, items = None):
if (gajim.config.get('publish_tune') == False) or \
(gajim.connections[account].pep_supported == False):
return
item = xmpp.Node('tune', {'xmlns': xmpp.NS_TUNE})
if artist != '':
i = item.addChild('artist')
i.addData(artist)
if title != '':
i = item.addChild('title')
i.addData(title)
if source != '':
i = item.addChild('source')
i.addData(source)
if track != 0:
i = item.addChild('track')
i.addData(track)
if length != 0:
i = item.addChild('length')
i.addData(length)
if items is not None:
item.addChild(payload=items)
gajim.connections[account].send_pb_publish('', xmpp.NS_TUNE, item, '0')

View file

@ -1,5 +1,6 @@
import xmpp
import gajim
import connection_handlers
class ConnectionPubSub:
def __init__(self):
@ -43,9 +44,61 @@ class ConnectionPubSub:
self.connection.send(query)
def send_pb_delete(self, jid, node):
'''Deletes node.'''
query = xmpp.Iq('set', to=jid)
d = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
d = d.addChild('delete', {'node': node})
self.connection.send(query)
def send_pb_create(self, jid, node, configure = False, configure_form = None):
'''Creates new node.'''
query = xmpp.Iq('set', to=jid)
c = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
c = c.addChild('create', {'node': node})
if configure:
conf = c.addChild('configure')
if configure_form is not None:
conf.addChild(node=configure_form)
self.connection.send(query)
def send_pb_configure(self, jid, node, cb, *cbargs, **cbkwargs):
query = xmpp.Iq('set', to=jid)
c = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
c = c.addChild('configure', {'node': node})
id = self.connection.send(query)
def on_configure(self, connection, query):
try:
filledform = cb(stanza['pubsub']['configure']['x'], *cbargs, **cbkwargs)
#TODO: Build a form
#TODO: Send it
except CancelConfigure:
cancel = xmpp.Iq('set', to=jid)
ca = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
ca = ca.addChild('configure', {'node': node})
#ca = ca.addChild('x', namespace=xmpp.NS_DATA, {'type': 'cancel'})
self.connection.send(cancel)
self.__callbacks[id] = (on_configure, (), {})
def _PubSubCB(self, conn, stanza):
try:
cb, args, kwargs = self.__callbacks.pop(stanza.getID())
cb(conn, stanza, *args, **kwargs)
except:
pass
def request_pb_configuration(self, jid, node):
query = xmpp.Iq('get', to=jid)
e = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB_OWNER)
e = e.addChild('configure', {'node': node})
id = self.connection.getAnID()
query.setID(id)
self.awaiting_answers[id] = (connection_handlers.PEP_ACCESS_MODEL,)
self.connection.send(query)

View file

@ -932,7 +932,7 @@ class Socks5Receiver(Socks5, IdleObject):
elif self.state == 3: # send 'connect' request
self.send_raw(self._get_request_buff(self._get_sha1_auth()))
elif self.file_props['type'] != 'r':
if self.file_props['paused'] == True:
if self.file_props['paused']:
self.idlequeue.plug_idle(self, False, False)
return
result = self.write_next()

View file

@ -1,7 +1,6 @@
from common import gajim
from common import xmpp
from common import helpers
from common import exceptions
import random
@ -571,9 +570,9 @@ class EncryptedStanzaSession(StanzaSession):
self.d = crypto.powmod(g, self.y, p)
to_add = { 'my_nonce': self.n_s,
'dhkeys': crypto.encode_mpi(self.d),
'counter': crypto.encode_mpi(self.c_o),
'nonce': self.n_o }
'dhkeys': crypto.encode_mpi(self.d),
'counter': crypto.encode_mpi(self.c_o),
'nonce': self.n_o }
for name in to_add:
b64ed = base64.b64encode(to_add[name])

View file

@ -27,11 +27,14 @@ NS_AGENTS ='jabber:iq:agents'
NS_AMP ='http://jabber.org/protocol/amp'
NS_AMP_ERRORS =NS_AMP+'#errors'
NS_AUTH ='jabber:iq:auth'
NS_AVATAR ='http://www.xmpp.org/extensions/xep-0084.html#ns-metadata'
NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind'
NS_BROWSE ='jabber:iq:browse'
NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # XEP-0065
NS_CAPS ='http://jabber.org/protocol/caps' # XEP-0115
NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # XEP-0085
NS_BROWSING ='http://jabber.org/protocol/browsing' # XEP-0195
NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065
NS_CAPS ='http://jabber.org/protocol/caps' # JEP-0115
NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # JEP-0085
NS_CHATTING ='http://jabber.org/protocol/chatting' # XEP-0194
NS_CLIENT ='jabber:client'
NS_COMMANDS ='http://jabber.org/protocol/commands'
NS_COMPONENT_ACCEPT='jabber:component:accept'
@ -48,8 +51,9 @@ NS_ENCRYPTED ='jabber:x:encrypted' # XEP-00
NS_ESESSION_INIT='http://www.xmpp.org/extensions/xep-0116.html#ns-init' # XEP-0116
NS_EVENT ='jabber:x:event' # XEP-0022
NS_FEATURE ='http://jabber.org/protocol/feature-neg'
NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # XEP-0096
NS_GEOLOC ='http://jabber.org/protocol/geoloc' # XEP-0080
NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096
NS_GAMING ='http://jabber.org/protocol/gaming' # XEP-0196
NS_GEOLOC ='http://jabber.org/protocol/geoloc' # JEP-0080
NS_GROUPCHAT ='gc-1.0'
NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # XEP-0070
NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # XEP-0124
@ -72,6 +76,7 @@ NS_PRIVACY ='jabber:iq:privacy'
NS_PRIVATE ='jabber:iq:private'
NS_PROFILE ='http://jabber.org/protocol/profile' # XEP-0154
NS_PUBSUB ='http://jabber.org/protocol/pubsub' # XEP-0060
NS_PUBSUB_OWNER ='http://jabber.org/protocol/pubsub#owner' # JEP-0060
NS_REGISTER ='jabber:iq:register'
NS_ROSTER ='jabber:iq:roster'
NS_ROSTERX ='http://jabber.org/protocol/rosterx' # XEP-0144
@ -90,12 +95,14 @@ NS_STREAMS ='http://etherx.jabber.org/streams'
NS_TIME ='jabber:iq:time' # XEP-0900
NS_TIME_REVISED ='urn:xmpp:time' # XEP-0202
NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls'
NS_TUNE ='http://jabber.org/protocol/tune' # XEP-0118
NS_VACATION ='http://jabber.org/protocol/vacation'
NS_VCARD ='vcard-temp'
NS_GMAILNOTIFY ='google:mail:notify'
NS_GTALKSETTING ='google:setting'
NS_VCARD_UPDATE =NS_VCARD+':x:update'
NS_VERSION ='jabber:iq:version'
NS_VIEWING ='http://jabber.org/protocol/viewing' # XEP--197
NS_PING ='urn:xmpp:ping' # XEP-0199
NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # XEP-0130
NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # XEP-0071

View file

@ -87,7 +87,11 @@ class Roster(PlugIn):
def PresenceHandler(self,dis,pres):
""" Presence tracker. Used internally for setting items' resources state in
internal roster representation. """
jid=JID(pres.getFrom())
jid=pres.getFrom()
if not jid:
# If no from attribue, it's from server
jid=self._owner.Server
jid=JID(jid)
if not self._data.has_key(jid.getStripped()): self._data[jid.getStripped()]={'name':None,'ask':None,'subscription':'none','groups':['Not in roster'],'resources':{}}
if type(self._data[jid.getStripped()]['resources'])!=type(dict()):
self._data[jid.getStripped()]['resources']={}

View file

@ -761,15 +761,18 @@ class NonBlockingTLS(PlugIn):
for line in lines:
if 'BEGIN CERTIFICATE' in line:
begin = i
continue
elif 'END CERTIFICATE' in line and begin > -1:
cert = ''.join(lines[begin:i+2])
try:
X509cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
store.add_cert(X509cert)
except OpenSSL.crypto.Error, exception_obj:
log.warning('Unable to load a certificate from file %s: %s' %\
(gajim.MY_CACERTS, exception_obj.args[0][0][2]))
except:
log.warning('Unable to load a certificate from file %s' % \
log.warning(
'Unknown error while loading certificate from file %s' % \
gajim.MY_CACERTS)
begin = -1
i += 1
@ -787,7 +790,8 @@ class NonBlockingTLS(PlugIn):
try:
self.starttls='in progress'
tcpsock._sslObj.do_handshake()
except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e:
# Errors are handeled in _do_receive function
except:
pass
tcpsock._sslObj.setblocking(False)
log.debug("Synchronous handshake completed")
@ -1027,7 +1031,7 @@ class NBSOCKS5PROXYsocket(NonBlockingTcp):
# use the IPv4 address request even if remote resolving was specified.
try:
self.ipaddr = socket.inet_aton(self.server[0])
req = req + "\x01" + ipaddr
req = req + "\x01" + self.ipaddr
except socket.error:
# Well it's not an IP number, so it's probably a DNS name.
# if self.__proxy[3]==True:

View file

@ -32,7 +32,6 @@ from calendar import timegm
from common import socks5
import common.xmpp
from common import GnuPG
from common import helpers
from common import gajim
from common.zeroconf import zeroconf
@ -726,13 +725,15 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
if not user_nick:
user_nick = ''
if encTag and GnuPG.USE_GPG:
if encTag and self.USE_GPG:
#decrypt
encmsg = encTag.getData()
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID:
decmsg = self.gpg.decrypt(encmsg, keyID)
# \x00 chars are not allowed in C (so in GTK)
decmsg = decmsg.replace('\x00', '')
if decmsg:
msgtxt = decmsg
encrypted = True

View file

@ -49,8 +49,6 @@ from common.zeroconf import client_zeroconf
from common.zeroconf import zeroconf
from connection_handlers_zeroconf import *
USE_GPG = GnuPG.USE_GPG
class ConnectionZeroconf(ConnectionHandlersZeroconf):
'''Connection class'''
def __init__(self, name):
@ -62,7 +60,9 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
self.connected = 0 # offline
self.connection = None
self.gpg = None
if USE_GPG:
self.USE_GPG = False
if gajim.HAVE_GPG:
self.USE_GPG = True
self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
self.is_zeroconf = True
self.privacy_rules_supported = False
@ -71,9 +71,9 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
self.status = ''
self.old_show = ''
self.priority = 0
self.call_resolve_timeout = False
self.time_to_reconnect = None
#self.new_account_info = None
self.bookmarks = []
@ -86,21 +86,25 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
self.no_log_for = False
self.pep_supported = False
self.mood = {}
self.tune = {}
self.activity = {}
# Do we continue connection when we get roster (send presence,get vcard...)
self.continue_connect_info = None
if USE_GPG:
if gajim.HAVE_GPG:
self.USE_GPG = True
self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
self.get_config_values_or_default()
self.muc_jid = {} # jid of muc server for each transport type
self.vcard_supported = False
self.private_storage_supported = False
def get_config_values_or_default(self):
''' get name, host, port from config, or
''' get name, host, port from config, or
create zeroconf account with default values'''
if not gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'name'):
gajim.log.debug('Creating zeroconf account')
gajim.config.add_per('accounts', gajim.ZEROCONF_ACC_NAME)
@ -146,7 +150,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
def quit(self, kill_core):
if kill_core and self.connected > 1:
self.disconnect()
def disable_account(self):
self.disconnect()
@ -160,17 +164,19 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
def get_signed_msg(self, msg):
signed = ''
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID and USE_GPG:
if keyID and self.USE_GPG:
use_gpg_agent = gajim.config.get('use_gpg_agent')
if self.connected < 2 and self.gpg.passphrase is None and \
not use_gpg_agent:
not use_gpg_agent:
# We didn't set a passphrase
self.dispatch('ERROR', (_('OpenPGP passphrase was not given'),
#%s is the account name here
_('You will be connected to %s without OpenPGP.') % self.name))
self.USE_GPG = False
elif self.gpg.passphrase is not None or use_gpg_agent:
signed = self.gpg.sign(msg, keyID)
if signed == 'BAD_PASSPHRASE':
self.USE_GPG = False
signed = ''
if self.connected < 2:
self.dispatch('BAD_PASSPHRASE', ())
@ -189,12 +195,12 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
#XXX open chat windows don't get refreshed (full name), add that
return self.call_resolve_timeout
# callbacks called from zeroconf
# callbacks called from zeroconf
def _on_new_service(self,jid):
self.roster.setItem(jid)
self.dispatch('ROSTER_INFO', (jid, self.roster.getName(jid), 'both', 'no', self.roster.getGroups(jid)))
self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), self.roster.getMessage(jid), 'local', 0, None, 0, None))
def _on_remove_service(self, jid):
self.roster.delItem(jid)
# 'NOTIFY' (account, (jid, status, status message, resource, priority,
@ -326,7 +332,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
elif show == 'offline' and self.connected:
self.disconnect()
self.time_to_reconnect = None
# update status
elif show != 'offline' and self.connected:
was_invisible = self.connected == STATUS_LIST.index('invisible')
@ -344,18 +350,18 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
if check:
self.dispatch('STATUS', show)
else:
# show notification that avahi or system bus is down
# show notification that avahi or system bus is down
self.dispatch('STATUS', 'offline')
self.status = 'offline'
self.dispatch('CONNECTION_LOST',
(_('Could not change status of account "%s"') % self.name,
_('Please check if avahi-daemon is running.')))
def get_status(self):
return STATUS_LIST[self.connected]
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
chatstate = None, msg_id = None, composing_xep = None, resource = None,
chatstate = None, msg_id = None, composing_xep = None, resource = None,
user_nick = None, session=None):
fjid = jid
@ -370,9 +376,14 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
msgtxt = msg
msgenc = ''
if keyID and USE_GPG:
# encrypt
msgenc, error = self.gpg.encrypt(msg, [keyID])
if keyID and self.USE_GPG:
if keyID == 'UNKNOWN':
error = _('Neither the remote presence is signed, nor a key was assigned.')
elif keyID[8:] == 'MISMATCH':
error = _('The contact\'s key (%s) does not match the key assigned in Gajim.' % keyID[:8])
else:
# encrypt
msgenc, error = self.gpg.encrypt(msg, [keyID])
if msgenc and not error:
msgtxt = '[This message is encrypted]'
lang = os.getenv('LANG')
@ -414,8 +425,8 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
msg_id = ''
chatstate_node.setTagData('id', msg_id)
# when msgtxt, requests JEP-0022 composing notification
if chatstate is 'composing' or msgtxt:
chatstate_node.addChild(name = 'composing')
if chatstate is 'composing' or msgtxt:
chatstate_node.addChild(name = 'composing')
if session:
session.last_send = time.time()
@ -440,15 +451,15 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
else:
kind = 'single_msg_sent'
gajim.logger.write(kind, jid, log_msg)
self.dispatch('MSGSENT', (jid, msg, keyID))
def send_stanza(self, stanza):
# send a stanza untouched
if not self.connection:
return
self.connection.send(stanza)
def ack_subscribed(self, jid):
gajim.log.debug('This should not happen (ack_subscribed)')
@ -471,11 +482,11 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
def unsubscribe_agent(self, agent):
gajim.log.debug('This should not happen (unsubscribe_agent)')
def update_contact(self, jid, name, groups):
def update_contact(self, jid, name, groups):
if self.connection:
self.connection.getRoster().setItem(jid = jid, name = name,
groups = groups)
def new_account(self, name, config, sync = False):
gajim.log.debug('This should not happen (new_account)')
@ -496,18 +507,18 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
def get_bookmarks(self):
gajim.log.debug('This should not happen (get_bookmarks)')
def store_bookmarks(self):
gajim.log.debug('This should not happen (store_bookmarks)')
def get_metacontacts(self):
gajim.log.debug('This should not happen (get_metacontacts)')
def send_agent_status(self, agent, ptype):
gajim.log.debug('This should not happen (send_agent_status)')
def gpg_passphrase(self, passphrase):
if USE_GPG:
if self.gpg:
use_gpg_agent = gajim.config.get('use_gpg_agent')
if use_gpg_agent:
self.gpg.passphrase = None
@ -515,13 +526,13 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
self.gpg.passphrase = passphrase
def ask_gpg_keys(self):
if USE_GPG:
if self.gpg:
keys = self.gpg.get_keys()
return keys
return None
def ask_gpg_secrete_keys(self):
if USE_GPG:
if self.gpg:
keys = self.gpg.get_secret_keys()
return keys
return None

File diff suppressed because it is too large Load diff

View file

@ -61,8 +61,8 @@ class TextViewImage(gtk.Image):
self.anchor = anchor
self._selected = False
self._disconnect_funcs = []
self.connect("parent-set", self.on_parent_set)
self.connect("expose-event", self.on_expose)
self.connect('parent-set', self.on_parent_set)
self.connect('expose-event', self.on_expose)
def _get_selected(self):
parent = self.get_parent()
@ -74,7 +74,7 @@ class TextViewImage(gtk.Image):
return True
else:
return False
def get_state(self):
parent = self.get_parent()
if not parent:
@ -110,22 +110,22 @@ class TextViewImage(gtk.Image):
self._disconnect_signals()
return
self._do_connect(parent, "style-set", self.do_queue_draw)
self._do_connect(parent, "focus-in-event", self.do_queue_draw)
self._do_connect(parent, "focus-out-event", self.do_queue_draw)
self._do_connect(parent, 'style-set', self.do_queue_draw)
self._do_connect(parent, 'focus-in-event', self.do_queue_draw)
self._do_connect(parent, 'focus-out-event', self.do_queue_draw)
textbuf = parent.get_buffer()
self._do_connect(textbuf, "mark-set", self.on_mark_set)
self._do_connect(textbuf, "mark-deleted", self.on_mark_deleted)
self._do_connect(textbuf, 'mark-set', self.on_mark_set)
self._do_connect(textbuf, 'mark-deleted', self.on_mark_deleted)
def do_queue_draw(self, *args):
self.queue_draw()
return False
def on_mark_set(self, buf, iterat, mark):
self.on_mark_modified(mark)
return False
def on_mark_deleted(self, buf, mark):
self.on_mark_modified(mark)
return False
@ -142,12 +142,12 @@ class TextViewImage(gtk.Image):
widget.window.draw_rectangle(gc, True, area.x, area.y,
area.width, area.height)
return False
class ConversationTextview:
'''Class for the conversation textview (where user reads already said messages)
for chat/groupchat windows'''
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)
@ -159,7 +159,7 @@ class ConversationTextview:
'''if used_in_history_window is True, then we do not show
Clear menuitem in context menu'''
self.used_in_history_window = used_in_history_window
# no need to inherit TextView, use it as atrribute is safer
self.tv = HtmlTextView()
self.tv.html_hyperlink_handler = self.html_hyperlink_handler
@ -185,8 +185,8 @@ class ConversationTextview:
id = self.tv.connect('button_press_event',
self.on_textview_button_press_event)
self.handlers[id] = self.tv
id = self.tv.connect("expose-event",
id = self.tv.connect('expose-event',
self.on_textview_expose_event)
self.handlers[id] = self.tv
@ -213,10 +213,9 @@ class ConversationTextview:
colors = gajim.config.get('gc_nicknames_colors')
colors = colors.split(':')
for color in xrange(len(colors)):
tagname = 'gc_nickname_color_' + str(color)
for i,color in enumerate(colors):
tagname = 'gc_nickname_color_' + str(i)
tag = buffer.create_tag(tagname)
color = colors[color]
tag.set_property('foreground', color)
tag = buffer.create_tag('marked')
@ -282,7 +281,7 @@ class ConversationTextview:
self.tv.destroy()
#FIXME:
# self.line_tooltip.destroy()
def update_tags(self):
self.tagIn.set_property('foreground', gajim.config.get('inmsgcolor'))
self.tagOut.set_property('foreground', gajim.config.get('outmsgcolor'))
@ -315,7 +314,7 @@ class ConversationTextview:
self.smooth_id = None
self.smooth_scroll_timer.cancel()
return False
return True
return True
def smooth_scroll_timeout(self):
gobject.idle_add(self.do_smooth_scroll_timeout)
@ -336,9 +335,9 @@ class ConversationTextview:
if None != self.smooth_id: # already scrolling
return False
self.smooth_id = gobject.timeout_add(self.SCROLL_DELAY,
self.smooth_scroll)
self.smooth_scroll)
self.smooth_scroll_timer = Timer(self.MAX_SCROLL_TIME,
self.smooth_scroll_timeout)
self.smooth_scroll_timeout)
self.smooth_scroll_timer.start()
return False
@ -353,9 +352,8 @@ class ConversationTextview:
adjustment.set_value(0)
return False # when called in an idle_add, just do it once
def bring_scroll_to_end(self, diff_y = 0,\
use_smooth =\
gajim.config.get('use_smooth_scrolling')):
def bring_scroll_to_end(self, diff_y = 0,
use_smooth=gajim.config.get('use_smooth_scrolling')):
''' scrolls to the end of textview if end is not visible '''
buffer = self.tv.get_buffer()
end_iter = buffer.get_end_iter()
@ -418,12 +416,13 @@ class ConversationTextview:
# add the new focus out line
end_iter = buffer.get_end_iter()
buffer.insert(end_iter, '\n')
buffer.insert_pixbuf(end_iter,
buffer.insert_pixbuf(end_iter,
ConversationTextview.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)
# one char back (an image also takes one char)
before_img_iter.backward_char()
buffer.apply_tag_by_name('focus-out-line', before_img_iter, end_iter)
self.allow_focus_out_line = False
@ -452,7 +451,8 @@ class ConversationTextview:
# check if the current pointer is still over the line
position = self.tv.window.get_origin()
self.line_tooltip.show_tooltip(_('Text below this line is what has '
'been said since the last time you paid attention to this group chat'), 8, position[1] + pointer[1])
'been said since the last time you paid attention to this group chat'),
8, position[1] + pointer[1])
def on_textview_expose_event(self, widget, event):
expalloc = event.area
@ -460,7 +460,7 @@ class ConversationTextview:
exp_y0 = expalloc.y
exp_x1 = exp_x0 + expalloc.width
exp_y1 = exp_y0 + expalloc.height
try:
tryfirst = [self.image_cache[(exp_x0, exp_y0)]]
except KeyError:
@ -582,7 +582,8 @@ class ConversationTextview:
else:
if dict_link.find('%s') == -1:
# we must have %s in the url if not WIKTIONARY
item = gtk.MenuItem(_('Dictionary URL is missing an "%s" and it is not WIKTIONARY'))
item = gtk.MenuItem(_(
'Dictionary URL is missing an "%s" and it is not WIKTIONARY'))
item.set_property('sensitive', False)
else:
link = dict_link % self.selected_phrase
@ -603,7 +604,7 @@ class ConversationTextview:
id = item.connect('activate', self.visit_url_from_menuitem, link)
self.handlers[id] = item
submenu.append(item)
item = gtk.MenuItem(_('Open as _Link'))
id = item.connect('activate', self.visit_url_from_menuitem, link)
self.handlers[id] = item
@ -639,7 +640,8 @@ class ConversationTextview:
if return_val: # if sth was selected when we right-clicked
# get the selected text
start_sel, finish_sel = return_val[0], return_val[1]
self.selected_phrase = buffer.get_text(start_sel, finish_sel).decode('utf-8')
self.selected_phrase = buffer.get_text(start_sel, finish_sel).decode(
'utf-8')
def on_open_link_activate(self, widget, kind, text):
helpers.launch_browser_mailer(kind, text)
@ -673,7 +675,8 @@ class ConversationTextview:
if kind == 'url':
id = childs[0].connect('activate', self.on_copy_link_activate, text)
self.handlers[id] = childs[0]
id = childs[1].connect('activate', self.on_open_link_activate, kind, text)
id = childs[1].connect('activate', self.on_open_link_activate, kind,
text)
self.handlers[id] = childs[1]
childs[2].hide() # copy mail address
childs[3].hide() # open mail composer
@ -685,13 +688,14 @@ class ConversationTextview:
# load muc icon
join_group_chat_menuitem = xml.get_widget('join_group_chat_menuitem')
muc_icon = gajim.interface.roster.load_icon('muc_active')
if muc_icon:
join_group_chat_menuitem.set_image(muc_icon)
if muc_icon:
join_group_chat_menuitem.set_image(muc_icon)
text = text.lower()
id = childs[2].connect('activate', self.on_copy_link_activate, text)
self.handlers[id] = childs[2]
id = childs[3].connect('activate', self.on_open_link_activate, kind, text)
id = childs[3].connect('activate', self.on_open_link_activate, kind,
text)
self.handlers[id] = childs[3]
id = childs[5].connect('activate', self.on_start_chat_activate, text)
self.handlers[id] = childs[5]
@ -708,7 +712,8 @@ class ConversationTextview:
allow_add = True
if allow_add:
id = childs[7].connect('activate', self.on_add_to_roster_activate, text)
id = childs[7].connect('activate', self.on_add_to_roster_activate,
text)
self.handlers[id] = childs[7]
childs[7].show() # show add to roster menuitem
else:
@ -729,7 +734,8 @@ class ConversationTextview:
# we get the end of the tag
while not end_iter.ends_tag(texttag):
end_iter.forward_char()
word = self.tv.get_buffer().get_text(begin_iter, end_iter).decode('utf-8')
word = self.tv.get_buffer().get_text(begin_iter, end_iter).decode(
'utf-8')
if event.button == 3: # right click
self.make_link_menu(event, kind, word)
else:
@ -786,16 +792,16 @@ class ConversationTextview:
exitcode = 0
# some latex commands are really bad
blacklist = ["\\def", "\\let", "\\futurelet",
"\\newcommand", "\\renewcomment", "\\else", "\\fi", "\\write",
"\\input", "\\include", "\\chardef", "\\catcode", "\\makeatletter",
"\\noexpand", "\\toksdef", "\\every", "\\errhelp", "\\errorstopmode",
"\\scrollmode", "\\nonstopmode", "\\batchmode", "\\read", "\\csname",
"\\newhelp", "\\relax", "\\afterground", "\\afterassignment",
"\\expandafter", "\\noexpand", "\\special", "\\command", "\\loop",
"\\repeat", "\\toks", "\\output", "\\line", "\\mathcode", "\\name",
"\\item", "\\section", "\\mbox", "\\DeclareRobustCommand", "\\[",
"\\]"]
blacklist = ['\\def', '\\let', '\\futurelet',
'\\newcommand', '\\renewcomment', '\\else', '\\fi', '\\write',
'\\input', '\\include', '\\chardef', '\\catcode', '\\makeatletter',
'\\noexpand', '\\toksdef', '\\every', '\\errhelp', '\\errorstopmode',
'\\scrollmode', '\\nonstopmode', '\\batchmode', '\\read', '\\csname',
'\\newhelp', '\\relax', '\\afterground', '\\afterassignment',
'\\expandafter', '\\noexpand', '\\special', '\\command', '\\loop',
'\\repeat', '\\toks', '\\output', '\\line', '\\mathcode', '\\name',
'\\item', '\\section', '\\mbox', '\\DeclareRobustCommand', '\\[',
'\\]']
str = str[2:len(str)-2]
@ -807,16 +813,18 @@ class ConversationTextview:
if exitcode == 0:
random.seed()
tmpfile = os.path.join(gettempdir(), "gajimtex_" + random.randint(0,
tmpfile = os.path.join(gettempdir(), 'gajimtex_' + random.randint(0,
100).__str__())
# build latex string
texstr = "\\documentclass[12pt]{article}\\usepackage[dvips]{graphicx}\\usepackage{amsmath}\\usepackage{amssymb}\\pagestyle{empty}"
texstr += "\\begin{document}\\begin{large}\\begin{gather*}"
texstr = '\\documentclass[12pt]{article}\\usepackage[dvips]{graphicx}'
texstr += '\\usepackage{amsmath}\\usepackage{amssymb}'
texstr += '\\pagestyle{empty}'
texstr += '\\begin{document}\\begin{large}\\begin{gather*}'
texstr += str
texstr += "\\end{gather*}\\end{large}\\end{document}"
texstr += '\\end{gather*}\\end{large}\\end{document}'
file = open(os.path.join(tmpfile + ".tex"), "w+")
file = open(os.path.join(tmpfile + '.tex'), 'w+')
file.write(texstr)
file.flush()
file.close()
@ -825,17 +833,17 @@ class ConversationTextview:
cwd=gettempdir())
exitcode = p.wait()
if exitcode == 0:
if exitcode == 0:
p = Popen(['dvips', '-E', '-o', tmpfile + '.ps', tmpfile + '.dvi'],
cwd=gettempdir())
exitcode = p.wait()
if exitcode == 0:
p = Popen(['convert', tmpfile + '.ps', tmpfile + '.png'],
cwd=gettempdir())
p = Popen(['convert', '-alpha', 'off', tmpfile + '.ps',
tmpfile + '.png'], cwd=gettempdir())
exitcode = p.wait()
extensions = [".tex", ".log", ".aux", ".dvi", ".ps"]
extensions = ['.tex', '.log', '.aux', '.dvi', '.ps']
for ext in extensions:
try:
os.remove(tmpfile + ext)
@ -895,11 +903,13 @@ class ConversationTextview:
use_other_tags = False
elif special_text.startswith('*'): # it's a bold text
tags.append('bold')
if special_text[1] == '/' and special_text[-2] == '/' and len(special_text) > 4: # it's also italic
if special_text[1] == '/' and special_text[-2] == '/' and\
len(special_text) > 4: # it's also italic
tags.append('italic')
if not show_ascii_formatting_chars:
special_text = special_text[2:-2] # remove */ /*
elif special_text[1] == '_' and special_text[-2] == '_' and len(special_text) > 4: # it's also underlined
elif special_text[1] == '_' and special_text[-2] == '_' and \
len(special_text) > 4: # it's also underlined
tags.append('underline')
if not show_ascii_formatting_chars:
special_text = special_text[2:-2] # remove *_ _*
@ -908,11 +918,13 @@ class ConversationTextview:
special_text = special_text[1:-1] # remove * *
elif special_text.startswith('/'): # it's an italic text
tags.append('italic')
if special_text[1] == '*' and special_text[-2] == '*' and len(special_text) > 4: # it's also bold
if special_text[1] == '*' and special_text[-2] == '*' and \
len(special_text) > 4: # it's also bold
tags.append('bold')
if not show_ascii_formatting_chars:
special_text = special_text[2:-2] # remove /* */
elif special_text[1] == '_' and special_text[-2] == '_' and len(special_text) > 4: # it's also underlined
elif special_text[1] == '_' and special_text[-2] == '_' and \
len(special_text) > 4: # it's also underlined
tags.append('underline')
if not show_ascii_formatting_chars:
special_text = special_text[2:-2] # remove /_ _/
@ -921,11 +933,13 @@ class ConversationTextview:
special_text = special_text[1:-1] # remove / /
elif special_text.startswith('_'): # it's an underlined text
tags.append('underline')
if special_text[1] == '*' and special_text[-2] == '*' and len(special_text) > 4: # it's also bold
if special_text[1] == '*' and special_text[-2] == '*' and \
len(special_text) > 4: # it's also bold
tags.append('bold')
if not show_ascii_formatting_chars:
special_text = special_text[2:-2] # remove _* *_
elif special_text[1] == '/' and special_text[-2] == '/' and len(special_text) > 4: # it's also italic
elif special_text[1] == '/' and special_text[-2] == '/' and \
len(special_text) > 4: # it's also italic
tags.append('italic')
if not show_ascii_formatting_chars:
special_text = special_text[2:-2] # remove _/ /_
@ -1123,8 +1137,8 @@ class ConversationTextview:
self.tv.display_html(xhtml.encode('utf-8'))
return
except Exception, e:
gajim.log.debug(str("Error processing xhtml")+str(e))
gajim.log.debug(str("with |"+xhtml+"|"))
gajim.log.debug(str('Error processing xhtml')+str(e))
gajim.log.debug(str('with |'+xhtml+'|'))
buffer = self.tv.get_buffer()
# /me is replaced by name if name is given
@ -1136,4 +1150,3 @@ class ConversationTextview:
# add the rest of text located in the index and after
end_iter = buffer.get_end_iter()
buffer.insert_with_tags_by_name(end_iter, text[index:], *text_tags)

View file

@ -342,7 +342,7 @@ class SingleForm(gtk.Table, object):
for value, label in field.iter_options():
radio = gtk.RadioButton(first_radio, label=label)
radio.connect('toggled',
self.on_list_single_radiobutton_toggled, field, value)
self.on_list_single_radiobutton_toggled, field, value)
if first_radio is None:
first_radio = radio
if field.value == '': # TODO: is None when done

View file

@ -9,7 +9,7 @@
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Julien Pivotto <roidelapluie@gmail.com>
## Stephan Erb <steve-e@h3c.de>
## Stephan Erb <steve-e@h3c.de>
##
## This file is part of Gajim.
##
@ -207,7 +207,7 @@ class EditGroupsDialog:
for group in groups:
if group not in helpers.special_groups or groups[group] > 0:
group_list.append(group)
group_list.sort()
group_list.sort()
for group in group_list:
iter = store.append()
store.set(iter, 0, group) # Group name
@ -256,7 +256,7 @@ class PassphraseDialog:
self.window.destroy()
return passphrase, checked
def __init__(self, titletext, labeltext, checkbuttontext=None, is_modal = True,
def __init__(self, titletext, labeltext, checkbuttontext=None, is_modal=True,
ok_handler = None, cancel_handler = None):
self.xml = gtkgui_helpers.get_glade('passphrase_dialog.glade')
self.window = self.xml.get_widget('passphrase_dialog')
@ -278,14 +278,14 @@ class PassphraseDialog:
self.xml.signal_autoconnect(self)
self.window.show_all()
self.check = bool(checkbuttontext)
checkbutton = self.xml.get_widget('save_passphrase_checkbutton')
if self.check:
checkbutton.set_label(checkbuttontext)
else:
checkbutton.hide()
def on_okbutton_clicked(self, widget):
passph = self.passphrase_entry.get_text().decode('utf-8')
@ -351,9 +351,9 @@ class ChooseGPGKeyDialog:
def run(self):
rep = self.window.run()
if rep == gtk.RESPONSE_OK:
selection = self.keys_treeview.get_selection()
(model, iter) = selection.get_selected()
selection = self.keys_treeview.get_selection()
(model, iter) = selection.get_selected()
if iter and rep == gtk.RESPONSE_OK:
keyID = [ model[iter][0].decode('utf-8'),
model[iter][1].decode('utf-8') ]
else:
@ -370,6 +370,125 @@ class ChooseGPGKeyDialog:
self.keys_treeview.set_cursor(path)
class ChangeActivityDialog:
activities = [_('doing_chores'), _('drinking'), _('eating'),
_('excercising'), _('grooming'), _('having_appointment'),
_('inactive'), _('relaxing'), _('talking'), _('traveling'),
_('working'), ]
subactivities = [_('at_the_spa'), _('brushing_teeth'),
_('buying_groceries'), _('cleaning'), _('coding'),
_('commuting'), _('cooking'), _('cycling'), _('day_off'),
_('doing_maintenance'), _('doing_the_dishes'),
_('doing_the_laundry'), _('driving'), _('gaming'),
_('gardening'), _('getting_a_haircut'), _('going_out'),
_('hanging_out'), _('having_a_beer'), _('having_a_snack'),
_('having_breakfast'), _('having_coffee'),
_('having_dinner'), _('having_lunch'), _('having_tea'),
_('hiking'), _('in_a_car'), _('in_a_meeting'),
_('in_real_life'), _('jogging'), _('on_a_bus'),
_('on_a_plane'), _('on_a_train'), _('on_a_trip'),
_('on_the_phone'), _('on_vacation'), _('other'),
_('partying'), _('playing_sports'), _('reading'),
_('rehearsing'), _('running'), _('running_an_errand'),
_('scheduled_holiday'), _('shaving'), _('shopping'),
_('skiing'), _('sleeping'), _('socializing'),
_('studying'), _('sunbathing'), _('swimming'),
_('taking_a_bath'), _('taking_a_shower'), _('walking'),
_('walking_the_dog'), _('watching_tv'),
_('watching_a_movie'), _('working_out'), _('writing'), ]
def __init__(self, account):
self.account = account
self.xml = gtkgui_helpers.get_glade('change_activity_dialog.glade')
self.window = self.xml.get_widget('change_activity_dialog')
self.window.set_transient_for(gajim.interface.roster.window)
self.window.set_title(_('Activity'))
self.entry = self.xml.get_widget('entry')
self.combo1 = self.xml.get_widget('combobox1')
self.liststore1 = gtk.ListStore(str)
self.combo1.set_model(self.liststore1)
for activity in self.activities:
self.liststore1.append((activity,))
cellrenderertext = gtk.CellRendererText()
self.combo1.pack_start(cellrenderertext, True)
self.combo1.add_attribute(cellrenderertext, 'text', 0)
self.combo2 = self.xml.get_widget('combobox2')
self.liststore2 = gtk.ListStore(str)
self.combo2.set_model(self.liststore2)
for subactivity in self.subactivities:
self.liststore2.append((subactivity,))
cellrenderertext = gtk.CellRendererText()
self.combo2.pack_start(cellrenderertext, True)
self.combo2.add_attribute(cellrenderertext, 'text', 0)
self.xml.signal_autoconnect(self)
self.window.show_all()
def on_ok_button_clicked(self, widget):
'''Return activity and messsage (None if no activity selected)'''
activity = None
subactivity = None
message = None
active1 = self.combo1.get_active()
active2 = self.combo2.get_active()
if active1 > -1:
activity = self.liststore1[active1][0].decode('utf-8')
if active2 > -1:
subactivity = self.liststore2[active2][0].decode('utf-8')
message = self.entry.get_text().decode('utf-8')
from common import pep
pep.user_send_activity(self.account, activity,
subactivity, message)
self.window.destroy()
def on_cancel_button_clicked(self, widget):
self.window.destroy()
class ChangeMoodDialog:
moods = [_('afraid'), _('amazed'), _('angry'), _('annoyed'), _('anxious'), _('aroused'), _('ashamed'), _('bored'), _('brave'), _('calm'), _('cold'), _('confused'), _('contented'), _('cranky'), _('curious'), _('depressed'), _('disappointed'), _('disgusted'), _('distracted'), _('embarrassed'), _('excited'), _('flirtatious'), _('frustrated'), _('grumpy'), _('guilty'), _('happy'), _('hot'), _('humbled'), _('humiliated'), _('hungry'), _('hurt'), _('impressed'), _('in_awe'), _('in_love'), _('indignant'), _('interested'), _('intoxicated'), _('invincible'), _('jealous'), _('lonely'), _('mean'), _('moody'), _('nervous'), _('neutral'), _('offended'), _('playful'), _('proud'), _('relieved'), _('remorseful'), _('restless'), _('sad'), _('sarcastic'), _('serious'), _('shocked'), _('shy'), _('sick'), _('sleepy'), _('stressed'), _('surprised'), _('thirsty'), _('worried')]
def __init__(self, account):
self.account = account
self.xml = gtkgui_helpers.get_glade('change_mood_dialog.glade')
self.window = self.xml.get_widget('change_mood_dialog')
self.window.set_transient_for(gajim.interface.roster.window)
self.window.set_title(_('Mood'))
self.entry = self.xml.get_widget('entry')
self.combo = self.xml.get_widget('combobox')
self.liststore = gtk.ListStore(str)
self.combo.set_model(self.liststore)
for mood in self.moods:
self.liststore.append((mood,))
cellrenderertext = gtk.CellRendererText()
self.combo.pack_start(cellrenderertext, True)
self.combo.add_attribute(cellrenderertext, 'text', 0)
self.xml.signal_autoconnect(self)
self.window.show_all()
def on_ok_button_clicked(self, widget):
'''Return mood and messsage (None if no mood selected)'''
mood = None
message = None
active = self.combo.get_active()
if active > -1:
mood = self.liststore[active][0].decode('utf-8')
message = self.entry.get_text().decode('utf-8')
from common import pep
pep.user_send_mood(self.account, mood, message)
self.window.destroy()
def on_cancel_button_clicked(self, widget):
self.window.destroy()
class ChangeStatusMessageDialog:
def __init__(self, show = None):
self.show = show
@ -502,7 +621,7 @@ class ChangeStatusMessageDialog:
self.preset_messages_dict[msg_name] = msg_text
iter_ = self.message_liststore.append((msg_name,))
gajim.config.add_per('statusmsg', msg_name)
# select in combobox the one we just saved
# select in combobox the one we just saved
self.message_combobox.set_active_iter(iter_)
gajim.config.set_per('statusmsg', msg_name, 'message', msg_text_1l)
@ -578,7 +697,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
self.agents[type_].append(jid_)
self.available_types.append(type_)
liststore = gtk.ListStore(str)
self.group_comboboxentry.set_model(liststore)
self.group_comboboxentry.set_model(liststore)
# Combobox with transport/jabber icons
liststore = gtk.ListStore(str, gtk.gdk.Pixbuf, str)
cell = gtk.CellRendererPixbuf()
@ -828,7 +947,7 @@ class AboutDialog:
text = open(copying_file_path).read()
dlg.set_license(text)
dlg.set_comments('%s\n%s %s\n%s %s'
dlg.set_comments('%s\n%s %s\n%s %s'
% (_('A GTK+ jabber client'), \
_('GTK+ Version:'), self.tuple2str(gtk.gtk_version), \
_('PyGTK Version:'), self.tuple2str(gtk.pygtk_version)))
@ -872,7 +991,7 @@ class AboutDialog:
dlg.props.wrap_license = True
pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(
gajim.DATA_DIR, 'pixmaps', 'gajim_about.png'))
gajim.DATA_DIR, 'pixmaps', 'gajim_about.png'))
dlg.set_logo(pixbuf)
#here you write your name in the form Name FamilyName <someone@somewhere>
@ -918,11 +1037,11 @@ class HigDialog(gtk.MessageDialog):
def __init__(self, parent, type, buttons, pritext, sectext,
on_response_ok = None, on_response_cancel = None, on_response_yes = None,
on_response_no = None):
gtk.MessageDialog.__init__(self, parent,
gtk.MessageDialog.__init__(self, parent,
gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
type, buttons, message_format = pritext)
self.format_secondary_text(sectext)
self.format_secondary_markup(sectext)
buttons = self.action_area.get_children()
possible_responses = {gtk.STOCK_OK: on_response_ok,
@ -969,7 +1088,7 @@ class FileChooserDialog(gtk.FileChooserDialog):
select_multiple = False, current_folder = None, on_response_ok = None,
on_response_cancel = None):
gtk.FileChooserDialog.__init__(self, title = title_text,
gtk.FileChooserDialog.__init__(self, title = title_text,
action = action, buttons = buttons)
self.set_default_response(default_response)
@ -1027,7 +1146,7 @@ class ConfirmationDialog(HigDialog):
on_response_cancel = None):
self.user_response_ok = on_response_ok
self.user_response_cancel = on_response_cancel
HigDialog.__init__(self, None,
HigDialog.__init__(self, None,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, pritext, sectext,
self.on_response_ok, self.on_response_cancel)
self.popup()
@ -1054,7 +1173,7 @@ class NonModalConfirmationDialog(HigDialog):
on_response_cancel = None):
self.user_response_ok = on_response_ok
self.user_response_cancel = on_response_cancel
HigDialog.__init__(self, None,
HigDialog.__init__(self, None,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, pritext, sectext,
self.on_response_ok, self.on_response_cancel)
self.set_modal(False)
@ -1078,7 +1197,7 @@ class NonModalConfirmationDialog(HigDialog):
class WarningDialog(HigDialog):
def __init__(self, pritext, sectext=''):
'''HIG compliant warning dialog.'''
HigDialog.__init__( self, None,
HigDialog.__init__( self, None,
gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, pritext, sectext)
self.set_modal(False)
self.set_transient_for(gajim.interface.roster.window)
@ -1087,7 +1206,7 @@ class WarningDialog(HigDialog):
class InformationDialog(HigDialog):
def __init__(self, pritext, sectext=''):
'''HIG compliant info dialog.'''
HigDialog.__init__( self, None,
HigDialog.__init__( self, None,
gtk.MESSAGE_INFO, gtk.BUTTONS_OK, pritext, sectext)
self.set_modal(False)
self.set_transient_for(gajim.interface.roster.window)
@ -1096,20 +1215,52 @@ class InformationDialog(HigDialog):
class ErrorDialog(HigDialog):
def __init__(self, pritext, sectext=''):
'''HIG compliant error dialog.'''
HigDialog.__init__( self, None,
HigDialog.__init__( self, None,
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, pritext, sectext)
self.popup()
class YesNoDialog(HigDialog):
def __init__(self, pritext, sectext='', on_response_yes = None,
on_response_no = None):
def __init__(self, pritext, sectext='', checktext='', on_response_yes=None,
on_response_no=None):
'''HIG compliant YesNo dialog.'''
HigDialog.__init__( self, None,
self.user_response_yes = on_response_yes
self.user_response_no = on_response_no
HigDialog.__init__( self, None,
gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, pritext, sectext,
on_response_yes = on_response_yes, on_response_no = on_response_no)
on_response_yes=self.on_response_yes,
on_response_no=self.on_response_no)
if checktext:
self.checkbutton = gtk.CheckButton(checktext)
self.vbox.pack_start(self.checkbutton, expand=False, fill=True)
else:
self.checkbutton = None
self.set_modal(False)
self.popup()
def on_response_yes(self, widget):
if self.user_response_yes:
if isinstance(self.user_response_yes, tuple):
self.user_response_yes[0](self.is_checked(),
*self.user_response_yes[1:])
else:
self.user_response_yes(self.is_checked())
self.destroy()
def on_response_no(self, widget):
if self.user_response_no:
if isinstance(self.user_response_no, tuple):
self.user_response_no[0](*self.user_response_no[1:])
else:
self.user_response_no()
self.destroy()
def is_checked(self):
''' Get active state of the checkbutton '''
if not self.checkbutton:
return False
return self.checkbutton.get_active()
class ConfirmationDialogCheck(ConfirmationDialog):
'''HIG compliant confirmation dialog with checkbutton.'''
def __init__(self, pritext, sectext='', checktext = '',
@ -1145,7 +1296,7 @@ class ConfirmationDialogCheck(ConfirmationDialog):
def on_response_cancel(self, widget):
if self.user_response_cancel:
if isinstance(self.user_response_cancel, tuple):
self.user_response_cancel[0](*self.user_response_ok[1:])
self.user_response_cancel[0](*self.user_response_cancel[1:])
else:
self.user_response_cancel()
self.destroy()
@ -1765,7 +1916,7 @@ class PopupNotificationWindow:
bg_color = 'yellowgreen'
elif event_type == _('Groupchat Invitation'):
bg_color = 'tan1'
elif event_type == _('Contact Changed Status'):
elif event_type == _('Contact Changed Status'):
bg_color = 'thistle2'
else: # Unknown event! Shouldn't happen but deal with it
bg_color = 'white'
@ -1773,7 +1924,7 @@ class PopupNotificationWindow:
close_button.modify_bg(gtk.STATE_NORMAL, popup_bg_color)
eventbox.modify_bg(gtk.STATE_NORMAL, popup_bg_color)
event_description_label.set_markup(
'<span foreground="black">%s</span>' % text)
'<span foreground="black">%s</span>' % text)
# set the image
image.set_from_file(path_to_image)
@ -1922,14 +2073,14 @@ class SingleMessageWindow:
self.completion_dict = {}
self.xml.signal_autoconnect(self)
if gajim.config.get('saveposition'):
# get window position and size from config
gtkgui_helpers.move_window(self.window,
gajim.config.get('single-msg-x-position'),
gajim.config.get('single-msg-y-position'))
gtkgui_helpers.resize_window(self.window,
gajim.config.get('single-msg-width'),
gajim.config.get('single-msg-height'))
# get window position and size from config
gtkgui_helpers.resize_window(self.window,
gajim.config.get('single-msg-width'),
gajim.config.get('single-msg-height'))
gtkgui_helpers.move_window(self.window,
gajim.config.get('single-msg-x-position'),
gajim.config.get('single-msg-y-position'))
self.window.show_all()
def on_single_message_window_destroy(self, widget):
@ -1940,15 +2091,14 @@ class SingleMessageWindow:
self.message_tv_buffer.place_cursor(end_iter)
def save_pos(self):
if gajim.config.get('saveposition'):
# save the window size and position
x, y = self.window.get_position()
gajim.config.set('single-msg-x-position', x)
gajim.config.set('single-msg-y-position', y)
width, height = self.window.get_size()
gajim.config.set('single-msg-width', width)
gajim.config.set('single-msg-height', height)
gajim.interface.save_config()
# save the window size and position
x, y = self.window.get_position()
gajim.config.set('single-msg-x-position', x)
gajim.config.set('single-msg-y-position', y)
width, height = self.window.get_size()
gajim.config.set('single-msg-width', width)
gajim.config.set('single-msg-height', height)
gajim.interface.save_config()
def on_single_message_window_delete_event(self, window, ev):
self.save_pos()
@ -1999,7 +2149,7 @@ class SingleMessageWindow:
if self.message:
self.conversation_textview.print_real_text(self.message)
fjid = self.from_whom
fjid = self.from_whom
if self.resource:
fjid += '/' + self.resource # Full jid of sender (with resource)
self.from_entry.set_text(fjid)
@ -2009,16 +2159,16 @@ class SingleMessageWindow:
self.cancel_button.hide()
self.close_button.show()
elif action == 'form': # prepare UI for Receiving
title = _('Form %s') % title
self.send_button.show()
self.send_and_close_button.show()
self.to_label.show()
self.to_entry.show()
self.reply_button.hide()
self.from_label.hide()
self.from_entry.hide()
self.conversation_scrolledwindow.hide()
self.message_scrolledwindow.hide()
title = _('Form %s') % title
self.send_button.show()
self.send_and_close_button.show()
self.to_label.show()
self.to_entry.show()
self.reply_button.hide()
self.from_label.hide()
self.from_entry.hide()
self.conversation_scrolledwindow.hide()
self.message_scrolledwindow.hide()
self.window.set_title(title)
@ -2266,7 +2416,7 @@ class PrivacyListWindow:
# set jabber id completion
jids_list_store = gtk.ListStore(gobject.TYPE_STRING)
for jid in gajim.contacts.get_jid_list(self.account):
jids_list_store.append([jid])
jids_list_store.append([jid])
jid_entry_completion = gtk.EntryCompletion()
jid_entry_completion.set_text_column(0)
jid_entry_completion.set_model(jids_list_store)
@ -2280,7 +2430,7 @@ class PrivacyListWindow:
self.list_of_groups[group] = count
count += 1
self.edit_type_group_combobox.append_text(group)
self.edit_type_group_combobox.set_active(0)
self.edit_type_group_combobox.set_active(0)
self.window.set_title(title)
@ -2302,7 +2452,7 @@ class PrivacyListWindow:
if a_d_dict['default'] == self.privacy_list_name:
self.privacy_list_default_checkbutton.set_active(True)
else:
self.privacy_list_default_checkbutton.set_active(False)
self.privacy_list_default_checkbutton.set_active(False)
def privacy_list_received(self, rules):
self.list_of_rules_combobox.get_model().clear()
@ -2437,7 +2587,7 @@ class PrivacyListWindow:
self.edit_queries_send_checkbutton.set_active(False)
self.edit_view_status_checkbutton.set_active(False)
self.edit_send_status_checkbutton.set_active(False)
self.edit_order_spinbutton.set_value(1)
self.edit_order_spinbutton.set_value(1)
self.edit_type_group_combobox.set_active(0)
self.edit_type_subscription_combobox.set_active(0)
self.add_edit_rule_label.set_label(
@ -2519,7 +2669,7 @@ class PrivacyListsWindow:
or edit an already there one'''
def __init__(self, account):
self.account = account
self.privacy_lists_save = []
self.privacy_lists_save = []
self.xml = gtkgui_helpers.get_glade('privacy_lists_window.glade')
@ -2529,7 +2679,7 @@ class PrivacyListsWindow:
'new_privacy_list_button', 'new_privacy_list_entry',
'privacy_lists_refresh_button', 'close_privacy_lists_window_button']:
self.__dict__[widget_to_add] = self.xml.get_widget(
widget_to_add)
widget_to_add)
self.draw_privacy_lists_in_combobox([])
self.privacy_lists_refresh()
@ -2967,8 +3117,8 @@ class AdvancedNotificationsWindow:
self.window.show_all()
def initiate_rule_state(self):
'''Set values for all widgets'''
def initiate_rule_state(self):
'''Set values for all widgets'''
if self.active_num < 0:
return
# event
@ -3209,7 +3359,7 @@ class AdvancedNotificationsWindow:
for st in ['online', 'away', 'xa', 'dnd', 'invisible']:
self.__dict__[st + '_cb'].hide()
self.special_status_rb.show()
self.special_status_rb.show()
else:
self.set_status_config()
# 'special status' clicked
@ -3417,6 +3567,15 @@ class TransformChatToMUC:
self.guests_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
# All contacts beside the following can be invited:
# transports, zeroconf contacts, minimized groupchats
invitable = lambda contact, contact_transport = None:\
contact.jid not in self.auto_jids and\
contact.jid != gajim.get_jid_from_account(self.account) and\
contact.jid not in gajim.interface.minimized_controls[account] and\
not contact.is_transport() and\
not contact_transport
# set jabber id and pseudos
for account in gajim.contacts.get_accounts():
if gajim.connections[account].is_zeroconf:
@ -3425,24 +3584,20 @@ class TransformChatToMUC:
contact = \
gajim.contacts.get_contact_with_highest_priority(account, jid)
contact_transport = gajim.get_transport_name_from_jid(jid)
# do not add transports, zeroconf contacs, minimized groupchats
# and selfjid to list of invitable jids
if contact.jid not in self.auto_jids and contact.jid != \
gajim.get_jid_from_account(self.account) and not contact_transport \
and not contact.is_transport() and contact.jid not in \
gajim.interface.minimized_controls[account]:
if contact.show not in ('offline', 'error'):
img = gajim.interface.roster.jabber_state_images['16'][
contact.show]
name = contact.name
if name == '':
name = jid.split('@')[0]
iter = self.store.append([img.get_pixbuf(), name, jid])
# preselect treeview rows
if self.preselected_jids and jid in self.preselected_jids:
path = self.store.get_path(iter)
self.guests_treeview.get_selection().\
select_path(path)
# Add contact if it can be invited
if invitable(contact, contact_transport) and \
contact.show not in ('offline', 'error'):
img = gajim.interface.roster.jabber_state_images['16'][
contact.show]
name = contact.name
if name == '':
name = jid.split('@')[0]
iter = self.store.append([img.get_pixbuf(), name, jid])
# preselect treeview rows
if self.preselected_jids and jid in self.preselected_jids:
path = self.store.get_path(iter)
self.guests_treeview.get_selection().\
select_path(path)
# show all
self.window.show_all()
@ -3460,7 +3615,6 @@ class TransformChatToMUC:
server = self.server_list_comboboxentry.get_active_text()
if server == '':
return
room_id = gajim.nicks[self.account] + str(randrange(9999999))
gajim.connections[self.account].check_unique_room_id_support(server, self)
def unique_room_id_supported(self, server, room_id):
@ -3473,7 +3627,7 @@ class TransformChatToMUC:
guest_list.append(guest)
room_jid = room_id + '@' + server
gajim.automatic_rooms[self.account][room_jid] = {}
gajim.automatic_rooms[self.account][room_jid]['invities'] = guest_list
gajim.automatic_rooms[self.account][room_jid]['invities'] = guest_list
gajim.automatic_rooms[self.account][room_jid]['continue_tag'] = True
gajim.interface.roster.join_gc_room(self.account, room_jid,
gajim.nicks[self.account], None, is_continued=True)

View file

@ -46,7 +46,6 @@ import inspect
import weakref
import gobject
import gtk
import gobject
import pango
import dialogs

View file

@ -21,7 +21,6 @@
import os
import sys
import gtk
import gobject
import gtkgui_helpers
import dialogs
@ -53,7 +52,7 @@ class FeaturesWindow:
_('Requires python-avahi.'),
_('Requires pybonjour (http://o2s.csail.mit.edu/o2s-wiki/pybonjour).')),
_('gajim-remote'): (self.dbus_available,
_('A script to controle gajim via commandline.'),
_('A script to controle Gajim via commandline.'),
_('Requires python-dbus.'),
_('Feature not available under Windows.')),
_('OpenGPG'): (self.gpg_available,
@ -86,14 +85,14 @@ class FeaturesWindow:
_('Feature not available under Windows.')),
_('Trayicon'): (self.trayicon_available,
_('A icon in systemtray reflecting the current presence.'),
_('Requires python-gnome2-extras or compiled trayicon module from Gajim sources.'),
_('Requires python-gnome2-extras or compiled trayicon module from Gajim sources.'),
_('Requires PyGTK >= 2.10.')),
_('Idle'): (self.idle_available,
_('Ability to measure idle time, in order to set auto status.'),
_('Requires compilation of the idle module from Gajim sources.'),
_('Requires compilation of the idle module from Gajim sources.')),
_('LaTeX'): (self.latex_available,
_('Transform LaTeX espressions between $$ $$.'),
_('Transform LaTeX expressions between $$ $$.'),
_('Requires texlive-latex-base, dvips and imagemagick. You have to set \'use_latex\' to True in the Advanced Configuration Editor.'),
_('Feature not available under Windows.')),
_('End to end encryption'): (self.pycrypto_available,
@ -141,6 +140,8 @@ class FeaturesWindow:
def on_features_treeview_cursor_changed(self, widget):
selection = widget.get_selection()
if not selection:
return
path = selection.get_selected_rows()[1][0]
available = self.model[path][1]
feature = self.model[path][0].decode('utf-8')
@ -178,8 +179,8 @@ class FeaturesWindow:
def gpg_available(self):
if os.name == 'nt':
return False
from common import GnuPG
return GnuPG.USE_GPG
from common import gajim
return gajim.HAVE_GPG
def network_manager_available(self):
if os.name == 'nt':

View file

@ -44,7 +44,6 @@ if os.name == 'nt':
# os.environ['GTK_BASEPATH'] = 'gtk'
import sys
import urllib
import logging
consoleloghandler = logging.StreamHandler()
@ -222,7 +221,6 @@ import gobject
import re
import signal
import getopt
import time
import math
@ -457,10 +455,13 @@ class Interface:
def handle_event_http_auth(self, account, data):
#('HTTP_AUTH', account, (method, url, transaction_id, iq_obj, msg))
def response(widget, account, iq_obj, answer):
def response(account, iq_obj, answer):
self.dialog.destroy()
gajim.connections[account].build_http_auth_answer(iq_obj, answer)
def on_yes(is_checked, account, iq_obj):
response(account, iq_obj, 'yes')
sec_msg = _('Do you accept this request?')
if gajim.get_number_of_connected_accounts() > 1:
sec_msg = _('Do you accept this request on account %s?') % account
@ -468,8 +469,8 @@ class Interface:
sec_msg = data[4] + '\n' + sec_msg
self.dialog = dialogs.YesNoDialog(_('HTTP (%s) Authorization for %s (id: %s)') \
% (data[0], data[1], data[2]), sec_msg,
on_response_yes = (response, account, data[3], 'yes'),
on_response_no = (response, account, data[3], 'no'))
on_response_yes=(on_yes, account, data[3]),
on_response_no=(response, account, data[3], 'no'))
def handle_event_error_answer(self, account, array):
#('ERROR_ANSWER', account, (id, jid_from, errmsg, errcode))
@ -581,10 +582,11 @@ class Interface:
jid = array[0].split('/')[0]
keyID = array[5]
contact_nickname = array[7]
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
# Get the proper keyID
keyID = helpers.prepare_and_validate_gpg_keyID(account,
jid, keyID)
resource = array[3]
if not resource:
resource = ''
@ -723,7 +725,7 @@ class Interface:
# remove in 2007
# It's maybe a GC_NOTIFY (specialy for MSN gc)
self.handle_event_gc_notify(account, (jid, array[1], status_message,
array[3], None, None, None, None, None, None, None, None))
array[3], None, None, None, None, None, [], None, None))
def handle_event_msg(self, account, array):
@ -848,8 +850,14 @@ class Interface:
msg = message
if subject:
msg = _('Subject: %s') % subject + '\n' + msg
focused = False
if chat_control:
parent_win = chat_control.parent_win
if chat_control == parent_win.get_active_control() and \
parent_win.window.has_focus:
focused = True
notify.notify('new_message', jid_of_control, account, [msg_type,
first, nickname, msg], advanced_notif_num)
first, nickname, msg, focused], advanced_notif_num)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('NewMessage', (account, array))
@ -949,13 +957,22 @@ class Interface:
self.remote_ctrl.raise_signal('Subscribed', (account, array))
def handle_event_unsubscribed(self, account, jid):
dialogs.InformationDialog(_('Contact "%s" removed subscription from you')\
% jid, _('You will always see him or her as offline.'))
# FIXME: Per RFC 3921, we can "deny" ack as well, but the GUI does not show deny
gajim.connections[account].ack_unsubscribed(jid)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('Unsubscribed', (account, jid))
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
if not contact:
return
def on_yes(is_checked, list_):
self.roster.on_req_usub(None, list_)
list_ = [(contact, account)]
dialogs.YesNoDialog(
_('Contact "%s" removed subscription from you') % jid,
_('You will always see him or her as offline.\nDo you want to remove him or her from your contact list?'),
on_response_yes=(on_yes, list_))
# FIXME: Per RFC 3921, we can "deny" ack as well, but the GUI does not show deny
def handle_event_agent_info_error(self, account, agent):
#('AGENT_ERROR_INFO', account, (agent))
try:
@ -999,6 +1016,11 @@ class Interface:
def handle_event_agent_info_items(self, account, array):
#('AGENT_INFO_ITEMS', account, (agent, node, items))
our_jid = gajim.get_jid_from_account(account)
if gajim.interface.instances[account].has_key('pep_services') and \
array[0] == our_jid:
gajim.interface.instances[account]['pep_services'].items_received(
array[2])
try:
gajim.connections[account].services_cache.agent_items(array[0],
array[1], array[2])
@ -1014,11 +1036,11 @@ class Interface:
return
def handle_event_new_acc_connected(self, account, array):
#('NEW_ACC_CONNECTED', account, (infos, is_form, ssl_msg, ssl_cert,
# ssl_fingerprint))
#('NEW_ACC_CONNECTED', account, (infos, is_form, ssl_msg, ssl_err,
# ssl_cert, ssl_fingerprint))
if self.instances.has_key('account_creation_wizard'):
self.instances['account_creation_wizard'].new_acc_connected(array[0],
array[1], array[2], array[3], array[4])
array[1], array[2], array[3], array[4], array[5])
def handle_event_new_acc_not_connected(self, account, array):
#('NEW_ACC_NOT_CONNECTED', account, (reason))
@ -1407,14 +1429,19 @@ class Interface:
gajim.connections[account].gpg_passphrase(self.gpg_passphrase[keyid])
callback()
return
if self.gpg_dialog:
# A GPG dialog is already open, retry in 0.5 second
gobject.timeout_add(500, self.handle_event_gpg_password_required,
account, array)
return
password_ok = False
count = 0
title = _('Passphrase Required')
second = _('Enter GPG key passphrase for account %s.') % account
while not password_ok and count < 3:
count += 1
w = dialogs.PassphraseDialog(title, second, '')
passphrase, save = w.run()
self.gpg_dialog = dialogs.PassphraseDialog(title, second, '')
passphrase, save = self.gpg_dialog.run()
if passphrase == -1:
# User pressed cancel
passphrase = None
@ -1424,6 +1451,7 @@ class Interface:
test_gpg_passphrase(passphrase)
title = _('Wrong Passphrase')
second = _('Please retype your GPG passphrase or press Cancel.')
self.gpg_dialog = None
if passphrase != None:
self.gpg_passphrase[keyid] = passphrase
gobject.timeout_add(30000, self.forget_gpg_passphrase, keyid)
@ -1857,6 +1885,7 @@ class Interface:
# block signed in notifications for 30 seconds
gajim.block_signed_in_notifications[account] = True
self.roster.set_actions_menu_needs_rebuild()
self.roster.draw_account(account)
if self.sleeper.getState() != common.sleepy.STATE_UNKNOWN and \
gajim.connections[account].connected in (2, 3):
# we go online or free for chat, so we activate auto status
@ -1922,12 +1951,12 @@ class Interface:
negotiated, not_acceptable, ask_user = session.verify_options_bob(form)
if ask_user:
def accept_nondefault_options(widget):
def accept_nondefault_options(is_checked):
self.dialog.destroy()
negotiated.update(ask_user)
session.respond_e2e_bob(form, negotiated, not_acceptable)
def reject_nondefault_options(widget):
def reject_nondefault_options():
self.dialog.destroy()
for key in ask_user.keys():
not_acceptable.append(key)
@ -1939,8 +1968,8 @@ class Interface:
%s
Are these options acceptable?''') % (negotiation.describe_features(ask_user)),
on_response_yes = accept_nondefault_options,
on_response_no = reject_nondefault_options)
on_response_yes=accept_nondefault_options,
on_response_no=reject_nondefault_options)
else:
session.respond_e2e_bob(form, negotiated, not_acceptable)
@ -1963,7 +1992,7 @@ class Interface:
session.check_identity = _cb
if ask_user:
def accept_nondefault_options(widget):
def accept_nondefault_options(is_checked):
dialog.destroy()
negotiated.update(ask_user)
@ -1973,7 +2002,7 @@ class Interface:
except exceptions.NegotiationError, details:
session.fail_bad_negotiation(details)
def reject_nondefault_options(widget):
def reject_nondefault_options():
session.reject_negotiation()
dialog.destroy()
@ -2169,6 +2198,11 @@ class Interface:
_('You are already connected to this account with the same resource. Please type a new one'), input_str = gajim.connections[account].server_resource,
is_modal = False, ok_handler = on_ok)
def handle_event_pep_access_model(self, account, data):
# ('PEP_ACCESS_MODEL', account, (node, model))
if self.instances[account].has_key('pep_services'):
self.instances[account]['pep_services'].new_service(data[0], data[1])
def handle_event_unique_room_id_supported(self, account, data):
'''Receive confirmation that unique_room_id are supported'''
# ('UNIQUE_ROOM_ID_SUPPORTED', server, instance, room_id)
@ -2181,44 +2215,76 @@ class Interface:
instance.unique_room_id_error(data[0])
def handle_event_ssl_error(self, account, data):
# ('SSL_ERROR', account, (text, cert, sha1_fingerprint))
# ('SSL_ERROR', account, (text, errnum, cert, sha1_fingerprint))
server = gajim.config.get_per('accounts', account, 'hostname')
def on_ok(is_checked):
def on_ok(is_checked=False):
if is_checked:
f = open(gajim.MY_CACERTS, 'a')
f.write(server + '\n')
f.write(data[1] + '\n\n')
f.close()
# Check if cert is already in file
certs = ''
if os.path.isfile(gajim.MY_CACERTS):
f = open(gajim.MY_CACERTS)
certs = f.read()
f.close()
if data[2] in certs:
dialogs.ErrorDialog(_('Certificate Already in File'),
_('This certificate is already in file %s, so it\'s not added again.') % gajim.MY_CACERTS)
else:
f = open(gajim.MY_CACERTS, 'a')
f.write(server + '\n')
f.write(data[2] + '\n\n')
f.close()
gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1',
data[2])
data[3])
gajim.connections[account].ssl_certificate_accepted()
def on_cancel():
gajim.connections[account].disconnect(on_purpose=True)
self.handle_event_status(account, 'offline')
pritext = _('Error verifying SSL certificate')
sectext = _('There was an error verifying the SSL certificate of your jabber server: %(error)s\nDo you still want to connect to this server?') % {'error': data[0]}
checktext = _('Add this certificate to the list of trusted certificates.\nSHA1 fingerprint of the certificate:\n%s') % data[2]
dialogs.ConfirmationDialogCheck(pritext, sectext, checktext,
on_response_ok=on_ok, on_response_cancel=on_cancel)
if data[1] in (18, 27):
checktext = _('Add this certificate to the list of trusted certificates.\nSHA1 fingerprint of the certificate:\n%s') % data[3]
dialogs.ConfirmationDialogCheck(pritext, sectext, checktext,
on_response_ok=on_ok, on_response_cancel=on_cancel)
else:
dialogs.ConfirmationDialog(pritext, sectext,
on_response_ok=on_ok, on_response_cancel=on_cancel)
def handle_event_fingerprint_error(self, account, data):
# ('FINGERPRINT_ERROR', account, (fingerprint,))
def on_yes(widget):
dialog.destroy()
# ('FINGERPRINT_ERROR', account, (new_fingerprint,))
def on_yes(is_checked):
gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1',
data[0])
gajim.connections[account].ssl_certificate_accepted()
def on_no(widget):
dialog.destroy()
def on_no():
gajim.connections[account].disconnect(on_purpose=True)
self.handle_event_status(account, 'offline')
pritext = _('SSL certificate error')
sectext = _('It seems SSL certificate has changed or your connection is '
'being hacked. Do you still want to connect and update the fingerprint'
'of the certificate?')
sectext = _('It seems the SSL certificate has changed or your connection '
'is being hacked.\nOld fingerprint: %s\nNew fingerprint: %s\n\nDo you '
'still want to connect and update the fingerprint of the certificate?'\
) % (gajim.config.get_per('accounts', account, 'ssl_fingerprint_sha1'),
data[0])
dialog = dialogs.YesNoDialog(pritext, sectext, on_response_yes=on_yes,
on_response_no=on_no)
def handle_event_plain_connection(self, account, data):
# ('PLAIN_CONNECTION', account, (connection))
server = gajim.config.get_per('accounts', account, 'hostname')
def on_yes(is_checked):
if is_checked:
gajim.config.set_per('accounts', account,
'warn_when_insecure_connection', False)
gajim.connections[account].connection_accepted(data[0], 'tcp')
def on_no():
gajim.connections[account].disconnect(on_purpose=True)
self.handle_event_status(account, 'offline')
pritext = _('Insecure connection')
sectext = _('You are about to send your password on an insecure '
'conection. Are you sure you want to do that?')
checktext = _('Do _not ask me again')
dialog = dialogs.YesNoDialog(pritext, sectext, checktext,
on_response_yes=on_yes, on_response_no=on_no)
def read_sleepy(self):
'''Check idle status and change that status if needed'''
if not self.sleeper.poll():
@ -2328,7 +2394,7 @@ class Interface:
#FIXME: recognize xmpp: and treat it specially
links = r'\b(%s)\S*[\w\/\=]|' % prefixes
links = r"(www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'\"]+[^!,\.\s<>\)'\"\]]"
#2nd one: at_least_one_char@at_least_one_char.at_least_one_char
mail = r'\bmailto:\S*[^\s\W]|' r'\b\S+@\S+\.\S*[^\s\W]'
@ -2340,7 +2406,7 @@ class Interface:
latex = r'|\$\$[^$\\]*?([\]\[0-9A-Za-z()|+*/-]|[\\][\]\[0-9A-Za-z()|{}$])(.*?[^\\])?\$\$'
basic_pattern = links + mail
basic_pattern = links + '|' + mail
if gajim.config.get('use_latex'):
basic_pattern += latex
@ -2551,6 +2617,7 @@ class Interface:
'SEARCH_FORM': self.handle_event_search_form,
'SEARCH_RESULT': self.handle_event_search_result,
'RESOURCE_CONFLICT': self.handle_event_resource_conflict,
'PEP_ACCESS_MODEL': self.handle_event_pep_access_model,
'UNIQUE_ROOM_ID_UNSUPPORTED': \
self.handle_event_unique_room_id_unsupported,
'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported,
@ -2558,6 +2625,7 @@ class Interface:
'GPG_PASSWORD_REQUIRED': self.handle_event_gpg_password_required,
'SSL_ERROR': self.handle_event_ssl_error,
'FINGERPRINT_ERROR': self.handle_event_fingerprint_error,
'PLAIN_CONNECTION': self.handle_event_plain_connection,
}
gajim.handlers = self.handlers
@ -2674,6 +2742,7 @@ class Interface:
self.status_sent_to_users = {}
self.status_sent_to_groups = {}
self.gpg_passphrase = {}
self.gpg_dialog = None
self.default_colors = {
'inmsgcolor': gajim.config.get('inmsgcolor'),
'outmsgcolor': gajim.config.get('outmsgcolor'),
@ -2903,7 +2972,7 @@ if __name__ == '__main__':
print >> sys.stderr, _('Session Management support not available (missing gnome.ui module)')
else:
def die_cb(cli):
gtk.main_quit()
gajim.interface.roster.quit_gtkgui_interface()
gnome.program_init('gajim', gajim.version)
cli = gnome.ui.master_client()
cli.connect('die', die_cb)
@ -2928,4 +2997,7 @@ if __name__ == '__main__':
osx.init()
Interface()
gtk.main()
try:
gtk.main()
except KeyboardInterrupt:
print >> sys.stderr, 'KeyboardInterrupt'

View file

@ -72,12 +72,19 @@ def tree_cell_data_func(column, renderer, model, iter, tv=None):
# reference to GroupchatControl instance (self)
theme = gajim.config.get('roster_theme')
# allocate space for avatar only if needed
parent_iter = model.iter_parent(iter)
if isinstance(renderer, gtk.CellRendererPixbuf):
if model[iter][C_AVATAR]:
avatar_position = gajim.config.get('avatar_position_in_roster')
if avatar_position == 'right':
renderer.set_property('xalign', 1) # align pixbuf to the right
else:
renderer.set_property('xalign', 0.5)
if parent_iter and (model[iter][C_AVATAR] or avatar_position == 'left'):
renderer.set_property('visible', True)
renderer.set_property('width', gajim.config.get('roster_avatar_width'))
else:
renderer.set_property('visible', False)
if model.iter_parent(iter):
if parent_iter:
bgcolor = gajim.config.get_per('themes', theme, 'contactbgcolor')
if bgcolor:
renderer.set_property('cell-background', bgcolor)
@ -228,6 +235,12 @@ class GroupchatControl(ChatControlBase):
self.tooltip = tooltips.GCTooltip()
# nickname coloring
self.gc_count_nicknames_colors = 0
self.gc_custom_colors = {}
self.number_of_colors = len(gajim.config.get('gc_nicknames_colors').\
split(':'))
# connect the menuitems to their respective functions
xm = gtkgui_helpers.get_glade('gc_control_popup_menu.glade')
@ -301,6 +314,16 @@ class GroupchatControl(ChatControlBase):
# first one img, second one text, third is sec pixbuf
column = gtk.TreeViewColumn()
def add_avatar_renderer():
renderer_pixbuf = gtk.CellRendererPixbuf() # avatar image
column.pack_start(renderer_pixbuf, expand = False)
column.add_attribute(renderer_pixbuf, 'pixbuf', C_AVATAR)
column.set_cell_data_func(renderer_pixbuf, tree_cell_data_func,
self.list_treeview)
if gajim.config.get('avatar_position_in_roster') == 'left':
add_avatar_renderer()
renderer_image = cell_renderer_image.CellRendererImage(0, 0) # status img
renderer_image.set_property('width', 26)
column.pack_start(renderer_image, expand = False)
@ -315,12 +338,8 @@ class GroupchatControl(ChatControlBase):
column.set_cell_data_func(renderer_text, tree_cell_data_func,
self.list_treeview)
renderer_pixbuf = gtk.CellRendererPixbuf() # avatar image
column.pack_start(renderer_pixbuf, expand = False)
column.add_attribute(renderer_pixbuf, 'pixbuf', C_AVATAR)
column.set_cell_data_func(renderer_pixbuf, tree_cell_data_func,
self.list_treeview)
renderer_pixbuf.set_property('xalign', 1) # align pixbuf to the right
if gajim.config.get('avatar_position_in_roster') == 'right':
add_avatar_renderer()
self.list_treeview.append_column(column)
@ -639,9 +658,6 @@ class GroupchatControl(ChatControlBase):
fin = True
return None
gc_count_nicknames_colors = 0
gc_custom_colors = {}
def print_old_conversation(self, text, contact = '', tim = None,
xhtml = None):
if isinstance(text, str):
@ -692,9 +708,7 @@ class GroupchatControl(ChatControlBase):
str(self.gc_custom_colors[contact]))
else:
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:
if self.gc_count_nicknames_colors == self.number_of_colors:
self.gc_count_nicknames_colors = 0
self.gc_custom_colors[contact] = \
self.gc_count_nicknames_colors
@ -813,6 +827,13 @@ class GroupchatControl(ChatControlBase):
return False
else: # Special word == word, no char after in word
return True
for special_word in special_words:
if special_word.find(' ') > -1:
# There is a space in this special word, do a global search
# without splitting by words as previously
# We don't search this in all cases so we don't loose time
if text.find(special_word) > -1:
return True
return False
def set_subject(self, subject):
@ -1979,7 +2000,8 @@ class GroupchatControl(ChatControlBase):
self.handlers[id] = item
item = xml.get_widget('add_to_roster_menuitem')
if not jid:
our_jid = gajim.get_jid_from_account(self.account)
if not jid or jid == our_jid:
item.set_sensitive(False)
else:
id = item.connect('activate', self.on_add_to_roster, jid)
@ -2079,12 +2101,7 @@ class GroupchatControl(ChatControlBase):
if not nick in gajim.contacts.get_nick_list(self.account,
self.room_jid):
# it's a group
col = widget.get_column(0)
avatar_cell = col.get_cell_renderers()[0]
(pos, avatar_size) = col.cell_get_position(avatar_cell)
status_cell = col.get_cell_renderers()[1]
(pos, status_size) = col.cell_get_position(status_cell)
if x > avatar_size and x < avatar_size + status_size:
if x < 27:
if (widget.row_expanded(path)):
widget.collapse_row(path)
else:
@ -2142,6 +2159,9 @@ class GroupchatControl(ChatControlBase):
self.tooltip.hide_tooltip()
def show_tooltip(self, contact):
if not self.list_treeview.window:
# control has been destroyed since tooltip was requested
return
pointer = self.list_treeview.get_pointer()
props = self.list_treeview.get_path_at_pos(pointer[0], pointer[1])
# check if the current pointer is at the same path

View file

@ -247,6 +247,11 @@ def move_window(window, x, y):
x = 0
if y < 0:
y = 0
w, h = window.get_size()
if x + w > screen_w:
x = screen_w - w
if y + h > screen_h:
y = screen_h - h
window.move(x, y)
def resize_window(window, w, h):

View file

@ -21,25 +21,25 @@
## NOTE: some method names may match those of logger.py but that's it
## someday (TM) should have common class that abstracts db connections and helpers on it
## the same can be said for history_window.py
import os
if os.name == 'nt':
import warnings
warnings.filterwarnings(action='ignore')
import os
if os.name == 'nt':
import warnings
warnings.filterwarnings(action='ignore')
# Used to create windows installer with GTK included
# paths = os.environ['PATH']
# list_ = paths.split(';')
# new_list = []
# for p in list_:
# if p.find('gtk') < 0 and p.find('GTK') < 0:
# new_list.append(p)
# new_list.insert(0, 'gtk/lib')
# new_list.insert(0, 'gtk/bin')
# os.environ['PATH'] = ';'.join(new_list)
# paths = os.environ['PATH']
# list_ = paths.split(';')
# new_list = []
# for p in list_:
# if p.find('gtk') < 0 and p.find('GTK') < 0:
# new_list.append(p)
# new_list.insert(0, 'gtk/lib')
# new_list.insert(0, 'gtk/bin')
# os.environ['PATH'] = ';'.join(new_list)
# os.environ['GTK_BASEPATH'] = 'gtk'
import sys
import signal
import gtk
@ -100,7 +100,7 @@ class HistoryManager:
self.logs_scrolledwindow = xml.get_widget('logs_scrolledwindow')
self.search_results_scrolledwindow = xml.get_widget(
'search_results_scrolledwindow')
self.welcome_label = xml.get_widget('welcome_label')
self.welcome_vbox = xml.get_widget('welcome_vbox')
self.jids_already_in = [] # holds jids that we already have in DB
self.AT_LEAST_ONE_DELETION_DONE = False
@ -236,7 +236,7 @@ class HistoryManager:
self.logs_liststore.clear() # clear the store
self.welcome_label.hide()
self.welcome_vbox.hide()
self.search_results_scrolledwindow.hide()
self.logs_scrolledwindow.show()
@ -579,7 +579,7 @@ class HistoryManager:
if text == '':
return
self.welcome_label.hide()
self.welcome_vbox.hide()
self.logs_scrolledwindow.hide()
self.search_results_scrolledwindow.show()

View file

@ -25,7 +25,7 @@ __version__ = '$Revision: 64 $'
from urllib import urlopen
from xml.dom import minidom
from time import time, strftime
from time import time
class LastFM:
# Where to fetch the played song information

View file

@ -1,6 +1,6 @@
## message_control.py
##
## Copyright (C) 2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2006-2007 Travis Shirk <travis@pobox.com>
## Copyright (C) 2007 Stephan Erb <steve-e@h3c.de>
##
## This file is part of Gajim.
@ -34,7 +34,7 @@ class MessageControl:
def __init__(self, type_id, parent_win, widget_name, contact, account, resource = None):
# dict { cb id : widget}
# keep all registered callbacks of widgets, created by self.xml
self.handlers = {}
self.handlers = {}
self.type_id = type_id
self.parent_win = parent_win
self.widget_name = widget_name
@ -86,7 +86,7 @@ class MessageControl:
pass # NOTE: Derived classes SHOULD implement this
def get_tab_label(self, chatstate):
'''Return a suitable the tab label string. Returns a tuple such as:
'''Return a suitable tab label string. Returns a tuple such as:
(label_str, color) either of which can be None
if chatstate is given that means we have HE SENT US a chatstate and
we want it displayed'''
@ -126,8 +126,8 @@ class MessageControl:
if self.session.enable_encryption:
was_encrypted = True
print "starting a new session, dropping the old one!"
gajim.connections[self.account].delete_session(self.session.jid, self.session.thread_id)
gajim.connections[self.account].delete_session(self.session.jid,
self.session.thread_id)
self.session = session

View file

@ -6,10 +6,9 @@
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <kourem@gmail.com>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2006 Travis Shirk <travis@pobox.com>
## Geobert Quach <geobert@gmail.com>
## Copyright (C) 2005-2007 Travis Shirk <travis@pobox.com>
## Copyright (C) 2006 Geobert Quach <geobert@gmail.com>
## Copyright (C) 2007 Stephan Erb <steve-e@h3c.de>
##
## This file is part of Gajim.
@ -39,7 +38,7 @@ from common import gajim
####################
class MessageWindow:
class MessageWindow(object):
'''Class for windows which contain message like things; chats,
groupchats, etc.'''
@ -53,8 +52,8 @@ class MessageWindow:
CLOSE_COMMAND,
CLOSE_CTRL_KEY
) = range(5)
def __init__(self, acct, type):
def __init__(self, acct, type, parent_window=None, parent_paned=None):
# A dictionary of dictionaries where _contacts[account][jid] == A MessageControl
self._controls = {}
# If None, the window is not tied to any specific account
@ -68,6 +67,18 @@ class MessageWindow:
self.widget_name = 'message_window'
self.xml = gtkgui_helpers.get_glade('%s.glade' % self.widget_name)
self.window = self.xml.get_widget(self.widget_name)
self.notebook = self.xml.get_widget('notebook')
self.parent_paned = None
if parent_window:
orig_window = self.window
self.window = parent_window
self.parent_paned = parent_paned
self.notebook.reparent(self.parent_paned)
self.parent_paned.pack2(self.notebook, resize=True, shrink=True)
orig_window.destroy()
del orig_window
id = self.window.connect('delete-event', self._on_window_delete)
self.handlers[id] = self.window
id = self.window.connect('destroy', self._on_window_destroy)
@ -91,7 +102,6 @@ class MessageWindow:
self.window.add_events(gtk.gdk.POINTER_MOTION_MASK)
self.alignment = self.xml.get_widget('alignment')
self.notebook = self.xml.get_widget('notebook')
id = self.notebook.connect('switch-page',
self._on_notebook_switch_page)
self.handlers[id] = self.notebook
@ -144,6 +154,9 @@ class MessageWindow:
n += len(dict)
return n
def resize(self, width, height):
gtkgui_helpers.resize_window(self.window, width, height)
def _on_window_focus(self, widget, event):
# window received focus, so if we had urgency REMOVE IT
# NOTE: we do not have to read the message (it maybe in a bg tab)
@ -179,6 +192,8 @@ class MessageWindow:
for ctrl in self.controls():
ctrl.shutdown()
self._controls.clear()
# Clean up handlers connected to the parent window, this is important since
# self.window may be the RosterWindow
for i in self.handlers.keys():
if self.handlers[i].handler_is_connected(i):
self.handlers[i].disconnect(i)
@ -210,8 +225,9 @@ class MessageWindow:
widget = xml.get_widget('tab_close_button')
id = widget.connect('clicked', self._on_close_button_clicked, control)
control.handlers[id] = widget
id = tab_label_box.connect('button-press-event', self.on_tab_eventbox_button_press_event, control.widget)
id = tab_label_box.connect('button-press-event', self.on_tab_eventbox_button_press_event,
control.widget)
control.handlers[id] = tab_label_box
self.notebook.append_page(control.widget, tab_label_box)
@ -222,7 +238,10 @@ class MessageWindow:
self.setup_tab_dnd(control.widget)
self.redraw_tab(control)
self.window.show_all()
if self.parent_paned:
self.notebook.show_all()
else:
self.window.show_all()
# NOTE: we do not call set_control_active(True) since we don't know whether
# the tab is the active one.
self.show_title()
@ -263,7 +282,7 @@ class MessageWindow:
if not control:
# No more control in this window
return
# CTRL mask
if modifier & gtk.gdk.CONTROL_MASK:
if keyval == gtk.keysyms.h:
@ -286,7 +305,7 @@ class MessageWindow:
# Tab switch bindings
if keyval == gtk.keysyms.Right: # ALT + RIGHT
new = self.notebook.get_current_page() + 1
if new >= self.notebook.get_n_pages():
if new >= self.notebook.get_n_pages():
new = 0
self.notebook.set_current_page(new)
elif keyval == gtk.keysyms.Left: # ALT + LEFT
@ -307,7 +326,7 @@ class MessageWindow:
'''When close button is pressed: close a tab'''
self.remove_tab(control, self.CLOSE_CLOSE_BUTTON)
def show_title(self, urgent = True, control = None):
def show_title(self, urgent=True, control=None):
'''redraw the window's title'''
if not control:
control = self.get_active_control()
@ -341,10 +360,7 @@ class MessageWindow:
name += '/' + control.resource
window_mode = gajim.interface.msg_win_mgr.mode
if self.get_num_controls() == 1:
label = name
elif window_mode == MessageWindowMgr.ONE_MSG_WINDOW_PERTYPE:
if window_mode == MessageWindowMgr.ONE_MSG_WINDOW_PERTYPE:
# Show the plural form since number of tabs > 1
if self.type == 'chat':
label = _('Chats')
@ -352,9 +368,16 @@ class MessageWindow:
label = _('Group Chats')
else:
label = _('Private Chats')
elif window_mode == MessageWindowMgr.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
label = None
elif self.get_num_controls() == 1:
label = name
else:
label = _('Messages')
title = _('%s - Gajim') % label
title = 'Gajim'
if label:
title = _('%s - %s') % (label, title)
if window_mode == MessageWindowMgr.ONE_MSG_WINDOW_PERACCT:
title = title + ": " + control.account
@ -370,7 +393,7 @@ class MessageWindow:
ctrl = self._controls[acct][jid]
ctrl_page = self.notebook.page_num(ctrl.widget)
self.notebook.set_current_page(ctrl_page)
def remove_tab(self, ctrl, method, reason = None, force = False):
'''reason is only for gc (offline status message)
if force is True, do not ask any confirmation'''
@ -413,7 +436,12 @@ class MessageWindow:
gajim.interface.msg_win_mgr._on_window_destroy(self.window)
# dnd clean up
self.notebook.drag_dest_unset()
self.window.destroy()
if self.parent_paned:
# Don't close parent window, just remove the child
child = self.parent_paned.get_child2()
self.parent_paned.remove(child)
else:
self.window.destroy()
return # don't show_title, we are dead
elif self.get_num_controls() == 1: # we are going from two tabs to one
show_tabs_if_one_tab = gajim.config.get('tabs_always_visible')
@ -542,7 +570,7 @@ class MessageWindow:
first_composing_ind = -1 # id of first composing ctrl to switch to
# if no others controls have awaiting events
# loop until finding an unread tab or having done a complete cycle
while True:
while True:
if forward == True: # look for the first unread tab on the right
ind = ind + 1
if ind >= self.notebook.get_n_pages():
@ -590,7 +618,7 @@ class MessageWindow:
if old_no >= 0:
old_ctrl = self._widget_to_control(notebook.get_nth_page(old_no))
old_ctrl.set_control_active(False)
new_ctrl = self._widget_to_control(notebook.get_nth_page(page_num))
new_ctrl.set_control_active(True)
self.show_title(control = new_ctrl)
@ -598,8 +626,8 @@ class MessageWindow:
def _on_notebook_key_press(self, widget, event):
control = self.get_active_control()
# Ctrl+PageUP / DOWN has to be handled by notebook
if event.state & gtk.gdk.CONTROL_MASK and event.keyval in (
gtk.keysyms.Page_Down, gtk.keysyms.Page_Up):
if (event.state & gtk.gdk.CONTROL_MASK and
event.keyval in (gtk.keysyms.Page_Down, gtk.keysyms.Page_Up)):
return False
if isinstance(control, ChatControlBase):
# we forwarded it to message textview
@ -609,7 +637,7 @@ class MessageWindow:
def setup_tab_dnd(self, child):
'''Set tab label as drag source and connect the drag_data_get signal'''
tab_label = self.notebook.get_tab_label(child)
tab_label.dnd_handler = tab_label.connect('drag_data_get',
tab_label.dnd_handler = tab_label.connect('drag_data_get',
self.on_tab_label_drag_data_get_cb)
self.handlers[tab_label.dnd_handler] = tab_label
tab_label.drag_source_set(gtk.gdk.BUTTON1_MASK, self.DND_TARGETS,
@ -630,11 +658,11 @@ class MessageWindow:
source_child = self.notebook.get_nth_page(source_page_num)
if dest_page_num != source_page_num:
self.notebook.reorder_child(source_child, dest_page_num)
def get_tab_at_xy(self, x, y):
'''Thanks to Gaim
Return the tab under xy and
if its nearer from left or right side of the tab
if its nearer from left or right side of the tab
'''
page_num = -1
to_right = False
@ -655,7 +683,7 @@ class MessageWindow:
if (y >= tab_alloc.y) and \
(y <= (tab_alloc.y + tab_alloc.height)):
page_num = i
if y > tab_alloc.y + (tab_alloc.height / 2.0):
to_right = True
break
@ -679,36 +707,53 @@ class MessageWindow:
tab_label.disconnect(tab_label.dnd_handler)
################################################################################
class MessageWindowMgr:
class MessageWindowMgr(gobject.GObject):
'''A manager and factory for MessageWindow objects'''
__gsignals__ = {
'window-delete': (gobject.SIGNAL_RUN_LAST, None, (object,)),
}
# These constants map to common.config.opt_one_window_types indices
(
ONE_MSG_WINDOW_NEVER,
ONE_MSG_WINDOW_ALWAYS,
ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER,
ONE_MSG_WINDOW_PERACCT,
ONE_MSG_WINDOW_PERTYPE
) = range(4)
# A key constant for the main window for all messages
ONE_MSG_WINDOW_PERTYPE,
) = range(5)
# A key constant for the main window in ONE_MSG_WINDOW_ALWAYS mode
MAIN_WIN = 'main'
# A key constant for the main window in ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER mode
ROSTER_MAIN_WIN = 'roster'
def __init__(self):
def __init__(self, parent_window, parent_paned):
''' A dictionary of windows; the key depends on the config:
ONE_MSG_WINDOW_NEVER: The key is the contact JID
ONE_MSG_WINDOW_ALWAYS: The key is MessageWindowMgr.MAIN_WIN
ONE_MSG_WINDOW_ALWAYS: The key is MessageWindowMgr.MAIN_WIN
ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER: The key is MessageWindowMgr.MAIN_WIN
ONE_MSG_WINDOW_PERACCT: The key is the account name
ONE_MSG_WINDOW_PERTYPE: The key is a message type constant'''
gobject.GObject.__init__(self)
self._windows = {}
# Map the mode to a int constant for frequent compares
mode = gajim.config.get('one_message_window')
self.mode = common.config.opt_one_window_types.index(mode)
self.parent_win = parent_window
self.parent_paned = parent_paned
def change_account_name(self, old_name, new_name):
for win in self.windows():
win.change_account_name(old_name, new_name)
def _new_window(self, acct, type):
win = MessageWindow(acct, type)
parent_win = None
parent_paned = None
if self.mode == self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
parent_win = self.parent_win
parent_paned = self.parent_paned
win = MessageWindow(acct, type, parent_win, parent_paned)
# we track the lifetime of this window
win.window.connect('delete-event', self._on_window_delete)
win.window.connect('destroy', self._on_window_destroy)
@ -729,7 +774,7 @@ class MessageWindowMgr:
def has_window(self, jid, acct):
return self.get_window(jid, acct) != None
def one_window_opened(self, contact, acct, type):
def one_window_opened(self, contact=None, acct=None, type=None):
try:
return self._windows[self._mode_to_key(contact, acct, type)] != None
except KeyError:
@ -737,12 +782,13 @@ class MessageWindowMgr:
def _resize_window(self, win, acct, type):
'''Resizes window according to config settings'''
if not gajim.config.get('saveposition'):
return
if self.mode == self.ONE_MSG_WINDOW_ALWAYS:
if self.mode in (self.ONE_MSG_WINDOW_ALWAYS,
self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER):
size = (gajim.config.get('msgwin-width'),
gajim.config.get('msgwin-height'))
if self.mode == self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
parent_size = win.window.get_size()
size = (parent_size[0] + size[0], size[1])
elif self.mode == self.ONE_MSG_WINDOW_PERACCT:
size = (gajim.config.get_per('accounts', acct, 'msgwin-width'),
gajim.config.get_per('accounts', acct, 'msgwin-height'))
@ -754,13 +800,12 @@ class MessageWindowMgr:
size = (gajim.config.get(opt_width), gajim.config.get(opt_height))
else:
return
win.resize(size[0], size[1])
gtkgui_helpers.resize_window(win.window, size[0], size[1])
def _position_window(self, win, acct, type):
'''Moves window according to config settings'''
if not gajim.config.get('saveposition') or\
self.mode == self.ONE_MSG_WINDOW_NEVER:
if (self.mode in [self.ONE_MSG_WINDOW_NEVER,
self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER]):
return
if self.mode == self.ONE_MSG_WINDOW_ALWAYS:
@ -782,21 +827,22 @@ class MessageWindowMgr:
key = acct + contact.jid
if resource:
key += '/' + resource
return key
elif self.mode == self.ONE_MSG_WINDOW_ALWAYS:
key = self.MAIN_WIN
return self.MAIN_WIN
elif self.mode == self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
return self.ROSTER_MAIN_WIN
elif self.mode == self.ONE_MSG_WINDOW_PERACCT:
key = acct
return acct
elif self.mode == self.ONE_MSG_WINDOW_PERTYPE:
key = type
return key
return type
def create_window(self, contact, acct, type, resource = None):
key = None
win_acct = None
win_type = None
win_role = 'messages'
win_role = None # X11 window role
key = self._mode_to_key(contact, acct, type, resource)
win_key = self._mode_to_key(contact, acct, type, resource)
if self.mode == self.ONE_MSG_WINDOW_PERACCT:
win_acct = acct
win_role = acct
@ -806,21 +852,24 @@ class MessageWindowMgr:
elif self.mode == self.ONE_MSG_WINDOW_NEVER:
win_type = type
win_role = contact.jid
elif self.mode == self.ONE_MSG_WINDOW_ALWAYS:
win_role = 'messages'
win = None
try:
win = self._windows[key]
win = self._windows[win_key]
except KeyError:
win = self._new_window(win_acct, win_type)
win.window.set_role(win_role)
if win_role:
win.window.set_role(win_role)
# Position and size window based on saved state and window mode
if not self.one_window_opened(contact, acct, type):
self._position_window(win, acct, type)
self._resize_window(win, acct, type)
self._position_window(win, acct, type)
self._windows[key] = win
self._windows[win_key] = win
return win
def change_key(self, old_jid, new_jid, acct):
@ -842,6 +891,7 @@ class MessageWindowMgr:
def _on_window_destroy(self, win):
for k in self._windows.keys():
if self._windows[k].window == win:
self.emit('window-delete', self._windows[k])
del self._windows[k]
return
@ -870,17 +920,16 @@ class MessageWindowMgr:
for c in w.controls():
yield c
def shutdown(self):
def shutdown(self, width_adjust=0):
for w in self.windows():
self.save_state(w)
w.window.hide()
w.window.destroy()
self.save_state(w, width_adjust)
if not w.parent_paned:
w.window.hide()
w.window.destroy()
gajim.interface.save_config()
def save_state(self, msg_win):
if not gajim.config.get('saveposition'):
return
def save_state(self, msg_win, width_adjust=0):
# Save window size and position
pos_x_key = 'msgwin-x-position'
pos_y_key = 'msgwin-y-position'
@ -907,6 +956,9 @@ class MessageWindowMgr:
type = msg_win.type
size_width_key = type + '-msgwin-width'
size_height_key = type + '-msgwin-height'
elif self.mode == self.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER:
# Ignore any hpaned width
width = msg_win.notebook.allocation.width
if acct:
gajim.config.set_per('accounts', acct, size_width_key, width)
@ -915,11 +967,12 @@ class MessageWindowMgr:
if self.mode != self.ONE_MSG_WINDOW_NEVER:
gajim.config.set_per('accounts', acct, pos_x_key, x)
gajim.config.set_per('accounts', acct, pos_y_key, y)
else:
width += width_adjust
gajim.config.set(size_width_key, width)
gajim.config.set(size_height_key, height)
if self.mode != self.ONE_MSG_WINDOW_NEVER:
gajim.config.set(pos_x_key, x)
gajim.config.set(pos_y_key, y)
@ -937,17 +990,24 @@ class MessageWindowMgr:
controls = []
for w in self.windows():
w.window.hide()
# Note, we are taking care not to hide/delete the roster window when the
# MessageWindow is embedded.
if not w.parent_paned:
w.window.hide()
while w.notebook.get_n_pages():
page = w.notebook.get_nth_page(0)
ctrl = w._widget_to_control(page)
w.notebook.remove_page(0)
page.unparent()
controls.append(ctrl)
# Must clear _controls from window to prevent
# MessageControl.shutdown calls
# Must clear _controls from window to prevent MessageControl.shutdown calls
w._controls = {}
w.window.destroy()
if not w.parent_paned:
w.window.destroy()
else:
# Don't close parent window, just remove the child
child = w.parent_paned.get_child2()
w.parent_paned.remove(child)
self._windows = {}

View file

@ -18,6 +18,7 @@
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import os
import gobject
if __name__ == '__main__':
# install _() func before importing dbus_support
@ -51,6 +52,13 @@ class MusicTrackListener(gobject.GObject):
bus = dbus.SessionBus()
## MPRIS
bus.add_signal_receiver(self._mpris_music_track_change_cb, 'TrackChange',
'org.freedesktop.MediaPlayer')
bus.add_signal_receiver(self._mpris_playing_changed_cb, 'StatusChange',
'org.freedesktop.MediaPlayer')
## Muine
bus.add_signal_receiver(self._muine_music_track_change_cb, 'SongChanged',
'org.gnome.Muine.Player')
@ -60,30 +68,32 @@ class MusicTrackListener(gobject.GObject):
'org.gnome.Muine.Player')
## Rhythmbox
bus.add_signal_receiver(self._rhythmbox_music_track_change_cb,
'playingUriChanged', 'org.gnome.Rhythmbox.Player')
bus.add_signal_receiver(self._player_name_owner_changed,
'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Rhythmbox')
bus.add_signal_receiver(self._player_playing_changed_cb,
bus.add_signal_receiver(self._rhythmbox_playing_changed_cb,
'playingChanged', 'org.gnome.Rhythmbox.Player')
bus.add_signal_receiver(self._player_playing_song_property_changed_cb,
'playingSongPropertyChanged', 'org.gnome.Rhythmbox.Player')
## Banshee
banshee_bus = dbus.SessionBus()
dubus = banshee_bus.get_object('org.freedesktop.DBus',
'/org/freedesktop/dbus')
self.dubus_methods = dbus.Interface(dubus, 'org.freedesktop.DBus')
self.current_banshee_title = ''
self.banshee_paused_before = False
self.banshee_is_here = False
gobject.timeout_add(10000, self._check_if_banshee_bus)
if self.dubus_methods.NameHasOwner('org.gnome.Banshee'):
self._get_banshee_bus()
self.banshee_is_here = True
# Otherwise, it opens Banshee!
self.banshee_props ={}
gobject.timeout_add(1000, self._banshee_check_track_status)
# Banshee sucks because it only supports polling.
# Thus, we only register this is we are very sure that it's
# installed.
if os.name == 'posix' and os.system('which banshee >/dev/null 2>&1') == 0:
banshee_bus = dbus.SessionBus()
dubus = banshee_bus.get_object('org.freedesktop.DBus',
'/org/freedesktop/dbus')
self.dubus_methods = dbus.Interface(dubus, 'org.freedesktop.DBus')
self.current_banshee_title = ''
self.banshee_paused_before = False
self.banshee_is_here = False
gobject.timeout_add(10000, self._check_if_banshee_bus)
if self.dubus_methods.NameHasOwner('org.gnome.Banshee'):
self._get_banshee_bus()
self.banshee_is_here = True
# Otherwise, it opens Banshee!
self.banshee_props ={}
gobject.timeout_add(1000, self._banshee_check_track_status)
def _check_if_banshee_bus(self):
if self.dubus_methods.NameHasOwner('org.gnome.Banshee'):
@ -116,6 +126,40 @@ class MusicTrackListener(gobject.GObject):
if b == 'rb:stream-song-title':
self.emit('music-track-changed', self._last_playing_music)
def _mpris_properties_extract(self, song):
info = MusicTrackInfo()
if song.has_key('title'):
info.title = song['title']
else:
info.title = ''
if song.has_key('album'):
info.album = song['album']
else:
info.album = ''
if song.has_key('artist'):
info.artist = song['artist']
else:
info.artist = ''
if song.has_key('length'):
info.duration = int(song['length'])
else:
info.duration = 0
return info
def _mpris_playing_changed_cb(self, playing):
if playing == 0:
self.emit('music-track-changed', self._last_playing_music)
else:
self.emit('music-track-changed', None)
def _mpris_music_track_change_cb(self, arg):
self._last_playing_music = self._mpris_properties_extract(arg)
def _muine_properties_extract(self, song_string):
d = dict((x.strip() for x in s1.split(':', 1)) for s1 in \
song_string.split('\n'))
@ -131,6 +175,13 @@ class MusicTrackListener(gobject.GObject):
info = self._muine_properties_extract(arg)
self.emit('music-track-changed', info)
def _rhythmbox_playing_changed_cb(self, playing):
if playing:
info = self.get_playing_track()
self.emit('music-track-changed', info)
else:
self.emit('music-track-changed', None)
def _rhythmbox_properties_extract(self, props):
info = MusicTrackInfo()
info.title = props['title']
@ -139,17 +190,6 @@ class MusicTrackListener(gobject.GObject):
info.duration = int(props['duration'])
info.track_number = int(props['track-number'])
return info
def _rhythmbox_music_track_change_cb(self, uri):
if not uri:
return
bus = dbus.SessionBus()
rbshellobj = bus.get_object('org.gnome.Rhythmbox',
'/org/gnome/Rhythmbox/Shell')
rbshell = dbus.Interface(rbshellobj, 'org.gnome.Rhythmbox.Shell')
props = rbshell.getSongProperties(uri)
info = self._rhythmbox_properties_extract(props)
self.emit('music-track-changed', info)
def _banshee_check_track_status(self):
if self.dubus_methods.NameHasOwner('org.gnome.Banshee') and \

View file

@ -170,17 +170,25 @@ def notify(event, jid, account, parameters, advanced_notif_num = None):
nickname = parameters[2]
if gajim.config.get('notification_preview_message'):
message = parameters[3]
if message.startswith('/me ') or message.startswith('/me\n'):
message = '* ' + nickname + message[3:]
else:
# We don't want message preview, do_preview = False
message = ''
focused = parameters[4]
if helpers.allow_showing_notification(account, 'notify_on_new_message',
advanced_notif_num, is_first_message):
do_popup = True
if is_first_message and helpers.allow_sound_notification(
'first_message_received', advanced_notif_num):
do_sound = True
elif not is_first_message and helpers.allow_sound_notification(
'next_message_received', advanced_notif_num):
elif not is_first_message and focused and \
helpers.allow_sound_notification('next_message_received_focused',
advanced_notif_num):
do_sound = True
elif not is_first_message and not focused and \
helpers.allow_sound_notification('next_message_received_unfocused',
advanced_notif_num):
do_sound = True
else:
print '*Event not implemeted yet*'
@ -283,8 +291,10 @@ def notify(event, jid, account, parameters, advanced_notif_num = None):
pass # do not set snd_event
elif is_first_message:
snd_event = 'first_message_received'
elif focused:
snd_event = 'next_message_received_focused'
else:
snd_event = 'next_message_received'
snd_event = 'next_message_received_unfocused'
elif event in ('contact_connected', 'contact_disconnected'):
snd_event = event
if snd_file:

View file

@ -1 +0,0 @@
# dummy

View file

@ -1 +0,0 @@
# dummy

View file

@ -1 +0,0 @@
# dummy

View file

@ -1 +0,0 @@
# dummy

View file

@ -114,6 +114,7 @@ static PyObject * idle_getIdleSec(PyObject *self, PyObject *args)
else
{
printf("Couldn't grab properties of system\n");
return NULL;
}
if (obj)

View file

@ -1 +0,0 @@
# dummy

View file

@ -1 +0,0 @@
# dummy

View file

@ -6,6 +6,7 @@
## Copyright (C) 2005-2006 Andrew Sayman <lorien420@myrealbox.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
## Copyright (C) 2007 Travis Shirk <travis@pobox.com>
##
## This file is part of Gajim.
##
@ -373,7 +374,6 @@ class SignalObject(dbus.service.Object):
if not specified status is changed for all accounts. '''
if status not in ('offline', 'online', 'chat',
'away', 'xa', 'dnd', 'invisible'):
raise InvalidArgument
return DBUS_BOOLEAN(False)
if account:
gobject.idle_add(gajim.interface.roster.send_status, account,
@ -615,7 +615,10 @@ class SignalObject(dbus.service.Object):
for contact in contacts:
resource_props = dbus.Struct((DBUS_STRING(contact.resource),
dbus.Int32(contact.priority), DBUS_STRING(contact.status)))
contact_dict['resources'].append(resource_props)
contact_dict['resources'].append(resource_props)
contact_dict['groups'] = dbus.Array([], signature='s')
for group in prim_contact.groups:
contact_dict['groups'].append(DBUS_STRING(group))
return contact_dict
@dbus.service.method(INTERFACE, in_signature='', out_signature='s')
@ -654,4 +657,4 @@ class SignalObject(dbus.service.Object):
gajim.interface.instances[account]['join_gc'] = \
JoinGroupchatWindow(account, room_jid, nick)
else:
gajim.connections[account].join_gc(nick, room_jid, password)
gajim.interface.roster.join_gc_room(account, room_jid, nick, password)

File diff suppressed because it is too large Load diff

View file

@ -22,7 +22,6 @@
import sys
import gtk
import systray
import gobject
from common import gajim
from common import helpers
@ -60,7 +59,7 @@ class StatusIcon(systray.Systray):
self.unsubscribe_events()
def on_status_icon_left_clicked(self, widget):
gobject.idle_add(self.on_left_click)
self.on_left_click()
def set_img(self):
'''apart from image, we also update tooltip text here'''

View file

@ -247,21 +247,17 @@ class Systray:
sounds_mute_menuitem.set_active(not gajim.config.get('sounds_on'))
if os.name == 'nt':
if os.name == 'nt':
if gtk.pygtk_version >= (2, 10, 0) and gtk.gtk_version >= (2, 10, 0):
if self.added_hide_menuitem is False:
self.systray_context_menu.prepend(gtk.SeparatorMenuItem())
item = gtk.MenuItem(_('Hide this menu'))
self.systray_context_menu.prepend(item)
self.added_hide_menuitem = True
self.systray_context_menu.popup(None, None,
gtk.status_icon_position_menu, event_button,
event_time, self.status_icon)
self.systray_context_menu.prepend(gtk.SeparatorMenuItem())
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.show_all()
self.systray_context_menu.popup(None, None, None, event_button,
event_time)
def on_show_all_events_menuitem_activate(self, widget):
events = gajim.events.get_systray_events()

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
## tooltips.py
##
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
@ -463,6 +464,10 @@ class RosterTooltip(NotificationAreaTooltip):
contact.last_status_time)
properties.append((self.table, None))
else: # only one resource
#FIXME: User {Mood, Activity, Tune} not shown if there are
#multiple resources
#FIXME: User {Mood, Activity, Tune} not shown for self
if contact.show:
show = helpers.get_uf_show(contact.show)
if contact.last_status_time:
@ -494,6 +499,52 @@ class RosterTooltip(NotificationAreaTooltip):
show = '<i>' + show + '</i>'
# we append show below
if contact.mood.has_key('mood'):
mood = contact.mood['mood'].strip()
mood = gobject.markup_escape_text(mood)
mood_string = _('Mood:') + ' <b>%s</b>' % mood
if contact.mood.has_key('text') and contact.mood['text'] != '':
mood_text = contact.mood['text'].strip()
mood_text = gobject.markup_escape_text(mood_text)
mood_string += ' (%s)' % mood_text
properties.append((mood_string, None))
if contact.activity.has_key('activity'):
activity = contact.activity['activity'].strip()
activity = gobject.markup_escape_text(activity)
activity_string = _('Activity:') + ' <b>%s' % activity
if contact.activity.has_key('subactivity'):
activity_sub = contact.activity['subactivity'].strip()
activity_sub = gobject.markup_escape_text(activity_sub)
activity_string += ' (%s)</b>' % activity_sub
else:
activity_string += '</b>'
if contact.activity.has_key('text'):
activity_text = contact.activity['text'].strip()
activity_text = gobject.markup_escape_text(activity_text)
activity_string += ' (%s)' % activity_text
properties.append((activity_string, None))
if contact.tune.has_key('artist') or contact.tune.has_key('title'):
if contact.tune.has_key('artist'):
artist = contact.tune['artist'].strip()
artist = gobject.markup_escape_text(artist)
else:
artist = _('Unknown Artist')
if contact.tune.has_key('title'):
title = contact.tune['title'].strip()
title = gobject.markup_escape_text(title)
else:
title = _('Unknown Title')
if contact.tune.has_key('source'):
source = contact.tune['source'].strip()
source = gobject.markup_escape_text(source)
else:
source = _('Unknown Source')
tune_string = _('Tune:') + ' ' + _('<b>"%(title)s"</b> by <i>%(artist)s</i>\nfrom <i>%(source)s</i>' %\
{'title': title, 'artist': artist, 'source': source})
properties.append((tune_string, None))
if contact.status:
status = contact.status.strip()
if status:
@ -551,7 +602,7 @@ class RosterTooltip(NotificationAreaTooltip):
vertical_fill, 0, 0)
else:
if isinstance(property[0], (unicode, str)): #FIXME: rm unicode?
label.set_markup(property[0])
label.set_markup(property[0])
label.set_line_wrap(True)
else:
label = property[0]

View file

@ -178,8 +178,6 @@ class VcardWindow:
pass
def set_values(self, vcard):
if not 'PHOTO' in vcard:
self.xml.get_widget('no_user_avatar_label').show()
for i in vcard.keys():
if i == 'PHOTO' and self.xml.get_widget('information_notebook').\
get_n_pages() > 4:
@ -187,6 +185,7 @@ class VcardWindow:
get_avatar_pixbuf_encoded_mime(vcard[i])
image = self.xml.get_widget('PHOTO_image')
image.show()
self.xml.get_widget('user_avatar_label').show()
if not pixbuf:
image.set_from_icon_name('stock_person',
gtk.ICON_SIZE_DIALOG)