merged trunk into session_centric branch
This commit is contained in:
parent
b8882ba48e
commit
0b48b05218
68 changed files with 13704 additions and 7863 deletions
13
TODO.pep
Normal file
13
TODO.pep
Normal 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
|
|
@ -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***"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"><b>Please fill in the data for your new account</b></property>
|
||||
<property name="label" translatable="yes"><b>Please fill in the data for your existing account</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
|
|
File diff suppressed because it is too large
Load diff
158
data/glade/change_activity_dialog.glade
Normal file
158
data/glade/change_activity_dialog.glade
Normal 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>
|
145
data/glade/change_mood_dialog.glade
Normal file
145
data/glade/change_mood_dialog.glade
Normal 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>
|
|
@ -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>
|
||||
|
|
135
data/glade/manage_pep_services_window.glade
Normal file
135
data/glade/manage_pep_services_window.glade
Normal 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>
|
|
@ -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
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
|
||||
data/gajim.desktop.in
|
||||
src/eggtrayicon.c
|
||||
|
|
402
po/fr.po
402
po/fr.po
|
@ -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>"
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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'''
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' ],
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
220
src/common/pep.py
Normal 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')
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']={}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
701
src/config.py
701
src/config.py
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
322
src/dialogs.py
322
src/dialogs.py
|
@ -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)
|
||||
|
|
|
@ -46,7 +46,6 @@ import inspect
|
|||
import weakref
|
||||
import gobject
|
||||
import gtk
|
||||
import gobject
|
||||
import pango
|
||||
|
||||
import dialogs
|
||||
|
|
|
@ -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':
|
||||
|
|
166
src/gajim.py
166
src/gajim.py
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 = {}
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# dummy
|
|
@ -1 +0,0 @@
|
|||
# dummy
|
|
@ -1 +0,0 @@
|
|||
# dummy
|
|
@ -1 +0,0 @@
|
|||
# dummy
|
|
@ -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)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# dummy
|
|
@ -1 +0,0 @@
|
|||
# dummy
|
|
@ -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
|
@ -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'''
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue