diff --git a/Changelog b/Changelog
index 7856b930b..f21490300 100644
--- a/Changelog
+++ b/Changelog
@@ -1,14 +1,38 @@
+Gajim 0.11 (XX October 2006)
+
+ * Put your stuff here [each dev put their own please; next time we do this everytime we commit sth major and not in the end]
+
+ * We can now operate on more than one contact in one in time in roster (#1514)
+ * Connection lost is now a non-intrusive popup
+ * Try to get contact desired nick when we add him to roster aka User Nickname (JEP-0172)
+ * Better design of User Profile window, with a progress bar
+ * New Add User dialog, with possibility to register to transport directly from it
+ * Completion for "Start Chat" input dialog
+ * We can now have a different spellchecking language in each chat window. (#2383 and #746)
+ * Support for Privacy Lists
+ * Forbid to run multiple instances (but you can use differents profiles)
+ * We can save avatar with right click on avatar in chat banner
+ * Use differents colors for nickname colors of occupants in groupchats.
+ * Ability to show only Join/Leave in groupchats instead of all status changes
+ * New possibilities to insert nickname of an occupant in groupchats conversations : Tab in an empty line now cycle through nicks, maj+right click->insert nickname, maj+click on name in gc-roster, /names command to show all users presents
+
+ * Fixed bugs when removing or renaming an account with tabs open (#2369 and #2370)
+
+ * FIXME : Ad-Hoc for 0.11 ?
+ * FIXME : does that work ? not tested : Metacontacts across accounts (#1596)
+ * Gajim now requires Python 2.4 to run
+
Gajim 0.10.1 (06 June 2006)
- * freeze and lost contacts in roster (#1953)
- * popup menus are correctly placed
- * high CPU usage on FreeBSD (#1963)
- * nickname can contain '|' (#1913)
- * update pl, cs, fr translations
- * don't play sound, when no event is shown (#1970)
- * set gajim icon for history manager
+ * Freeze and lost contacts in roster (#1953)
+ * Popup menus are correctly placed
+ * High CPU usage on FreeBSD (#1963)
+ * Nickname can contain '|' (#1913)
+ * Update pl, cs, fr translations
+ * Don't play sound, when no event is shown (#1970)
+ * Set gajim icon for history manager
* gajim.desktop is generated with translation (#834)
- * preventing several TBs and annoyances (r6273, r6275, r6279, r6301,
+ * Preventing several TBs and annoyances (r6273, r6275, r6279, r6301,
r6308, r6311, r6323, r6326, r6327, r6335, r6342, r6346, r6348)
Gajim 0.10 (01 May 2006)
diff --git a/README b/README
index 9f11c8552..5e2ad7468 100644
--- a/README
+++ b/README
@@ -1,10 +1,10 @@
Welcome and thanks for trying out Gajim.
=RUNTIME REQUIREMENTS=
-python2.4 (python2.3 should work too)
+python2.4 or higher
pygtk2.6 or higher
python-libglade
-pysqlite2 (aka. python-pysqlite2)
+pysqlite2 (if you have python 2.5, you already have this)
some distros also split too much python standard library.
I know SUSE does. In such distros you also need python-xml
@@ -29,7 +29,8 @@ notification-daemon (and D-Bus) to get cooler popups
D-Bus to have gajim-remote working
NOTE TO PACKAGERS:
-Gajim is a GTK+ app and not a gnome one. Just do 'make' so you don't require gnomepythonextras
+Gajim is a GTK+ app and not a gnome one.
+Just do 'make' so you don't require gnomepythonextras
which is gnome dep
=INSTALLATION PROCEDURE=
@@ -65,9 +66,6 @@ you're advised to enable verbose via advanced configuration window.
If you don't want to make this permanent, execute gajim with --verbose
everytime you want to have verbose output.
-Cannot join room with password:
-please read the FAQ for the reply on this issue
-
=FAQ/Wiki=
FAQ can be found at http://trac.gajim.org/wiki/GajimFaq
Wiki can be found at http://trac.gajim.org/wiki
@@ -75,13 +73,13 @@ Wiki can be found at http://trac.gajim.org/wiki
That is all, enjoy!
-(C) 2003-2005
+(C) 2003-2006
The Gajim Team
http://gajim.org
PS.
-we use original art and parts of sounds and other art from Psi, Gossip,
+We use original art and parts of sounds and other art from Psi, Gossip,
Gnomebaker, Gaim and some icons from various gnome-icons
(mostly Dropline Etiquette) we found at art.gnome.org
-If you think we're violating a license please inform us
+If you think we're violating a license please inform us. Thank you
diff --git a/data/glade/gajim_themes_window.glade b/data/glade/gajim_themes_window.glade
index 44d7e381c..9f0dd0092 100644
--- a/data/glade/gajim_themes_window.glade
+++ b/data/glade/gajim_themes_window.glade
@@ -554,52 +554,6 @@ Banner
-
-
- True
- Active
- False
- False
- GTK_JUSTIFY_LEFT
- False
- False
- 0
- 0.5
- 0
- 0
- PANGO_ELLIPSIZE_NONE
- -1
- False
- 0
-
-
- 0
- 1
- 1
- 2
- fill
-
-
-
-
-
-
- True
- True
- False
- True
-
-
-
- 1
- 2
- 1
- 2
- fill
-
-
-
-
True
diff --git a/data/glade/message_window.glade b/data/glade/message_window.glade
index ecd51204d..d4678bfd3 100644
--- a/data/glade/message_window.glade
+++ b/data/glade/message_window.glade
@@ -374,7 +374,7 @@ Status message
True
- gtk-preferences
+ gtk-execute
4
0.5
0.5
@@ -916,7 +916,7 @@ topic
True
- gtk-preferences
+ gtk-execute
4
0.5
0.5
diff --git a/data/glade/preferences_window.glade b/data/glade/preferences_window.glade
index f5aceb5ff..573c182a8 100644
--- a/data/glade/preferences_window.glade
+++ b/data/glade/preferences_window.glade
@@ -819,7 +819,7 @@ Per type
0.5
0
0
- before_time_entry
+ scrolledwindow25
PANGO_ELLIPSIZE_NONE
-1
False
@@ -848,7 +848,7 @@ Per type
0.5
0
0
- after_nickname_entry
+ scrolledwindow28
PANGO_ELLIPSIZE_NONE
-1
False
@@ -998,7 +998,7 @@ Per type
0.5
0
0
- after_time_entry
+ scrolledwindow26
PANGO_ELLIPSIZE_NONE
-1
False
@@ -1027,7 +1027,7 @@ Per type
0.5
0
0
- before_nickname_entry
+ scrolledwindow27
PANGO_ELLIPSIZE_NONE
-1
False
@@ -1043,54 +1043,6 @@ Per type
-
-
- 39
- True
- True
- True
- True
- 0
-
- True
- *
- False
-
-
-
- 3
- 4
- 0
- 1
-
-
-
-
-
-
-
- 40
- True
- True
- True
- True
- 0
-
- True
- *
- False
-
-
-
- 3
- 4
- 1
- 2
-
-
-
-
-
True
@@ -1132,54 +1084,6 @@ Per type
-
-
- 40
- True
- True
- True
- True
- 0
-
- True
- *
- False
-
-
-
- 1
- 2
- 1
- 2
-
-
-
-
-
-
-
- 40
- True
- True
- True
- True
- 0
-
- True
- *
- False
-
-
-
- 1
- 2
- 0
- 1
-
-
-
-
-
True
@@ -1298,6 +1202,170 @@ Per type
fill
+
+
+
+ 30
+ True
+ True
+ GTK_POLICY_NEVER
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+ True
+ False
+ True
+ GTK_JUSTIFY_LEFT
+ GTK_WRAP_CHAR
+ True
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 3
+ 4
+ 0
+ 1
+ fill
+
+
+
+
+
+
+ 30
+ True
+ True
+ GTK_POLICY_NEVER
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+ True
+ False
+ True
+ GTK_JUSTIFY_LEFT
+ GTK_WRAP_CHAR
+ True
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 3
+ 4
+ 1
+ 2
+ fill
+
+
+
+
+
+
+ 30
+ True
+ True
+ GTK_POLICY_NEVER
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+ True
+ False
+ True
+ GTK_JUSTIFY_LEFT
+ GTK_WRAP_CHAR
+ True
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 1
+ 2
+ 1
+ 2
+ fill
+
+
+
+
+
+
+ 30
+ True
+ True
+ GTK_POLICY_NEVER
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_IN
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+ True
+ False
+ True
+ GTK_JUSTIFY_LEFT
+ GTK_WRAP_CHAR
+ True
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 1
+ 2
+ 0
+ 1
+ fill
+
+
+
0
@@ -2543,6 +2611,26 @@ Disabled
+
+
+ True
+ True
+ Set status message to reflect currently playing _music track
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
True
diff --git a/data/glade/privacy_list_window.glade b/data/glade/privacy_list_window.glade
new file mode 100644
index 000000000..1f14d6b71
--- /dev/null
+++ b/data/glade/privacy_list_window.glade
@@ -0,0 +1,779 @@
+
+
+
+
+
+
+ 6
+ True
+ Privacy List
+ GTK_WINDOW_TOPLEVEL
+ GTK_WIN_POS_NONE
+ False
+ False
+ False
+ True
+ False
+ False
+ GDK_WINDOW_TYPE_HINT_NORMAL
+ GDK_GRAVITY_NORTH_WEST
+ True
+ False
+
+
+
+
+ 600
+ True
+ False
+ 0
+
+
+
+ True
+ True
+ 0
+
+
+
+ True
+ <i>Privacy List</i>
+ False
+ True
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ True
+ Active for this session
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ True
+ Active on each startup
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ False
+ True
+
+
+
+
+
+ True
+
+
+ 5
+ False
+ False
+
+
+
+
+
+ True
+ <b>List of rules</b>
+ False
+ True
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 5
+ False
+ False
+
+
+
+
+
+ True
+
+ False
+ True
+
+
+
+ 5
+ False
+ True
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ 5
+ True
+ True
+ gtk-add
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ 5
+ True
+ True
+ gtk-remove
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ 6
+ True
+ True
+ gtk-edit
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ False
+ True
+
+
+
+
+
+ 5
+ False
+ 0
+
+
+
+ True
+
+
+ 5
+ True
+ True
+
+
+
+
+
+ True
+ <b>Add / Edit a rule</b>
+ False
+ True
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 5
+ False
+ False
+
+
+
+
+
+ True
+ False
+ 0
+
+
+
+ True
+ True
+ 0
+
+
+
+ True
+ True
+ Allow
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ True
+ Deny
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+ edit_allow_radiobutton
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ 5
+ True
+ False
+ 0
+
+
+
+ True
+ True
+ JabberID
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+ 5
+ False
+ False
+
+
+
+
+
+ True
+ True
+ True
+ True
+ 0
+
+ True
+ ●
+ False
+
+
+ 5
+ True
+ True
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ 5
+ True
+ False
+ 0
+
+
+
+ True
+ True
+ all in the group
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+ edit_type_jabberid_radiobutton
+
+
+ 5
+ False
+ False
+
+
+
+
+
+ True
+
+ False
+ True
+
+
+ 5
+ True
+ True
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ 5
+ True
+ False
+ 0
+
+
+
+ True
+ True
+ all by subscription
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+ edit_type_jabberid_radiobutton
+
+
+ 5
+ False
+ False
+
+
+
+
+
+ True
+ none
+both
+from
+to
+ False
+ True
+
+
+ 5
+ True
+ True
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ 10
+ True
+ False
+ 0
+
+
+
+ True
+ True
+ All
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+ edit_type_jabberid_radiobutton
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ True
+ True
+ to send me messages
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ True
+ to send me queries
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ True
+ to view my status
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ True
+ to send me status
+ True
+ GTK_RELIEF_NORMAL
+ True
+ False
+ False
+ True
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ True
+ False
+ 0
+
+
+
+ True
+ Order:
+ False
+ False
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 5
+ False
+ False
+
+
+
+
+
+ True
+ True
+ 1
+ 0
+ False
+ GTK_UPDATE_ALWAYS
+ False
+ False
+ 1 0 100 1 10 10
+
+
+ 0
+ False
+ True
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ 5
+ True
+ True
+ gtk-save
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+
+
+ 0
+ False
+ True
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ 5
+ True
+ True
+ gtk-refresh
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ 5
+ True
+ True
+ gtk-close
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ False
+ True
+
+
+
+
+
+
+
diff --git a/data/glade/privacy_lists_window.glade b/data/glade/privacy_lists_window.glade
new file mode 100644
index 000000000..7a7470123
--- /dev/null
+++ b/data/glade/privacy_lists_window.glade
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+ 12
+ True
+ window1
+ GTK_WINDOW_TOPLEVEL
+ GTK_WIN_POS_NONE
+ False
+ True
+ False
+ True
+ False
+ False
+ GDK_WINDOW_TYPE_HINT_NORMAL
+ GDK_GRAVITY_NORTH_WEST
+ True
+ False
+
+
+
+
+ True
+ False
+ 0
+
+
+
+ True
+ Server-based Privacy Lists
+ False
+ False
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0.5
+ 0.5
+ 0
+ 5
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ 4
+ True
+
+ False
+ True
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ 5
+ True
+ True
+ gtk-delete
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ 5
+ True
+ True
+ gtk-open
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+
+
+ 5
+ True
+ True
+
+
+
+
+
+ True
+ Create your own Privacy Lists
+ False
+ False
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0.5
+ 0.5
+ 0
+ 5
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ True
+ True
+ True
+ 0
+
+ True
+ ●
+ False
+
+
+ 4
+ False
+ False
+
+
+
+
+
+ 5
+ True
+ True
+ gtk-new
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+
+
+ 5
+ True
+ True
+
+
+
+
+
+ True
+ True
+ 0
+
+
+
+ 5
+ True
+ True
+ gtk-refresh
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ 5
+ True
+ True
+ gtk-close
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+ 0
+ False
+ False
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+
+
diff --git a/data/glade/profile_window.glade b/data/glade/profile_window.glade
index aeaaa8518..723df7548 100644
--- a/data/glade/profile_window.glade
+++ b/data/glade/profile_window.glade
@@ -4,7 +4,6 @@
- 12
Personal Information
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_NONE
@@ -29,6 +28,7 @@
+ 6
True
True
True
@@ -1016,7 +1016,7 @@
- 0
+ 1
4
6
7
@@ -1024,6 +1024,34 @@
expand
+
+
+
+ True
+ Avatar:
+ False
+ False
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 0
+ 1
+ 6
+ 7
+ fill
+
+
+
False
@@ -1800,159 +1828,201 @@
-
+
+ 6
True
- GTK_BUTTONBOX_END
- 12
+ False
+ 0
-
+
True
- True
- True
- GTK_RELIEF_NORMAL
- True
-
-
-
-
- True
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
-
-
-
- True
- False
- 2
-
-
-
- True
- gtk-go-up
- 4
- 0
- 0
- 0
- 0
-
-
- 0
- False
- False
-
-
-
-
-
- True
- _Publish
- True
- False
- GTK_JUSTIFY_LEFT
- False
- False
- 0
- 0.5
- 0
- 0
- PANGO_ELLIPSIZE_NONE
- -1
- False
- 0
-
-
- 0
- False
- False
-
-
-
-
-
-
+ GTK_PROGRESS_LEFT_TO_RIGHT
+ 0
+ 0.10000000149
+ PANGO_ELLIPSIZE_NONE
+
+ 0
+ False
+ False
+
-
+
True
- True
- True
- GTK_RELIEF_NORMAL
- True
-
+ GTK_BUTTONBOX_END
+ 12
-
+
True
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
+ True
+ True
+ GTK_RELIEF_NORMAL
+ True
+
-
+
True
- False
- 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
-
+
True
- gtk-go-down
- 4
- 0
- 0
- 0
- 0
-
-
- 0
- False
- False
-
-
+ False
+ 2
-
-
- True
- _Retrieve
- True
- False
- GTK_JUSTIFY_LEFT
- False
- False
- 0
- 0.5
- 0
- 0
- PANGO_ELLIPSIZE_NONE
- -1
- False
- 0
+
+
+ True
+ gtk-go-up
+ 4
+ 0
+ 0
+ 0
+ 0
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ _Publish
+ True
+ False
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 0
+ False
+ False
+
+
-
- 0
- False
- False
-
+
+
+
+ True
+ True
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+
+ True
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+ True
+ False
+ 2
+
+
+
+ True
+ gtk-go-down
+ 4
+ 0
+ 0
+ 0
+ 0
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ _Retrieve
+ True
+ False
+ GTK_JUSTIFY_LEFT
+ False
+ False
+ 0
+ 0.5
+ 0
+ 0
+ PANGO_ELLIPSIZE_NONE
+ -1
+ False
+ 0
+
+
+ 0
+ False
+ False
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ True
+ gtk-close
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+
+ 0
+ True
+ True
+
@@ -1961,6 +2031,18 @@
True
+
+
+
+ True
+ False
+
+
+ 0
+ False
+ False
+
+
diff --git a/data/glade/roster_window.glade b/data/glade/roster_window.glade
index 94c8b896a..d1c9cf194 100644
--- a/data/glade/roster_window.glade
+++ b/data/glade/roster_window.glade
@@ -1,328 +1,413 @@
-
-
-
+
+
+
-
- 85
- 200
- Gajim
- roster
- 150
- 400
-
-
-
-
-
-
- True
-
-
-
- False
- False
-
-
-
-
- True
- True
- 2
- GTK_POLICY_NEVER
- GTK_POLICY_AUTOMATIC
-
-
- True
- True
- False
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
- True
-
-
-
- False
- 2
-
-
-
-
-
+
+
+ 85
+ 200
+ Gajim
+ GTK_WINDOW_TOPLEVEL
+ GTK_WIN_POS_NONE
+ False
+ 150
+ 400
+ True
+ False
+ roster
+ True
+ False
+ False
+ GDK_WINDOW_TYPE_HINT_NORMAL
+ GDK_GRAVITY_NORTH_WEST
+ True
+ False
+
+
+
+
+
+
+
+ True
+ False
+ 0
+
+
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ 2
+ True
+ True
+ GTK_POLICY_NEVER
+ GTK_POLICY_AUTOMATIC
+ GTK_SHADOW_NONE
+ GTK_CORNER_TOP_LEFT
+
+
+
+ True
+ True
+ False
+ False
+ True
+ True
+ False
+ False
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+
+ True
+ False
+ True
+
+
+
+ 0
+ False
+ True
+
+
+
+
+
+
diff --git a/data/glade/systray_context_menu.glade b/data/glade/systray_context_menu.glade
index 0f3aaec88..c82aec544 100644
--- a/data/glade/systray_context_menu.glade
+++ b/data/glade/systray_context_menu.glade
@@ -2,6 +2,7 @@
+
diff --git a/data/glade/vcard_information_window.glade b/data/glade/vcard_information_window.glade
index 7b9eed488..3b88416c0 100644
--- a/data/glade/vcard_information_window.glade
+++ b/data/glade/vcard_information_window.glade
@@ -652,7 +652,7 @@
-
+
12
True
6
@@ -1640,34 +1640,6 @@
-
-
- True
- True
-
- False
- False
- GTK_JUSTIFY_LEFT
- False
- True
- 0
- 0
- 5
- 5
- PANGO_ELLIPSIZE_NONE
- -1
- False
- 0
-
-
- 1
- 4
- 3
- 4
-
-
-
-
True
@@ -2581,6 +2553,60 @@
True
+
+
+
+ True
+ False
+ 0
+
+
+
+ True
+ GTK_PROGRESS_LEFT_TO_RIGHT
+ 0
+ 0.10000000149
+ PANGO_ELLIPSIZE_NONE
+
+
+ 0
+ False
+ False
+
+
+
+
+
+ True
+ GTK_BUTTONBOX_END
+ 0
+
+
+
+ True
+ True
+ True
+ gtk-close
+ True
+ GTK_RELIEF_NORMAL
+ True
+
+
+
+
+
+ 0
+ True
+ True
+
+
+
+
+ 0
+ True
+ True
+
+
diff --git a/po/pl.po b/po/pl.po
index 54bdb4a3b..6303d7024 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -1334,7 +1334,7 @@ msgstr "W każdej _wiadomości"
#: ../data/glade/preferences_window.glade.h:58
msgid "One message _window:"
-msgstr "Wyślij wiadomość i _zamknij okno"
+msgstr "Grupuj okna:"
#: ../data/glade/preferences_window.glade.h:59
msgid "Play _sounds"
diff --git a/src/chat_control.py b/src/chat_control.py
index b9cf730a7..7b19fc563 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -196,7 +196,6 @@ class ChatControlBase(MessageControl):
self.msg_textview.lang = lang
spell.set_language(lang)
except (gobject.GError, RuntimeError), msg:
- #FIXME: add a ui for this use spell.set_language()
dialogs.ErrorDialog(unicode(msg), _('If that is not your language '
'for which you want to highlight misspelled words, then please '
'set your $LANG as appropriate. Eg. for French do export '
@@ -1018,7 +1017,7 @@ class ChatControl(ChatControlBase):
acct_info = ''
self.account_displayed = False
for ctrl in self.parent_win.controls():
- if ctrl == self:
+ if ctrl == self or ctrl.type_id == 'gc':
continue
if self.contact.get_shown_name() == ctrl.contact.get_shown_name()\
and not avoid_showing_account_too:
@@ -1276,9 +1275,6 @@ class ChatControl(ChatControlBase):
elif chatstate == 'paused':
color = gajim.config.get_per('themes', theme,
'state_paused_color')
- else:
- color = gajim.config.get_per('themes', theme,
- 'state_active_color')
if color:
# We set the color for when it's the current tab or not
color = gtk.gdk.colormap_get_system().alloc_color(color)
@@ -1287,6 +1283,9 @@ class ChatControl(ChatControlBase):
if chatstate in ('inactive', 'gone') and\
self.parent_win.get_active_control() != self:
color = self.lighten_color(color)
+ elif chatstate == 'active' : # active, get color from gtk
+ color = self.parent_win.notebook.style.fg[gtk.STATE_ACTIVE]
+
name = self.contact.get_shown_name()
if self.resource:
diff --git a/src/common/config.py b/src/common/config.py
index 1a0f18865..08b198644 100644
--- a/src/common/config.py
+++ b/src/common/config.py
@@ -37,6 +37,8 @@ opt_bool = [ 'boolean', 0 ]
opt_color = [ 'color', '^(#[0-9a-fA-F]{6})|()$' ]
opt_one_window_types = ['never', 'always', 'peracct', 'pertype']
+DEFAULT_ICONSET = 'dcraven'
+
class Config:
__options = {
@@ -67,7 +69,7 @@ class Config:
'last_status_msg_invisible': [ opt_str, '' ],
'last_status_msg_offline': [ opt_str, '' ],
'trayicon': [ opt_bool, True, '', True ],
- 'iconset': [ opt_str, 'dcraven', '', True ],
+ 'iconset': [ opt_str, DEFAULT_ICONSET, '', True ],
'use_transports_iconsets': [ opt_bool, True, '', True ],
'inmsgcolor': [ opt_color, '#a34526', '', True ],
'outmsgcolor': [ opt_color, '#164e6f', '', True ],
@@ -126,6 +128,7 @@ class Config:
'before_nickname': [ opt_str, '' ],
'after_nickname': [ opt_str, ':' ],
'send_os_info': [ opt_bool, True ],
+ 'set_status_msg_from_current_music_track': [ opt_bool, False ],
'notify_on_new_gmail_email': [ opt_bool, True ],
'notify_on_new_gmail_email_extra': [ opt_bool, False ],
'usegpg': [ opt_bool, False, '', True ],
@@ -188,7 +191,7 @@ class Config:
'restored_messages_color': [opt_str, 'grey'],
'restored_messages_small': [opt_bool, True, _('If True, restored messages will use a smaller font than the default one.')],
'hide_avatar_of_transport': [opt_bool, False, _('Don\'t show avatar for the transport itself.')],
- 'roster_window_skip_taskbar': [opt_bool, False],
+ 'roster_window_skip_taskbar': [opt_bool, False, _('Don\'t show roster in the system taskbar.')],
'use_urgency_hint': [opt_bool, True, _('If True and installed GTK+ and PyGTK versions are at least 2.8, make the window flash (the default behaviour in most Window Managers) when holding pending events.')],
'notification_timeout': [opt_int, 5],
'send_sha_in_gc_presence': [opt_bool, True, _('Jabberd1.4 does not like sha info when one join a password protected room. Turn this option to False to stop sending sha info in group chat presences.')],
@@ -290,8 +293,6 @@ class Config:
'bannerfontattrs': [ opt_str, 'B', '', True ],
# http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html
- # FIXME: not black but the default color from gtk+ theme
- 'state_active_color': [ opt_color, 'black' ],
'state_inactive_color': [ opt_color, 'grey62' ],
'state_composing_color': [ opt_color, 'green4' ],
'state_paused_color': [ opt_color, 'mediumblue' ],
diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py
index dc15c9b8e..c29cf539a 100644
--- a/src/common/connection_handlers.py
+++ b/src/common/connection_handlers.py
@@ -175,7 +175,7 @@ class ConnectionBytestream:
except socket.gaierror:
self.dispatch('ERROR', (_('Wrong host'), _('The host you configured as the ft_override_host_to_send advanced option is not valid, so ignored.')))
ft_override_host_to_send = self.peerhost[0]
- listener = gajim.socks5queue.start_listener(self.peerhost[0], port,
+ listener = gajim.socks5queue.start_listener(port,
sha_str, self._result_socks5_sid, file_props['sid'])
if listener == None:
file_props['error'] = -5
@@ -1134,6 +1134,12 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
raise common.xmpp.NodeProcessed
def _ErrorCB(self, con, iq_obj):
+ gajim.log.debug('ErrorCB')
+ if iq_obj.getQueryNS() == common.xmpp.NS_VERSION:
+ who = helpers.get_full_jid_from_iq(iq_obj)
+ jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
+ self.dispatch('OS_INFO', (jid_stripped, resource, '', ''))
+ return
errmsg = iq_obj.getErrorMsg()
errcode = iq_obj.getErrorCode()
jid_from = helpers.get_full_jid_from_iq(iq_obj)
diff --git a/src/common/helpers.py b/src/common/helpers.py
index 1226f2cd5..1f3a7206b 100644
--- a/src/common/helpers.py
+++ b/src/common/helpers.py
@@ -290,6 +290,21 @@ def get_uf_role(role, plural = False):
else:
role_name = _('Visitor')
return role_name
+
+def get_uf_affiliation(affiliation):
+ '''Get a nice and translated affilition for muc'''
+ if affiliation == 'none':
+ affiliation_name = Q_('?Group Chat Contact Affiliation:None')
+ elif affiliation == 'owner':
+ affiliation_name = _('Owner')
+ elif affiliation == 'admin':
+ affiliation_name = _('Administrator')
+ elif affiliation == 'member':
+ affiliation_name = _('Member')
+ else: # Argl ! An unknown affiliation !
+ affiliation_name = affiliation.capitalize()
+ return affiliation_name
+
def get_sorted_keys(adict):
keys = adict.keys()
diff --git a/src/common/socks5.py b/src/common/socks5.py
index 6e472e1b8..26d8e5672 100644
--- a/src/common/socks5.py
+++ b/src/common/socks5.py
@@ -74,13 +74,13 @@ class SocksQueue:
self.on_success = None
self.on_failure = None
- def start_listener(self, host, port, sha_str, sha_handler, sid):
+ def start_listener(self, port, sha_str, sha_handler, sid):
''' start waiting for incomming connections on (host, port)
and do a socks5 authentication using sid for generated sha
'''
self.sha_handlers[sha_str] = (sha_handler, sid)
if self.listener == None:
- self.listener = Socks5Listener(self.idlequeue, host, port)
+ self.listener = Socks5Listener(self.idlequeue, port)
self.listener.queue = self
self.listener.bind()
if self.listener.started is False:
@@ -790,12 +790,12 @@ class Socks5Sender(Socks5, IdleObject):
self.queue.remove_sender(self.queue_idx, False)
class Socks5Listener(IdleObject):
- def __init__(self, idlequeue, host, port):
- ''' handle all incomming connections on (host, port)
+ def __init__(self, idlequeue, port):
+ ''' handle all incomming connections on (0.0.0.0, port)
This class implements IdleObject, but we will expect
only pollin events though
'''
- self.host, self.port = host, port
+ self.port = port
self.queue_idx = -1
self.idlequeue = idlequeue
self.queue = None
diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py
index 2ac25069d..b9d2ad9ff 100644
--- a/src/common/xmpp/protocol.py
+++ b/src/common/xmpp/protocol.py
@@ -19,7 +19,7 @@ Protocol module contains tools that is needed for processing of
xmpp-related data structures.
"""
-from simplexml import Node,ustr
+from simplexml import Node,NodeBuilder,ustr
import time
NS_ACTIVITY ='http://jabber.org/protocol/activity' # JEP-0108
NS_ADDRESS ='http://jabber.org/protocol/address' # JEP-0033
@@ -94,6 +94,7 @@ NS_VCARD_UPDATE =NS_VCARD+':x:update'
NS_VERSION ='jabber:iq:version'
NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # JEP-0130
NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # JEP-0071
+NS_XHTML = 'http://www.w3.org/1999/xhtml' # "
NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # JEP-0141
NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # JEP-0122
NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams'
@@ -385,16 +386,21 @@ class Protocol(Node):
class Message(Protocol):
""" XMPP Message stanza - "push" mechanism."""
- def __init__(self, to=None, body=None, typ=None, subject=None, attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, node=None):
+ def __init__(self, to=None, body=None, xhtml=None, typ=None, subject=None, attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, node=None):
""" Create message object. You can specify recipient, text of message, type of message
any additional attributes, sender of the message, any additional payload (f.e. jabber:x:delay element) and namespace in one go.
Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as message. """
Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
if body: self.setBody(body)
+ if xhtml: self.setXHTML(xhtml)
if subject: self.setSubject(subject)
def getBody(self):
""" Returns text of the message. """
return self.getTagData('body')
+ def getXHTML(self):
+ """ Returns serialized xhtml-im body text of the message. """
+ xhtml = self.getTag('html')
+ return str(xhtml.getTag('body'))
def getSubject(self):
""" Returns subject of the message. """
return self.getTagData('subject')
@@ -404,6 +410,11 @@ class Message(Protocol):
def setBody(self,val):
""" Sets the text of the message. """
self.setTagData('body',val)
+ def setXHTML(self,val):
+ """ Sets the xhtml text of the message (JEP-0071).
+ The parameter is the "inner html" to the body."""
+ dom = NodeBuilder(val)
+ self.setTag('html',namespace=NS_XHTML_IM).setTag('body',namespace=NS_XHTML).addChild(node=dom.getDom())
def setSubject(self,val):
""" Sets the subject of the message. """
self.setTagData('subject',val)
diff --git a/src/config.py b/src/config.py
index 30748c007..312b13408 100644
--- a/src/config.py
+++ b/src/config.py
@@ -1,7 +1,7 @@
## config.py
##
## Copyright (C) 2003-2006 Yann Le Boulanger
-## Copyright (C) 2005-2006 Nikos Kouremenos
+## Copyright (C) 2005-2006 Nikos Kouremenos
## Copyright (C) 2005 Dimitur Kirov
## Copyright (C) 2003-2005 Vincent Hanquez
##
@@ -182,7 +182,7 @@ class PreferencesWindow:
theme = config_theme.replace('_', ' ')
model.append([theme])
if gajim.config.get('roster_theme') == config_theme:
- theme_combobox.set_active(i)
+ theme_combobox.set_active(i)
i += 1
self.on_theme_combobox_changed(theme_combobox)
@@ -212,19 +212,23 @@ class PreferencesWindow:
#before time
st = gajim.config.get('before_time')
- self.xml.get_widget('before_time_entry').set_text(st)
+ st = helpers.from_one_line(st)
+ self.xml.get_widget('before_time_textview').get_buffer().set_text(st)
#after time
st = gajim.config.get('after_time')
- self.xml.get_widget('after_time_entry').set_text(st)
+ st = helpers.from_one_line(st)
+ self.xml.get_widget('after_time_textview').get_buffer().set_text(st)
#before nickname
st = gajim.config.get('before_nickname')
- self.xml.get_widget('before_nickname_entry').set_text(st)
+ st = helpers.from_one_line(st)
+ self.xml.get_widget('before_nickname_textview').get_buffer().set_text(st)
#after nickanme
st = gajim.config.get('after_nickname')
- self.xml.get_widget('after_nickname_entry').set_text(st)
+ st = helpers.from_one_line(st)
+ self.xml.get_widget('after_nickname_textview').get_buffer().set_text(st)
#Color for incomming messages
colSt = gajim.config.get('inmsgcolor')
@@ -455,12 +459,18 @@ class PreferencesWindow:
# send os info
st = gajim.config.get('send_os_info')
self.xml.get_widget('send_os_info_checkbutton').set_active(st)
+
+ # set status msg from currently playing music track
+ st = gajim.config.get('set_status_msg_from_current_music_track')
+ self.xml.get_widget(
+ 'set_status_msg_from_current_music_track_checkbutton').set_active(st)
# Notify user of new gmail e-mail messages,
# only show checkbox if user has a gtalk account
frame_gmail = self.xml.get_widget('frame_gmail')
notify_gmail_checkbutton = self.xml.get_widget('notify_gmail_checkbutton')
- notify_gmail_extra_checkbutton = self.xml.get_widget('notify_gmail_extra_checkbutton')
+ notify_gmail_extra_checkbutton = self.xml.get_widget(
+ 'notify_gmail_extra_checkbutton')
frame_gmail.set_no_show_all(True)
for account in gajim.config.get_per('accounts'):
@@ -512,6 +522,8 @@ class PreferencesWindow:
gajim.interface.systray.change_status(show)
else:
gajim.config.set('trayicon', False)
+ if not gajim.interface.roster.window.get_property('visible'):
+ gajim.interface.roster.window.present()
gajim.interface.hide_systray()
gajim.config.set('show_roster_on_startup', True) # no tray, show roster!
gajim.interface.roster.draw_roster()
@@ -523,7 +535,7 @@ class PreferencesWindow:
def on_sort_by_show_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'sort_by_show')
gajim.interface.roster.draw_roster()
-
+
def on_show_status_msgs_in_roster_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'show_status_msgs_in_roster')
gajim.interface.roster.draw_roster()
@@ -643,9 +655,9 @@ class PreferencesWindow:
def _set_sensitivity_for_before_after_time_widgets(self, sensitive):
self.xml.get_widget('before_time_label').set_sensitive(sensitive)
- self.xml.get_widget('before_time_entry').set_sensitive(sensitive)
+ self.xml.get_widget('before_time_textview').set_sensitive(sensitive)
self.xml.get_widget('after_time_label').set_sensitive(sensitive)
- self.xml.get_widget('after_time_entry').set_sensitive(sensitive)
+ self.xml.get_widget('after_time_textview').set_sensitive(sensitive)
def on_time_never_radiobutton_toggled(self, widget):
if widget.get_active():
@@ -665,20 +677,33 @@ class PreferencesWindow:
self._set_sensitivity_for_before_after_time_widgets(True)
gajim.interface.save_config()
- def on_before_time_entry_focus_out_event(self, widget, event):
- gajim.config.set('before_time', widget.get_text().decode('utf-8'))
+ def _get_textview_text(self, tv):
+ buffer = tv.get_buffer()
+ begin, end = buffer.get_bounds()
+ return buffer.get_text(begin, end).decode('utf-8')
+
+ def on_before_time_textview_focus_out_event(self, widget, event):
+ text = self._get_textview_text(widget)
+ text = helpers.to_one_line(text)
+ gajim.config.set('before_time', text)
gajim.interface.save_config()
- def on_after_time_entry_focus_out_event(self, widget, event):
- gajim.config.set('after_time', widget.get_text().decode('utf-8'))
+ def on_after_time_textview_focus_out_event(self, widget, event):
+ text = self._get_textview_text(widget)
+ text = helpers.to_one_line(text)
+ gajim.config.set('after_time', text)
gajim.interface.save_config()
- def on_before_nickname_entry_focus_out_event(self, widget, event):
- gajim.config.set('before_nickname', widget.get_text().decode('utf-8'))
+ def on_before_nickname_textview_focus_out_event(self, widget, event):
+ text = self._get_textview_text(widget)
+ text = helpers.to_one_line(text)
+ gajim.config.set('before_nickname', text)
gajim.interface.save_config()
- def on_after_nickname_entry_focus_out_event(self, widget, event):
- gajim.config.set('after_nickname', widget.get_text().decode('utf-8'))
+ def on_after_nickname_textview_focus_out_event(self, widget, event):
+ text = self._get_textview_text(widget)
+ text = helpers.to_one_line(text)
+ gajim.config.set('after_nickname', text)
gajim.interface.save_config()
def update_text_tags(self):
@@ -1064,6 +1089,13 @@ class PreferencesWindow:
gajim.interface.instances['advanced_config'] = \
dialogs.AdvancedConfigurationWindow()
+ def set_status_msg_from_current_music_track_checkbutton_toggled(self,
+ widget):
+ self.on_checkbutton_toggled(widget,
+ 'set_status_msg_from_current_music_track')
+ gajim.interface.roster.enable_syncing_status_msg_from_current_music_track(
+ widget.get_active())
+
#---------- AccountModificationWindow class -------------#
class AccountModificationWindow:
'''Class for account informations'''
@@ -1097,11 +1129,12 @@ class AccountModificationWindow:
'''set or unset sensitivity of widgets when widget is toggled'''
for w in widgets:
w.set_sensitive(widget.get_active())
-
+
def init_account_gpg(self):
keyid = gajim.config.get_per('accounts', self.account, 'keyid')
keyname = gajim.config.get_per('accounts', self.account, 'keyname')
- savegpgpass = gajim.config.get_per('accounts', self.account,'savegpgpass')
+ savegpgpass = gajim.config.get_per('accounts', self.account,
+ 'savegpgpass')
if not keyid or not gajim.config.get('usegpg'):
return
@@ -1358,8 +1391,7 @@ class AccountModificationWindow:
gajim.events.change_account_name(self.account, name)
# change account variable for chat / gc controls
- for ctrl in gajim.interface.msg_win_mgr.get_controls():
- ctrl.account = name
+ gajim.interface.msg_win_mgr.change_account_name(self.account, name)
# upgrade account variable in opened windows
for kind in ('infos', 'disco', 'gc_config'):
for j in gajim.interface.instances[name][kind]:
@@ -1857,6 +1889,7 @@ class AccountsWindow:
else:
gajim.interface.roster.regroup = False
gajim.interface.roster.draw_roster()
+
def on_enable_zeroconf_checkbutton_toggled(self, widget):
if gajim.config.get('enable_zeroconf'):
@@ -2987,9 +3020,6 @@ _('You can set advanced account options by pressing Advanced button, or later by
con = connection.Connection(self.account)
con.password = password
- if not savepass:
- password = ""
-
config = {}
config['name'] = login
config['hostname'] = server
@@ -3018,6 +3048,10 @@ _('You can set advanced account options by pressing Advanced button, or later by
def create_vars(self, config):
gajim.config.add_per('accounts', self.account)
+
+ if not config['savepass']:
+ config['password'] = ''
+
for opt in config:
gajim.config.set_per('accounts', self.account, opt, config[opt])
diff --git a/src/conversation_textview.py b/src/conversation_textview.py
index 888c9d2b3..daf1fc7c3 100644
--- a/src/conversation_textview.py
+++ b/src/conversation_textview.py
@@ -138,6 +138,9 @@ class ConversationTextview:
self.focus_out_end_iter_offset = None
self.line_tooltip = tooltips.BaseTooltip()
+
+ path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'muc_separator.png')
+ self.focus_out_line_pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
def del_handlers(self):
for i in self.handlers.keys():
@@ -230,19 +233,14 @@ class ConversationTextview:
end_iter_for_previous_line)
# add the new focus out line
- # FIXME: Why is this loaded from disk everytime
- path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'muc_separator.png')
- focus_out_line_pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
end_iter = buffer.get_end_iter()
buffer.insert(end_iter, '\n')
- buffer.insert_pixbuf(end_iter, focus_out_line_pixbuf)
+ buffer.insert_pixbuf(end_iter, self.focus_out_line_pixbuf)
end_iter = buffer.get_end_iter()
before_img_iter = end_iter.copy()
before_img_iter.backward_char() # one char back (an image also takes one char)
buffer.apply_tag_by_name('focus-out-line', before_img_iter, end_iter)
- #FIXME: remove this workaround when bug is fixed
- # c http://bugzilla.gnome.org/show_bug.cgi?id=318569
self.allow_focus_out_line = False
@@ -562,6 +560,7 @@ class ConversationTextview:
img.show()
#add with possible animation
self.tv.add_child_at_anchor(img, anchor)
+ #FIXME: one day, somehow sync with regexp in gajim.py
elif special_text.startswith('http://') or \
special_text.startswith('www.') or \
special_text.startswith('ftp://') or \
@@ -664,7 +663,9 @@ class ConversationTextview:
current_print_time = gajim.config.get('print_time')
if current_print_time == 'always' and kind != 'info':
before_str = gajim.config.get('before_time')
+ before_str = helpers.from_one_line(before_str)
after_str = gajim.config.get('after_time')
+ after_str = helpers.from_one_line(after_str)
# get difference in days since epoch (86400 = 24*3600)
# number of days since epoch for current time (in GMT) -
# number of days since epoch for message (in GMT)
@@ -748,7 +749,9 @@ class ConversationTextview:
name_tags = other_tags_for_name[:] # create a new list
name_tags.append(kind)
before_str = gajim.config.get('before_nickname')
+ before_str = helpers.from_one_line(before_str)
after_str = gajim.config.get('after_nickname')
+ after_str = helpers.from_one_line(after_str)
format = before_str + name + after_str + ' '
buffer.insert_with_tags_by_name(end_iter, format, *name_tags)
diff --git a/src/dbus_support.py b/src/dbus_support.py
index 59e751c41..d4f7542a3 100644
--- a/src/dbus_support.py
+++ b/src/dbus_support.py
@@ -23,25 +23,17 @@ from common import exceptions
try:
import dbus
- version = getattr(dbus, 'version', (0, 20, 0))
- supported = True
+ import dbus.service
+ import dbus.glib
+ supported = True # does use have D-Bus bindings?
except ImportError:
- version = (0, 0, 0)
supported = False
if not os.name == 'nt': # only say that to non Windows users
print _('D-Bus python bindings are missing in this computer')
print _('D-Bus capabilities of Gajim cannot be used')
-
-# dbus 0.23 leads to segfault with threads_init()
-if sys.version[:4] >= '2.4' and version[1] < 30:
- supported = False
-
-if version >= (0, 41, 0):
- import dbus.service
- import dbus.glib # cause dbus 0.35+ doesn't return signal replies without it
class SessionBus:
- '''A Singleton for the DBus SessionBus'''
+ '''A Singleton for the D-Bus SessionBus'''
def __init__(self):
self.session_bus = None
diff --git a/src/dialogs.py b/src/dialogs.py
index 6a8ec5de3..d6183ea8a 100644
--- a/src/dialogs.py
+++ b/src/dialogs.py
@@ -837,27 +837,31 @@ class FileChooserDialog(gtk.FileChooserDialog):
self.set_current_folder(current_folder)
else:
self.set_current_folder(helpers.get_documents_path())
-
- buttons = self.action_area.get_children()
- possible_responses = {gtk.STOCK_OPEN: on_response_ok,
- gtk.STOCK_SAVE: on_response_ok,
- gtk.STOCK_CANCEL: on_response_cancel}
- for b in buttons:
- for response in possible_responses:
- if b.get_label() == response:
- if not possible_responses[response]:
- b.connect('clicked', self.just_destroy)
- elif isinstance(possible_responses[response], tuple):
- if len(possible_responses[response]) == 1:
- b.connect('clicked', possible_responses[response][0])
- else:
- b.connect('clicked', *possible_responses[response])
- else:
- b.connect('clicked', possible_responses[response])
- break
-
+ self.response_ok, self.response_cancel = \
+ on_response_ok, on_response_cancel
+ # in gtk+-2.10 clicked signal on some of the buttons in a dialog
+ # is emitted twice, so we cannot rely on 'clicked' signal
+ self.connect('response', self.on_dialog_response)
self.show_all()
+ def on_dialog_response(self, dialog, response):
+ if response in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_CLOSE):
+ if self.response_cancel:
+ if isinstance(self.response_cancel, tuple):
+ self.response_cancel[0](dialog, *self.response_cancel[1:])
+ else:
+ self.response_cancel(dialog)
+ else:
+ self.just_destroy(dialog)
+ elif response == gtk.RESPONSE_OK:
+ if self.response_ok:
+ if isinstance(self.response_ok, tuple):
+ self.response_ok[0](dialog, *self.response_ok[1:])
+ else:
+ self.response_ok(dialog)
+ else:
+ self.just_destroy(dialog)
+
def just_destroy(self, widget):
self.destroy()
@@ -1192,7 +1196,7 @@ class NewChatDialog(InputDialog):
title = _('Start Chat with account %s') % account
else:
title = _('Start Chat')
- prompt_text = _('Fill in the jid, or nick of the contact you would like\nto send a chat message to:')
+ prompt_text = _('Fill in the nickname or the Jabber ID of the contact you would like\nto send a chat message to:')
InputDialog.__init__(self, title, prompt_text, is_modal = False)
self.completion_dict = {}
@@ -1725,10 +1729,13 @@ class XMLConsoleWindow:
self.input_textview.grab_focus()
class PrivacyListWindow:
- def __init__(self, account, privacy_list, list_type):
- '''list_type can be 0 if list is created or 1 if it id edited'''
+ '''Window that is used for creating NEW or EDITING already there privacy
+ lists'''
+ def __init__(self, account, privacy_list_name, action):
+ '''action is 'edit' or 'new' depending on if we create a new priv list
+ or edit an already existing one'''
self.account = account
- self.privacy_list = privacy_list
+ self.privacy_list_name = privacy_list_name
# Dicts and Default Values
self.active_rule = ''
@@ -1740,7 +1747,7 @@ class PrivacyListWindow:
self.allow_deny = 'allow'
# Connect to glade
- self.xml = gtkgui_helpers.get_glade('privacy_list_edit_window.glade')
+ self.xml = gtkgui_helpers.get_glade('privacy_list_window.glade')
self.window = self.xml.get_widget('privacy_list_edit_window')
# Add Widgets
@@ -1762,10 +1769,10 @@ class PrivacyListWindow:
'privacy_list_default_checkbutton']:
self.__dict__[widget_to_add] = self.xml.get_widget(widget_to_add)
- # Send translations
+
self.privacy_lists_title_label.set_label(
_('Privacy List %s') % \
- gtkgui_helpers.escape_for_pango_markup(self.privacy_list))
+ gtkgui_helpers.escape_for_pango_markup(self.privacy_list_name))
if len(gajim.connections) > 1:
title = _('Privacy List for %s') % self.account
@@ -1777,8 +1784,7 @@ class PrivacyListWindow:
self.privacy_list_active_checkbutton.set_sensitive(False)
self.privacy_list_default_checkbutton.set_sensitive(False)
- # Check if list is created (0) or edited (1)
- if list_type == 1:
+ if action == 'edit':
self.refresh_rules()
count = 0
@@ -1799,16 +1805,16 @@ class PrivacyListWindow:
def on_privacy_list_edit_window_destroy(self, widget):
'''close window'''
if gajim.interface.instances[self.account].has_key('privacy_list_%s' % \
- self.privacy_list):
+ self.privacy_list_name):
del gajim.interface.instances[self.account]['privacy_list_%s' % \
- self.privacy_list]
+ self.privacy_list_name]
def check_active_default(self, a_d_dict):
- if a_d_dict['active'] == self.privacy_list:
+ if a_d_dict['active'] == self.privacy_list_name:
self.privacy_list_active_checkbutton.set_active(True)
else:
self.privacy_list_active_checkbutton.set_active(False)
- if a_d_dict['default'] == self.privacy_list:
+ 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)
@@ -1845,7 +1851,7 @@ class PrivacyListWindow:
gajim.connections[self.account].get_active_default_lists()
def refresh_rules(self):
- gajim.connections[self.account].get_privacy_list(self.privacy_list)
+ gajim.connections[self.account].get_privacy_list(self.privacy_list_name)
def on_delete_rule_button_clicked(self, widget):
tags = []
@@ -1854,7 +1860,7 @@ class PrivacyListWindow:
self.list_of_rules_combobox.get_active_text().decode('utf-8'):
tags.append(self.global_rules[rule])
gajim.connections[self.account].set_privacy_list(
- self.privacy_list, tags)
+ self.privacy_list_name, tags)
self.privacy_list_received(tags)
self.add_edit_vbox.hide()
@@ -1918,13 +1924,13 @@ class PrivacyListWindow:
def on_privacy_list_active_checkbutton_toggled(self, widget):
if widget.get_active():
- gajim.connections[self.account].set_active_list(self.privacy_list)
+ gajim.connections[self.account].set_active_list(self.privacy_list_name)
else:
gajim.connections[self.account].set_active_list(None)
def on_privacy_list_default_checkbutton_toggled(self, widget):
if widget.get_active():
- gajim.connections[self.account].set_default_list(self.privacy_list)
+ gajim.connections[self.account].set_default_list(self.privacy_list_name)
else:
gajim.connections[self.account].set_default_list(None)
@@ -1994,7 +2000,7 @@ class PrivacyListWindow:
else:
tags.append(current_tags)
- gajim.connections[self.account].set_privacy_list(self.privacy_list, tags)
+ gajim.connections[self.account].set_privacy_list(self.privacy_list_name, tags)
self.privacy_list_received(tags)
self.add_edit_vbox.hide()
@@ -2019,7 +2025,9 @@ class PrivacyListWindow:
self.add_edit_vbox.hide()
class PrivacyListsWindow:
-# To do: UTF-8 ???????
+ '''Window that is the main window for Privacy Lists;
+ we can list there the privacy lists and ask to create a new one
+ or edit an already there one'''
def __init__(self, account):
self.account = account
@@ -2027,7 +2035,7 @@ class PrivacyListsWindow:
self.privacy_lists_save = []
- self.xml = gtkgui_helpers.get_glade('privacy_lists_first_window.glade')
+ self.xml = gtkgui_helpers.get_glade('privacy_lists_window.glade')
self.window = self.xml.get_widget('privacy_lists_first_window')
for widget_to_add in ['list_of_privacy_lists_combobox',
@@ -2087,7 +2095,7 @@ class PrivacyListsWindow:
self.list_of_privacy_lists_combobox.get_active()]
gajim.connections[self.account].del_privacy_list(active_list)
self.privacy_lists_save.remove(active_list)
- self.privacy_lists_received({'lists':self.privacy_lists_save})
+ self.privacy_lists_received({'lists': self.privacy_lists_save})
def privacy_lists_received(self, lists):
if not lists:
@@ -2107,7 +2115,7 @@ class PrivacyListsWindow:
window.present()
else:
gajim.interface.instances[self.account]['privacy_list_%s' % name] = \
- PrivacyListWindow(self.account, name, 0)
+ PrivacyListWindow(self.account, name, 'new')
self.new_privacy_list_entry.set_text('')
def on_privacy_lists_refresh_button_clicked(self, widget):
@@ -2122,7 +2130,7 @@ class PrivacyListsWindow:
window.present()
else:
gajim.interface.instances[self.account]['privacy_list_%s' % name] = \
- PrivacyListWindow(self.account, name, 1)
+ PrivacyListWindow(self.account, name, 'edit')
class InvitationReceivedDialog:
def __init__(self, account, room_jid, contact_jid, password = None, comment = None):
@@ -2291,6 +2299,18 @@ class ImageChooserDialog(FileChooserDialog):
return
widget.get_preview_widget().set_from_pixbuf(pixbuf)
+class AvatarChooserDialog(ImageChooserDialog):
+ def __init__(self, path_to_file = '', on_response_ok = None,
+ on_response_cancel = None, on_response_clear = None):
+ ImageChooserDialog.__init__(self, path_to_file, on_response_ok,
+ on_response_cancel)
+ button = gtk.Button(None, gtk.STOCK_CLEAR)
+ if on_response_clear:
+ button.connect('clicked', on_response_clear)
+ button.show_all()
+ self.action_area.pack_start(button)
+ self.action_area.reorder_child(button, 0)
+
class AddSpecialNotificationDialog:
def __init__(self, jid):
'''jid is the jid for which we want to add special notification
diff --git a/src/disco.py b/src/disco.py
index f56f70a1c..e0f928f91 100644
--- a/src/disco.py
+++ b/src/disco.py
@@ -36,10 +36,10 @@
# - def update_actions(self)
# - def default_action(self)
# - def _find_item(self, jid, node)
-# - def _add_item(self, model, jid, node, item, force)
-# - def _update_item(self, model, iter, jid, node, item)
-# - def _update_info(self, model, iter, jid, node, identities, features, data)
-# - def _update_error(self, model, iter, jid, node)
+# - def _add_item(self, jid, node, item, force)
+# - def _update_item(self, iter, jid, node, item)
+# - def _update_info(self, iter, jid, node, identities, features, data)
+# - def _update_error(self, iter, jid, node)
#
# * Should call the super class for this method.
# All others do not have to call back to the super class. (but can if they want
@@ -215,8 +215,8 @@ class ServicesCache:
ServiceCache instance.'''
def __init__(self, account):
self.account = account
- self._items = CacheDictionary(15, getrefresh = False)
- self._info = CacheDictionary(15, getrefresh = False)
+ self._items = CacheDictionary(1, getrefresh = True)
+ self._info = CacheDictionary(1, getrefresh = True)
self._cbs = {}
def _clean_closure(self, cb, type, addr):
@@ -422,6 +422,7 @@ _('Without a connection, you can not browse available services'))
self.xml = gtkgui_helpers.get_glade('service_discovery_window.glade')
self.window = self.xml.get_widget('service_discovery_window')
self.services_treeview = self.xml.get_widget('services_treeview')
+ self.model = None
# This is more reliable than the cursor-changed signal.
selection = self.services_treeview.get_selection()
selection.connect_after('changed',
@@ -452,7 +453,6 @@ _('Without a connection, you can not browse available services'))
liststore = gtk.ListStore(str)
self.address_comboboxentry.set_model(liststore)
- self.address_comboboxentry.set_text_column(0)
self.latest_addresses = gajim.config.get(
'latest_disco_addresses').split()
if jid in self.latest_addresses:
@@ -720,9 +720,9 @@ class AgentBrowser:
note that the first two columns should ALWAYS be of type string and
contain the JID and node of the item respectively.'''
# JID, node, name, address
- model = gtk.ListStore(str, str, str, str)
- model.set_sort_column_id(3, gtk.SORT_ASCENDING)
- self.window.services_treeview.set_model(model)
+ self.model = gtk.ListStore(str, str, str, str)
+ self.model.set_sort_column_id(3, gtk.SORT_ASCENDING)
+ self.window.services_treeview.set_model(self.model)
# Name column
col = gtk.TreeViewColumn(_('Name'))
renderer = gtk.CellRendererText()
@@ -740,7 +740,7 @@ class AgentBrowser:
self.window.services_treeview.set_headers_visible(True)
def _clean_treemodel(self):
- self.window.services_treeview.get_model().clear()
+ self.model.clear()
for col in self.window.services_treeview.get_columns():
self.window.services_treeview.remove_column(col)
self.window.services_treeview.set_headers_visible(False)
@@ -872,8 +872,7 @@ class AgentBrowser:
def browse(self, force = False):
'''Fill the treeview with agents, fetching the info if necessary.'''
- model = self.window.services_treeview.get_model()
- model.clear()
+ self.model.clear()
self._total_items = self._progress = 0
self.window.progressbar.show()
self._pulse_timeout = gobject.timeout_add(250, self._pulse_timeout_cb)
@@ -890,21 +889,21 @@ class AgentBrowser:
def _find_item(self, jid, node):
'''Check if an item is already in the treeview. Return an iter to it
if so, None otherwise.'''
- model = self.window.services_treeview.get_model()
- iter = model.get_iter_root()
+ iter = self.model.get_iter_root()
while iter:
- cjid = model.get_value(iter, 0).decode('utf-8')
- cnode = model.get_value(iter, 1).decode('utf-8')
+ cjid = self.model.get_value(iter, 0).decode('utf-8')
+ cnode = self.model.get_value(iter, 1).decode('utf-8')
if jid == cjid and node == cnode:
break
- iter = model.iter_next(iter)
+ iter = self.model.iter_next(iter)
if iter:
return iter
return None
def _agent_items(self, jid, node, items, force):
'''Callback for when we receive a list of agent items.'''
- model = self.window.services_treeview.get_model()
+ self.model.clear()
+ self._total_items = 0
gobject.source_remove(self._pulse_timeout)
self.window.progressbar.hide()
# The server returned an error
@@ -916,53 +915,48 @@ class AgentBrowser:
_('This service does not contain any items to browse.'))
return
# We got a list of items
+ self.window.services_treeview.set_model(None)
for item in items:
jid = item['jid']
node = item.get('node', '')
- iter = self._find_item(jid, node)
- if iter:
- # Already in the treeview
- self._update_item(model, iter, jid, node, item)
- else:
- # Not in the treeview
- self._total_items += 1
- self._add_item(model, jid, node, item, force)
+ self._total_items += 1
+ self._add_item(jid, node, item, force)
+ self.window.services_treeview.set_model(self.model)
def _agent_info(self, jid, node, identities, features, data):
'''Callback for when we receive info about an agent's item.'''
addr = get_agent_address(jid, node)
- model = self.window.services_treeview.get_model()
iter = self._find_item(jid, node)
if not iter:
# Not in the treeview, stop
return
if identities == 0:
# The server returned an error
- self._update_error(model, iter, jid, node)
+ self._update_error(iter, jid, node)
else:
# We got our info
- self._update_info(model, iter, jid, node,
+ self._update_info(iter, jid, node,
identities, features, data)
self.update_actions()
- def _add_item(self, model, jid, node, item, force):
+ def _add_item(self, jid, node, item, force):
'''Called when an item should be added to the model. The result of a
disco#items query.'''
- model.append((jid, node, item.get('name', ''),
+ self.model.append((jid, node, item.get('name', ''),
get_agent_address(jid, node)))
- def _update_item(self, model, iter, jid, node, item):
+ def _update_item(self, iter, jid, node, item):
'''Called when an item should be updated in the model. The result of a
disco#items query. (seldom)'''
if item.has_key('name'):
- model[iter][2] = item['name']
+ self.model[iter][2] = item['name']
- def _update_info(self, model, iter, jid, node, identities, features, data):
+ def _update_info(self, iter, jid, node, identities, features, data):
'''Called when an item should be updated in the model with further info.
The result of a disco#info query.'''
- model[iter][2] = identities[0].get('name', '')
+ self.model[iter][2] = identities[0].get('name', '')
- def _update_error(self, model, iter, jid, node):
+ def _update_error(self, iter, jid, node):
'''Called when a disco#info query failed for an item.'''
pass
@@ -1046,14 +1040,12 @@ class ToplevelAgentBrowser(AgentBrowser):
# These are all callbacks to make tooltips work
def on_treeview_leave_notify_event(self, widget, event):
- model = widget.get_model()
props = widget.get_path_at_pos(int(event.x), int(event.y))
if self.tooltip.timeout > 0:
if not props or self.tooltip.id == props[0]:
self.tooltip.hide_tooltip()
def on_treeview_motion_notify_event(self, widget, event):
- model = widget.get_model()
props = widget.get_path_at_pos(int(event.x), int(event.y))
if self.tooltip.timeout > 0:
if not props or self.tooltip.id != props[0]:
@@ -1062,12 +1054,12 @@ class ToplevelAgentBrowser(AgentBrowser):
[row, col, x, y] = props
iter = None
try:
- iter = model.get_iter(row)
+ iter = self.model.get_iter(row)
except:
self.tooltip.hide_tooltip()
return
- jid = model[iter][0]
- state = model[iter][4]
+ jid = self.model[iter][0]
+ state = self.model[iter][4]
# Not a category, and we have something to say about state
if jid and state > 0 and \
(self.tooltip.timeout == 0 or self.tooltip.id != props[0]):
@@ -1084,10 +1076,10 @@ class ToplevelAgentBrowser(AgentBrowser):
# JID, node, icon, description, state
# State means 2 when error, 1 when fetching, 0 when succes.
view = self.window.services_treeview
- model = gtk.TreeStore(str, str, gtk.gdk.Pixbuf, str, int)
- model.set_sort_func(4, self._treemodel_sort_func)
- model.set_sort_column_id(4, gtk.SORT_ASCENDING)
- view.set_model(model)
+ self.model = gtk.TreeStore(str, str, gtk.gdk.Pixbuf, str, int)
+ self.model.set_sort_func(4, self._treemodel_sort_func)
+ self.model.set_sort_column_id(4, gtk.SORT_ASCENDING)
+ view.set_model(self.model)
col = gtk.TreeViewColumn()
# Icon Renderer
@@ -1329,41 +1321,38 @@ class ToplevelAgentBrowser(AgentBrowser):
def _create_category(self, cat, type=None):
'''Creates a category row.'''
- model = self.window.services_treeview.get_model()
cat, prio = self._friendly_category(cat, type)
- return model.append(None, ('', '', None, cat, prio))
+ return self.model.append(None, ('', '', None, cat, prio))
def _find_category(self, cat, type=None):
'''Looks up a category row and returns the iterator to it, or None.'''
- model = self.window.services_treeview.get_model()
cat, prio = self._friendly_category(cat, type)
- iter = model.get_iter_root()
+ iter = self.model.get_iter_root()
while iter:
- if model.get_value(iter, 3).decode('utf-8') == cat:
+ if self.model.get_value(iter, 3).decode('utf-8') == cat:
break
- iter = model.iter_next(iter)
+ iter = self.model.iter_next(iter)
if iter:
return iter
return None
def _find_item(self, jid, node):
- model = self.window.services_treeview.get_model()
iter = None
- cat_iter = model.get_iter_root()
+ cat_iter = self.model.get_iter_root()
while cat_iter and not iter:
- iter = model.iter_children(cat_iter)
+ iter = self.model.iter_children(cat_iter)
while iter:
- cjid = model.get_value(iter, 0).decode('utf-8')
- cnode = model.get_value(iter, 1).decode('utf-8')
+ cjid = self.model.get_value(iter, 0).decode('utf-8')
+ cnode = self.model.get_value(iter, 1).decode('utf-8')
if jid == cjid and node == cnode:
break
- iter = model.iter_next(iter)
- cat_iter = model.iter_next(cat_iter)
+ iter = self.model.iter_next(iter)
+ cat_iter = self.model.iter_next(cat_iter)
if iter:
return iter
return None
- def _add_item(self, model, jid, node, item, force):
+ def _add_item(self, jid, node, item, force):
# Row text
addr = get_agent_address(jid, node)
if item.has_key('name'):
@@ -1387,21 +1376,21 @@ class ToplevelAgentBrowser(AgentBrowser):
cat = self._find_category(*cat_args)
if not cat:
cat = self._create_category(*cat_args)
- model.append(cat, (item['jid'], item.get('node', ''), pix, descr, 1))
+ self.model.append(cat, (item['jid'], item.get('node', ''), pix, descr, 1))
self._expand_all()
# Grab info on the service
self.cache.get_info(jid, node, self._agent_info, force = force)
self._update_progressbar()
- def _update_item(self, model, iter, jid, node, item):
+ def _update_item(self, iter, jid, node, item):
addr = get_agent_address(jid, node)
if item.has_key('name'):
descr = "%s\n%s" % (item['name'], addr)
else:
descr = "%s" % addr
- model[iter][3] = descr
+ self.model[iter][3] = descr
- def _update_info(self, model, iter, jid, node, identities, features, data):
+ def _update_info(self, iter, jid, node, identities, features, data):
addr = get_agent_address(jid, node)
name = identities[0].get('name', '')
if name:
@@ -1423,32 +1412,32 @@ class ToplevelAgentBrowser(AgentBrowser):
break
# Check if we have to move categories
- old_cat_iter = model.iter_parent(iter)
- old_cat = model.get_value(old_cat_iter, 3).decode('utf-8')
- if model.get_value(old_cat_iter, 3) == cat:
+ old_cat_iter = self.model.iter_parent(iter)
+ old_cat = self.model.get_value(old_cat_iter, 3).decode('utf-8')
+ if self.model.get_value(old_cat_iter, 3) == cat:
# Already in the right category, just update
- model[iter][2] = pix
- model[iter][3] = descr
- model[iter][4] = 0
+ self.model[iter][2] = pix
+ self.model[iter][3] = descr
+ self.model[iter][4] = 0
return
# Not in the right category, move it.
- model.remove(iter)
+ self.model.remove(iter)
# Check if the old category is empty
- if not model.iter_is_valid(old_cat_iter):
+ if not self.model.iter_is_valid(old_cat_iter):
old_cat_iter = self._find_category(old_cat)
- if not model.iter_children(old_cat_iter):
- model.remove(old_cat_iter)
+ if not self.model.iter_children(old_cat_iter):
+ self.model.remove(old_cat_iter)
cat_iter = self._find_category(cat, type)
if not cat_iter:
cat_iter = self._create_category(cat, type)
- model.append(cat_iter, (jid, node, pix, descr, 0))
+ self.model.append(cat_iter, (jid, node, pix, descr, 0))
self._expand_all()
- def _update_error(self, model, iter, jid, node):
+ def _update_error(self, iter, jid, node):
addr = get_agent_address(jid, node)
- model[iter][4] = 2
+ self.model[iter][4] = 2
self._progress += 1
self._update_progressbar()
@@ -1462,11 +1451,13 @@ class MucBrowser(AgentBrowser):
# JID, node, name, users, description, fetched
# This is rather long, I'd rather not use a data_func here though.
# Users is a string, because want to be able to leave it empty.
- model = gtk.ListStore(str, str, str, str, str, bool)
- model.set_sort_column_id(2, gtk.SORT_ASCENDING)
- self.window.services_treeview.set_model(model)
+ self.model = gtk.ListStore(str, str, str, str, str, bool)
+ self.model.set_sort_column_id(2, gtk.SORT_ASCENDING)
+ self.window.services_treeview.set_model(self.model)
# Name column
col = gtk.TreeViewColumn(_('Name'))
+ col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ col.set_fixed_width(100)
renderer = gtk.CellRendererText()
col.pack_start(renderer)
col.set_attributes(renderer, text = 2)
@@ -1486,6 +1477,13 @@ class MucBrowser(AgentBrowser):
col.set_attributes(renderer, text = 4)
self.window.services_treeview.insert_column(col, -1)
col.set_resizable(True)
+ # Id column
+ col = gtk.TreeViewColumn(_('Id'))
+ renderer = gtk.CellRendererText()
+ col.pack_start(renderer)
+ col.set_attributes(renderer, text = 0)
+ self.window.services_treeview.insert_column(col, -1)
+ col.set_resizable(True)
self.window.services_treeview.set_headers_visible(True)
# Source id for idle callback used to start disco#info queries.
self._fetch_source = None
@@ -1568,7 +1566,6 @@ class MucBrowser(AgentBrowser):
# Prevent a silly warning, try again in a bit.
self._fetch_source = gobject.timeout_add(100, self._start_info_query)
return
- model = view.get_model()
# We have to do this in a pygtk <2.8 compatible way :/
#start, end = self.window.services_treeview.get_visible_range()
rect = view.get_visible_rect()
@@ -1577,7 +1574,7 @@ class MucBrowser(AgentBrowser):
try:
sx, sy = view.tree_to_widget_coords(rect.x, rect.y)
spath = view.get_path_at_pos(sx, sy)[0]
- iter = model.get_iter(spath)
+ iter = self.model.get_iter(spath)
except TypeError:
self._fetch_source = None
return
@@ -1591,14 +1588,14 @@ class MucBrowser(AgentBrowser):
except TypeError:
# We're at the end of the model, we can leave end=None though.
pass
- while iter and model.get_path(iter) != end:
- if not model.get_value(iter, 5):
- jid = model.get_value(iter, 0).decode('utf-8')
- node = model.get_value(iter, 1).decode('utf-8')
+ while iter and self.model.get_path(iter) != end:
+ if not self.model.get_value(iter, 5):
+ jid = self.model.get_value(iter, 0).decode('utf-8')
+ node = self.model.get_value(iter, 1).decode('utf-8')
self.cache.get_info(jid, node, self._agent_info)
self._fetch_source = True
return
- iter = model.iter_next(iter)
+ iter = self.model.iter_next(iter)
self._fetch_source = None
def _channel_altinfo(self, jid, node, items, name = None):
@@ -1619,22 +1616,21 @@ class MucBrowser(AgentBrowser):
self._fetch_source = None
return
else:
- model = self.window.services_treeview.get_model()
iter = self._find_item(jid, node)
if iter:
if name:
- model[iter][2] = name
- model[iter][3] = len(items) # The number of users
- model[iter][5] = True
+ self.model[iter][2] = name
+ self.model[iter][3] = len(items) # The number of users
+ self.model[iter][5] = True
self._fetch_source = None
self._query_visible()
- def _add_item(self, model, jid, node, item, force):
- model.append((jid, node, item.get('name', ''), '', '', False))
+ def _add_item(self, jid, node, item, force):
+ self.model.append((jid, node, item.get('name', ''), '', '', False))
if not self._fetch_source:
self._fetch_source = gobject.idle_add(self._start_info_query)
- def _update_info(self, model, iter, jid, node, identities, features, data):
+ def _update_info(self, iter, jid, node, identities, features, data):
name = identities[0].get('name', '')
for form in data:
typefield = form.getField('FORM_TYPE')
@@ -1644,14 +1640,14 @@ class MucBrowser(AgentBrowser):
users = form.getField('muc#roominfo_occupants')
descr = form.getField('muc#roominfo_description')
if users:
- model[iter][3] = users.getValue()
+ self.model[iter][3] = users.getValue()
if descr:
- model[iter][4] = descr.getValue()
+ self.model[iter][4] = descr.getValue()
# Only set these when we find a form with additional info
# Some servers don't support forms and put extra info in
# the name attribute, so we preserve it in that case.
- model[iter][2] = name
- model[iter][5] = True
+ self.model[iter][2] = name
+ self.model[iter][5] = True
break
else:
# We didn't find a form, switch to alternate query mode
@@ -1661,7 +1657,7 @@ class MucBrowser(AgentBrowser):
self._fetch_source = None
self._query_visible()
- def _update_error(self, model, iter, jid, node):
+ def _update_error(self, iter, jid, node):
# switch to alternate query mode
self.cache.get_items(jid, node, self._channel_altinfo)
diff --git a/src/filetransfers_window.py b/src/filetransfers_window.py
index 0e31e8a4e..3560d784c 100644
--- a/src/filetransfers_window.py
+++ b/src/filetransfers_window.py
@@ -246,11 +246,16 @@ _('Connection with peer cannot be established.'))
gtk.RESPONSE_OK,
True, # select multiple true as we can select many files to send
gajim.config.get('last_send_dir'),
+ on_response_ok = on_ok,
+ on_response_cancel = lambda e:dialog.destroy()
)
- btn = dialog.add_button(_('_Send'), gtk.RESPONSE_OK)
- btn.set_use_stock(True) # FIXME: add send icon to this button (JUMP_TO)
- btn.connect('clicked', on_ok)
+ btn = gtk.Button(_('_Send'))
+ btn.set_property('can-default', True)
+ # FIXME: add send icon to this button (JUMP_TO)
+ dialog.add_action_widget(btn, gtk.RESPONSE_OK)
+ dialog.set_default_response(gtk.RESPONSE_OK)
+ btn.show()
def send_file(self, account, contact, file_path):
''' start the real transfer(upload) of the file '''
@@ -450,8 +455,10 @@ _('Connection with peer cannot be established.'))
for ev_type in ('file-error', 'file-completed', 'file-request-error',
'file-send-error', 'file-stopped'):
for event in gajim.events.get_events(account, jid, [ev_type]):
- if event.parameters[1]['sid'] == file_props['sid']:
+ if event.parameters['sid'] == file_props['sid']:
gajim.events.remove_events(account, jid, event)
+ gajim.interface.roster.draw_contact(jid, account)
+ gajim.interface.roster.show_title()
del(self.files_props[sid[0]][sid[1:]])
del(file_props)
diff --git a/src/gajim-remote.py b/src/gajim-remote.py
index 563cc121a..ffe9db7fc 100755
--- a/src/gajim-remote.py
+++ b/src/gajim-remote.py
@@ -51,13 +51,10 @@ def send_error(error_message):
try:
import dbus
-except:
- raise exceptions.DbusNotSupported
-
-_version = getattr(dbus, 'version', (0, 20, 0))
-if _version[1] >= 41:
import dbus.service
import dbus.glib
+except:
+ raise exceptions.DbusNotSupported
OBJ_PATH = '/org/gajim/dbus/RemoteObject'
INTERFACE = 'org.gajim.dbus.RemoteInterface'
@@ -320,14 +317,8 @@ class GajimRemote:
except:
raise exceptions.SessionBusNotPresent
- if _version[1] >= 30:
- obj = self.sbus.get_object(SERVICE, OBJ_PATH)
- interface = dbus.Interface(obj, INTERFACE)
- elif _version[1] < 30:
- self.service = self.sbus.get_service(SERVICE)
- interface = self.service.get_object(OBJ_PATH, INTERFACE)
- else:
- send_error(_('Unknown D-Bus version: %s') % _version[1])
+ obj = self.sbus.get_object(SERVICE, OBJ_PATH)
+ interface = dbus.Interface(obj, INTERFACE)
# get the function asked
self.method = interface.__getattr__(self.command)
@@ -447,10 +438,7 @@ class GajimRemote:
''' calls self.method with arguments from sys.argv[2:] '''
args = sys.argv[2:]
args = [i.decode(PREFERRED_ENCODING) for i in sys.argv[2:]]
- if _version[1] >= 41:
- args = [dbus.String(i) for i in args]
- else:
- args = [i.encode('UTF-8') for i in sys.argv[2:]]
+ args = [dbus.String(i) for i in args]
try:
res = self.method(*args)
return res
diff --git a/src/gajim.py b/src/gajim.py
index b8e96dd63..70d8bfe54 100755
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -552,7 +552,7 @@ class Interface:
chat_control = self.msg_win_mgr.get_control(jid, account)
# Handle chat states
- contact = gajim.contacts.get_contact(account, jid, resource)
+ contact = gajim.contacts.get_contact(account, jid)
if contact and isinstance(contact, list):
contact = contact[0]
if contact:
@@ -582,7 +582,10 @@ class Interface:
if gajim.config.get('ignore_unknown_contacts') and \
not gajim.contacts.get_contact(account, jid) and not pm:
return
-
+ if not contact:
+ # contact is not in the roster, create a fake one to display
+ # notification
+ contact = common.contacts.Contact(jid = jid, resource = resource)
advanced_notif_num = notify.get_advanced_notification('message_received',
account, contact)
@@ -606,7 +609,7 @@ class Interface:
msg = message
if subject:
msg = _('Subject: %s') % subject + '\n' + msg
- notify.notify('new_message', jid, account, [msg_type, first, nickname,
+ notify.notify('new_message', full_jid_with_resource, account, [msg_type, first, nickname,
msg], advanced_notif_num)
if self.remote_ctrl:
@@ -851,6 +854,7 @@ class Interface:
self.remote_ctrl.raise_signal('LastStatusTime', (account, array))
def handle_event_os_info(self, account, array):
+ #'OS_INFO' (account, (jid, resource, client_info, os_info))
win = None
if self.instances[account]['infos'].has_key(array[0]):
win = self.instances[account]['infos'][array[0]]
@@ -1010,7 +1014,7 @@ class Interface:
return
# Add it to roster
contact = gajim.contacts.create_contact(jid = jid, name = name,
- groups = groups, show = 'offline', sub = sub, ask = ask)
+ groups = groups, show = 'offline', sub = sub, ask = ask)
gajim.contacts.add_contact(account, contact)
self.roster.add_contact_to_roster(jid, account)
else:
@@ -1075,7 +1079,7 @@ class Interface:
gmail_messages_list = array[2]
if gajim.config.get('notify_on_new_gmail_email'):
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
- 'single_msg_recv.png') #FIXME: find a better image
+ 'new_email_recv.png')
title = _('New E-mail on %(gmail_mail_address)s') % \
{'gmail_mail_address': jid}
text = i18n.ngettext('You have %d new E-mail message', 'You have %d new E-mail messages', gmail_new_messages, gmail_new_messages, gmail_new_messages)
@@ -1328,7 +1332,9 @@ class Interface:
self.instances[account]['xml_console'].print_stanza(stanza, 'outgoing')
def handle_event_vcard_published(self, account, array):
- dialogs.InformationDialog(_('vCard publication succeeded'), _('Your personal information has been published successfully.'))
+ if self.instances[account].has_key('profile'):
+ win = self.instances[account]['profile']
+ win.vcard_published()
for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC):
if gc_control.account == account:
show = gajim.SHOW_LIST[gajim.connections[account].connected]
@@ -1337,7 +1343,9 @@ class Interface:
gc_control.room_jid, show, status)
def handle_event_vcard_not_published(self, account, array):
- dialogs.InformationDialog(_('vCard publication failed'), _('There was an error while publishing your personal information, try again later.'))
+ if self.instances[account].has_key('profile'):
+ win = self.instances[account]['profile']
+ win.vcard_not_published()
def handle_event_signed_in(self, account, empty):
'''SIGNED_IN event is emitted when we sign in, so handle it'''
@@ -1406,10 +1414,10 @@ class Interface:
if response == gtk.RESPONSE_OK:
new_name = dlg.input_entry.get_text()
print 'account, data', account, data, new_name
- gajim.config.set_per('accounts', gajim.LOCAL_ACC, 'name', new_name)
+ gajim.config.set_per('accounts', account, 'name', new_name)
status = gajim.connections[account].status
- print 'status', status
- gajim.connections[account].reconnect()
+ gajim.connections[account].username = new_name
+ gajim.connections[account].change_status(status, '')
def read_sleepy(self):
@@ -1744,14 +1752,17 @@ class Interface:
jid = gajim.get_jid_without_resource(jid)
if type_ in ('printed_gc_msg', 'gc_msg'):
w = self.msg_win_mgr.get_window(jid, account)
- elif type_ in ('printed_chat', 'chat'):
+ elif type_ in ('printed_chat', 'chat', ''):
+ # '' is for log in/out notifications
if self.msg_win_mgr.has_window(fjid, account):
w = self.msg_win_mgr.get_window(fjid, account)
+ elif self.msg_win_mgr.has_window(jid, account):
+ w = self.msg_win_mgr.get_window(jid, account)
else:
contact = gajim.contacts.get_contact(account, jid, resource)
- if isinstance(contact, list):
+ if not contact or isinstance(contact, list):
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
- self.roster.new_chat(contact, account, resource = resource)
+ self.roster.new_chat(contact, account)
w = self.msg_win_mgr.get_window(fjid, account)
gajim.last_message_time[account][jid] = 0 # long time ago
elif type_ in ('printed_pm', 'pm'):
@@ -1774,9 +1785,15 @@ class Interface:
elif type_ in ('normal', 'file-request', 'file-request-error',
'file-send-error', 'file-error', 'file-stopped', 'file-completed'):
# Get the first single message event
- event = gajim.events.get_first_event(account, jid, type_)
- # Open the window
- self.roster.open_event(account, jid, event)
+ event = gajim.events.get_first_event(account, fjid, type_)
+ if not event:
+ # default to jid without resource
+ event = gajim.events.get_first_event(account, jid, type_)
+ # Open the window
+ self.roster.open_event(account, jid, event)
+ else:
+ # Open the window
+ self.roster.open_event(account, fjid, event)
elif type_ == 'gmail':
if gajim.config.get_per('accounts', account, 'savepass'):
url = ('http://www.google.com/accounts/ServiceLoginAuth?service=mail&Email=%s&Passwd=%s&continue=https://mail.google.com/mail') %\
@@ -1873,9 +1890,12 @@ class Interface:
for account in gajim.config.get_per('accounts'):
if account != gajim.ZEROCONF_ACC_NAME:
gajim.connections[account] = common.connection.Connection(account)
-
+
+ # gtk hooks
gtk.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
gtk.about_dialog_set_url_hook(self.on_launch_browser_mailer, 'url')
+ if gtk.pygtk_version >= (2, 10, 0) and gtk.gtk_version >= (2, 10, 0):
+ gtk.link_button_set_uri_hook(self.on_launch_browser_mailer, 'url')
self.instances = {'logs': {}}
@@ -1919,6 +1939,8 @@ class Interface:
self.systray_capabilities = False
if os.name == 'nt':
+ pass
+ '''
try:
import systraywin32
except: # user doesn't have trayicon capabilities
@@ -1926,6 +1948,7 @@ class Interface:
else:
self.systray_capabilities = True
self.systray = systraywin32.SystrayWin32()
+ '''
else:
self.systray_capabilities = systray.HAS_SYSTRAY_CAPABILITIES
if self.systray_capabilities:
diff --git a/src/gajim_themes_window.py b/src/gajim_themes_window.py
index cc9b1742e..ef40d4b22 100644
--- a/src/gajim_themes_window.py
+++ b/src/gajim_themes_window.py
@@ -51,7 +51,7 @@ class GajimThemesWindow:
self.themes_tree = self.xml.get_widget('themes_treeview')
self.theme_options_vbox = self.xml.get_widget('theme_options_vbox')
self.colorbuttons = {}
- for chatstate in ('active', 'inactive', 'composing', 'paused', 'gone',
+ for chatstate in ('inactive', 'composing', 'paused', 'gone',
'muc_msg', 'muc_directed_msg'):
self.colorbuttons[chatstate] = self.xml.get_widget(chatstate + \
'_colorbutton')
@@ -198,7 +198,7 @@ class GajimThemesWindow:
self.no_update = False
gajim.interface.roster.change_roster_style(None)
- for chatstate in ('active', 'inactive', 'composing', 'paused', 'gone',
+ for chatstate in ('inactive', 'composing', 'paused', 'gone',
'muc_msg', 'muc_directed_msg'):
color = gajim.config.get_per('themes', theme, 'state_' + chatstate + \
'_color')
@@ -333,11 +333,6 @@ class GajimThemesWindow:
font_props[1] = True
return font_props
- def on_active_colorbutton_color_set(self, widget):
- self.no_update = True
- self._set_color(True, widget, 'state_active_color')
- self.no_update = False
-
def on_inactive_colorbutton_color_set(self, widget):
self.no_update = True
self._set_color(True, widget, 'state_inactive_color')
diff --git a/src/groupchat_control.py b/src/groupchat_control.py
index 38e20e5bf..607053d48 100644
--- a/src/groupchat_control.py
+++ b/src/groupchat_control.py
@@ -226,9 +226,6 @@ class GroupchatControl(ChatControlBase):
self.gc_popup_menu = xm.get_widget('gc_control_popup_menu')
self.name_label = self.xml.get_widget('banner_name_label')
- id = self.parent_win.window.connect('focus-in-event',
- self._on_window_focus_in_event)
- self.handlers[id] = self.parent_win.window
# set the position of the current hpaned
self.hpaned_position = gajim.config.get('gc-hpaned-position')
@@ -320,11 +317,6 @@ class GroupchatControl(ChatControlBase):
return gajim.config.get('notify_on_all_muc_messages') or \
self.attention_flag
- def _on_window_focus_in_event(self, widget, event):
- '''When window gets focus'''
- if self.parent_win.get_active_jid() == self.room_jid:
- self.conv_textview.allow_focus_out_line = True
-
def on_treeview_size_allocate(self, widget, allocation):
'''The MUC treeview has resized. Move the hpaned in all tabs to match'''
self.hpaned_position = self.hpaned.get_position()
@@ -371,23 +363,24 @@ class GroupchatControl(ChatControlBase):
has_focus = self.parent_win.window.get_property('has-toplevel-focus')
current_tab = self.parent_win.get_active_control() == self
+ color_name = None
color = None
theme = gajim.config.get('roster_theme')
if chatstate == 'attention' and (not has_focus or not current_tab):
self.attention_flag = True
- color = gajim.config.get_per('themes', theme,
+ color_name = gajim.config.get_per('themes', theme,
'state_muc_directed_msg_color')
elif chatstate:
if chatstate == 'active' or (current_tab and has_focus):
self.attention_flag = False
- color = gajim.config.get_per('themes', theme,
- 'state_active_color')
+ # get active color from gtk
+ color = self.parent_win.notebook.style.fg[gtk.STATE_ACTIVE]
elif chatstate == 'newmsg' and (not has_focus or not current_tab) and\
not self.attention_flag:
- color = gajim.config.get_per('themes', theme, 'state_muc_msg_color')
- if color:
- color = gtk.gdk.colormap_get_system().alloc_color(color)
-
+ color_name = gajim.config.get_per('themes', theme, 'state_muc_msg_color')
+ if color_name:
+ color = gtk.gdk.colormap_get_system().alloc_color(color_name)
+
label_str = self.name
return (label_str, color)
@@ -865,7 +858,9 @@ class GroupchatControl(ChatControlBase):
print_status = gajim.config.get('print_status_in_muc')
nick_jid = nick
if jid:
- nick_jid += ' (%s)' % jid
+ # delete ressource
+ simple_jid = gajim.get_jid_without_resource(jid)
+ nick_jid += ' (%s)' % simple_jid
if show == 'offline' and print_status in ('all', 'in_and_out'):
st = _('%s has left') % nick_jid
if reason:
@@ -1268,6 +1263,10 @@ class GroupchatControl(ChatControlBase):
del self.handlers[i]
def allow_shutdown(self):
+ model, iter = self.list_treeview.get_selection().get_selected()
+ if iter:
+ self.list_treeview.get_selection().unselect_all()
+ return False
retval = True
includes = gajim.config.get('confirm_close_muc_rooms').split(' ')
excludes = gajim.config.get('noconfirm_close_muc_rooms').split(' ')
@@ -1293,6 +1292,7 @@ class GroupchatControl(ChatControlBase):
return retval
def set_control_active(self, state):
+ self.conv_textview.allow_focus_out_line = True
self.attention_flag = False
ChatControlBase.set_control_active(self, state)
if not state:
@@ -1453,7 +1453,11 @@ class GroupchatControl(ChatControlBase):
def on_list_treeview_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
- widget.get_selection().unselect_all()
+ selection = widget.get_selection()
+ model, iter = selection.get_selected()
+ if iter:
+ widget.get_selection().unselect_all()
+ return True
def on_list_treeview_row_expanded(self, widget, iter, path):
'''When a row is expanded: change the icon of the arrow'''
diff --git a/src/gtkgui_helpers.py b/src/gtkgui_helpers.py
index fb6e3eaa8..7b99c3ae9 100644
--- a/src/gtkgui_helpers.py
+++ b/src/gtkgui_helpers.py
@@ -390,7 +390,7 @@ def possibly_move_window_in_current_desktop(window):
current virtual desktop
window is GTK window'''
if os.name == 'nt':
- return
+ return False
root_window = gtk.gdk.screen_get_default().get_root_window()
# current user's vd
@@ -406,6 +406,8 @@ def possibly_move_window_in_current_desktop(window):
# we are in another VD that the window was
# so show it in current VD
window.present()
+ return True
+ return False
def file_is_locked(path_to_file):
'''returns True if file is locked (WINDOWS ONLY)'''
@@ -680,6 +682,14 @@ default_name = ''):
file_path = dialog.get_filename()
file_path = decode_filechooser_file_paths((file_path,))[0]
if os.path.exists(file_path):
+ # check if we have write permissions
+ if not os.access(file_path, os.W_OK):
+ file_name = os.path.basename(file_path)
+ dialogs.ErrorDialog(_('Cannot overwrite existing file "%s"' %
+ file_name),
+ _('A file with this name already exists and you do not have '
+ 'permission to overwrite it.'))
+ return
dialog2 = dialogs.FTOverwriteConfirmationDialog(
_('This file already exists'), _('What do you want to do?'),
False)
@@ -688,6 +698,13 @@ default_name = ''):
response = dialog2.get_response()
if response < 0:
return
+ else:
+ dirname = os.path.dirname(file_path)
+ if not os.access(dirname, os.W_OK):
+ dialogs.ErrorDialog(_('Directory "%s" is not writable') % \
+ dirname, _('You do not have permission to create files in this'
+ ' directory.'))
+ return
# Get pixbuf
pixbuf = None
@@ -710,8 +727,8 @@ default_name = ''):
try:
pixbuf.save(file_path, type_)
except:
- #XXX Check for permissions
- os.remove(file_path)
+ if os.path.exists(file_path):
+ os.remove(file_path)
new_file_path = '.'.join(file_path.split('.')[:-1]) + '.jpeg'
dialog2 = dialogs.ConfirmationDialog(_('Extension not supported'),
_('Image cannot be saved in %(type)s format. Save as %(new_filename)s?') % {'type': type_, 'new_filename': new_file_path},
@@ -735,3 +752,6 @@ default_name = ''):
dialog.set_current_name(default_name)
dialog.connect('delete-event', lambda widget, event:
on_cancel(widget))
+
+def on_bm_header_changed_state(widget, event):
+ widget.set_state(gtk.STATE_NORMAL) #do not allow selected_state
diff --git a/src/message_window.py b/src/message_window.py
index f190956c8..42be57e1c 100644
--- a/src/message_window.py
+++ b/src/message_window.py
@@ -104,6 +104,16 @@ class MessageWindow:
self.notebook.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.DND_TARGETS,
gtk.gdk.ACTION_MOVE)
+ def change_account_name(self, old_name, new_name):
+ if self._controls.has_key(old_name):
+ self._controls[new_name] = self._controls[old_name]
+ del self._controls[old_name]
+ for ctrl in self.controls():
+ if ctrl.account == old_name:
+ ctrl.account = new_name
+ if self.account == old_name:
+ self.account = new_name
+
def get_num_controls(self):
n = 0
for dict in self._controls.values():
@@ -618,7 +628,11 @@ class MessageWindowMgr:
# 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)
-
+
+ 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)
# we track the lifetime of this window
diff --git a/src/music_track_listener.py b/src/music_track_listener.py
new file mode 100644
index 000000000..b5bccbdc4
--- /dev/null
+++ b/src/music_track_listener.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+## musictracklistener.py
+##
+## Copyright (C) 2006 Gustavo Carneiro
+## Copyright (C) 2006 Nikos Kouremenos
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 2 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+import gobject
+import dbus_support
+if dbus_support.supported:
+ import dbus
+ import dbus.glib
+
+class MusicTrackInfo(object):
+ __slots__ = ['title', 'album', 'artist', 'duration', 'track_number']
+
+
+class MusicTrackListener(gobject.GObject):
+ __gsignals__ = { 'music-track-changed': (gobject.SIGNAL_RUN_LAST, None,
+ (object,)) }
+
+ _instance = None
+ @classmethod
+ def get(cls):
+ if cls._instance is None:
+ cls._instance = cls()
+ return cls._instance
+
+ def __init__(self):
+ super(MusicTrackListener, self).__init__()
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(self._muine_music_track_change_cb, 'SongChanged',
+ 'org.gnome.Muine.Player')
+ bus.add_signal_receiver(self._rhythmbox_music_track_change_cb,
+ 'playingUriChanged', 'org.gnome.Rhythmbox.Player')
+
+ def _muine_properties_extract(self, song_string):
+ d = dict((x.strip() for x in s1.split(':', 1)) for s1 in song_string.split('\n'))
+ info = MusicTrackInfo()
+ info.title = d['title']
+ info.album = d['album']
+ info.artist = d['artist']
+ info.duration = int(d['duration'])
+ info.track_number = int(d['track_number'])
+ return info
+
+ def _muine_music_track_change_cb(self, arg):
+ info = self._muine_properties_extract(arg)
+ self.emit('music-track-changed', info)
+
+ def _rhythmbox_properties_extract(self, props):
+ info = MusicTrackInfo()
+ info.title = props['title']
+ info.album = props['album']
+ info.artist = props['artist']
+ info.duration = int(props['duration'])
+ info.track_number = int(props['track-number'])
+ return info
+
+ def _rhythmbox_music_track_change_cb(self, uri):
+ 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 get_playing_track(self):
+ '''Return a MusicTrackInfo for the currently playing
+ song, or None if no song is playing'''
+
+ bus = dbus.SessionBus()
+
+ ## Check Muine playing track
+ if dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(),
+ 'org.gnome.Muine'):
+ obj = bus.get_object('org.gnome.Muine', '/org/gnome/Muine/Player')
+ player = dbus.Interface(obj, 'org.gnome.Muine.Player')
+ if player.GetPlaying():
+ song_string = player.GetCurrentSong()
+ song = self._muine_properties_extract(song_string)
+ return song
+
+ ## Check Rhythmbox playing song
+ if dbus.dbus_bindings.bus_name_has_owner(bus.get_connection(),
+ 'org.gnome.Rhythmbox'):
+ rbshellobj = bus.get_object('org.gnome.Rhythmbox', '/org/gnome/Rhythmbox/Shell')
+ player = dbus.Interface(
+ bus.get_object('org.gnome.Rhythmbox', '/org/gnome/Rhythmbox/Player'),
+ 'org.gnome.Rhythmbox.Player')
+ rbshell = dbus.Interface(rbshellobj, 'org.gnome.Rhythmbox.Shell')
+ uri = player.getPlayingUri()
+ props = rbshell.getSongProperties(uri)
+ info = self._rhythmbox_properties_extract(props)
+ return info
+
+ return None
+
+# here we test :)
+if __name__ == '__main__':
+ def music_track_change_cb(listener, music_track_info):
+ print music_track_info.title
+ listener = MusicTrackListener.get()
+ listener.connect('music-track-changed', music_track_change_cb)
+ track = listener.get_playing_track()
+ if track is None:
+ print 'Now not playing anything'
+ else:
+ print 'Now playing: "%s" by %s' % (track.title, track.artist)
+ gobject.MainLoop().run()
diff --git a/src/notify.py b/src/notify.py
index 908ff25d9..79fd847a9 100644
--- a/src/notify.py
+++ b/src/notify.py
@@ -28,9 +28,8 @@ from common import helpers
import dbus_support
if dbus_support.supported:
import dbus
- if dbus_support.version >= (0, 41, 0):
- import dbus.glib
- import dbus.service
+ import dbus.glib
+ import dbus.service
def get_show_in_roster(event, account, contact):
'''Return True if this event must be shown in roster, else False'''
@@ -42,11 +41,9 @@ def get_show_in_roster(event, account, contact):
return False
if event == 'message_received':
chat_control = helpers.get_chat_control(account, contact)
- if not chat_control:
- return True
- elif event == 'ft_request':
- return True
- return False
+ if chat_control:
+ return False
+ return True
def get_show_in_systray(event, account, contact):
'''Return True if this event must be shown in roster, else False'''
@@ -56,10 +53,7 @@ def get_show_in_systray(event, account, contact):
return True
if gajim.config.get_per('notifications', str(num), 'systray') == 'no':
return False
- if event in ('message_received', 'ft_request', 'gc_msg_highlight',
- 'ft_request'):
- return True
- return False
+ return True
def get_advanced_notification(event, account, contact):
'''Returns the number of the first advanced notification or None'''
diff --git a/src/profile_window.py b/src/profile_window.py
index 41bbd8de3..badcbb310 100644
--- a/src/profile_window.py
+++ b/src/profile_window.py
@@ -60,17 +60,40 @@ class ProfileWindow:
def __init__(self, account):
self.xml = gtkgui_helpers.get_glade('profile_window.glade')
self.window = self.xml.get_widget('profile_window')
+ self.progressbar = self.xml.get_widget('progressbar')
+ self.statusbar = self.xml.get_widget('statusbar')
+ self.context_id = self.statusbar.get_context_id('profile')
self.account = account
self.jid = gajim.get_jid_from_account(account)
self.avatar_mime_type = None
self.avatar_encoded = None
+ self.message_id = self.statusbar.push(self.context_id,
+ _('Retrieving profile...'))
+ self.update_progressbar_timeout_id = gobject.timeout_add(100,
+ self.update_progressbar)
+ self.remove_statusbar_timeout_id = None
+ # Create Image for avatar button
+ image = gtk.Image()
+ self.xml.get_widget('PHOTO_button').set_image(image)
self.xml.signal_autoconnect(self)
self.window.show_all()
+ def update_progressbar(self):
+ self.progressbar.pulse()
+ return True # loop forever
+
+ def remove_statusbar(self, message_id):
+ self.statusbar.remove(self.context_id, message_id)
+ self.remove_statusbar_timeout_id = None
+
def on_profile_window_destroy(self, widget):
+ if self.update_progressbar_timeout_id is not None:
+ gobject.source_remove(self.update_progressbar_timeout_id)
+ if self.remove_statusbar_timeout_id is not None:
+ gobject.source_remove(self.remove_statusbar_timeout_id)
del gajim.interface.instances[self.account]['profile']
def on_profile_window_key_press_event(self, widget, event):
@@ -79,8 +102,10 @@ class ProfileWindow:
def on_clear_button_clicked(self, widget):
# empty the image
- self.xml.get_widget('PHOTO_image').set_from_icon_name('stock_person',
- gtk.ICON_SIZE_DIALOG)
+ button = self.xml.get_widget('PHOTO_button')
+ image = button.get_image()
+ image.set_from_pixbuf(None)
+ button.set_label(_('Click to set your avatar'))
self.avatar_encoded = None
self.avatar_mime_type = None
@@ -124,24 +149,39 @@ class ProfileWindow:
pixbuf = gtkgui_helpers.get_pixbuf_from_data(data)
# rescale it
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
- image = self.xml.get_widget('PHOTO_image')
+ button = self.xml.get_widget('PHOTO_button')
+ image = button.get_image()
image.set_from_pixbuf(pixbuf)
+ button.set_label('')
self.avatar_encoded = base64.encodestring(data)
# returns None if unknown type
self.avatar_mime_type = mimetypes.guess_type(path_to_file)[0]
- self.dialog = dialogs.ImageChooserDialog(on_response_ok = on_ok)
+ def on_clear(widget):
+ self.dialog.destroy()
+ self.on_clear_button_clicked(widget)
+
+ self.dialog = dialogs.AvatarChooserDialog(on_response_ok = on_ok,
+ on_response_clear = on_clear)
def on_PHOTO_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3 and self.avatar_encoded: # right click
menu = gtk.Menu()
- nick = gajim.config.get_per('accounts', self.account, 'name')
- menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
- menuitem.connect('activate',
- gtkgui_helpers.on_avatar_save_as_menuitem_activate,
- self.jid, None, nick + '.jpeg')
- menu.append(menuitem)
+
+ # Try to get pixbuf
+ is_fake = False
+ if account and gajim.contacts.is_pm_from_jid(account, jid):
+ is_fake = True
+ pixbuf = get_avatar_pixbuf_from_cache(jid, is_fake)
+
+ if pixbuf:
+ nick = gajim.config.get_per('accounts', self.account, 'name')
+ menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
+ menuitem.connect('activate',
+ gtkgui_helpers.on_avatar_save_as_menuitem_activate,
+ self.jid, None, nick + '.jpeg')
+ menu.append(menuitem)
# show clear
menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
menuitem.connect('activate', self.on_clear_button_clicked)
@@ -162,18 +202,23 @@ class ProfileWindow:
def set_values(self, vcard):
if not 'PHOTO' in vcard:
# set default image
- image = self.xml.get_widget('PHOTO_image')
- image.set_from_icon_name('stock_person', gtk.ICON_SIZE_DIALOG)
+ button = self.xml.get_widget('PHOTO_button')
+ image = button.get_image()
+ image.set_from_pixbuf(None)
+ button.set_label(_('Click to set your avatar'))
for i in vcard.keys():
if i == 'PHOTO':
pixbuf, self.avatar_encoded, self.avatar_mime_type = \
get_avatar_pixbuf_encoded_mime(vcard[i])
- image = self.xml.get_widget('PHOTO_image')
+ button = self.xml.get_widget('PHOTO_button')
+ image = button.get_image()
if not pixbuf:
- image.set_from_icon_name('stock_person', gtk.ICON_SIZE_DIALOG)
+ image.set_from_pixbuf(None)
+ button.set_label(_('Click to set your avatar'))
continue
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
image.set_from_pixbuf(pixbuf)
+ button.set_label('')
continue
if i == 'ADR' or i == 'TEL' or i == 'EMAIL':
for entry in vcard[i]:
@@ -191,6 +236,18 @@ class ProfileWindow:
vcard[i], 0)
else:
self.set_value(i + '_entry', vcard[i])
+ if self.update_progressbar_timeout_id is not None:
+ if self.message_id:
+ self.statusbar.remove(self.context_id, self.message_id)
+ self.message_id = self.statusbar.push(self.context_id,
+ _('Information received'))
+ self.remove_statusbar_timeout_id = gobject.timeout_add(3000,
+ self.remove_statusbar, self.message_id)
+ gobject.source_remove(self.update_progressbar_timeout_id)
+ # redraw progressbar after avatar is set so that windows is already
+ # resized. Else progressbar is not correctly redrawn
+ gobject.idle_add(self.progressbar.set_fraction, 0)
+ self.update_progressbar_timeout_id = None
def add_to_vcard(self, vcard, entry, txt):
'''Add an information to the vCard dictionary'''
@@ -248,6 +305,9 @@ class ProfileWindow:
return vcard
def on_publish_button_clicked(self, widget):
+ if self.update_progressbar_timeout_id:
+ # Operation in progress
+ return
if gajim.connections[self.account].connected < 2:
dialogs.ErrorDialog(_('You are not connected to the server'),
_('Without a connection you can not publish your contact '
@@ -261,8 +321,42 @@ class ProfileWindow:
nick = gajim.config.get_per('accounts', self.account, 'name')
gajim.nicks[self.account] = nick
gajim.connections[self.account].send_vcard(vcard)
+ self.message_id = self.statusbar.push(self.context_id,
+ _('Sending profile...'))
+ self.update_progressbar_timeout_id = gobject.timeout_add(100,
+ self.update_progressbar)
+
+ def vcard_published(self):
+ if self.message_id:
+ self.statusbar.remove(self.context_id, self.message_id)
+ self.message_id = self.statusbar.push(self.context_id,
+ _('Information published'))
+ self.remove_statusbar_timeout_id = gobject.timeout_add(3000,
+ self.remove_statusbar, self.message_id)
+ if self.update_progressbar_timeout_id is not None:
+ gobject.source_remove(self.update_progressbar_timeout_id)
+ self.progressbar.set_fraction(0)
+ self.update_progressbar_timeout_id = None
+
+ def vcard_not_published(self):
+ if self.message_id:
+ self.statusbar.remove(self.context_id, self.message_id)
+ self.message_id = self.statusbar.push(self.context_id,
+ _('Information NOT published'))
+ self.remove_statusbar_timeout_id = gobject.timeout_add(3000,
+ self.remove_statusbar, self.message_id)
+ if self.update_progressbar_timeout_id is not None:
+ gobject.source_remove(self.update_progressbar_timeout_id)
+ self.progressbar.set_fraction(0)
+ self.update_progressbar_timeout_id = None
+ dialogs.InformationDialog(_('vCard publication failed'),
+ _('There was an error while publishing your personal information, '
+ 'try again later.'))
def on_retrieve_button_clicked(self, widget):
+ if self.update_progressbar_timeout_id:
+ # Operation in progress
+ return
entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
@@ -275,9 +369,18 @@ class ProfileWindow:
for e in entries:
self.xml.get_widget(e + '_entry').set_text('')
self.xml.get_widget('DESC_textview').get_buffer().set_text('')
- self.xml.get_widget('PHOTO_image').set_from_icon_name('stock_person',
- gtk.ICON_SIZE_DIALOG)
+ button = self.xml.get_widget('PHOTO_button')
+ image = button.get_image()
+ image.set_from_pixbuf(None)
+ button.set_label(_('Click to set your avatar'))
gajim.connections[self.account].request_vcard(self.jid)
else:
dialogs.ErrorDialog(_('You are not connected to the server'),
- _('Without a connection, you can not get your contact information.'))
+ _('Without a connection, you can not get your contact information.'))
+ self.message_id = self.statusbar.push(self.context_id,
+ _('Retrieving profile...'))
+ self.update_progressbar_timeout_id = gobject.timeout_add(100,
+ self.update_progressbar)
+
+ def on_close_button_clicked(self, widget):
+ self.window.destroy()
diff --git a/src/remote_control.py b/src/remote_control.py
index 05156cf84..0ce901e24 100644
--- a/src/remote_control.py
+++ b/src/remote_control.py
@@ -1,19 +1,9 @@
## remote_control.py
##
-## Contributors for this file:
-## - Yann Le Boulanger
-## - Nikos Kouremenos
-## - Dimitur Kirov
-## - Andrew Sayman
-##
-## Copyright (C) 2003-2004 Yann Le Boulanger
-## Vincent Hanquez
-## Copyright (C) 2005 Yann Le Boulanger
-## Vincent Hanquez
-## Nikos Kouremenos
-## Dimitur Kirov
-## Travis Shirk
-## Norman Rasmussen
+## Copyright (C) 2005-2006 Yann Le Boulanger
+## Copyright (C) 2005-2006 Nikos Kouremenos
+## Copyright (C) 2005-2006 Dimitur Kirov
+## Copyright (C) 2005-2006 Andrew Sayman
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@@ -36,57 +26,30 @@ from dialogs import AddNewContactWindow, NewChatDialog
import dbus_support
if dbus_support.supported:
import dbus
- if dbus_support.version >= (0, 41, 0):
+ if dbus_support:
import dbus.service
- import dbus.glib # cause dbus 0.35+ doesn't return signal replies without it
- DbusPrototype = dbus.service.Object
- elif dbus_support.version >= (0, 20, 0):
- DbusPrototype = dbus.Object
- else: #dbus is not defined
- DbusPrototype = str
+ import dbus.glib
INTERFACE = 'org.gajim.dbus.RemoteInterface'
OBJ_PATH = '/org/gajim/dbus/RemoteObject'
SERVICE = 'org.gajim.dbus'
-# type mapping, it is different in each version
-ident = lambda e: e
-if dbus_support.version[1] >= 43:
- # in most cases it is a utf-8 string
- DBUS_STRING = dbus.String
+# type mapping
- # general type (for use in dicts,
- # where all values should have the same type)
- DBUS_VARIANT = dbus.Variant
- DBUS_BOOLEAN = dbus.Boolean
- DBUS_DOUBLE = dbus.Double
- DBUS_INT32 = dbus.Int32
- # dictionary with string key and binary value
- DBUS_DICT_SV = lambda : dbus.Dictionary({}, signature="sv")
- # dictionary with string key and value
- DBUS_DICT_SS = lambda : dbus.Dictionary({}, signature="ss")
- # empty type
- DBUS_NONE = lambda : dbus.Variant(0)
+# in most cases it is a utf-8 string
+DBUS_STRING = dbus.String
-else: # 33, 35, 36
- DBUS_DICT_SV = lambda : {}
- DBUS_DICT_SS = lambda : {}
- DBUS_STRING = lambda e: unicode(e).encode('utf-8')
- # this is the only way to return lists and dicts of mixed types
- DBUS_VARIANT = lambda e: (isinstance(e, (str, unicode)) and \
- DBUS_STRING(e)) or repr(e)
- DBUS_NONE = lambda : ''
- if dbus_support.version[1] >= 41: # 35, 36
- DBUS_BOOLEAN = dbus.Boolean
- DBUS_DOUBLE = dbus.Double
- DBUS_INT32 = dbus.Int32
- else: # 33
- DBUS_BOOLEAN = ident
- DBUS_INT32 = ident
- DBUS_DOUBLE = ident
-
-STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
- 'invisible']
+# general type (for use in dicts, where all values should have the same type)
+DBUS_VARIANT = dbus.Variant
+DBUS_BOOLEAN = dbus.Boolean
+DBUS_DOUBLE = dbus.Double
+DBUS_INT32 = dbus.Int32
+# dictionary with string key and binary value
+DBUS_DICT_SV = lambda : dbus.Dictionary({}, signature="sv")
+# dictionary with string key and value
+DBUS_DICT_SS = lambda : dbus.Dictionary({}, signature="ss")
+# empty type
+DBUS_NONE = lambda : dbus.Variant(0)
def get_dbus_struct(obj):
''' recursively go through all the items and replace
@@ -123,65 +86,35 @@ class Remote:
self.signal_object = None
session_bus = dbus_support.session_bus.SessionBus()
- if dbus_support.version[1] >= 41:
- service = dbus.service.BusName(SERVICE, bus=session_bus)
- self.signal_object = SignalObject(service)
- elif dbus_support.version[1] <= 40 and dbus_support.version[1] >= 20:
- service=dbus.Service(SERVICE, session_bus)
- self.signal_object = SignalObject(service)
+ service = dbus.service.BusName(SERVICE, bus=session_bus)
+ self.signal_object = SignalObject(service)
def raise_signal(self, signal, arg):
if self.signal_object:
self.signal_object.raise_signal(signal,
- get_dbus_struct(arg))
+ get_dbus_struct(arg))
-class SignalObject(DbusPrototype):
- ''' Local object definition for /org/gajim/dbus/RemoteObject. This doc must
- not be visible, because the clients can access only the remote object. '''
+class SignalObject(dbus.service.Object):
+ ''' Local object definition for /org/gajim/dbus/RemoteObject.
+ (This docstring is not be visible, because the clients can access only the remote object.)'''
def __init__(self, service):
self.first_show = True
self.vcard_account = None
# register our dbus API
- if dbus_support.version[1] >= 41:
- DbusPrototype.__init__(self, service, OBJ_PATH)
- elif dbus_support.version[1] >= 30:
- DbusPrototype.__init__(self, OBJ_PATH, service)
- else:
- DbusPrototype.__init__(self, OBJ_PATH, service,
- [ self.toggle_roster_appearance,
- self.show_next_unread,
- self.list_contacts,
- self.list_accounts,
- self.account_info,
- self.change_status,
- self.open_chat,
- self.send_message,
- self.send_single_message,
- self.contact_info,
- self.send_file,
- self.prefs_list,
- self.prefs_store,
- self.prefs_del,
- self.prefs_put,
- self.add_contact,
- self.remove_contact,
- self.get_status,
- self.get_status_message,
- self.start_chat,
- self.send_xml,
- ])
+ dbus.service.Object.__init__(self, service, OBJ_PATH)
def raise_signal(self, signal, arg):
- ''' raise a signal, with a single string message '''
+ '''raise a signal, with a single string message'''
from dbus import dbus_bindings
message = dbus_bindings.Signal(OBJ_PATH, INTERFACE, signal)
i = message.get_iter(True)
i.append(arg)
self._connection.send(message)
+ @dbus.service.method(INTERFACE)
def get_status(self, *args):
'''get_status(account = None)
returns status (show to be exact) which is the global one
@@ -193,8 +126,9 @@ class SignalObject(DbusPrototype):
return helpers.get_global_show()
# return show for the given account
index = gajim.connections[account].connected
- return DBUS_STRING(STATUS_LIST[index])
+ return DBUS_STRING(gajim.SHOW_LIST[index])
+ @dbus.service.method(INTERFACE)
def get_status_message(self, *args):
'''get_status(account = None)
returns status which is the global one
@@ -208,7 +142,7 @@ class SignalObject(DbusPrototype):
status = gajim.connections[account].status
return DBUS_STRING(status)
-
+ @dbus.service.method(INTERFACE)
def get_account_and_contact(self, account, jid):
''' get the account (if not given) and contact instance from jid'''
connected_account = None
@@ -236,6 +170,7 @@ class SignalObject(DbusPrototype):
return connected_account, contact
+ @dbus.service.method(INTERFACE)
def send_file(self, *args):
'''send_file(file_path, jid, account=None)
send file, located at 'file_path' to 'jid', using account
@@ -254,7 +189,7 @@ class SignalObject(DbusPrototype):
return False
def _send_message(self, jid, message, keyID, account, type = 'chat', subject = None):
- ''' can be called from send_chat_message (default when send_message)
+ '''can be called from send_chat_message (default when send_message)
or send_single_message'''
if not jid or not message:
return None # or raise error
@@ -269,22 +204,25 @@ class SignalObject(DbusPrototype):
return True
return False
+ @dbus.service.method(INTERFACE)
def send_chat_message(self, *args):
- ''' send_message(jid, message, keyID=None, account=None)
+ '''send_message(jid, message, keyID=None, account=None)
send chat 'message' to 'jid', using account (optional) 'account'.
if keyID is specified, encrypt the message with the pgp key '''
jid, message, keyID, account = self._get_real_arguments(args, 4)
jid = self._get_real_jid(jid, account)
return self._send_message(jid, message, keyID, account)
+ @dbus.service.method(INTERFACE)
def send_single_message(self, *args):
- ''' send_single_message(jid, subject, message, keyID=None, account=None)
+ '''send_single_message(jid, subject, message, keyID=None, account=None)
send single 'message' to 'jid', using account (optional) 'account'.
if keyID is specified, encrypt the message with the pgp key '''
jid, subject, message, keyID, account = self._get_real_arguments(args, 5)
jid = self._get_real_jid(jid, account)
return self._send_message(jid, message, keyID, account, type, subject)
+ @dbus.service.method(INTERFACE)
def open_chat(self, *args):
''' start_chat(jid, account=None) -> shows the tabbed window for new
message to 'jid', using account(optional) 'account' '''
@@ -332,6 +270,7 @@ class SignalObject(DbusPrototype):
return True
return False
+ @dbus.service.method(INTERFACE)
def change_status(self, *args, **keywords):
''' change_status(status, message, account). account is optional -
if not specified status is changed for all accounts. '''
@@ -352,13 +291,15 @@ class SignalObject(DbusPrototype):
status, message)
return None
+ @dbus.service.method(INTERFACE)
def show_next_unread(self, *args):
- ''' Show the window(s) with next waiting messages in tabbed/group chats. '''
+ '''Show the window(s) with next waiting messages in tabbed/group chats. '''
if gajim.events.get_nb_events():
gajim.interface.systray.handle_first_event()
+ @dbus.service.method(INTERFACE)
def contact_info(self, *args):
- ''' get vcard info for a contact. Return cached value of the vcard.
+ '''get vcard info for a contact. Return cached value of the vcard.
'''
[jid] = self._get_real_arguments(args, 1)
if not isinstance(jid, unicode):
@@ -375,8 +316,9 @@ class SignalObject(DbusPrototype):
# return empty dict
return DBUS_DICT_SV()
+ @dbus.service.method(INTERFACE)
def list_accounts(self, *args):
- ''' list register accounts '''
+ '''list register accounts'''
result = gajim.contacts.get_accounts()
if result and len(result) > 0:
result_array = []
@@ -385,8 +327,9 @@ class SignalObject(DbusPrototype):
return result_array
return None
+ @dbus.service.method(INTERFACE)
def account_info(self, *args):
- ''' show info on account: resource, jid, nick, prio, message '''
+ '''show info on account: resource, jid, nick, prio, message'''
[for_account] = self._get_real_arguments(args, 1)
if not gajim.connections.has_key(for_account):
# account is invalid
@@ -394,19 +337,20 @@ class SignalObject(DbusPrototype):
account = gajim.connections[for_account]
result = DBUS_DICT_SS()
index = account.connected
- result['status'] = DBUS_STRING(STATUS_LIST[index])
+ result['status'] = DBUS_STRING(gajim.SHOW_LIST[index])
result['name'] = DBUS_STRING(account.name)
result['jid'] = DBUS_STRING(gajim.get_jid_from_account(account.name))
result['message'] = DBUS_STRING(account.status)
result['priority'] = DBUS_STRING(unicode(gajim.config.get_per('accounts',
- account.name, 'priority')))
+ account.name, 'priority')))
result['resource'] = DBUS_STRING(unicode(gajim.config.get_per('accounts',
- account.name, 'resource')))
+ account.name, 'resource')))
return result
+ @dbus.service.method(INTERFACE)
def list_contacts(self, *args):
- ''' list all contacts in the roster. If the first argument is specified,
- then return the contacts for the specified account '''
+ '''list all contacts in the roster. If the first argument is specified,
+ then return the contacts for the specified account'''
[for_account] = self._get_real_arguments(args, 1)
result = []
accounts = gajim.contacts.get_accounts()
@@ -428,6 +372,7 @@ class SignalObject(DbusPrototype):
return None
return result
+ @dbus.service.method(INTERFACE)
def toggle_roster_appearance(self, *args):
''' shows/hides the roster window '''
win = gajim.interface.roster.window
@@ -441,6 +386,7 @@ class SignalObject(DbusPrototype):
else:
win.window.focus(long(time()))
+ @dbus.service.method(INTERFACE)
def prefs_list(self, *args):
prefs_dict = DBUS_DICT_SS()
def get_prefs(data, name, path, value):
@@ -455,6 +401,7 @@ class SignalObject(DbusPrototype):
gajim.config.foreach(get_prefs)
return prefs_dict
+ @dbus.service.method(INTERFACE)
def prefs_store(self, *args):
try:
gajim.interface.save_config()
@@ -462,6 +409,7 @@ class SignalObject(DbusPrototype):
return False
return True
+ @dbus.service.method(INTERFACE)
def prefs_del(self, *args):
[key] = self._get_real_arguments(args, 1)
if not key:
@@ -475,6 +423,7 @@ class SignalObject(DbusPrototype):
gajim.config.del_per(key_path[0], key_path[1], key_path[2])
return True
+ @dbus.service.method(INTERFACE)
def prefs_put(self, *args):
[key] = self._get_real_arguments(args, 1)
if not key:
@@ -488,6 +437,7 @@ class SignalObject(DbusPrototype):
gajim.config.set_per(key_path[0], key_path[1], subname, value)
return True
+ @dbus.service.method(INTERFACE)
def add_contact(self, *args):
[jid, account] = self._get_real_arguments(args, 2)
if account:
@@ -503,6 +453,7 @@ class SignalObject(DbusPrototype):
AddNewContactWindow(account = None, jid = jid)
return True
+ @dbus.service.method(INTERFACE)
def remove_contact(self, *args):
[jid, account] = self._get_real_arguments(args, 2)
jid = self._get_real_jid(jid, account)
@@ -597,9 +548,11 @@ class SignalObject(DbusPrototype):
contact_dict['resources'] = DBUS_VARIANT(contact_dict['resources'])
return contact_dict
+ @dbus.service.method(INTERFACE)
def get_unread_msgs_number(self, *args):
return str(gajim.events.get_nb_events)
+ @dbus.service.method(INTERFACE)
def start_chat(self, *args):
[account] = self._get_real_arguments(args, 1)
if not account:
@@ -608,6 +561,7 @@ class SignalObject(DbusPrototype):
NewChatDialog(account)
return True
+ @dbus.service.method(INTERFACE)
def send_xml(self, *args):
xml, account = self._get_real_arguments(args, 2)
if account:
@@ -615,36 +569,3 @@ class SignalObject(DbusPrototype):
else:
for acc in gajim.contacts.get_accounts():
gajim.connections[acc].send_stanza(xml)
-
- if dbus_support.version[1] >= 30 and dbus_support.version[1] <= 40:
- method = dbus.method
- signal = dbus.signal
- elif dbus_support.version[1] >= 41:
- method = dbus.service.method
- signal = dbus.service.signal
-
- # prevent using decorators, because they are not supported
- # on python < 2.4
- # FIXME: use decorators when python2.3 (and dbus 0.23) is OOOOOOLD
- toggle_roster_appearance = method(INTERFACE)(toggle_roster_appearance)
- list_contacts = method(INTERFACE)(list_contacts)
- list_accounts = method(INTERFACE)(list_accounts)
- show_next_unread = method(INTERFACE)(show_next_unread)
- change_status = method(INTERFACE)(change_status)
- open_chat = method(INTERFACE)(open_chat)
- contact_info = method(INTERFACE)(contact_info)
- send_message = method(INTERFACE)(send_chat_message)
- send_single_message = method(INTERFACE)(send_single_message)
- send_file = method(INTERFACE)(send_file)
- prefs_list = method(INTERFACE)(prefs_list)
- prefs_put = method(INTERFACE)(prefs_put)
- prefs_del = method(INTERFACE)(prefs_del)
- prefs_store = method(INTERFACE)(prefs_store)
- remove_contact = method(INTERFACE)(remove_contact)
- add_contact = method(INTERFACE)(add_contact)
- get_status = method(INTERFACE)(get_status)
- get_status_message = method(INTERFACE)(get_status_message)
- account_info = method(INTERFACE)(account_info)
- get_unread_msgs_number = method(INTERFACE)(get_unread_msgs_number)
- start_chat = method(INTERFACE)(start_chat)
- send_xml = method(INTERFACE)(send_xml)
diff --git a/src/roster_window.py b/src/roster_window.py
index 0ce9cd2dd..4100e54b3 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
## roster_window.py
##
## Copyright (C) 2003-2006 Yann Le Boulanger
@@ -39,6 +40,10 @@ from chat_control import ChatControl
from groupchat_control import GroupchatControl
from groupchat_control import PrivateChatControl
+import dbus_support
+if dbus_support.supported:
+ from music_track_listener import MusicTrackListener
+
#(icon, name, type, jid, account, editable, second pixbuf)
(
C_IMG, # image to show state (online, new message etc)
@@ -50,9 +55,6 @@ C_EDITABLE, # cellrenderer text that holds name editable or not?
C_SECPIXBUF, # secondary_pixbuf (holds avatar or padlock)
) = range(7)
-
-DEFAULT_ICONSET = 'dcraven'
-
class RosterWindow:
'''Class for main window of gtkgui interface'''
@@ -624,9 +626,6 @@ class RosterWindow:
self.join_gc_room(account, bookmark['jid'], bookmark['nick'],
bookmark['password'])
- def on_bm_header_changed_state(self, widget, event):
- widget.set_state(gtk.STATE_NORMAL) #do not allow selected_state
-
def on_send_server_message_menuitem_activate(self, widget, account):
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/online'
@@ -717,6 +716,11 @@ class RosterWindow:
return
new_chat_menuitem = self.xml.get_widget('new_chat_menuitem')
join_gc_menuitem = self.xml.get_widget('join_gc_menuitem')
+ iconset = gajim.config.get('iconset')
+ path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
+ state_images = self.load_iconset(path)
+ if state_images.has_key('muc_active'):
+ join_gc_menuitem.set_image(state_images['muc_active'])
add_new_contact_menuitem = self.xml.get_widget('add_new_contact_menuitem')
service_disco_menuitem = self.xml.get_widget('service_disco_menuitem')
advanced_menuitem = self.xml.get_widget('advanced_menuitem')
@@ -787,7 +791,7 @@ class RosterWindow:
label.set_use_underline(False)
gc_item = gtk.MenuItem()
gc_item.add(label)
- gc_item.connect('state-changed', self.on_bm_header_changed_state)
+ gc_item.connect('state-changed', gtkgui_helpers.on_bm_header_changed_state)
gc_sub_menu.append(gc_item)
self.add_bookmarks_list(gc_sub_menu, account)
@@ -1084,8 +1088,12 @@ class RosterWindow:
win.redraw_tab(ctrl)
name = contact.get_shown_name()
- if contact.resource != '':
+
+ # if multiple resources (or second one disconnecting)
+ if (len(contact_instances) > 1 or (len(contact_instances) == 1 and \
+ show in ('offline', 'error'))) and contact.resource != '':
name += '/' + contact.resource
+
uf_show = helpers.get_uf_show(show)
if status:
ctrl.print_conversation(_('%s is now %s (%s)') % (name, uf_show,
@@ -1204,7 +1212,7 @@ class RosterWindow:
gajim.connections[account].request_register_agent_info(contact.jid)
def on_remove_agent(self, widget, list_):
- '''When an agent is requested to log in or off. list_ is a list of
+ '''When an agent is requested to be removed. list_ is a list of
(contact, account) tuple'''
for (contact, account) in list_:
if gajim.config.get_per('accounts', account, 'hostname') == \
@@ -1226,6 +1234,17 @@ class RosterWindow:
gajim.contacts.remove_jid(account, contact.jid)
gajim.contacts.remove_contact(account, contact)
+ # Check if there are unread events from some contacts
+ has_unread_events = False
+ for (contact, account) in list_:
+ for jid in gajim.events.get_events(account):
+ if jid.endswith(contact.jid):
+ has_unread_events = True
+ break
+ if has_unread_events:
+ dialogs.ErrorDialog(_('You have unread messages'),
+ _('You must read them before removing this transport.'))
+ return
if len(list_) == 1:
pritext = _('Transport "%s" will be removed') % contact.jid
sectext = _('You will no longer be able to send and receive messages to contacts from this transport.')
@@ -1332,7 +1351,7 @@ class RosterWindow:
'''Make contact's popup menu'''
model = self.tree.get_model()
jid = model[iter][C_JID].decode('utf-8')
- path = model.get_path(iter)
+ tree_path = model.get_path(iter)
account = model[iter][C_ACCOUNT].decode('utf-8')
our_jid = jid == gajim.get_jid_from_account(account)
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
@@ -1368,6 +1387,12 @@ class RosterWindow:
img.set_from_file(path_to_kbd_input_img)
rename_menuitem.set_image(img)
+ iconset = gajim.config.get('iconset')
+ path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
+ state_images = self.load_iconset(path)
+ if state_images.has_key('muc_active'):
+ invite_menuitem.set_image(state_images['muc_active'])
+
above_subscription_separator = xml.get_widget(
'above_subscription_separator')
subscription_menuitem = xml.get_widget('subscription_menuitem')
@@ -1387,8 +1412,6 @@ class RosterWindow:
start_chat_menuitem.set_submenu(sub_menu)
iconset = gajim.config.get('iconset')
- if not iconset:
- iconset = DEFAULT_ICONSET
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
for c in contacts:
# icon MUST be different instance for every item
@@ -1403,7 +1426,7 @@ class RosterWindow:
else: # one resource
start_chat_menuitem.connect('activate',
- self.on_roster_treeview_row_activated, path)
+ self.on_roster_treeview_row_activated, tree_path)
if contact.resource:
send_file_menuitem.connect('activate',
@@ -1444,7 +1467,7 @@ class RosterWindow:
menuitem.connect('activate', self.on_invite_to_room,
[(contact, account)], room_jid, acct)
submenu.append(menuitem)
- rename_menuitem.connect('activate', self.on_rename, iter, path)
+ rename_menuitem.connect('activate', self.on_rename, iter, tree_path)
remove_from_roster_menuitem.connect('activate', self.on_req_usub,
[(contact, account)])
information_menuitem.connect('activate', self.on_info, contact,
@@ -1774,8 +1797,6 @@ class RosterWindow:
# we have to create our own set of icons for the menu
# using self.jabber_status_images is poopoo
iconset = gajim.config.get('iconset')
- if not iconset:
- iconset = DEFAULT_ICONSET
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
state_images = self.load_iconset(path)
@@ -1908,8 +1929,6 @@ class RosterWindow:
else:
menu = gtk.Menu()
iconset = gajim.config.get('iconset')
- if not iconset:
- iconset = DEFAULT_ICONSET
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
accounts = [] # Put accounts in a list to sort them
for account in gajim.connections:
@@ -2419,6 +2438,41 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
self.send_status(acct, status, message)
self.update_status_combobox()
+ ## enable setting status msg from currently playing music track
+ def enable_syncing_status_msg_from_current_music_track(self, enabled):
+ '''if enabled is True, we listen to events from music players about
+ currently played music track, and we update our
+ status message accordinly'''
+ if not dbus_support.supported:
+ # do nothing if user doesn't have D-Bus bindings
+ return
+ if enabled:
+ if self._music_track_changed_signal is None:
+ listener = MusicTrackListener.get()
+ self._music_track_changed_signal = listener.connect(
+ 'music-track-changed', self._music_track_changed)
+ track = listener.get_playing_track()
+ self._music_track_changed(listener, track)
+ else:
+ if self._music_track_changed_signal is not None:
+ listener = MusicTrackListener.get()
+ listener.disconnect(self._music_track_changed_signal)
+ self._music_track_changed_signal = None
+ self._music_track_changed(None, None)
+
+ def _music_track_changed(self, unused_listener, music_track_info):
+ accounts = gajim.connections.keys()
+ if music_track_info is None:
+ status_message = ''
+ else:
+ status_message = _('♪ "%(title)s" by %(artist)s ♪') % \
+ {'title': music_track_info.title,
+ 'artist': music_track_info.artist }
+ for acct in accounts:
+ current_show = gajim.SHOW_LIST[gajim.connections[acct].connected]
+ self.send_status(acct, current_show, status_message)
+
+
def update_status_combobox(self):
# table to change index in connection.connected to index in combobox
table = {'offline':9, 'connecting':9, 'online':0, 'chat':1, 'away':2,
@@ -2599,12 +2653,12 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
# We save it in a queue
type_ = 'chat'
+ event_type = 'message_received'
if msg_type == 'normal':
type_ = 'normal'
- show_in_roster = notify.get_show_in_roster('message_received', account,
- contact)
- show_in_systray = notify.get_show_in_systray('message_received', account,
- contact)
+ event_type = 'single_message_received'
+ show_in_roster = notify.get_show_in_roster(event_type, account, contact)
+ show_in_systray = notify.get_show_in_systray(event_type, account, contact)
event = gajim.events.create_event(type_, (msg, subject, msg_type, tim,
encrypted, resource, msg_id), show_in_roster = show_in_roster,
show_in_systray = show_in_systray)
@@ -3134,8 +3188,13 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
def make_jabber_state_images(self):
'''initialise jabber_state_images dict'''
iconset = gajim.config.get('iconset')
- if not iconset:
- iconset = 'dcraven'
+ if iconset:
+ path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
+ if not os.path.exists(path):
+ iconset = gajim.config.DEFAULT_ICONSET
+ else:
+ iconset = gajim.config.DEFAULT_ICONSET
+
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '32x32')
self.jabber_state_images['32'] = self.load_iconset(path)
@@ -3174,7 +3233,8 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
model[iter][1] = self.jabber_state_images['16'][model[iter][2]]
iter = model.iter_next(iter)
# Update the systray
- gajim.interface.systray.set_img()
+ if gajim.interface.systray_enabled:
+ gajim.interface.systray.set_img()
for win in gajim.interface.msg_win_mgr.windows():
for ctrl in win.controls():
@@ -3722,6 +3782,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
def __init__(self):
self.xml = gtkgui_helpers.get_glade('roster_window.glade')
self.window = self.xml.get_widget('roster_window')
+ self._music_track_changed_signal = None
gajim.interface.msg_win_mgr = MessageWindowMgr()
self.advanced_menus = [] # We keep them to destroy them
if gajim.config.get('roster_window_skip_taskbar'):
@@ -3898,6 +3959,13 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
self.tooltip = tooltips.RosterTooltip()
self.draw_roster()
+ ## Music Track notifications
+ ## FIXME: we use a timeout because changing status of
+ ## accounts has no effect until they are connected.
+ gobject.timeout_add(1000,
+ self.enable_syncing_status_msg_from_current_music_track,
+ gajim.config.get('set_status_msg_from_current_music_track'))
+
if gajim.config.get('show_roster_on_startup'):
self.window.show_all()
else:
diff --git a/src/systray.py b/src/systray.py
index 926c077d1..ade7ffd81 100644
--- a/src/systray.py
+++ b/src/systray.py
@@ -124,11 +124,12 @@ class Systray:
# We need our own set of status icons, let's make 'em!
iconset = gajim.config.get('iconset')
- if not iconset:
- iconset = 'dcraven'
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
state_images = gajim.interface.roster.load_iconset(path)
+ if state_images.has_key('muc_active'):
+ join_gc_menuitem.set_image(state_images['muc_active'])
+
for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
uf_show = helpers.get_uf_show(show, use_mnemonic = True)
item = gtk.ImageMenuItem(uf_show)
@@ -194,6 +195,7 @@ class Systray:
label.set_use_underline(False)
gc_item = gtk.MenuItem()
gc_item.add(label)
+ gc_item.connect('state-changed', gtkgui_helpers.on_bm_header_changed_state)
gc_sub_menu.append(gc_item)
gajim.interface.roster.add_bookmarks_list(gc_sub_menu, account)
@@ -250,11 +252,11 @@ class Systray:
if len(gajim.events.get_systray_events()) == 0:
# no pending events, so toggle visible/hidden for roster window
if win.get_property('visible'): # visible in ANY virtual desktop?
- win.hide() # we hide it from VD that was visible in
- # but we could be in another VD right now. eg vd2
- # and we want not only to hide it in vd1 but also show it in vd2
- gtkgui_helpers.possibly_move_window_in_current_desktop(win)
+ # we could be in another VD right now. eg vd2
+ # and we want to show it in vd2
+ if not gtkgui_helpers.possibly_move_window_in_current_desktop(win):
+ win.hide() # else we hide it from VD that was visible in
else:
win.present()
else:
diff --git a/src/vcard.py b/src/vcard.py
index 009605b38..975d39a74 100644
--- a/src/vcard.py
+++ b/src/vcard.py
@@ -61,6 +61,7 @@ class VcardWindow:
# the contact variable is the jid if vcard is true
self.xml = gtkgui_helpers.get_glade('vcard_information_window.glade')
self.window = self.xml.get_widget('vcard_information_window')
+ self.progressbar = self.xml.get_widget('progressbar')
self.contact = contact
self.account = account
@@ -68,13 +69,23 @@ class VcardWindow:
self.avatar_mime_type = None
self.avatar_encoded = None
+ self.vcard_arrived = False
+ self.os_info_arrived = False
+ self.update_progressbar_timeout_id = gobject.timeout_add(100,
+ self.update_progressbar)
self.fill_jabber_page()
self.xml.signal_autoconnect(self)
self.window.show_all()
+ def update_progressbar(self):
+ self.progressbar.pulse()
+ return True # loop forever
+
def on_vcard_information_window_destroy(self, widget):
+ if self.update_progressbar_timeout_id is not None:
+ gobject.source_remove(self.update_progressbar_timeout_id)
del gajim.interface.instances[self.account]['infos'][self.contact.jid]
def on_vcard_information_window_key_press_event(self, widget, event):
@@ -113,7 +124,15 @@ class VcardWindow:
def set_value(self, entry_name, value):
try:
- self.xml.get_widget(entry_name).set_text(value)
+ if value and entry_name == 'URL_label':
+ if gtk.pygtk_version >= (2, 10, 0) and gtk.gtk_version >= (2, 10, 0):
+ widget = gtk.LinkButton(value, value)
+ else:
+ widget = gtk.Label(value)
+ table = self.xml.get_widget('personal_info_table')
+ table.attach(widget, 1, 4, 3, 4, yoptions = 0)
+ else:
+ self.xml.get_widget(entry_name).set_text(value)
except AttributeError:
pass
@@ -144,8 +163,17 @@ class VcardWindow:
if i == 'DESC':
self.xml.get_widget('DESC_textview').get_buffer().set_text(
vcard[i], 0)
- else:
+ elif i != 'jid': # Do not override jid_label
self.set_value(i + '_label', vcard[i])
+ self.vcard_arrived = True
+ self.test_remove_progressbar()
+
+ def test_remove_progressbar(self):
+ if self.update_progressbar_timeout_id is not None and \
+ self.vcard_arrived and self.os_info_arrived:
+ gobject.source_remove(self.update_progressbar_timeout_id)
+ self.progressbar.hide()
+ self.update_progressbar_timeout_id = None
def set_last_status_time(self):
self.fill_status_label()
@@ -174,6 +202,8 @@ class VcardWindow:
os = Q_('?OS:Unknown')
self.xml.get_widget('client_name_version_label').set_text(client)
self.xml.get_widget('os_label').set_text(os)
+ self.os_info_arrived = True
+ self.test_remove_progressbar()
def fill_status_label(self):
if self.xml.get_widget('information_notebook').get_n_pages() < 4:
@@ -251,8 +281,10 @@ class VcardWindow:
gajim.connections[self.account].request_last_status_time(self.contact.jid,
self.contact.resource)
- # Request os info in contact is connected
- if self.contact.show not in ('offline', 'error'):
+ # do not wait for os_info if contact is not connected
+ if self.contact.show in ('offline', 'error'):
+ self.os_info_arrived = True
+ else: # Request os info if contact is connected
gobject.idle_add(gajim.connections[self.account].request_os_info,
self.contact.jid, self.contact.resource)
self.os_info = {0: {'resource': self.contact.resource, 'client': '',
@@ -283,3 +315,6 @@ class VcardWindow:
self.fill_status_label()
gajim.connections[self.account].request_vcard(self.contact.jid, self.is_fake)
+
+ def on_close_button_clicked(self, widget):
+ self.window.destroy()