merge with trunk

This commit is contained in:
Stefan Bethge 2006-10-09 00:27:03 +00:00
parent cd4c823eb5
commit 0d0ac51fa8
40 changed files with 1042 additions and 1107 deletions

View File

@ -1,6 +1,7 @@
CURRENT DEVELOPERS:
Yann Le Boulanger (asterix AT lagaule.org)
Nikos Kouremenos (kourem AT gmail.com)
Dimitur Kirov (dkirov AT gmail.com)
Travis Shirk (travis AT pobox.com)
Jean-Marie Traissard (jim AT lapin.org)
@ -8,4 +9,3 @@ Jean-Marie Traissard (jim AT lapin.org)
PAST DEVELOPERS:
Vincent Hanquez (tab AT snarc.org)
Nikos Kouremenos (kourem AT gmail.com)

208
ChangeLog
View File

@ -1,208 +0,0 @@
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
* gajim.desktop is generated with translation (#834)
* Preventing several TBs and annoyances (r6273, r6275, r6279, r6301,
r6308, r6311, r6323, r6326, r6327, r6335, r6342, r6346, r6348)
Gajim 0.10 (01 May 2006)
* One Messages Window ability (default to it) with tab reordering ability
* Non blocking socket connections. Gajim no longer remains unresponsive.
* Gajim now uses less memory
* File Transfer improvements (now should work out of the box for all)
* Meta Contacts ability (relationships between contacts)
* Support for legacy composing event (JEP-0022). Now 'Contact is composing a message' will always work
* Gajim now defaults to theme that uses GTK colors
* Roster Management Improvements (f.e. editablity of transport names, extended Drag and Drop Functionality)
* History (chat logs) Manager (search globally, delete, etc)
* Animated Emoticons ability
* Support for GTalk email notifications for GMail
* Room administrators can modify room ban list
* Gajim no longer optionally depends on pydns or dnspython. Requires
dnsutils (or whatever package provides the nslookup binary)
* gajim-remote has extended functionality
* Improved Preset Status Messages Experience
* Detection for CRUX as user's operating system
* New art included, appropriate sizes of icons used where available
* Translations under Windows now work okay
* Tons of fixes for bugs and annoyances: http://trac.gajim.org/query?status=closed&milestone=0.10
Gajim 0.9.1 (27 December 2005)
* Fix bug when joining a Groupchat
* Fix bug when starting Gajim without old logs
Gajim 0.9 (23 December 2005)
* Avatars and status messages in roster window
* Improved service discovery window
* Emoticons selector, Cooler Popup Windows (notification-daemon). Read more information in case you did not notice something different in http://trac.gajim.org/wiki/GajimDBus#notif_daemon
* Caching of Avatars, Less UI freezing
* New Account creation wizard
* Better History Window with searching capabilities
* Gajim now tries to reconnect to a jabber server if connection is lost
* Queue for all events (File Transfer, private messages, etc)
* A lot of new irc-like commands in group chat. Do for example /help invite
* X11 Session Management support
* Gajim registers and handles xmpp: and xmpp:// (GNOME/gconfd only)
* Use pysqlite for conversation history. Automigration for old logs
* New translations: Italian, Swedish, Slovak, Basque
Gajim 0.8.2 (06 Sep 2005)
* Fix so Gajim runs in pygtk2.8.x
* Gajim can use pydns too (apart from dnspython) to do SRV lookup
* Other minor fixes
Gajim 0.8.1 (02 Sep 2005)
* Systray icon for windows
* Gajim is available in Dutch
* Gajim can use gpg-agent
Gajim 0.8 (18 Aug 2005)
* Avatars (JEP-0153)
* Chat state notifications aka. typing notification (JEP-0085)
* Bookmark storage (JEP-0048)
* File Transfer (JEP-0096)
* Major changes to adhere to GNOME HIG
* Complete vcard fields support
* New and better user interface for chat and groupchat windows
* SRV capabilities and custom hostname/port
* Many improvements in group chat and IRC emulation (eg. nick autocompletation and cycling)
* Gajim can now send and receive single messages
* New iconsets and new dialog for customizing the user interface
* Mouseover information for contacts in the roster window (aka tooltips)
* DBus Capabilities. Now Gajim can be remote controlled
* Authenticating HTTP Requests via XMPP (JEP-0070)
* Now you can lookup a word in Wikipedia, dictionary or in search engine
* XML Console
* Gajim is now also available in norwegian and czech language
Gajim 0.7.1 (5 Jun 2005)
* Transports icon as an option and error/mesage icon for transports
* Gajim is more HIG compatible
* Editing registration information on transports
* Messages stanza without <body> element are not printed
* SASL bugfix
* GtkSpell capabilities
* Support SSL (legacy) connection
* Assign gpg key to specific contact
* Contacts are sortable by status
* Gajim remembers last lines when reopening chat
* New translations available: German, Russian, Spanish, Bulgarian
Gajim 0.7 (23 May 2005)
* Ability for groupchat reserved rooms with full affiliations and roles support
* Popup notification for incoming events
* Protocol icons for contacts from transports
* Gajim's user interface is now more HIG compliant
* Gajim now detects and can send operating system information
* Gajim now can inform the user about new version availability
* Gajim jabber library migration from jabberpy to xmpppy
* Rewrite the plugin system to remove threads and improve latency
* Gajim now supports Nodes in Service Discovery
* Greek and Polish translations
Gajim 0.6.1 (03 April 2005)
* Rewrite of service discovery. It doesn't freeze Gajim anymore.
* More HIG Compliant.
* Gajim is faster (do not redraw preferences_window each time we open it, use
of psyco if available)
Gajim 0.6 (23 March 2005)
* Gajim's user interface is now nicer.
* Groupchat just got better.
* URL, mailto and ascii formatin (* / _) detection
* Better transports detection, group management, and many minor additions/bugfixes
Gajim 0.5.1 (27 February 2005)
* Minor bugfixes.
Gajim 0.5 (26 February 2005)
* Possibility to use tabbed chat window
* Sound support under GNU/linux
* Autoaway available under Microsoft Windows
Gajim 0.4.1 (23 January 2005)
* Bugfix in config file parser (fix config file parser to handle emoticons)
* Bugfix with GPG signatures
Gajim 0.4 (21 January 2005)
* New option: regroup accounts
* Emoticons support with a binder
* GUI improvements
* Bugfixes
Gajim 0.3 (18 December 2004)
* GUI improvements
* group chat support with MUC (JEP 45)
* New agent browser (JEP 30)
* GnuPG support
* Autoconnect at startup
* New socket plugin
Gajim 0.2.1 (1 July 2004)
* bugfixes : when configfile is incomplete
* icon in systray with popup menu (for linux)
* "auto away even if not online" option
* always show contacts with unread messages
* new imageCellRenderer to show animated gifs
* allow agents unregistration
Gajim 0.2 (8 June 2004)
* bugfix for french translation
* multi-resource support
* auto away support (for linux)
* invisible support
* priority support
Gajim 0.1 (21 May 2004)
* Initial release.

View File

@ -1,17 +1,19 @@
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)
* Intoducing View Menu (GNOME HIG)
* GNOME Keyring Support (if GNOME keyring available, manage passwords and save them in an encrypted file).
* Ability to now hide the Transports group
* Support for notify-python so if notification-daemon is not available, we still can show cool popups
* Ability to 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)
* Ability to 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
* Ability to 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

86
README
View File

@ -1,85 +1 @@
Welcome and thanks for trying out Gajim.
=RUNTIME REQUIREMENTS=
python2.4 or higher
pygtk2.6 or higher
python-libglade
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
the xml lib that *comes* with python and not pyxml or whatever
=COMPILE-TIME REQUIREMENTS=
python-dev
python-gtk2-dev
libgtk2.0-dev # aka. gtk2-devel
libxss-dev # for idle detection module (Some distributions (f.e. Debian) split xscreensaver)
libgtkspell-dev # for gtkspell module
intltool
NOTE:
if you still have problems compiling, you may want to try removing the gtk1 series of the above dependencies
Optionally:
dnsutils (or whatever package provides the nslookup binary) for SRV support; if you don't know what that is, you don't need it
gtkspell and aspell-LANG where lang is your locale eg. en, fr etc
GnomePythonExtras 2.10 or above so you can avoid compiling trayicon and gtkspell
notification-daemon or notify-python (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
which is gnome dep
=INSTALLATION PROCEDURE=
tar jxvf gajim-version.tar.bz2
cd gajim
make # builds all modules
su -c make install
To specify what modules to build do:
make help
To specify where to install do:
su -c make PREFIX=custom_path install
=RUNNING GAJIM=
gajim
or if you didn't 'make install' you can also run from gajim folder with
./launch.sh
Last but not least, you can run Gajim from your GNOME/XFCE/KDE/whatever menus.
=UNINSTALLATION PROCEDURE=
su -c make uninstall
this will try to remove Gajim from the default directories.
If you want to remove it from custom directory provide it as:
make PREFIX=custom_path uninstall
=MISCELLANEOUS=
XML & Debugging:
If you want to see the xml stanzas and/or help us debugging
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.
=FAQ/Wiki=
FAQ can be found at http://trac.gajim.org/wiki/GajimFaq
Wiki can be found at http://trac.gajim.org/wiki
That is all, enjoy!
(C) 2003-2006
The Gajim Team
http://gajim.org
PS.
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. Thank you
see README.html

130
README.html Normal file
View File

@ -0,0 +1,130 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Gajim - Read Me</title>
</head>
<body>
<h1>Gajim Read Me</h1>
<p>
Welcome to Gajim and thank you for trying out our client.
</p>
<h2>Runtime Requirements</h2>
<ul>
<li>python2.4 or higher</li>
<li>pygtk2.6 or higher</li>
<li>python-libglade</li>
<li>pysqlite2 (if you have python 2.5, you already have this)</li>
</ul>
<p>
<strong>Note to packagers</strong>
Gajim is a GTK+ app that loves GNOME. You can do 'make' so you don't require gnomepythonextras (aka gnome-python-desktop) which is gnome dep, but you will miss gnomekeyring intergration.
</p>
<h2>Optional Runtime Requirements</h2>
<ul>
<li>dnsutils (or whatever package provides the nslookup binary) for SRV support; if you don't know what that is, you don't need it</li>
<li>gtkspell and aspell-LANG where lang is your locale eg. en, fr etc</li>
<li>GnomePythonExtras 2.10 or above (aka gnome-python-desktop) so you can avoid compiling trayicon and gtkspell</li>
<li>gnome-python-desktop (for GnomeKeyring support)</li>
<li>notification-daemon or notify-python (and D-Bus) to get cooler popups</li>
<li>D-Bus to have gajim-remote working</li>
</ul>
<p>
Some distributions also split too much python standard library.
I know SUSE does. In such distros you also need python-xml
the xml lib that *comes* with python and not pyxml or whatever.
</p>
<h2>Compile-time Requirements</h2>
<ul>
<li>python-dev</li>
<li>python-gtk2-dev</li>
<li>libgtk2.0-dev aka. gtk2-devel</li>
<li>libxss-dev (for idle detection module; some distributions such as Debian split xscreensaver)</li>
<li>libgtkspell-dev (for the gtkspell module)</li>
<li>intltool</li>
</ul>
<p>
<strong>NOTE:</strong>
If you still have problems compiling, you may want to try removing the gtk1 series of the above dependencies.
</p>
<h2>Installation Procedure</h2>
<ol>
<li>tar jxvf gajim-version.tar.bz2</li>
</li>cd gajim</li>
</li>make (builds all modules)</li>
</li>su -c make install</li>
</ol>
<p>
To specify what modules to build do:
make help
</p>
<p>
To specify where to install do:
su -c make PREFIX=custom_path install
</p>
<h2>Running Gajim</h2>
<p>
Just do <em>gajim</em> or you can run Gajim from your GNOME/XFCE/KDE/whatever menus.<br/><br/>
or if you didn't 'make install' you can also run from gajim folder with
<em>./launch.sh</em>
</p>
<h2>Uninstallation Procedure</h2>
<p>
su -c make uninstall<br/>
this will try to remove Gajim from the default directories.
If you want to remove it from custom directory provide it as:<br/>
make PREFIX=custom_path uninstall
</p>
<h2>Miscellaneous</h2>
<h3>XML & Debugging</h3>
<p>
If you want to see the xml stanzas and/or help us debugging
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.
</p>
<h3>FAQ/Wiki</h3>
<p>
FAQ can be found at <a href="http://trac.gajim.org/wiki/GajimFaq">http://trac.gajim.org/wiki/GajimFaq</a><br/>
Wiki can be found at <a href="http://trac.gajim.org/wiki">http://trac.gajim.org/wiki</a>
</p>
<p>
That is all, <strong>enjoy!</strong>
</p>
<p>
<br/>
<br/>
<br/>
(C) 2003-2006<br/>
The Gajim Team<br/>
http://gajim.org<br/>
<br/>
<br/>
PS.
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. Thank you.
</p>
</body></html>

View File

@ -17,6 +17,7 @@
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="destroy" handler="on_join_groupchat_window_destroy" last_modification_time="Wed, 02 Mar 2005 12:20:14 GMT"/>
<signal name="key_press_event" handler="on_join_groupchat_window_key_press_event" last_modification_time="Tue, 05 Apr 2005 13:13:40 GMT"/>
@ -29,58 +30,14 @@
<child>
<widget class="GtkTable" id="table15">
<property name="visible">True</property>
<property name="n_rows">5</property>
<property name="n_rows">4</property>
<property name="n_columns">2</property>
<property name="homogeneous">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
<widget class="GtkEntry" id="password_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">False</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="server_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
<signal name="key_press_event" handler="on_server_entry_key_press_event" last_modification_time="Fri, 25 Nov 2005 22:09:39 GMT"/>
<signal name="changed" handler="on_required_entry_changed" last_modification_time="Sat, 14 Jan 2006 21:51:55 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="room_entry">
<widget class="GtkEntry" id="room_jid_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
@ -91,7 +48,6 @@
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
<signal name="key_press_event" handler="on_room_entry_key_press_event" last_modification_time="Fri, 25 Nov 2005 22:07:30 GMT"/>
<signal name="changed" handler="on_required_entry_changed" last_modification_time="Sat, 14 Jan 2006 21:51:39 GMT"/>
</widget>
<packing>
@ -125,62 +81,6 @@
</packing>
</child>
<child>
<widget class="GtkLabel" id="label145">
<property name="visible">True</property>
<property name="label" translatable="yes">Password:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label144">
<property name="visible">True</property>
<property name="label" translatable="yes">Server:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label143">
<property name="visible">True</property>
@ -281,6 +181,55 @@
<property name="y_options">fill</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label145">
<property name="visible">True</property>
<property name="label" translatable="yes">Password:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="password_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">False</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>

View File

@ -900,7 +900,6 @@ Per type</property>
<widget class="GtkFontButton" id="conversation_fontbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="title" translatable="yes">Επιλογή μιας γραμματοσειράς</property>
<property name="show_style">True</property>
<property name="show_size">True</property>
<property name="use_font">False</property>
@ -952,7 +951,6 @@ Per type</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="use_alpha">False</property>
<property name="title" translatable="yes">Επιλογή χρώματος</property>
<property name="focus_on_click">True</property>
<signal name="color_set" handler="on_outgoing_msg_colorbutton_color_set" last_modification_time="Sun, 06 Mar 2005 14:07:56 GMT"/>
</widget>
@ -971,7 +969,6 @@ Per type</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="use_alpha">False</property>
<property name="title" translatable="yes">Επιλογή χρώματος</property>
<property name="focus_on_click">True</property>
<signal name="color_set" handler="on_url_msg_colorbutton_color_set" last_modification_time="Sun, 25 Dec 2005 15:22:17 GMT"/>
</widget>
@ -1051,7 +1048,6 @@ Per type</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="use_alpha">False</property>
<property name="title" translatable="yes">Επιλογή χρώματος</property>
<property name="focus_on_click">True</property>
<signal name="color_set" handler="on_incoming_msg_colorbutton_color_set" last_modification_time="Sun, 06 Mar 2005 14:07:44 GMT"/>
</widget>
@ -1070,7 +1066,6 @@ Per type</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="use_alpha">False</property>
<property name="title" translatable="yes">Επιλογή χρώματος</property>
<property name="focus_on_click">True</property>
<signal name="color_set" handler="on_status_msg_colorbutton_color_set" last_modification_time="Sun, 06 Mar 2005 14:08:04 GMT"/>
</widget>

View File

@ -448,7 +448,7 @@
</child>
<child>
<widget class="GtkLabel" id="label45">
<widget class="GtkLabel" id="subscription_title_label">
<property name="visible">True</property>
<property name="label" translatable="yes" comments="Family Name">Subscription:</property>
<property name="use_underline">False</property>
@ -476,7 +476,7 @@
</child>
<child>
<widget class="GtkLabel" id="label46">
<widget class="GtkLabel" id="ask_title_label">
<property name="visible">True</property>
<property name="label" translatable="yes" comments="Given Name">Ask:</property>
<property name="use_underline">False</property>

View File

@ -1,114 +0,0 @@
# ls data/gajim.desktop.in.in data/glade/*.glade src/*py
# src/common/*py src/common/zeroconf/*.py -1 -U
# to produce this list
[encoding: UTF-8]
data/gajim.desktop.in.in
data/glade/account_context_menu.glade
data/glade/account_creation_wizard_window.glade
data/glade/account_modification_window.glade
data/glade/accounts_window.glade
data/glade/add_new_contact_window.glade
data/glade/advanced_configuration_window.glade
data/glade/advanced_menuitem_menu.glade
data/glade/advanced_notifications_window.glade
data/glade/change_password_dialog.glade
data/glade/change_status_message_dialog.glade
data/glade/chat_context_menu.glade
data/glade/chat_control_popup_menu.glade
data/glade/choose_gpg_key_dialog.glade
data/glade/data_form_window.glade
data/glade/edit_groups_dialog.glade
data/glade/filetransfers.glade
data/glade/gajim_themes_window.glade
data/glade/gc_control_popup_menu.glade
data/glade/gc_occupants_menu.glade
data/glade/history_manager.glade
data/glade/history_window.glade
data/glade/input_dialog.glade
data/glade/invitation_received_dialog.glade
data/glade/join_groupchat_window.glade
data/glade/manage_accounts_window.glade
data/glade/manage_bookmarks_window.glade
data/glade/manage_proxies_window.glade
data/glade/message_window.glade
data/glade/passphrase_dialog.glade
data/glade/popup_notification_window.glade
data/glade/preferences_window.glade
data/glade/privacy_lists_window.glade
data/glade/privacy_list_window.glade
data/glade/profile_window.glade
data/glade/progress_dialog.glade
data/glade/remove_account_window.glade
data/glade/roster_contact_context_menu.glade
data/glade/roster_window.glade
data/glade/service_discovery_window.glade
data/glade/service_registration_window.glade
data/glade/single_message_window.glade
data/glade/subscription_request_popup_menu.glade
data/glade/subscription_request_window.glade
data/glade/systray_context_menu.glade
data/glade/vcard_information_window.glade
data/glade/xml_console_window.glade
data/glade/zeroconf_contact_context_menu.glade
data/glade/zeroconf_context_menu.glade
data/glade/zeroconf_information_window.glade
data/glade/zeroconf_properties_window.glade
src/advanced.py
src/cell_renderer_image.py
src/chat_control.py
src/config.py
src/conversation_textview.py
src/dbus_support.py
src/dialogs.py
src/disco.py
src/filetransfers_window.py
src/gajim.py
src/gajim-remote.py
src/gajim_themes_window.py
src/groupchat_control.py
src/gtkexcepthook.py
src/gtkgui_helpers.py
src/history_manager.py
src/history_window.py
src/htmltextview.py
src/message_control.py
src/message_textview.py
src/message_window.py
src/music_track_listener.py
src/notify.py
src/profile_window.py
src/remote_control.py
src/roster_window.py
src/rst_xhtml_generator.py
src/systray.py
src/systraywin32.py
src/tooltips.py
src/vcard.py
src/common/check_paths.py
src/common/config.py
src/common/connection_handlers.py
src/common/connection.py
src/common/contacts.py
src/common/dbus_support.py
src/common/events.py
src/common/exceptions.py
src/common/fuzzyclock.py
src/common/gajim.py
src/common/GnuPGInterface.py
src/common/GnuPG.py
src/common/helpers.py
src/common/i18n.py
src/common/__init__.py
src/common/logger.py
src/common/nslookup.py
src/common/optparser.py
src/common/proxy65_manager.py
src/common/sleepy.py
src/common/socks5.py
src/common/xmpp_stringprep.py
src/common/zeroconf/client_zeroconf.py
src/common/zeroconf/connection_handlers_zeroconf.py
src/common/zeroconf/connection_zeroconf.py
src/common/zeroconf/__init__.py
src/common/zeroconf/roster_zeroconf.py

View File

@ -373,7 +373,7 @@ msgstr "Konto-Status mit globalem Status abgleichen"
#: ../data/glade/account_modification_window.glade.h:40
msgid "Use _SSL (legacy)"
msgstr "_SSL verwenden"
msgstr "_SSL verwenden (veraltet)"
#: ../data/glade/account_modification_window.glade.h:41
msgid "Use custom hostname/port"
@ -4485,7 +4485,7 @@ msgstr "für Konto %s"
#. History manager
#: ../src/roster_window.py:933
msgid "History Manager"
msgstr "Verlaufmanager"
msgstr "Verlaufsmanager"
#: ../src/roster_window.py:942
msgid "_Join New Room"

View File

@ -1,18 +1,8 @@
## advanced.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Nikos Kouremenos <kourem@gmail.com>
## - Vincent Hanquez <tab@snarc.org>
##
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <kourem@gmail.com>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2005-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005 Vincent Hanquez <tab@snarc.org>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -42,7 +32,7 @@ C_TYPE
GTKGUI_GLADE = 'manage_accounts_window.glade'
class AdvancedConfigurationWindow:
class AdvancedConfigurationWindow(object):
def __init__(self):
self.xml = gtkgui_helpers.get_glade('advanced_configuration_window.glade')
self.window = self.xml.get_widget('advanced_configuration_window')

View File

@ -34,7 +34,7 @@ from message_textview import MessageTextView
from common.contacts import GC_Contact
from common.logger import Constants
constants = Constants()
from rst_xhtml_generator import create_xhtml
from common.rst_xhtml_generator import create_xhtml
from common.xmpp.protocol import NS_XHTML
try:
@ -1045,7 +1045,7 @@ class ChatControl(ChatControlBase):
status = contact.status
if status is not None:
banner_name_label.set_ellipsize(pango.ELLIPSIZE_END)
status = gtkgui_helpers.reduce_chars_newlines(status, max_lines = 2)
status = helpers.reduce_chars_newlines(status, max_lines = 2)
status_escaped = gtkgui_helpers.escape_for_pango_markup(status)
font_attrs, font_attrs_small = self.get_font_attrs()
@ -1254,7 +1254,9 @@ class ChatControl(ChatControlBase):
kind = 'outgoing'
name = gajim.nicks[self.account]
if not xhtml and not encrypted and gajim.config.get('rst_formatting_outgoing_messages'):
xhtml = '<body xmlns="%s">%s</body>' % (NS_XHTML, create_xhtml(text))
xhtml = create_xhtml(text)
if xhtml:
xhtml = '<body xmlns="%s">%s</body>' % (NS_XHTML, xhtml)
ChatControlBase.print_conversation_line(self, text, kind, name, tim,
subject = subject, old_kind = self.old_msg_kind, xhtml = xhtml)
if text.startswith('/me ') or text.startswith('/me\n'):
@ -1674,7 +1676,7 @@ class ChatControl(ChatControlBase):
else:
kind = 'print_queue'
self.print_conversation(data[0], kind, tim = data[3],
encrypted = data[4], subject = data[1])
encrypted = data[4], subject = data[1], xhtml = data[7])
if len(data) > 6 and isinstance(data[6], int):
message_ids.append(data[6])
if message_ids:

View File

@ -1,16 +1,7 @@
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Nikos Kouremenos <kourem@gmail.com>
## - Travis Shirk <travis@pobox.com>
##
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <kourem@gmail.com>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2005-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -57,11 +48,13 @@ def create_log_db():
jid_id INTEGER
);
CREATE INDEX idx_unread_messages_jid_id ON unread_messages (jid_id);
CREATE TABLE transports_cache (
transport TEXT UNIQUE,
type INTEGER
);
CREATE TABLE logs(
log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
jid_id INTEGER,
@ -72,6 +65,8 @@ def create_log_db():
message TEXT,
subject TEXT
);
CREATE INDEX idx_logs_jid_id_kind ON logs (jid_id, kind);
'''
)

View File

@ -94,7 +94,7 @@ class Config:
'show_ascii_formatting_chars': [ opt_bool, True , _('If True, do not '
'remove */_ . So *abc* will be bold but with * * not removed.')],
'rst_formatting_outgoing_messages': [ opt_bool, False,
_('Uses ReStructured text markup for HTML, plus ascii formatting if selected.')],
_('Uses ReStructured text markup for HTML, plus ascii formatting if selected. (If you want to use this, install docutils)')],
'sounds_on': [ opt_bool, True ],
# 'aplay', 'play', 'esdplay', 'artsplay' detected first time only
'soundplayer': [ opt_str, '' ],
@ -143,7 +143,7 @@ class Config:
'send_on_ctrl_enter': [opt_bool, False, _('Send message on Ctrl+Enter and with Enter make new line (Mirabilis ICQ Client default behaviour).')],
'show_roster_on_startup': [opt_bool, True],
'key_up_lines': [opt_int, 25, _('How many lines to store for Ctrl+KeyUP.')],
'version': [ opt_str, '0.10.1.3' ], # which version created the config
'version': [ opt_str, '0.10.1.5' ], # which version created the config
'search_engine': [opt_str, 'http://www.google.com/search?&q=%s&sourceid=gajim'],
'dictionary_url': [opt_str, 'WIKTIONARY', _("Either custom url with %s in it where %s is the word/phrase or 'WIKTIONARY' which means use wiktionary.")],
'always_english_wikipedia': [opt_bool, False],

View File

@ -38,11 +38,12 @@ import common.xmpp
from common import helpers
from common import gajim
from common import GnuPG
from common import passwords
from connection_handlers import *
USE_GPG = GnuPG.USE_GPG
from rst_xhtml_generator import create_xhtml
from common.rst_xhtml_generator import create_xhtml
class Connection(ConnectionHandlers):
'''Connection class'''
@ -69,7 +70,7 @@ class Connection(ConnectionHandlers):
self.last_io = gajim.idlequeue.current_time()
self.last_sent = []
self.last_history_line = {}
self.password = gajim.config.get_per('accounts', name, 'password')
self.password = passwords.get_password(name)
self.server_resource = gajim.config.get_per('accounts', name, 'resource')
if gajim.config.get_per('accounts', self.name, 'keep_alives_enabled'):
self.keepalives = gajim.config.get_per('accounts', self.name,'keep_alive_every_foo_secs')
@ -687,7 +688,7 @@ class Connection(ConnectionHandlers):
user_nick = None, xhtml = None):
if not self.connection:
return
if not xhtml and gajim.config.get('rst_formatting_outgoing_messages'):
if msg and not xhtml and gajim.config.get('rst_formatting_outgoing_messages'):
xhtml = create_xhtml(msg)
if not msg and chatstate is None:
return
@ -977,14 +978,15 @@ class Connection(ConnectionHandlers):
p = self.add_sha(p, ptype != 'unavailable')
self.connection.send(p)
def join_gc(self, nick, room, server, password):
def join_gc(self, nick, room_jid, password):
# FIXME: This room JID needs to be normalized; see #1364
if not self.connection:
return
show = helpers.get_xmpp_show(STATUS_LIST[self.connected])
if show == 'invisible':
# Never join a room when invisible
return
p = common.xmpp.Presence(to = '%s@%s/%s' % (room, server, nick),
p = common.xmpp.Presence(to = '%s/%s' % (room_jid, nick),
show = show, status = self.status)
if gajim.config.get('send_sha_in_gc_presence'):
p = self.add_sha(p)
@ -993,12 +995,11 @@ class Connection(ConnectionHandlers):
t.setTagData('password', password)
self.connection.send(p)
#last date/time in history to avoid duplicate
# FIXME: This JID needs to be normalized; see #1364
jid='%s@%s' % (room, server)
last_log = gajim.logger.get_last_date_that_has_logs(jid, is_room = True)
last_log = gajim.logger.get_last_date_that_has_logs(room_jid,
is_room = True)
if last_log is None:
last_log = 0
self.last_history_line[jid]= last_log
self.last_history_line[room_jid]= last_log
def send_gc_message(self, jid, msg, xhtml = None):
if not self.connection:

View File

@ -712,6 +712,7 @@ class ConnectionDisco:
q.addChild('feature', attrs = {'var': common.xmpp.NS_SI})
q.addChild('feature', attrs = {'var': common.xmpp.NS_FILE})
q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC})
q.addChild('feature', attrs = {'var': common.xmpp.NS_XHTML_IM})
self.connection.send(iq)
raise common.xmpp.NodeProcessed
@ -819,6 +820,8 @@ class ConnectionVcard:
puny_jid = helpers.sanitize_filename(jid)
path = os.path.join(gajim.VCARD_PATH, puny_jid)
if jid in self.room_jids or os.path.isdir(path):
if not nick:
return
# remove room_jid file if needed
if os.path.isfile(path):
os.remove(path)

View File

@ -1,11 +1,7 @@
## exceptions.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## -
##
## Copyright (C) 2005-2006 Yann Le Boulanger <asterix@lagaule.org>
## Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -48,3 +44,12 @@ class SessionBusNotPresent(Exception):
def __str__(self):
return _('Session bus is not available.\nTry reading http://trac.gajim.org/wiki/GajimDBus')
class GajimGeneralException(Exception):
'''This exception ir our general exception'''
def __init__(self, text=''):
Exception.__init__(self)
self.text = text
def __str__(self):
return self.text

View File

@ -139,10 +139,10 @@ def get_nick_from_fjid(jid):
# gaim@conference.jabber.no/nick/nick-continued
return jid.split('/', 1)[1]
def get_room_name_and_server_from_room_jid(jid):
room_name = get_nick_from_jid(jid)
def get_name_and_server_from_jid(jid):
name = get_nick_from_jid(jid)
server = get_server_from_jid(jid)
return room_name, server
return name, server
def get_room_and_nick_from_fjid(jid):
# fake jid is the jid for a contact in a room
@ -335,7 +335,8 @@ def get_priority(account, show):
'''return the priority an account must have'''
if not show:
show = 'online'
if show in priority_dict and config.get_per('accounts', account,
'adjust_priority_with_status'):
return priority_dict[show]
if show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible') and \
config.get_per('accounts', account, 'adjust_priority_with_status'):
return config.get_per('accounts', account, 'autopriority_' + show)
return config.get_per('accounts', account, 'priority')

View File

@ -28,8 +28,10 @@ from encodings.punycode import punycode_encode
import gajim
from i18n import Q_
from i18n import ngettext
from xmpp_stringprep import nodeprep, resourceprep, nameprep
try:
import winsound # windows-only built-in module for playing wav
import win32api
@ -814,3 +816,110 @@ def get_chat_control(account, contact):
highest_contact.resource:
return None
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
def reduce_chars_newlines(text, max_chars = 0, max_lines = 0):
'''Cut the chars after 'max_chars' on each line
and show only the first 'max_lines'.
If any of the params is not present (None or 0) the action
on it is not performed'''
def _cut_if_long(string):
if len(string) > max_chars:
string = string[:max_chars - 3] + '...'
return string
if isinstance(text, str):
text = text.decode('utf-8')
if max_lines == 0:
lines = text.split('\n')
else:
lines = text.split('\n', max_lines)[:max_lines]
if max_chars > 0:
if lines:
lines = map(lambda e: _cut_if_long(e), lines)
if lines:
reduced_text = reduce(lambda e, e1: e + '\n' + e1, lines)
else:
reduced_text = ''
return reduced_text
def get_notification_icon_tooltip_text():
text = None
unread_chat = gajim.events.get_nb_events(types = ['printed_chat',
'chat'])
unread_single_chat = gajim.events.get_nb_events(types = ['normal'])
unread_gc = gajim.events.get_nb_events(types = ['printed_gc_msg',
'gc_msg'])
unread_pm = gajim.events.get_nb_events(types = ['printed_pm', 'pm'])
accounts = get_accounts_info()
if unread_chat or unread_single_chat or unread_gc or unread_pm:
text = 'Gajim '
awaiting_events = unread_chat + unread_single_chat + unread_gc + unread_pm
if awaiting_events == unread_chat or awaiting_events == unread_single_chat \
or awaiting_events == unread_gc or awaiting_events == unread_pm:
# This condition is like previous if but with xor...
# Print in one line
text += '-'
else:
# Print in multiple lines
text += '\n '
if unread_chat:
text += ngettext(
' %d unread message',
' %d unread messages',
unread_chat, unread_chat, unread_chat)
text += '\n '
if unread_single_chat:
text += ngettext(
' %d unread single message',
' %d unread single messages',
unread_single_chat, unread_single_chat, unread_single_chat)
text += '\n '
if unread_gc:
text += ngettext(
' %d unread group chat message',
' %d unread group chat messages',
unread_gc, unread_gc, unread_gc)
text += '\n '
if unread_pm:
text += ngettext(
' %d unread private message',
' %d unread private messages',
unread_pm, unread_pm, unread_pm)
text += '\n '
text = text[:-4] # remove latest '\n '
elif len(accounts) > 1:
text = _('Gajim')
elif len(accounts) == 1:
message = accounts[0]['status_line']
message = reduce_chars_newlines(message, 100, 1)
text = _('Gajim - %s') % message
else:
text = _('Gajim - %s') % get_uf_show('offline')
return text
def get_accounts_info():
'''helper for notification icon tooltip'''
accounts = []
accounts_list = gajim.contacts.get_accounts()
accounts_list.sort()
for account in accounts_list:
status_idx = gajim.connections[account].connected
# uncomment the following to hide offline accounts
# if status_idx == 0: continue
status = gajim.SHOW_LIST[status_idx]
message = gajim.connections[account].status
single_line = get_uf_show(status)
if message is None:
message = ''
else:
message = message.strip()
if message != '':
single_line += ': ' + message
accounts.append({'name': account, 'status_line': single_line,
'show': status, 'message': message})
return accounts

View File

@ -1,17 +1,7 @@
## logger.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Nikos Kouremenos <kourem@gmail.com>
##
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <kourem@gmail.com>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Yann Le Boulanger <asterix@lagaule.org>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -136,9 +126,11 @@ class Logger:
and after that all okay'''
possible_room_jid, possible_nick = jid.split('/', 1)
return self.jid_is_room_jid(possible_room_jid)
def jid_is_room_jid(self, jid):
self.cur.execute('SELECT jid_id FROM jids WHERE jid=? AND type=?',
(possible_room_jid, constants.JID_ROOM_TYPE))
(jid, constants.JID_ROOM_TYPE))
row = self.cur.fetchone()
if row is None:
return False

View File

@ -126,8 +126,7 @@ class OptionsParser:
os.chmod(self.__filename, 0600)
def update_config(self, old_version, new_version):
# Convert '0.x.y' to (0, x, y)
old_version_list = old_version.split('.')
old_version_list = old_version.split('.') # convert '0.x.y' to (0, x, y)
old = []
while len(old_version_list):
old.append(int(old_version_list.pop(0)))
@ -146,7 +145,11 @@ class OptionsParser:
self.update_config_to_01012()
if old < [0, 10, 1, 3] and new >= [0, 10, 1, 3]:
self.update_config_to_01013()
if old < [0, 10, 1, 4] and new >= [0, 10, 1, 4]:
self.update_config_to_01014()
if old < [0, 10, 1, 5] and new >= [0, 10, 1, 5]:
self.update_config_to_01015()
gajim.logger.init_vars()
gajim.config.set('version', new_version)
@ -301,3 +304,46 @@ class OptionsParser:
pass
con.close()
gajim.config.set('version', '0.10.1.3')
def update_config_to_01014(self):
'''apply indeces to the logs database'''
import exceptions
try:
from pysqlite2 import dbapi2 as sqlite
except ImportError:
raise exceptions.PysqliteNotAvailable
import logger
print _('migrating logs database to indeces')
con = sqlite.connect(logger.LOG_DB_PATH)
cur = con.cursor()
# apply indeces
try:
cur.executescript(
'''
CREATE INDEX idx_logs_jid_id_kind ON logs (jid_id, kind);
CREATE INDEX idx_unread_messages_jid_id ON unread_messages (jid_id);
'''
)
con.commit()
except:
pass
con.close()
gajim.config.set('version', '0.10.1.4')
def update_config_to_01015(self):
'''clean show values in logs database'''
from pysqlite2 import dbapi2 as sqlite
import logger
con = sqlite.connect(logger.LOG_DB_PATH)
cur = con.cursor()
status = dict((i[5:].lower(), logger.constants.__dict__[i]) for i in \
logger.constants.__dict__.keys() if i.startswith('SHOW_'))
for show in status:
cur.execute('update logs set show = ? where show = ?;', (status[show],
show))
cur.execute('update logs set show = NULL where show not in (0, 1, 2, 3, 4, 5);')
con.commit()
cur.close()
con.close()
gajim.config.set('version', '0.10.1.5')

85
src/common/passwords.py Normal file
View File

@ -0,0 +1,85 @@
##
## Copyright (C) 2006 Gustavo J. A. M. Carneiro <gjcarneiro@gmail.com>
## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
__all__ = ['get_password', 'save_password']
import gobject
from common import gajim
try:
import gnomekeyring
except ImportError:
USER_HAS_GNOMEKEYRING = False
else:
USER_HAS_GNOMEKEYRING = True
class SimplePasswordStorage(object):
def get_password(self, account_name):
return gajim.config.get_per('accounts', account_name, 'password')
def save_password(self, account_name, password):
gajim.connections[account_name].password = password
class GnomePasswordStorage(object):
def __init__(self):
self.keyring = gnomekeyring.get_default_keyring_sync()
def get_password(self, account_name):
conf = gajim.config.get_per('accounts', account_name, 'password')
if conf is None:
return None
try:
unused, auth_token = conf.split('gnomekeyring:')
auth_token = int(auth_token)
except ValueError:
password = conf
## migrate the password over to keyring
self.save_password(account_name, password, update=False)
return password
try:
return gnomekeyring.item_get_info_sync(self.keyring,
auth_token).get_secret()
except gnomekeyring.DeniedError:
return None
def save_password(self, account_name, password, update=True):
display_name = _('Gajim account %s') % account_name
attributes = dict(account_name=str(account_name), gajim=1)
auth_token = gnomekeyring.item_create_sync(
self.keyring, gnomekeyring.ITEM_GENERIC_SECRET,
display_name, attributes, password, update)
token = 'gnomekeyring:%i' % auth_token
gajim.config.set_per('accounts', account_name, 'password', token)
storage = None
def get_storage():
global storage
if storage is None: # None is only in first time get_storage is called
if USER_HAS_GNOMEKEYRING:
#FIXME: detect if we're running under GNOME or not
#before deciding to use the GnomeKeyring backend
storage = GnomePasswordStorage()
else:
storage = SimplePasswordStorage()
return storage
def get_password(account_name):
return get_storage().get_password(account_name)
def save_password(account_name, password):
return get_storage().save_password(account_name, password)

View File

@ -0,0 +1,126 @@
## rst_xhtml_generator.py
##
## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2006 Santiago Gala
##
## 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.
##
try:
from docutils import io
from docutils.core import Publisher
from docutils.parsers.rst import roles
from docutils import nodes,utils
from docutils.parsers.rst.roles import set_classes
except:
def create_xhtml(text):
return None
else:
def jep_reference_role(role, rawtext, text, lineno, inliner,
options={}, content=[]):
'''Role to make handy references to Jabber Enhancement Proposals (JEP).
Use as :JEP:`71` (or jep, or jep-reference).
Modeled after the sample in docutils documentation.
'''
jep_base_url = 'http://www.jabber.org/jeps/'
jep_url = 'jep-%04d.html'
try:
jepnum = int(text)
if jepnum <= 0:
raise ValueError
except ValueError:
msg = inliner.reporter.error(
'JEP number must be a number greater than or equal to 1; '
'"%s" is invalid.' % text, line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
ref = jep_base_url + jep_url % jepnum
set_classes(options)
node = nodes.reference(rawtext, 'JEP ' + utils.unescape(text), refuri=ref,
**options)
return [node], []
roles.register_canonical_role('jep-reference', jep_reference_role)
from docutils.parsers.rst.languages.en import roles
roles['jep-reference'] = 'jep-reference'
roles['jep'] = 'jep-reference'
class HTMLGenerator:
'''Really simple HTMLGenerator starting from publish_parts.
It reuses the docutils.core.Publisher class, which means it is *not*
threadsafe.
'''
def __init__(self,
settings_spec=None,
settings_overrides=dict(report_level=5, halt_level=5),
config_section='general'):
self.pub = Publisher(reader=None, parser=None, writer=None,
settings=None,
source_class=io.StringInput,
destination_class=io.StringOutput)
self.pub.set_components(reader_name='standalone',
parser_name='restructuredtext',
writer_name='html')
# hack: JEP-0071 does not allow HTML char entities, so we hack our way
# out of it.
# &mdash; == u"\u2014"
# a setting to only emit charater entities in the writer would be nice
# FIXME: several &nbsp; are emitted, and they are explicitly forbidden
# in the JEP
# &nbsp; == u"\u00a0"
self.pub.writer.translator_class.attribution_formats['dash'] = (
u'\u2014', '')
self.pub.process_programmatic_settings(settings_spec,
settings_overrides,
config_section)
def create_xhtml(self, text,
destination=None,
destination_path=None,
enable_exit_status=None):
''' Create xhtml for a fragment of IM dialog.
We can use the source_name to store info about
the message.'''
self.pub.set_source(text, None)
self.pub.set_destination(destination, destination_path)
output = self.pub.publish(enable_exit_status=enable_exit_status)
# kludge until we can get docutils to stop generating (rare) &nbsp;
# entities
return u'\u00a0'.join(self.pub.writer.parts['fragment'].strip().split(
'&nbsp;'))
Generator = HTMLGenerator()
def create_xhtml(text):
return Generator.create_xhtml(text)
if __name__ == '__main__':
print Generator.create_xhtml('''
test::
>>> print 1
1
*I* like it. It is for :JEP:`71`
this `` should trigger`` should trigger the &nbsp; problem.
''')
print Generator.create_xhtml('''
*test1
test2_
''')

View File

@ -38,8 +38,11 @@ except:
from common import helpers
from common import gajim
from common import connection
from common import passwords
from common import zeroconf
from common.exceptions import GajimGeneralException as GajimGeneralException
#---------- PreferencesWindow class -------------#
class PreferencesWindow:
'''Class for Preferences window'''
@ -435,8 +438,7 @@ class PreferencesWindow:
self.xml.get_widget('custom_apps_frame').set_no_show_all(True)
if gajim.config.get('autodetect_browser_mailer'):
self.applications_combobox.set_active(0)
gtkgui_helpers.autodetect_browser_mailer()
# autodetect_browser_mailer is now False.
# else autodetect_browser_mailer is False.
# so user has 'Always Use GNOME/KDE' or Custom
elif gajim.config.get('openwith') == 'gnome-open':
self.applications_combobox.set_active(1)
@ -1324,6 +1326,8 @@ class AccountModificationWindow:
config['password'] = self.xml.get_widget('password_entry').get_text().\
decode('utf-8')
config['resource'] = resource
config['adjust_priority_with_status'] = self.xml.get_widget(
'adjust_priority_with_status_checkbutton').get_active()
config['priority'] = self.xml.get_widget('priority_spinbutton').\
get_value_as_int()
config['autoconnect'] = self.xml.get_widget('autoconnect_checkbutton').\
@ -1442,27 +1446,28 @@ class AccountModificationWindow:
# check if relogin is needed
relogin_needed = False
if self.options_changed_need_relogin(config,
('resource', 'proxy', 'usessl', 'keyname',
'use_custom_host', 'custom_host')):
('resource', 'proxy', 'usessl', 'keyname',
'use_custom_host', 'custom_host')):
relogin_needed = True
elif config['use_custom_host'] and (self.option_changed(config,
'custom_host') or self.option_changed(config, 'custom_port')):
'custom_host') or self.option_changed(config, 'custom_port')):
relogin_needed = True
if self.option_changed(config, 'use_ft_proxies') and \
config['use_ft_proxies']:
gajim.connections[self.account].discover_ft_proxies()
if self.option_changed(config, 'priority'):
if self.option_changed(config, 'priority') or self.option_changed(
config, 'adjust_priority_with_status'):
resend_presence = True
for opt in config:
gajim.config.set_per('accounts', name, opt, config[opt])
if config['savepass']:
gajim.connections[name].password = config['password']
passwords.save_password(name, config['password'])
else:
gajim.connections[name].password = None
passwords.save_password(name, None)
# refresh accounts window
if gajim.interface.instances.has_key('accounts'):
gajim.interface.instances['accounts'].init_accounts()
@ -1511,7 +1516,7 @@ class AccountModificationWindow:
def on_change_password_button_clicked(self, widget):
try:
dialog = dialogs.ChangePasswordDialog(self.account)
except RuntimeError:
except GajimGeneralException:
#if we showed ErrorDialog, there will not be dialog instance
return
@ -2497,10 +2502,10 @@ class ManageBookmarksWindow:
self.window = self.xml.get_widget('manage_bookmarks_window')
self.window.set_transient_for(gajim.interface.roster.window)
#Account-JID, RoomName, Room-JID, Autojoin, Passowrd, Nick, Show_Status
# Account-JID, RoomName, Room-JID, Autojoin, Passowrd, Nick, Show_Status
self.treestore = gtk.TreeStore(str, str, str, bool, str, str, str)
#Store bookmarks in treeview.
# Store bookmarks in treeview.
for account in gajim.connections:
if gajim.connections[account].connected <= 1:
continue
@ -2605,7 +2610,7 @@ class ManageBookmarksWindow:
account = model[add_to][1].decode('utf-8')
nick = gajim.nicks[account]
self.treestore.append(add_to, [account, _('New Room'), '', False, '',
self.treestore.append(add_to, [account, _('New Group Chat'), '', False, '',
nick, 'in_and_out'])
self.view.expand_row(model.get_path(add_to), True)
@ -2943,7 +2948,7 @@ class AccountCreationWizardWindow:
self.account = server + str(i)
i += 1
username, server = gajim.get_room_name_and_server_from_room_jid(jid)
username, server = gajim.get_name_and_server_from_jid(jid)
self.save_account(username, server, savepass, password)
self.cancel_button.hide()
self.back_button.hide()

View File

@ -1,17 +1,8 @@
## conversation_textview.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Nikos Kouremenos <kourem@gmail.com>
##
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <kourem@gmail.com>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2005-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -40,13 +31,17 @@ from calendar import timegm
from common.fuzzyclock import FuzzyClock
from htmltextview import HtmlTextView
from common.exceptions import GajimGeneralException as GajimGeneralException
class ConversationTextview:
'''Class for the conversation textview (where user reads already said messages)
for chat/groupchat windows'''
def __init__(self, account):
# no need to inherit TextView, use it as property is safer
def __init__(self, account, used_in_history_window = False):
'''if used_in_history_window is True, then we do not show
Clear menuitem in context menu'''
self.used_in_history_window = used_in_history_window
# no need to inherit TextView, use it as atrribute is safer
self.tv = HtmlTextView()
self.tv.html_hyperlink_handler = self.html_hyperlink_handler
@ -61,11 +56,13 @@ class ConversationTextview:
self.handlers = {}
# connect signals
id = self.tv.connect('motion_notify_event', self.on_textview_motion_notify_event)
id = self.tv.connect('motion_notify_event',
self.on_textview_motion_notify_event)
self.handlers[id] = self.tv
id = self.tv.connect('populate_popup', self.on_textview_populate_popup)
self.handlers[id] = self.tv
id = self.tv.connect('button_press_event', self.on_textview_button_press_event)
id = self.tv.connect('button_press_event',
self.on_textview_button_press_event)
self.handlers[id] = self.tv
self.account = account
@ -154,7 +151,7 @@ class ConversationTextview:
self.handlers[i].disconnect(i)
del self.handlers
self.tv.destroy()
#TODO
#FIXME:
# self.line_tooltip.destroy()
def update_tags(self):
@ -320,19 +317,29 @@ class ConversationTextview:
def on_textview_populate_popup(self, textview, menu):
'''we override the default context menu and we prepend Clear
(only if used_in_history_window is False)
and if we have sth selected we show a submenu with actions on the phrase
(see on_conversation_textview_button_press_event)'''
item = gtk.SeparatorMenuItem()
menu.prepend(item)
item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
menu.prepend(item)
id = item.connect('activate', self.clear)
self.handlers[id] = item
separator_menuitem_was_added = False
if not self.used_in_history_window:
item = gtk.SeparatorMenuItem()
menu.prepend(item)
separator_menuitem_was_added = True
item = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
menu.prepend(item)
id = item.connect('activate', self.clear)
self.handlers[id] = item
if self.selected_phrase:
s = self.selected_phrase
if len(s) > 25:
s = s[:21] + '...'
item = gtk.MenuItem(_('Actions for "%s"') % s)
if not separator_menuitem_was_added:
item = gtk.SeparatorMenuItem()
menu.prepend(item)
self.selected_phrase = helpers.reduce_chars_newlines(
self.selected_phrase, 25)
item = gtk.MenuItem(_('_Actions for "%s"') % self.selected_phrase)
menu.prepend(item)
submenu = gtk.Menu()
item.set_submenu(submenu)
@ -369,7 +376,8 @@ class ConversationTextview:
item.set_property('sensitive', False)
else:
link = dict_link % self.selected_phrase
id = item.connect('activate', self.visit_url_from_menuitem, link)
id = item.connect('activate', self.visit_url_from_menuitem,
link)
self.handlers[id] = item
submenu.append(item)
@ -385,13 +393,17 @@ class ConversationTextview:
id = item.connect('activate', self.visit_url_from_menuitem, link)
self.handlers[id] = item
submenu.append(item)
item = gtk.MenuItem(_('Open as _Link'))
id = item.connect('activate', self.visit_url_from_menuitem, link)
self.handlers[id] = item
submenu.append(item)
menu.show_all()
def on_textview_button_press_event(self, widget, event):
# If we clicked on a taged text do NOT open the standard popup menu
# if normal text check if we have sth selected
self.selected_phrase = ''
if event.button != 3: # if not right click
@ -430,18 +442,16 @@ class ConversationTextview:
def on_start_chat_activate(self, widget, jid):
gajim.interface.roster.new_chat_from_jid(self.account, jid)
def on_join_group_chat_menuitem_activate(self, widget, jid):
room, server = jid.split('@')
if gajim.interface.instances[self.account].has_key('join_gc'):
def on_join_group_chat_menuitem_activate(self, widget, room_jid):
if 'join_gc' in gajim.interface.instances[self.account]:
instance = gajim.interface.instances[self.account]['join_gc']
instance.xml.get_widget('server_entry').set_text(server)
instance.xml.get_widget('room_entry').set_text(room)
instance.xml.get_widget('room_jid_entry').set_text(room_jid)
gajim.interface.instances[self.account]['join_gc'].window.present()
else:
try:
gajim.interface.instances[self.account]['join_gc'] = \
dialogs.JoinGroupchatWindow(self.account, server, room)
except RuntimeError:
dialogs.JoinGroupchatWindow(self.account, room_jid)
except GajimGeneralException:
pass
def on_add_to_roster_activate(self, widget, jid):
@ -463,6 +473,7 @@ class ConversationTextview:
childs[6].hide() # join group chat
childs[7].hide() # add to roster
else: # It's a mail or a JID
text = text.lower()
id = childs[2].connect('activate', self.on_copy_link_activate, text)
self.handlers[id] = childs[2]
id = childs[3].connect('activate', self.on_open_link_activate, kind, text)
@ -701,7 +712,6 @@ class ConversationTextview:
# if tim_format comes as unicode because of day_str.
# we convert it to the encoding that we want (and that is utf-8)
tim_format = helpers.ensure_utf8_string(tim_format)
tim_format = tim_format.encode('utf-8')
buffer.insert_with_tags_by_name(end_iter, tim_format + ' ',
*other_tags_for_time)
elif current_print_time == 'sometimes' and kind != 'info':

View File

@ -40,6 +40,7 @@ from advanced import AdvancedConfigurationWindow
from common import gajim
from common import helpers
from common.exceptions import GajimGeneralException as GajimGeneralException
class EditGroupsDialog:
'''Class for the edit group dialog window'''
@ -139,6 +140,9 @@ class EditGroupsDialog:
group = self.xml.get_widget('group_entry').get_text().decode('utf-8')
if not group:
return
# Do not allow special groups
if group in helpers.special_groups:
return
# check if it already exists
model = self.list.get_model()
iter = model.get_iter_root()
@ -180,14 +184,16 @@ class EditGroupsDialog:
if account not in accounts:
accounts.append(account)
for g in gajim.groups[account].keys():
if g in helpers.special_groups:
continue
if g in groups:
continue
groups[g] = 0
for g in contact.groups:
groups[g] += 1
group_list = groups.keys()
group_list = []
# Remove special groups if they are empty
for group in groups:
if group not in helpers.special_groups or groups[group] > 0:
group_list.append(group)
group_list.sort()
for group in group_list:
iter = store.append()
@ -506,7 +512,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
if type_ == 'jabber':
self.uid_entry.set_text(jid)
else:
uid, transport = gajim.get_room_name_and_server_from_room_jid(jid)
uid, transport = gajim.get_name_and_server_from_jid(jid)
self.uid_entry.set_text(uid.replace('%', '@', 1))
#set protocol_combobox
model = self.protocol_combobox.get_model()
@ -1076,32 +1082,33 @@ class SubscriptionRequestWindow:
class JoinGroupchatWindow:
def __init__(self, account, server = '', room = '', nick = '',
automatic = False):
def __init__(self, account, room_jid = '', nick = '', automatic = False):
'''automatic is a dict like {'invities': []}
If automatic is not empty, this means room must be automaticaly configured
and when done, invities must be automatically invited'''
if server and room:
jid = room + '@' + server
if jid in gajim.gc_connected[account] and gajim.gc_connected[account][jid]:
ErrorDialog(_('You are already in room %s') % jid)
raise RuntimeError, 'You are already in this room'
if room_jid != '':
if room_jid in gajim.gc_connected[account] and\
gajim.gc_connected[account][room_jid]:
ErrorDialog(_('You are already in room %s') % room_jid)
raise GajimGeneralException, 'You are already in this room'
self.account = account
self.automatic = automatic
if nick == '':
nick = gajim.nicks[self.account]
if gajim.connections[account].connected < 2:
ErrorDialog(_('You are not connected to the server'),
_('You can not join a group chat unless you are connected.'))
raise RuntimeError, 'You must be connected to join a groupchat'
_('You can not join a group chat unless you are connected.'))
raise GajimGeneralException, 'You must be connected to join a groupchat'
self._empty_required_widgets = []
self.xml = gtkgui_helpers.get_glade('join_groupchat_window.glade')
self.window = self.xml.get_widget('join_groupchat_window')
self.xml.get_widget('server_entry').set_text(server)
self.xml.get_widget('room_entry').set_text(room)
self.xml.get_widget('nickname_entry').set_text(nick)
self._room_jid_entry = self.xml.get_widget('room_jid_entry')
self._nickname_entry = self.xml.get_widget('nickname_entry')
self._room_jid_entry.set_text(room_jid)
self._nickname_entry.set_text(nick)
self.xml.signal_autoconnect(self)
gajim.interface.instances[account]['join_gc'] = self #now add us to open windows
if len(gajim.connections) > 1:
@ -1121,18 +1128,13 @@ _('You can not join a group chat unless you are connected.'))
self.recently_combobox.append_text(g)
if len(self.recently_groupchat) == 0:
self.recently_combobox.set_sensitive(False)
elif server == '' and room == '':
elif room_jid == '':
self.recently_combobox.set_active(0)
self.xml.get_widget('room_entry').select_region(0, -1)
elif room and server:
self._room_jid_entry.select_region(0, -1)
elif room_jid != '':
self.xml.get_widget('join_button').grab_focus()
self._server_entry = self.xml.get_widget('server_entry')
self._room_entry = self.xml.get_widget('room_entry')
self._nickname_entry = self.xml.get_widget('nickname_entry')
if not self._server_entry.get_text():
self._empty_required_widgets.append(self._server_entry)
if not self._room_entry.get_text():
if not self._room_jid_entry.get_text():
self._empty_required_widgets.append(self._room_entry)
if not self._nickname_entry.get_text():
self._empty_required_widgets.append(self._nickname_entry)
@ -1160,27 +1162,11 @@ _('You can not join a group chat unless you are connected.'))
if len(self._empty_required_widgets) == 0:
self.xml.get_widget('join_button').set_sensitive(True)
def on_room_entry_key_press_event(self, widget, event):
# Check for pressed @ and jump to server_entry if found
if event.keyval == gtk.keysyms.at:
self.xml.get_widget('server_entry').grab_focus()
return True
def on_server_entry_key_press_event(self, widget, event):
# If backspace is pressed in empty server_entry, return to the room entry
backspace = event.keyval == gtk.keysyms.BackSpace
server_entry = self.xml.get_widget('server_entry')
empty = len(server_entry.get_text()) == 0
if backspace and empty:
self.xml.get_widget('room_entry').grab_focus()
return True
def on_recently_combobox_changed(self, widget):
model = widget.get_model()
iter = widget.get_active_iter()
gid = model[iter][0].decode('utf-8')
self.xml.get_widget('room_entry').set_text(gid.split('@')[0])
self.xml.get_widget('server_entry').set_text(gid.split('@')[1])
iter_ = widget.get_active_iter()
room_jid = model[iter_][0].decode('utf-8')
self._room_jid_entry.set_text(room_jid)
def on_cancel_button_clicked(self, widget):
'''When Cancel button is clicked'''
@ -1188,30 +1174,29 @@ _('You can not join a group chat unless you are connected.'))
def on_join_button_clicked(self, widget):
'''When Join button is clicked'''
nickname = self.xml.get_widget('nickname_entry').get_text().decode(
'utf-8')
room = self.xml.get_widget('room_entry').get_text().decode('utf-8')
server = self.xml.get_widget('server_entry').get_text().decode('utf-8')
nickname = self._nickname_entry.get_text().decode('utf-8')
room_jid = self._room_jid_entry.get_text().decode('utf-8')
password = self.xml.get_widget('password_entry').get_text().decode(
'utf-8')
jid = '%s@%s' % (room, server)
try:
jid = helpers.parse_jid(jid)
room_jid = helpers.parse_jid(room_jid)
except:
ErrorDialog(_('Invalid room or server name'),
_('The room name or server name has not allowed characters.'))
ErrorDialog(_('Invalid room Jabber ID'),
_('The room Jabber ID has not allowed characters.'))
return
if jid in self.recently_groupchat:
self.recently_groupchat.remove(jid)
self.recently_groupchat.insert(0, jid)
if room_jid in self.recently_groupchat:
self.recently_groupchat.remove(room_jid)
self.recently_groupchat.insert(0, room_jid)
if len(self.recently_groupchat) > 10:
self.recently_groupchat = self.recently_groupchat[0:10]
gajim.config.set('recently_groupchat', ' '.join(self.recently_groupchat))
gajim.config.set('recently_groupchat',
' '.join(self.recently_groupchat))
if self.automatic:
gajim.automatic_rooms[self.account][jid] = self.automatic
gajim.interface.roster.join_gc_room(self.account, jid, nickname, password)
gajim.automatic_rooms[self.account][room_jid] = self.automatic
gajim.interface.roster.join_gc_room(self.account, room_jid, nickname,
password)
self.window.destroy()
@ -1271,7 +1256,7 @@ class ChangePasswordDialog:
if not account or gajim.connections[account].connected < 2:
ErrorDialog(_('You are not connected to the server'),
_('Without a connection, you can not change your password.'))
raise RuntimeError, 'You are not connected to the server'
raise GajimGeneralException, 'You are not connected to the server'
self.account = account
self.xml = gtkgui_helpers.get_glade('change_password_dialog.glade')
self.dialog = self.xml.get_widget('change_password_dialog')
@ -2189,10 +2174,9 @@ class InvitationReceivedDialog:
def on_accept_button_clicked(self, widget):
self.dialog.destroy()
room, server = gajim.get_room_name_and_server_from_room_jid(self.room_jid)
try:
JoinGroupchatWindow(self.account, server = server, room = room)
except RuntimeError:
JoinGroupchatWindow(self.account, self.room_jid)
except GajimGeneralException:
pass
class ProgressDialog:

View File

@ -49,6 +49,7 @@ import gtkgui_helpers
from common import gajim
from common import xmpp
from common.exceptions import GajimGeneralException as GajimGeneralException
# Dictionary mapping category, type pairs to browser class, image pairs.
# This is a function, so we can call it after the classes are declared.
@ -121,6 +122,13 @@ class CacheDictionary:
def __call__(self):
return self.value
def cleanup(self):
for key in self.cache.keys():
item = self.cache[key]
if item.source:
gobject.source_remove(item.source)
del self.cache[key]
def _expire_timeout(self, key):
'''The timeout has expired, remove the object.'''
if key in self.cache:
@ -132,8 +140,9 @@ class CacheDictionary:
item = self.cache[key]
if item.source:
gobject.source_remove(item.source)
source = gobject.timeout_add(self.lifetime, self._expire_timeout, key)
item.source = source
if self.lifetime:
source = gobject.timeout_add(self.lifetime, self._expire_timeout, key)
item.source = source
def __getitem__(self, key):
item = self.cache[key]
@ -205,10 +214,14 @@ class ServicesCache:
ServiceCache instance.'''
def __init__(self, account):
self.account = account
self._items = CacheDictionary(1, getrefresh = True)
self._info = CacheDictionary(1, getrefresh = True)
self._items = CacheDictionary(0, getrefresh = False)
self._info = CacheDictionary(0, getrefresh = False)
self._cbs = {}
def cleanup(self):
self._items.cleanup()
self._info.cleanup()
def _clean_closure(self, cb, type, addr):
# A closure died, clean up
cbkey = (type, addr)
@ -584,6 +597,7 @@ _('Without a connection, you can not browse available services'))
self.browser = None
self.window.destroy()
self.cache.cleanup()
for child in self.children[:]:
child.parent = None
if chain:
@ -1178,16 +1192,10 @@ class ToplevelAgentBrowser(AgentBrowser):
if not iter:
return
service = model[iter][0].decode('utf-8')
if service.find('@') != -1:
services = service.split('@', 1)
room = services[0]
service = services[1]
else:
room = ''
if not gajim.interface.instances[self.account].has_key('join_gc'):
try:
dialogs.JoinGroupchatWindow(self.account, service, room)
except RuntimeError:
dialogs.JoinGroupchatWindow(self.account, service)
except GajimGeneralException:
pass
else:
gajim.interface.instances[self.account]['join_gc'].window.present()
@ -1489,7 +1497,8 @@ class MucBrowser(AgentBrowser):
self.vadj = self.window.services_scrollwin.get_property('vadjustment')
self.vadj_cbid = self.vadj.connect('value-changed', self.on_scroll)
# And to size changes
self.size_cbid = self.window.services_scrollwin.connect('size-allocate', self.on_scroll)
self.size_cbid = self.window.services_scrollwin.connect(
'size-allocate', self.on_scroll)
def _clean_treemodel(self):
if self.size_cbid:
@ -1518,16 +1527,12 @@ class MucBrowser(AgentBrowser):
if not iter:
return
service = model[iter][0].decode('utf-8')
if service.find('@') != -1:
services = service.split('@', 1)
room = services[0]
service = services[1]
else:
room = model[iter][1].decode('utf-8')
room = model[iter][1].decode('utf-8')
if 'join_gc' not in gajim.interface.instances[self.account]:
try:
dialogs.JoinGroupchatWindow(self.account, service, room)
except RuntimeError:
room_jid = '%s@%s' % (service, room)
dialogs.JoinGroupchatWindow(self.account, service)
except GajimGeneralException:
pass
else:
gajim.interface.instances[self.account]['join_gc'].window.present()

View File

@ -4,20 +4,11 @@ exec python -OOt "$0" ${1+"$@"}
' '''
## gajim.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Nikos Kouremenos <kourem@gmail.com>
## - Dimitur Kirov <dkirov@gmail.com>
## - Travis Shirk <travis@pobox.com>
##
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <kourem@gmail.com>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005 Travis Shirk <travis@pobox.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -82,16 +73,14 @@ except exceptions.PysqliteNotAvailable, e:
if os.name == 'nt':
try:
import winsound # windows-only built-in module for playing wav
import win32api
import win32con
except:
pritext = _('Gajim needs pywin32 to run')
sectext = _('Please make sure that Pywin32 is installed on your system. You can get it at %s') % 'http://sourceforge.net/project/showfiles.php?group_id=78018'
if pritext:
dlg = gtk.MessageDialog(None,
gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message_format = pritext)
gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message_format = pritext)
dlg.format_secondary_text(sectext)
dlg.run()
@ -194,7 +183,6 @@ atexit.register(on_exit)
parser = optparser.OptionsParser(config_filename)
import roster_window
import systray
import profile_window
import config
@ -509,7 +497,8 @@ class Interface:
def handle_event_msg(self, account, array):
# 'MSG' (account, (jid, msg, time, encrypted, msg_type, subject,
# chatstate, msg_id, composing_jep, user_nick, xhtml)) user_nick is JEP-0172
# chatstate, msg_id, composing_jep, user_nick, xhtml))
# user_nick is JEP-0172
full_jid_with_resource = array[0]
jid = gajim.get_jid_without_resource(full_jid_with_resource)
@ -609,7 +598,7 @@ class Interface:
msg_type, subject, resource, msg_id, array[9],
advanced_notif_num)
else:
#xhtml in last element
# xhtml in last element
self.roster.on_message(jid, message, array[2], account, array[3],
msg_type, subject, resource, msg_id, array[9],
advanced_notif_num, xhtml = array[10])
@ -1092,12 +1081,16 @@ class Interface:
if gajim.config.get('notify_on_new_gmail_email'):
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'new_email_recv.png')
title = _('New E-mail on %(gmail_mail_address)s') % \
title = _('New 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)
text = i18n.ngettext('You have %d new mail conversation',
'You have %d new mail conversations', gmail_new_messages,
gmail_new_messages, gmail_new_messages)
if gajim.config.get('notify_on_new_gmail_email_extra'):
for gmessage in gmail_messages_list:
#FIXME: emulate Gtalk client popups. find out what they parse and how
#they decide what to show
# each message has a 'From', 'Subject' and 'Snippet' field
text += _('\nFrom: %(from_address)s') % \
{'from_address': gmessage['From']}
@ -1382,12 +1375,11 @@ class Interface:
if gajim.gc_connected[account].has_key(room_jid) and\
gajim.gc_connected[account][room_jid]:
continue
room, server = gajim.get_room_name_and_server_from_room_jid(room_jid)
nick = gc_control.nick
password = ''
if gajim.gc_passwords.has_key(room_jid):
password = gajim.gc_passwords[room_jid]
gajim.connections[account].join_gc(nick, room, server, password)
gajim.connections[account].join_gc(nick, room_jid, password)
def handle_event_metacontacts(self, account, tags_list):
gajim.contacts.define_metacontacts(account, tags_list)
@ -1946,21 +1938,18 @@ class Interface:
self.systray_enabled = False
self.systray_capabilities = False
if os.name == 'nt':
pass
'''
try:
import systraywin32
except: # user doesn't have trayicon capabilities
pass
else:
self.systray_capabilities = True
self.systray = systraywin32.SystrayWin32()
'''
else:
if os.name == 'nt' and gtk.pygtk_version >= (2, 10, 0) and\
gtk.gtk_version >= (2, 10, 0):
import statusicon
self.systray = statusicon.StatusIcon()
self.systray_capabilities = True
else: # use ours, not GTK+ one
# [FIXME: remove this when we migrate to 2.10 and we can do
# cool tooltips somehow and (not dying to keep) animation]
import systray
self.systray_capabilities = systray.HAS_SYSTRAY_CAPABILITIES
if self.systray_capabilities:
self.systray = systray.Systray()
self.systray = systray.Systray()
if self.systray_capabilities and gajim.config.get('trayicon'):
self.show_systray()
@ -2013,7 +2002,8 @@ if __name__ == '__main__':
cli = gnome.ui.master_client()
cli.connect('die', die_cb)
path_to_gajim_script = gtkgui_helpers.get_abspath_for_script('gajim')
path_to_gajim_script = gtkgui_helpers.get_abspath_for_script(
'gajim')
if path_to_gajim_script:
argv = [path_to_gajim_script]
@ -2028,5 +2018,6 @@ if __name__ == '__main__':
gtkgui_helpers.possibly_set_gajim_as_xmpp_handler()
check_paths.check_and_possibly_create_paths()
Interface()
gtk.main()

View File

@ -39,12 +39,13 @@ from common import helpers
from chat_control import ChatControl
from chat_control import ChatControlBase
from conversation_textview import ConversationTextview
from common.exceptions import GajimGeneralException as GajimGeneralException
#(status_image, type, nick, shown_nick)
(
C_IMG, # image to show state (online, new message etc)
C_TYPE, # type of the row ('contact' or 'group')
C_NICK, # contact nickame or group name
C_NICK, # contact nickame or ROLE name
C_TYPE, # type of the row ('contact' or 'role')
C_TEXT, # text shown in the cellrenderer
C_AVATAR, # avatar of the contact
) = range(5)
@ -253,7 +254,7 @@ class GroupchatControl(ChatControlBase):
id = self.list_treeview.connect('size-allocate',
self.on_treeview_size_allocate)
self.handlers[id] = self.list_treeview
#status_image, type, nickname, shown_nick
#status_image, shown_nick, type, nickname, avatar
store = gtk.TreeStore(gtk.Image, str, str, str, gtk.gdk.Pixbuf)
store.set_sort_column_id(C_TEXT, gtk.SORT_ASCENDING)
self.list_treeview.set_model(store)
@ -397,7 +398,9 @@ class GroupchatControl(ChatControlBase):
jid = self.contact.jid
num_unread = len(gajim.events.get_events(self.account, jid,
['printed_gc_msg']))
if num_unread > 1:
if num_unread == 1:
unread = '*'
elif num_unread > 1:
unread = '[' + unicode(num_unread) + ']'
label_str = unread + label_str
return (label_str, color)
@ -664,7 +667,7 @@ class GroupchatControl(ChatControlBase):
self.subject = subject
self.name_label.set_ellipsize(pango.ELLIPSIZE_END)
subject = gtkgui_helpers.reduce_chars_newlines(subject, max_lines = 2)
subject = helpers.reduce_chars_newlines(subject, max_lines = 2)
subject = gtkgui_helpers.escape_for_pango_markup(subject)
font_attrs, font_attrs_small = self.get_font_attrs()
text = '<span %s>%s</span>' % (font_attrs, self.room_jid)
@ -672,8 +675,6 @@ class GroupchatControl(ChatControlBase):
text += '\n<span %s>%s</span>' % (font_attrs_small, subject)
self.name_label.set_markup(text)
event_box = self.name_label.get_parent()
if subject == '':
self.subject = _('This room has no subject')
# tooltip must always hold ALL the subject
self.subject_tooltip.set_tip(event_box, self.subject)
@ -735,7 +736,7 @@ class GroupchatControl(ChatControlBase):
if status and gajim.config.get('show_status_msgs_in_roster'):
status = status.strip()
if status != '':
status = gtkgui_helpers.reduce_chars_newlines(status, max_lines = 1)
status = helpers.reduce_chars_newlines(status, max_lines = 1)
# escape markup entities and make them small italic and fg color
color = gtkgui_helpers._get_fade_color(self.list_treeview,
selected, focus)
@ -913,9 +914,9 @@ class GroupchatControl(ChatControlBase):
role_iter = self.get_role_iter(role)
if not role_iter:
role_iter = model.append(None,
(gajim.interface.roster.jabber_state_images['16']['closed'], 'role',
role, '<b>%s</b>' % role_name, None))
iter = model.append(role_iter, (None, 'contact', nick, name, None))
(gajim.interface.roster.jabber_state_images['16']['closed'], role,
'role', '<b>%s</b>' % role_name, None))
iter = model.append(role_iter, (None, nick, 'contact', name, None))
if not nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
gc_contact = gajim.contacts.create_gc_contact(room_jid = self.room_jid,
name = nick, show = show, status = status, role = role,
@ -924,7 +925,7 @@ class GroupchatControl(ChatControlBase):
self.draw_contact(nick)
self.draw_avatar(nick)
# Do not ask avatar to irc rooms as irc transports reply with messages
r, server = gajim.get_room_name_and_server_from_room_jid(self.room_jid)
server = gajim.get_server_from_jid(self.room_jid)
if gajim.config.get('ask_avatars_on_startup') and \
not server.startswith('irc'):
fjid = self.room_jid + '/' + nick
@ -1038,8 +1039,10 @@ class GroupchatControl(ChatControlBase):
new_topic = message_array.pop(0)
gajim.connections[self.account].send_gc_subject(self.room_jid,
new_topic)
else:
elif self.subject is not '':
self.print_conversation(self.subject, 'info')
else:
self.print_conversation(_('This room has no subject'), 'info')
self.clear(self.msg_textview)
return True
elif command == 'invite':
@ -1067,15 +1070,13 @@ class GroupchatControl(ChatControlBase):
elif command == 'join':
# example: /join room@conference.example.com/nick
if len(message_array):
message_array = message_array[0]
if message_array.find('@') >= 0:
room, servernick = message_array.split('@')
if servernick.find('/') >= 0:
server, nick = servernick.split('/', 1)
room_jid = message_array[0]
if room_jid.find('@') >= 0:
if room_jid.find('/') >= 0:
room_jid, nick = room_jid.split('/', 1)
else:
server = servernick
nick = ''
#join_gc window is needed in order to provide for password entry.
# join_gc window is needed in order to provide for password entry.
if gajim.interface.instances[self.account].has_key('join_gc'):
gajim.interface.instances[self.account]['join_gc'].\
window.present()
@ -1083,13 +1084,13 @@ class GroupchatControl(ChatControlBase):
try:
gajim.interface.instances[self.account]['join_gc'] =\
dialogs.JoinGroupchatWindow(self.account,
server = server, room = room, nick = nick)
except RuntimeError:
room_jid = room_jid, nick = nick)
except GajimGeneralException:
pass
self.clear(self.msg_textview)
else:
#%s is something the user wrote but it is not a jid so we inform
s = _('%s does not appear to be a valid JID') % message_array
s = _('%s does not appear to be a valid JID') % message_array[0]
self.print_conversation(s, 'info')
else:
self.get_command_help(command)
@ -1376,7 +1377,7 @@ class GroupchatControl(ChatControlBase):
if bookmark['jid'] == bm['jid']:
dialogs.ErrorDialog(
_('Bookmark already set'),
_('Room "%s" is already in your bookmarks.') % bm['jid'])
_('Group Chat "%s" is already in your bookmarks.') % bm['jid'])
return
gajim.connections[self.account].bookmarks.append(bm)
@ -1832,7 +1833,7 @@ class GroupchatControl(ChatControlBase):
present()
else:
gajim.interface.instances[self.account]['infos'][c2.jid] = \
vcard.VcardWindow(c2, self.account, is_fake = True)
vcard.VcardWindow(c2, self.account, c)
def on_history(self, widget, nick):
jid = gajim.construct_fjid(self.room_jid, nick)

View File

@ -22,6 +22,7 @@ import threading
import gtk
import pango
from common import i18n
import dialogs
from cStringIO import StringIO

View File

@ -169,33 +169,6 @@ def get_default_font():
return None
def reduce_chars_newlines(text, max_chars = 0, max_lines = 0):
'''Cut the chars after 'max_chars' on each line
and show only the first 'max_lines'.
If any of the params is not present (None or 0) the action
on it is not performed'''
def _cut_if_long(string):
if len(string) > max_chars:
string = string[:max_chars - 3] + '...'
return string
if isinstance(text, str):
text = text.decode('utf-8')
if max_lines == 0:
lines = text.split('\n')
else:
lines = text.split('\n', max_lines)[:max_lines]
if max_chars > 0:
if lines:
lines = map(lambda e: _cut_if_long(e), lines)
if lines:
reduced_text = reduce(lambda e, e1: e + '\n' + e1, lines)
else:
reduced_text = ''
return reduced_text
def escape_for_pango_markup(string):
# escapes < > & ' "
# for pango markup not to break
@ -210,7 +183,22 @@ def escape_for_pango_markup(string):
return escaped_str
def autodetect_browser_mailer():
# recognize the environment for appropriate browser/mailer
# recognize the environment and set appropriate browser/mailer
if user_runs_gnome():
gajim.config.set('openwith', 'gnome-open')
elif user_runs_kde():
gajim.config.set('openwith', 'kfmclient exec')
else:
gajim.config.set('openwith', 'custom')
def user_runs_gnome():
return 'gnome-session' in get_running_processes()
def user_runs_kde():
return 'startkde' in get_running_processes()
def get_running_processes():
'''returns running processes or None (if not /proc exists)'''
if os.path.isdir('/proc'):
# under Linux: checking if 'gnome-session' or
# 'startkde' programs were run before gajim, by
@ -240,12 +228,8 @@ def autodetect_browser_mailer():
# list of processes
processes = [os.path.basename(os.readlink('/proc/' + f +'/exe')) for f in files]
if 'gnome-session' in processes:
gajim.config.set('openwith', 'gnome-open')
elif 'startkde' in processes:
gajim.config.set('openwith', 'kfmclient exec')
else:
gajim.config.set('openwith', 'custom')
return processes
def move_window(window, x, y):
'''moves the window but also checks if out of screen'''

View File

@ -1,7 +1,4 @@
#!/bin/sh
''':'
exec python -OOt "$0" ${1+"$@"}
' '''
#!/usr/bin/env python
## history_manager.py
##
## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com>
@ -33,6 +30,9 @@ import dialogs
import gtkgui_helpers
from common.logger import LOG_DB_PATH, constants
#FIXME: constants should implement 2 way mappings
status = dict((constants.__dict__[i], i[5:].lower()) for i in \
constants.__dict__.keys() if i.startswith('SHOW_'))
from common import gajim
from common import helpers
@ -51,7 +51,6 @@ except ImportError:
class HistoryManager:
def __init__(self):
path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps/gajim.png')
pix = gtk.gdk.pixbuf_new_from_file(path_to_file)
@ -312,6 +311,7 @@ class HistoryManager:
except ValueError:
pass
else:
color = None
if kind in (constants.KIND_SINGLE_MSG_RECV,
constants.KIND_CHAT_MSG_RECV, constants.KIND_GC_MSG):
# it is the other side
@ -327,11 +327,14 @@ class HistoryManager:
message = ''
else:
message = ' : ' + message
message = helpers.get_uf_show(show) + message
message = '<span foreground="%s">%s</span>' % (color,
gtkgui_helpers.escape_for_pango_markup(message))
self.logs_liststore.append((log_line_id, jid_id, time_, message,
message = helpers.get_uf_show(gajim.SHOW_LIST[show]) + message
message_ = '<span'
if color:
message_ += ' foreground="%s"' % color
message_ += '>%s</span>' % \
gtkgui_helpers.escape_for_pango_markup(message)
self.logs_liststore.append((log_line_id, jid_id, time_, message_,
subject, nickname))
def _fill_search_results_listview(self, text):

View File

@ -59,7 +59,8 @@ class HistoryWindow:
self.calendar = xml.get_widget('calendar')
scrolledwindow = xml.get_widget('scrolledwindow')
self.history_textview = conversation_textview.ConversationTextview(account)
self.history_textview = conversation_textview.ConversationTextview(
account, used_in_history_window = True)
scrolledwindow.add(self.history_textview.tv)
self.history_buffer = self.history_textview.tv.get_buffer()
self.history_buffer.create_tag('highlight', background = 'yellow')
@ -209,7 +210,9 @@ class HistoryWindow:
if gajim.config.get('print_time') == 'always':
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)
format = before_str + '%X' + after_str + ' '
tim = time.strftime(format, time.localtime(float(tim)))
buf.insert(end_iter, tim) # add time
@ -277,7 +280,9 @@ class HistoryWindow:
if contact_name and kind != constants.KIND_GCSTATUS:
# add stuff before and after contact name
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 + contact_name + after_str + ' '
buf.insert_with_tags_by_name(end_iter, format, tag_name)

View File

@ -228,10 +228,10 @@ def notify(event, jid, account, parameters, advanced_notif_num = None):
text = message
elif message_type == 'pm': # private message
event_type = _('New Private Message')
room_name, t = gajim.get_room_name_and_server_from_room_jid(jid)
room_name = gajim.get_nick_from_jid(jid)
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'priv_msg_recv.png')
title = _('New Private Message from room %s') % room_name
title = _('New Private Message from group chat %s') % room_name
text = _('%(nickname)s: %(message)s') % {'nickname': nickname,
'message': message}
else: # chat message

View File

@ -35,6 +35,9 @@ import notify
from common import gajim
from common import helpers
from common import passwords
from common.exceptions import GajimGeneralException as GajimGeneralException
from message_window import MessageWindowMgr
from chat_control import ChatControl
from groupchat_control import GroupchatControl
@ -192,6 +195,7 @@ class RosterWindow:
if self.regroup:
account = _('Merged accounts')
if not self.tree.row_expanded(path) and model.iter_has_child(iter):
# account row not expanded
model[iter][C_NAME] = '[%s]' % account
else:
model[iter][C_NAME] = account
@ -512,7 +516,7 @@ class RosterWindow:
if contact.status and gajim.config.get('show_status_msgs_in_roster'):
status = contact.status.strip()
if status != '':
status = gtkgui_helpers.reduce_chars_newlines(status, max_lines = 1)
status = helpers.reduce_chars_newlines(status, max_lines = 1)
# escape markup entities and make them small italic and fg color
color = gtkgui_helpers._get_fade_color(self.tree, selected, focus)
colorstring = "#%04x%04x%04x" % (color.red, color.green, color.blue)
@ -609,13 +613,12 @@ class RosterWindow:
dialogs.ErrorDialog(_('You cannot join a room while you are invisible')
)
return
room, server = room_jid.split('@')
if not gajim.interface.msg_win_mgr.has_window(room_jid, account):
self.new_room(room_jid, nick, account)
gc_win = gajim.interface.msg_win_mgr.get_window(room_jid, account)
gc_win.set_active_tab(room_jid, account)
gc_win.window.present()
gajim.connections[account].join_gc(nick, room, server, password)
gajim.connections[account].join_gc(nick, room_jid, password)
if password:
gajim.gc_passwords[room_jid] = password
@ -785,7 +788,9 @@ class RosterWindow:
disco_sub_menu = gtk.Menu()
new_chat_sub_menu = gtk.Menu()
for account in gajim.connections:
accounts_list = gajim.contacts.get_accounts()
accounts_list.sort()
for account in accounts_list:
if gajim.connections[account].connected <= 1:
# if offline or connecting
continue
@ -796,7 +801,8 @@ class RosterWindow:
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_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)
@ -960,8 +966,8 @@ class RosterWindow:
item.connect('activate', self.on_history_manager_menuitem_activate)
def add_bookmarks_list(self, gc_sub_menu, account):
'''Print join new room item and bookmarks list for an account'''
item = gtk.MenuItem(_('_Join New Room'))
'''Show join new group chat item and bookmarks list for an account'''
item = gtk.MenuItem(_('_Join New Group Chat'))
item.connect('activate', self.on_join_gc_activate, account)
gc_sub_menu.append(item)
@ -1690,9 +1696,9 @@ class RosterWindow:
try:
gajim.interface.instances[account]['join_gc'] = \
dialogs.JoinGroupchatWindow(account,
server = gajim.connections[account].muc_jid[type_],
gajim.connections[account].muc_jid[type_],
automatic = {'invities': jid_list})
except RuntimeError:
except GajimGeneralException:
continue
break
@ -2048,7 +2054,7 @@ class RosterWindow:
model = self.tree.get_model()
account = model[iter][C_ACCOUNT].decode('utf-8')
if account != 'all':
if account != 'all': # not in merged mode
menu = self.build_account_menu(account)
else:
menu = gtk.Menu()
@ -2385,8 +2391,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
gajim.connections[account].password = passphrase
if save:
gajim.config.set_per('accounts', account, 'savepass', True)
gajim.config.set_per('accounts', account, 'password',
passphrase)
passwords.save_password(account, passphrase)
keyid = None
use_gpg_agent = gajim.config.get('use_gpg_agent')
@ -2848,7 +2853,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
try:
gajim.interface.instances[account]['join_gc'] = \
dialogs.JoinGroupchatWindow(account)
except RuntimeError:
except GajimGeneralException:
pass
def on_new_message_menuitem_activate(self, widget, account):
@ -3138,7 +3143,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
def on_roster_treeview_row_expanded(self, widget, iter, path):
'''When a row is expanded change the icon of the arrow'''
model = self.tree.get_model()
if gajim.config.get('mergeaccounts'):
if self.regroup: # merged accounts
accounts = gajim.connections.keys()
else:
accounts = [model[iter][C_ACCOUNT].decode('utf-8')]
@ -3170,7 +3175,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
'''When a row is collapsed :
change the icon of the arrow'''
model = self.tree.get_model()
if gajim.config.get('mergeaccounts'):
if self.regroup: # merged accounts
accounts = gajim.connections.keys()
else:
accounts = [model[iter][C_ACCOUNT].decode('utf-8')]
@ -3287,7 +3292,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
try:
# Object will add itself to the window dict
disco.ServiceDiscoveryWindow(account, address_entry = True)
except RuntimeError:
except GajimGeneralException:
pass
def load_iconset(self, path, pixbuf2 = None, transport = False):

View File

@ -1,116 +0,0 @@
## rst_xhtml_generator.py
##
## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2006 Santiago Gala
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
from docutils import io
from docutils.core import Publisher
from docutils.parsers.rst import roles
def jep_reference_role(role, rawtext, text, lineno, inliner,
options={}, content=[]):
'''Role to make handy references to Jabber Enhancement Proposals (JEP).
Use as :JEP:`71` (or jep, or jep-reference).
Modeled after the sample in docutils documentation.
'''
from docutils import nodes,utils
from docutils.parsers.rst.roles import set_classes
jep_base_url = 'http://www.jabber.org/jeps/'
jep_url = 'jep-%04d.html'
try:
jepnum = int(text)
if jepnum <= 0:
raise ValueError
except ValueError:
msg = inliner.reporter.error(
'JEP number must be a number greater than or equal to 1; '
'"%s" is invalid.' % text, line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
ref = jep_base_url + jep_url % jepnum
set_classes(options)
node = nodes.reference(rawtext, 'JEP ' + utils.unescape(text), refuri=ref,
**options)
return [node], []
roles.register_canonical_role('jep-reference', jep_reference_role)
from docutils.parsers.rst.languages.en import roles
roles['jep-reference'] = 'jep-reference'
roles['jep'] = 'jep-reference'
class HTMLGenerator:
'''Really simple HTMLGenerator starting from publish_parts.
It reuses the docutils.core.Publisher class, which means it is *not*
threadsafe.
'''
def __init__(self,
settings_spec=None,
settings_overrides=dict(report_level=5, halt_level=5),
config_section='general'):
self.pub = Publisher(reader=None, parser=None, writer=None,
settings=None,
source_class=io.StringInput,
destination_class=io.StringOutput)
self.pub.set_components(reader_name='standalone',
parser_name='restructuredtext',
writer_name='html')
#hack: JEP-0071 does not allow HTML char entities, so we hack our way out of it.
# &mdash; == u"\u2014"
# a setting to only emit charater entities in the writer would be nice
# FIXME: several &nbsp; are emitted, and they are explicitly forbidden in the JEP
# &nbsp; == u"\u00a0"
self.pub.writer.translator_class.attribution_formats['dash'] = (u'\u2014', '')
self.pub.process_programmatic_settings(settings_spec,
settings_overrides,
config_section)
def create_xhtml(self, text,
destination=None,
destination_path=None,
enable_exit_status=None):
''' Create xhtml for a fragment of IM dialog.
We can use the source_name to store info about
the message.'''
self.pub.set_source(text, None)
self.pub.set_destination(destination, destination_path)
output = self.pub.publish(enable_exit_status=enable_exit_status)
#kludge until we can get docutils to stop generating (rare) &nbsp; entities
return u'\u00a0'.join(self.pub.writer.parts['fragment'].strip().split('&nbsp;'))
Generator = HTMLGenerator()
def create_xhtml(text):
return Generator.create_xhtml(text)
if __name__ == '__main__':
print Generator.create_xhtml('''
test::
>>> print 1
1
*I* like it. It is for :JEP:`71`
this `` should trigger`` should trigger the &nbsp; problem.
''')
print Generator.create_xhtml('''
*test1
test2_
''')

69
src/statusicon.py Normal file
View File

@ -0,0 +1,69 @@
## statusicon.py
##
## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com>
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 2
## of the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
import gtk
import gobject
import systray
from common import gajim
from common import helpers
class StatusIcon(systray.Systray):
'''Class for the notification area icon'''
#FIXME: when we migrate to GTK 2.10 stick only to this class
# (move base stuff from systray.py and rm it)
#NOTE: gtk api does NOT allow:
# leave, enter motion notify
# and can't do cool tooltips we use
# and we could use blinking instead of unsupported animation
# or we could emulate animation by every foo ms chaning the image
def __init__(self):
systray.Systray.__init__(self)
self.status_icon = gtk.StatusIcon()
def show_icon(self):
self.status_icon.connect('activate', self.on_status_icon_left_clicked)
self.status_icon.connect('popup-menu', self.on_status_icon_right_clicked)
self.set_img()
self.status_icon.props.visible = True
def on_status_icon_right_clicked(self, widget, event_button, event_time):
self.make_menu(event_button, event_time)
def hide_icon(self):
self.status_icon.props.visible = False
def on_status_icon_left_clicked(self, widget):
self.on_left_click()
def set_img(self):
'''apart from image, we also update tooltip text here'
if not gajim.interface.systray_enabled:
return
text = helpers.get_notification_icon_tooltip_text()
self.status_icon.set_tooltip(text)
if gajim.events.get_nb_systray_events():
state = 'message'
else:
state = self.status
#FIXME: do not always use 16x16 (ask actually used size and use that)
image = gajim.interface.roster.jabber_state_images['16'][state]
if image.get_storage_type() == gtk.IMAGE_PIXBUF:
self.status_icon.props.pixbuf = image.get_pixbuf()
#FIXME: oops they forgot to support GIF animation?
#or they were lazy to get it to work under Windows! WTF!
#elif image.get_storage_type() == gtk.IMAGE_ANIMATION:
# self.img_tray.set_from_animation(image.get_animation())

View File

@ -43,7 +43,7 @@ except:
class Systray:
'''Class for icon in the notification area
This class is both base class (for systraywin32.py) and normal class
This class is both base class (for statusicon.py) and normal class
for trayicon in GNU/Linux'''
def __init__(self):
@ -94,16 +94,14 @@ class Systray:
def on_new_chat(self, widget, account):
dialogs.NewChatDialog(account)
def make_menu(self, event = None):
'''create chat with and new message (sub) menus/menuitems
event is None when we're in Windows
'''
def make_menu(self, event_button, event_time):
'''create chat with and new message (sub) menus/menuitems'''
for m in self.popup_menus:
m.destroy()
chat_with_menuitem = self.xml.get_widget('chat_with_menuitem')
single_message_menuitem = self.xml.get_widget('single_message_menuitem')
single_message_menuitem = self.xml.get_widget(
'single_message_menuitem')
status_menuitem = self.xml.get_widget('status_menu')
join_gc_menuitem = self.xml.get_widget('join_gc_menuitem')
@ -171,7 +169,8 @@ class Systray:
self.popup_menus.append(account_menu_for_chat_with)
account_menu_for_single_message = gtk.Menu()
single_message_menuitem.set_submenu(account_menu_for_single_message)
single_message_menuitem.set_submenu(
account_menu_for_single_message)
self.popup_menus.append(account_menu_for_single_message)
accounts_list = gajim.contacts.get_accounts()
@ -195,9 +194,11 @@ 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_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)
gajim.interface.roster.add_bookmarks_list(gc_sub_menu,
account)
elif connected_accounts == 1: # one account
# one account connected, no need to show 'as jid'
@ -207,24 +208,24 @@ class Systray:
'activate', self.on_new_chat, account)
# for single message
single_message_menuitem.remove_submenu()
self.single_message_handler_id = single_message_menuitem.connect(
'activate', self.on_single_message_menuitem_activate, account)
self.single_message_handler_id = single_message_menuitem.\
connect('activate',
self.on_single_message_menuitem_activate, account)
# join gc
gajim.interface.roster.add_bookmarks_list(gc_sub_menu, account)
gajim.interface.roster.add_bookmarks_list(gc_sub_menu,
account)
break # No other connected account
if event is None:
# None means windows (we explicitly popup in systraywin32.py)
if self.added_hide_menuitem is False:
self.systray_context_menu.prepend(gtk.SeparatorMenuItem())
item = gtk.MenuItem(_('Hide this menu'))
self.systray_context_menu.prepend(item)
self.added_hide_menuitem = True
if os.name == 'nt' and gtk.pygtk_version >= (2, 10, 0) and\
gtk.gtk_version >= (2, 10, 0):
self.systray_context_menu.popup(None, None,
gtk.status_icon_position_menu, event_button,
event_time, self.status_icon)
else: # GNU and Unices
self.systray_context_menu.popup(None, None, None, event.button,
event.time)
self.systray_context_menu.popup(None, None, None, event_button,
event_time)
self.systray_context_menu.show_all()
def on_show_all_events_menuitem_activate(self, widget):
@ -277,12 +278,14 @@ class Systray:
def on_clicked(self, widget, event):
self.on_tray_leave_notify_event(widget, None)
if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1: # Left click
if event.type != gtk.gdk.BUTTON_PRESS:
return
if event.button == 1: # Left click
self.on_left_click()
elif event.button == 2: # middle click
self.on_middle_click()
elif event.button == 3: # right click
self.make_menu(event)
self.make_menu(event.button, event.time)
def on_show_menuitem_activate(self, widget, show):
# we all add some fake (we cannot select those nor have them as show)

View File

@ -38,7 +38,7 @@ class BaseTooltip:
tooltip.hide_tooltip()
* data - the text to be displayed (extenders override this argument and
dislpay more complex contents)
display more complex contents)
* widget_height - the height of the widget on which we want to show tooltip
* widget_y_position - the vertical position of the widget on the screen
@ -135,7 +135,7 @@ class BaseTooltip:
preferred_y = widget_y_position + widget_height + 4
self.preferred_position = [pointer_x, preferred_y]
self.widget_height =widget_height
self.widget_height = widget_height
self.win.ensure_style()
self.win.show_all()
@ -177,10 +177,12 @@ class StatusTable:
# make sure 'status' is unicode before we send to to reduce_chars
if isinstance(status, str):
status = unicode(status, encoding='utf-8')
# reduce to 200 chars, 1 line
status = gtkgui_helpers.reduce_chars_newlines(status, 200, 1)
str_status += ' - ' + status
return gtkgui_helpers.escape_for_pango_markup(str_status)
# reduce to 100 chars, 1 line
status = helpers.reduce_chars_newlines(status, 100, 1)
str_status = gtkgui_helpers.escape_for_pango_markup(str_status)
status = gtkgui_helpers.escape_for_pango_markup(status)
str_status += ' - <i>' + status + '</i>'
return str_status
def add_status_row(self, file_path, show, str_status, status_time = None, show_lock = False):
''' appends a new row with status icon to the table '''
@ -227,27 +229,6 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable):
BaseTooltip.__init__(self)
StatusTable.__init__(self)
def get_accounts_info(self):
accounts = []
accounts_list = gajim.contacts.get_accounts()
accounts_list.sort()
for account in accounts_list:
status_idx = gajim.connections[account].connected
# uncomment the following to hide offline accounts
# if status_idx == 0: continue
status = gajim.SHOW_LIST[status_idx]
message = gajim.connections[account].status
single_line = helpers.get_uf_show(status)
if message is None:
message = ''
else:
message = message.strip()
if message != '':
single_line += ': ' + message
accounts.append({'name': account, 'status_line': single_line,
'show': status, 'message': message})
return accounts
def fill_table_with_accounts(self, accounts):
iconset = gajim.config.get('iconset')
if not iconset:
@ -259,7 +240,7 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable):
# there are possible pango TBs on 'set_markup'
if isinstance(message, str):
message = unicode(message, encoding = 'utf-8')
message = gtkgui_helpers.reduce_chars_newlines(message, 50, 1)
message = helpers.reduce_chars_newlines(message, 100, 1)
message = gtkgui_helpers.escape_for_pango_markup(message)
if gajim.con_types.has_key(acct['name']) and \
gajim.con_types[acct['name']] in ('tls', 'ssl'):
@ -278,67 +259,16 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable):
def populate(self, data):
self.create_window()
self.create_table()
self.hbox = gtk.HBox()
self.table.set_property('column-spacing', 1)
text, single_line = '', ''
unread_chat = gajim.events.get_nb_events(types = ['printed_chat', 'chat'])
unread_single_chat = gajim.events.get_nb_events(types = ['normal'])
unread_gc = gajim.events.get_nb_events(types = ['printed_gc_msg',
'gc_msg'])
unread_pm = gajim.events.get_nb_events(types = ['printed_pm', 'pm'])
accounts = self.get_accounts_info()
if unread_chat or unread_single_chat or unread_gc or unread_pm:
text = 'Gajim '
awaiting_events = unread_chat + unread_single_chat + unread_gc + unread_pm
if awaiting_events == unread_chat or awaiting_events == unread_single_chat \
or awaiting_events == unread_gc or awaiting_events == unread_pm:
# This condition is like previous if but with xor...
# Print in one line
text += '-'
else:
# Print in multiple lines
text += '\n '
if unread_chat:
text += i18n.ngettext(
' %d unread message',
' %d unread messages',
unread_chat, unread_chat, unread_chat)
text += '\n '
if unread_single_chat:
text += i18n.ngettext(
' %d unread single message',
' %d unread single messages',
unread_single_chat, unread_single_chat, unread_single_chat)
text += '\n '
if unread_gc:
text += i18n.ngettext(
' %d unread group chat message',
' %d unread group chat messages',
unread_gc, unread_gc, unread_gc)
text += '\n '
if unread_pm:
text += i18n.ngettext(
' %d unread private message',
' %d unread private messages',
unread_pm, unread_pm, unread_pm)
text += '\n '
text = text[:-4] # remove latest '\n '
elif len(accounts) > 1:
text = _('Gajim')
self.current_current_row = 1
accounts = helpers.get_accounts_info()
if len(accounts) > 1:
self.table.resize(2, 1)
self.fill_table_with_accounts(accounts)
self.hbox = gtk.HBox()
self.table.set_property('column-spacing', 1)
elif len(accounts) == 1:
message = accounts[0]['status_line']
message = gtkgui_helpers.reduce_chars_newlines(message, 50, 1)
message = gtkgui_helpers.escape_for_pango_markup(message)
text = _('Gajim - %s') % message
else:
text = _('Gajim - %s') % helpers.get_uf_show('offline')
text = helpers.get_notification_icon_tooltip_text()
text = gtkgui_helpers.escape_for_pango_markup(text)
self.add_text_row(text)
self.hbox.add(self.table)
self.win.add(self.hbox)
@ -364,7 +294,25 @@ class GCTooltip(BaseTooltip):
vcard_table.set_homogeneous(False)
vcard_current_row = 1
properties = []
nick_markup = '<b>' + \
gtkgui_helpers.escape_for_pango_markup(contact.get_shown_name()) \
+ '</b>'
properties.append((nick_markup, None))
if contact.status: # status message
status = contact.status.strip()
if status != '':
# escape markup entities
status = helpers.reduce_chars_newlines(status, 100, 5)
status = '<i>' +\
gtkgui_helpers.escape_for_pango_markup(status) + '</i>'
properties.append((status, None))
else: # no status message, show SHOW instead
show = helpers.get_uf_show(contact.show)
show = '<i>' + show + '</i>'
properties.append((show, None))
if contact.jid.strip() != '':
jid_markup = '<span weight="bold">' + contact.jid + '</span>'
else:
@ -445,8 +393,7 @@ class RosterTooltip(NotificationAreaTooltip):
self.create_table()
if not contacts or len(contacts) == 0:
# Tooltip for merged accounts row
accounts = self.get_accounts_info()
self.current_current_row = 0
accounts = helpers.get_accounts_info()
self.table.resize(2, 1)
self.spacer_label = ''
self.fill_table_with_accounts(accounts)
@ -539,8 +486,8 @@ class RosterTooltip(NotificationAreaTooltip):
status = contact.status.strip()
if status:
# reduce long status
# (no more than 200 chars on line and no more than 5 lines)
status = gtkgui_helpers.reduce_chars_newlines(status, 200, 5)
# (no more than 100 chars on line and no more than 5 lines)
status = helpers.reduce_chars_newlines(status, 100, 5)
# escape markup entities.
status = gtkgui_helpers.escape_for_pango_markup(status)
show += ' - ' + status

View File

@ -60,7 +60,7 @@ def get_avatar_pixbuf_encoded_mime(photo):
class VcardWindow:
'''Class for contact's information window'''
def __init__(self, contact, account, is_fake = False):
def __init__(self, contact, account, gc_contact = None):
# 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')
@ -68,7 +68,7 @@ class VcardWindow:
self.contact = contact
self.account = account
self.is_fake = is_fake
self.gc_contact = gc_contact
self.avatar_mime_type = None
self.avatar_encoded = None
@ -246,26 +246,38 @@ class VcardWindow:
self.contact.get_shown_name() +
'</span></b>')
self.xml.get_widget('jid_label').set_text(self.contact.jid)
uf_sub = helpers.get_uf_sub(self.contact.sub)
self.xml.get_widget('subscription_label').set_text(uf_sub)
eb = self.xml.get_widget('subscription_label_eventbox')
if self.contact.sub == 'from':
tt_text = _("This contact is interested in your presence information, but you are not interested in his/her presence")
elif self.contact.sub == 'to':
tt_text = _("You are interested in the contact's presence information, but he/she is not interested in yours")
elif self.contact.sub == 'both':
tt_text = _("You and the contact are interested in each other's presence information")
else: # None
tt_text = _("You are not interested in the contact's presence, and neither he/she is interested in yours")
tooltips.set_tip(eb, tt_text)
subscription_label = self.xml.get_widget('subscription_label')
ask_label = self.xml.get_widget('ask_label')
if self.gc_contact:
self.xml.get_widget('subscription_title_label').set_text(_("Role:"))
uf_role = helpers.get_uf_role(self.gc_contact.role)
subscription_label.set_text(uf_role)
self.xml.get_widget('ask_title_label').set_text(_("Affiliation:"))
uf_affiliation = helpers.get_uf_affiliation(self.gc_contact.affiliation)
ask_label.set_text(uf_affiliation)
else:
uf_sub = helpers.get_uf_sub(self.contact.sub)
subscription_label.set_text(uf_sub)
eb = self.xml.get_widget('subscription_label_eventbox')
if self.contact.sub == 'from':
tt_text = _("This contact is interested in your presence information, but you are not interested in his/her presence")
elif self.contact.sub == 'to':
tt_text = _("You are interested in the contact's presence information, but he/she is not interested in yours")
elif self.contact.sub == 'both':
tt_text = _("You and the contact are interested in each other's presence information")
else: # None
tt_text = _("You are not interested in the contact's presence, and neither he/she is interested in yours")
tooltips.set_tip(eb, tt_text)
uf_ask = helpers.get_uf_ask(self.contact.ask)
ask_label.set_text(uf_ask)
eb = self.xml.get_widget('ask_label_eventbox')
if self.contact.ask == 'subscribe':
tooltips.set_tip(eb,
_("You are waiting contact's answer about your subscription request"))
label = self.xml.get_widget('ask_label')
uf_ask = helpers.get_uf_ask(self.contact.ask)
label.set_text(uf_ask)
eb = self.xml.get_widget('ask_label_eventbox')
if self.contact.ask == 'subscribe':
tooltips.set_tip(eb,
_("You are waiting contact's answer about your subscription request"))
log = True
if self.contact.jid in gajim.config.get_per('accounts', self.account,
'no_log_for').split(' '):
@ -318,7 +330,8 @@ class VcardWindow:
self.fill_status_label()
gajim.connections[self.account].request_vcard(self.contact.jid, self.is_fake)
gajim.connections[self.account].request_vcard(self.contact.jid,
self.gc_contact is not None)
def on_close_button_clicked(self, widget):
self.window.destroy()