Merge with trunk

This commit is contained in:
Piotr Gaczkowski 2007-06-03 10:04:20 +00:00
parent 19c5c70f80
commit e3ef0821b3
74 changed files with 18085 additions and 9111 deletions

View File

@ -9,7 +9,7 @@ Packager: Filippos Papadopoulos <psybases@gmail.com>
Summary: Gajim is a Jabber client written in Python.
URL: http://www.gajim.org/
License: GNU General Public License, Version 2
SoftwareVersion: 0.10
SoftwareVersion: 0.11.1
AutopackageTarget: 1.0
PackageVersion: 1
@ -28,13 +28,7 @@ File tranfers, Emoticons, URL grabber, Systray icon, GPG support, Multiple accou
[BuildPrepare]
echo "-------==========$USER i am making the package...===============------"
export build_root="/tmp/build-root.$$"
echo "Build root is $build_root"
mkdir "$build_root"
make PREFIX=$build_root CC=apgcc CXX=apg++ || exit 1
make install PREFIX=$build_root CC=apgcc CXX=apg++ || exit 1
prepareBuild
[BuildUnprepare]
@ -44,24 +38,28 @@ unprepareBuild
[Imports]
echo '*' | import
import <<EOF
$source_dir/gajim.desktop
$source_dir/gajim.1
EOF
[Prepare]
# Dependency checking
#PyGTK 2.4 requires python 2.3
require @python.org/python 2.4
require @python.org/python-xml 2.4
require @gtk.org/gtk 2.6
########The 2.5 version is for SUSE 9.3
require @gnome.org/pygtk 2.5
require @gnome.org/pyglade 2.5
require @glade.gnome.org/libglade 2
require @pysqlite.org/pysqlite 2
recommend @gtkspell.sourceforge.net/gtkspell 0
if ! require @dnspython.org/dnspython 1; then
recommend @pydns.sourceforge.net/pydns 2
fi
#recommend @dnspython.org/dnspython 1
#recommend @pydns.sourceforge.net/pydns 2
[Install]
@ -69,17 +67,25 @@ recommend @gtkspell.sourceforge.net/gtkspell 0
copyFiles lib/gajim "$PREFIX/lib"
copyFiles share/gajim "$PREFIX/share/"
copyFiles share/doc "$PREFIX/share/"
installLocale share/locale
installIcon share/pixmaps/gajim.png
installDesktop "Network/Instant Messaging" gajim.desktop
installMan 1 gajim.1
installIcon share/pixmaps/gajim_about.png
installDesktop "Network/Instant Messaging" share/applications/gajim.desktop
installMan 1 share/man/man1/gajim.1 share/man/man1/gajim-remote.1
#In the following safeSed we assume that the original Makefile is a bit modified so that to be relocatable by AP
#so you have to manually remove the sed in Makefile for AP to work
safeSed bin/gajim "s!PREFIX!$PREFIX!g"
safeSed bin/gajim-remote "s!PREFIX!$PREFIX!g"
locateCommand python
safeSed bin/gajim "s!PYBIN!$lc_location!g"
safeSed bin/gajim-remote "s!PYBIN!$lc_location!g"
installExe bin/*
chmod +x "$PREFIX/bin/gajim"
chmod +x "$PREFIX/bin/gajim-remote"
#chmod +x "$PREFIX/bin/gajim"
#chmod +x "$PREFIX/bin/gajim-remote"
[Uninstall]

View File

@ -8,28 +8,28 @@ EMOTICONS_FILES = **/{*.png,*.gif,emoticons.py}
install-data-local:
@for d in $(EMOTICONS_DIRS);do \
if test -d $$d;then \
@for d in $$(cd $(srcdir); echo $(EMOTICONS_DIRS));do \
if test -d $(srcdir)/$$d;then \
echo " $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/data/emoticons/$$d"; \
$(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/data/emoticons/$$d || exit 1; \
fi; \
done; \
for f in $(EMOTICONS_FILES);do \
if test -f $$f; then \
echo " $(INSTALL_DATA) $$f $(DESTDIR)$(pkgdatadir)/data/emoticons/$$f"; \
$(INSTALL_DATA) $$f $(DESTDIR)$(pkgdatadir)/data/emoticons/$$f || exit 1; \
for f in $$(cd $(srcdir); echo $(EMOTICONS_FILES));do \
if test -f $(srcdir)/$$f; then \
echo " $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(pkgdatadir)/data/emoticons/$$f"; \
$(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(pkgdatadir)/data/emoticons/$$f || exit 1; \
fi; \
done;
dist-hook:
@for d in $(EMOTICONS_DIRS);do \
if test -d $$d;then \
@for d in $$(cd $(srcdir); echo $(EMOTICONS_DIRS));do \
if test -d $(srcdir)/$$d;then \
echo " $(mkdir_p) $(distdir)/$$d"; \
$(mkdir_p) $(distdir)/$$d || exit 1; \
fi; \
done; \
for f in $(EMOTICONS_FILES);do \
if test -f $$f; then \
for f in $$(cd $(srcdir); echo $(EMOTICONS_FILES));do \
if test -f $(srcdir)/$$f; then \
echo " cp -pR $(srcdir)/$$f $(distdir)/$$f"; \
cp -pR $(srcdir)/$$f $(distdir)/$$f || exit 1; \
fi; \

File diff suppressed because it is too large Load Diff

View File

@ -99,7 +99,7 @@
<widget class="GtkTable" id="table24">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="n_rows">4</property>
<property name="n_rows">5</property>
<property name="n_columns">3</property>
<property name="homogeneous">False</property>
<property name="row_spacing">6</property>
@ -134,27 +134,6 @@
</packing>
</child>
<child>
<widget class="GtkEntry" id="jid_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label202">
<property name="visible">True</property>
@ -260,50 +239,6 @@
</packing>
</child>
<child>
<widget class="GtkEntry" id="resource_entry">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Resource is sent to the Jabber server in order to separate the same JID in two or more parts depending on the number of the clients connected in the same server with the same account. So you might be connected in the same account with resource 'Home' and 'Work' at the same time. The resource which has the highest priority will get the events. (see below)</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">Gajim</property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">expand|shrink|fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkButton" id="change_password_button">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Click to change account's password</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Chan_ge Password</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_change_password_button_clicked" last_modification_time="Fri, 04 Mar 2005 11:33:37 GMT"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label220">
<property name="visible">True</property>
@ -379,6 +314,134 @@
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkExpander" id="expander1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="expanded">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkHBox" id="hbox2968">
<property name="visible">True</property>
<property name="homogeneous">True</property>
<property name="spacing">11</property>
<child>
<widget class="GtkButton" id="synchronise_contacts_button">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Click to request authorization to all contacts of another account</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Synchronise contacts</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_synchronise_contacts_button_clicked" last_modification_time="Thu, 01 Mar 2007 11:05:20 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="change_password_button">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Click to change account's password</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Chan_ge Password</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_change_password_button_clicked" last_modification_time="Fri, 04 Mar 2005 11:33:37 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label361">
<property name="visible">True</property>
<property name="label" translatable="yes">Administration operations</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">fill</property>
<property name="y_options">fill</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="jid_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="visibility">True</property>
<property name="max_length">0</property>
<property name="text" translatable="yes"></property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">0</property>
<property name="bottom_attach">1</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="resource_entry">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Resource is sent to the Jabber server in order to separate the same JID in two or more parts depending on the number of the clients connected in the same server with the same account. So you might be connected in the same account with resource 'Home' and 'Work' at the same time. The resource which has the highest priority will get the events. (see below)</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">Gajim</property>
<property name="has_frame">True</property>
<property name="invisible_char">*</property>
<property name="activates_default">False</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">expand|shrink|fill</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
<packing>
<property name="tab_expand">False</property>

View File

@ -11,7 +11,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1457">
<widget class="GtkImage" id="image1466">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon_size">1</property>
@ -31,6 +31,27 @@
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="blocked_contacts_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Blocked Contacts</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_blocked_contacts_menuitem_activate" last_modification_time="Thu, 19 Apr 2007 15:32:47 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1467">
<property name="visible">True</property>
<property name="stock">gtk-stop</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="privacy_lists_menuitem">
<property name="label" translatable="yes">_Privacy Lists</property>
@ -58,7 +79,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1458">
<widget class="GtkImage" id="image1468">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon_size">1</property>
@ -100,7 +121,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1459">
<widget class="GtkImage" id="image1469">
<property name="visible">True</property>
<property name="stock">gtk-clear</property>
<property name="icon_size">1</property>

View File

@ -0,0 +1,147 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="blocked_contacts_window">
<property name="visible">True</property>
<property name="title" translatable="yes">Blocked Contacts</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="destroy" handler="on_blocked_contacts_window_destroy" last_modification_time="Sun, 22 Apr 2007 14:44:11 GMT"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="border_width">5</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkScrolledWindow" id="contacts_scrolledwindow">
<property name="border_width">3</property>
<property name="width_request">250</property>
<property name="height_request">300</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="contacts_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="rules_hint">True</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal name="row_activated" handler="on_contacts_treeview_row_activated" last_modification_time="Wed, 25 Apr 2007 13:09:39 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="remove_button">
<property name="border_width">3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_remove_button_clicked" last_modification_time="Sun, 22 Apr 2007 14:02:48 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-remove</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label">_Unblock</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -13,7 +13,7 @@
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1366">
<widget class="GtkImage" id="image1370">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
@ -42,7 +42,7 @@
<signal name="activate" handler="_on_send_file_menuitem_activate" last_modification_time="Tue, 03 Jan 2006 04:26:55 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1367">
<widget class="GtkImage" id="image1371">
<property name="visible">True</property>
<property name="stock">gtk-file</property>
<property name="icon_size">1</property>
@ -83,7 +83,7 @@
<signal name="activate" handler="_on_add_to_roster_menuitem_activate" last_modification_time="Tue, 03 Jan 2006 04:26:37 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1368">
<widget class="GtkImage" id="image1372">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>

View File

@ -584,7 +584,7 @@
</child>
<child>
<widget class="GtkVBox" id="vbox114">
<widget class="GtkVBox" id="buttons_vbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>

View File

@ -1,133 +1,131 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkMenu" id="gc_control_popup_menu">
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="tooltip" translatable="yes">Click to see past conversation in this room</property>
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1378">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="configure_room_menuitem">
<property name="label" translatable="yes">Configure _Room</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1379">
<property name="visible">True</property>
<property name="stock">gtk-preferences</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="destroy_room_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Destroy room</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1380">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="change_subject_menuitem">
<property name="label" translatable="yes">Change _Subject</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1381">
<property name="visible">True</property>
<property name="stock">gtk-edit</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="change_nick_menuitem">
<property name="label" translatable="yes">Change _Nickname</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1382">
<property name="visible">True</property>
<property name="stock">gtk-redo</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="bookmark_room_menuitem">
<property name="label" translatable="yes">_Bookmark This Room</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1383">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="compact_view_menuitem">
<property name="label" translatable="yes">_Compact View Alt+C</property>
<property name="use_underline">True</property>
<property name="active">False</property>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="gc_control_popup_menu">
<child>
<widget class="GtkImageMenuItem" id="change_nick_menuitem">
<property name="label" translatable="yes">Change _Nickname</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1409">
<property name="visible">True</property>
<property name="stock">gtk-redo</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="manage_room_menuitem">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Manage room</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkImageMenuItem" id="change_subject_menuitem">
<property name="label" translatable="yes">Change _Subject</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1408">
<property name="visible">True</property>
<property name="stock">gtk-edit</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="configure_room_menuitem">
<property name="label" translatable="yes">Configure _Room</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1406">
<property name="visible">True</property>
<property name="stock">gtk-preferences</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="destroy_room_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Destroy room</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1407">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="compact_view_menuitem">
<property name="label" translatable="yes">_Compact View Alt+C</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="minimize_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Minimize</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1411">
<property name="visible">True</property>
<property name="stock">gtk-goto-bottom</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separatormenuitem2">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="bookmark_room_menuitem">
<property name="label" translatable="yes">_Bookmark</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1410">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="tooltip" translatable="yes">Click to see past conversation in this room</property>
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1405">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -1,163 +1,132 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkMenu" id="gc_occupants_menu">
<child>
<widget class="GtkMenuItem" id="group_chat_actions_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Occupant Actions</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="group_chat_actions_menuitem_menu">
<child>
<widget class="GtkCheckMenuItem" id="voice_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Voice</property>
<property name="use_underline">True</property>
<property name="active">False</property>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="moderator_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Mo_derator</property>
<property name="use_underline">True</property>
<property name="active">False</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator5">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="member_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Member</property>
<property name="use_underline">True</property>
<property name="active">False</property>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="admin_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Admin</property>
<property name="use_underline">True</property>
<property name="active">False</property>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="owner_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Owner</property>
<property name="use_underline">True</property>
<property name="active">False</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator4">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="kick_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Kick</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="ban_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Ban</property>
<property name="use_underline">True</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="information_menuitem">
<property name="visible">True</property>
<property name="label">gtk-info</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1047">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_to_roster_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Add to Roster</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1048">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_private_message_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Send Private Message</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1049">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="gc_occupants_menu">
<child>
<widget class="GtkImageMenuItem" id="send_private_message_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Send Private Message</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1051">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_to_roster_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Add to Roster</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1052">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="group_chat_actions_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Occupant Actions</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="group_chat_actions_menuitem_menu">
<child>
<widget class="GtkCheckMenuItem" id="voice_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Voice</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="moderator_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Mo_derator</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator5">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="member_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Member</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="admin_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Admin</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="owner_checkmenuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Owner</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator4">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="kick_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Kick</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="ban_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Ban</property>
<property name="use_underline">True</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator6">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="information_menuitem">
<property name="visible">True</property>
<property name="label">gtk-info</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1053">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -87,6 +87,25 @@
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="log_history_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">_Log conversation history</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">True</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkExpander" id="search_expander">
<property name="visible">True</property>

View File

@ -79,7 +79,7 @@
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ypad">5</property>
</widget>
<packing>
<property name="padding">5</property>

View File

@ -1,341 +1,331 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkMenu" id="roster_contact_context_menu">
<child>
<widget class="GtkImageMenuItem" id="start_chat_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Start _Chat</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1511">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_single_message_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send Single _Message</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1512">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="invite_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">In_vite to</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1513">
<property name="visible">True</property>
<property name="stock">gtk-go-back</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="rename_menuitem">
<property name="label" translatable="yes">_Rename</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1514">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="edit_groups_menuitem">
<property name="label" translatable="yes">Edit _Groups</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_send_file_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_file_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send _File</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1515">
<property name="visible">True</property>
<property name="stock">gtk-file</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="assign_openpgp_key_menuitem">
<property name="label" translatable="yes">Assign Open_PGP Key</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_assign_openpgp_key_menuitem_activate" last_modification_time="Thu, 30 Jun 2005 22:57:59 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image1516">
<property name="visible">True</property>
<property name="stock">gtk-dialog-authentication</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_special_notification_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Add Special _Notification</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1517">
<property name="visible">True</property>
<property name="stock">gtk-info</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="execute_command_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Execute Command...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1467">
<property name="visible">True</property>
<property name="stock">gtk-execute</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_subscription_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="subscription_menuitem">
<property name="label" translatable="yes">_Subscription</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1518">
<property name="visible">True</property>
<property name="stock">gtk-dialog-question</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
<child>
<widget class="GtkMenu" id="subscription_menuitem_menu">
<child>
<widget class="GtkImageMenuItem" id="resend_authorization_to_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Allow him/her to see my status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1519">
<property name="visible">True</property>
<property name="stock">gtk-go-up</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="rerequest_authorization_from_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">A_sk to see his/her status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1520">
<property name="visible">True</property>
<property name="stock">gtk-go-down</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="forbid_him/her_to_see_my_status1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Forbid him/her to see my status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1521">
<property name="visible">True</property>
<property name="stock">gtk-stop</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_to_roster_menuitem">
<property name="label" translatable="yes">_Add to Roster</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1522">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="remove_from_roster_menuitem">
<property name="label" translatable="yes">_Remove from Roster</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1523">
<property name="visible">True</property>
<property name="stock">gtk-remove</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator6">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="information_menuitem">
<property name="label">gtk-info</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1524">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="roster_contact_context_menu">
<child>
<widget class="GtkImageMenuItem" id="start_chat_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Start _Chat</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1701">
<property name="visible">True</property>
<property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_single_message_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send Single _Message</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1703">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="send_file_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send _File</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1706">
<property name="visible">True</property>
<property name="stock">gtk-floppy</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="invite_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">In_vite to</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1704">
<property name="visible">True</property>
<property name="stock">gtk-go-back</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="above_send_file_separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="send_custom_status_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Send cus_tom status</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="execute_command_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Execute Command...</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1709">
<property name="visible">True</property>
<property name="stock">gtk-execute</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="manage_contact">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Manage contact</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkImageMenuItem" id="rename_menuitem">
<property name="label" translatable="yes">_Rename</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1705">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="edit_groups_menuitem">
<property name="label" translatable="yes">Edit _Groups</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="menu-item-image21">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-edit</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="assign_openpgp_key_menuitem">
<property name="label" translatable="yes">Assign Open_PGP Key</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_assign_openpgp_key_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image1707">
<property name="visible">True</property>
<property name="stock">gtk-missing-image</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="custom_avatar">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Set Custom _Avatar</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="menu-item-image20">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-orientation-portrait</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_special_notification_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">Add Special _Notification</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1708">
<property name="visible">True</property>
<property name="stock">gtk-info</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="subscription_menuitem">
<property name="label" translatable="yes">_Subscription</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="subscription_menuitem_menu">
<child>
<widget class="GtkImageMenuItem" id="resend_authorization_to_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Allow him/her to see my status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1711">
<property name="visible">True</property>
<property name="stock">gtk-go-up</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="rerequest_authorization_from_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">A_sk to see his/her status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1712">
<property name="visible">True</property>
<property name="stock">gtk-go-down</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="forbid_him/her_to_see_my_status1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Forbid him/her to see my status</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1713">
<property name="visible">True</property>
<property name="stock">gtk-stop</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child internal-child="image">
<widget class="GtkImage" id="image1710">
<property name="visible">True</property>
<property name="stock">gtk-dialog-question</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="unblock_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Unblock</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_unblock_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image1715">
<property name="visible">True</property>
<property name="stock">gtk-yes</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="block_menuitem">
<property name="visible">True</property>
<property name="label" translatable="yes">_Block</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_block_menuitem_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image1714">
<property name="visible">True</property>
<property name="stock">gtk-stop</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="remove_from_roster_menuitem">
<property name="label" translatable="yes">_Remove</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1717">
<property name="visible">True</property>
<property name="stock">gtk-remove</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="add_to_roster_menuitem">
<property name="label" translatable="yes">_Add to Roster</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1716">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child internal-child="image">
<widget class="GtkImage" id="menu-item-image19">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-properties</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator6">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="information_menuitem">
<property name="label">gtk-dialog-info</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="history_menuitem">
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<child internal-child="image">
<widget class="GtkImage" id="image1718">
<property name="visible">True</property>
<property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -0,0 +1,191 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="search_window">
<property name="border_width">12</property>
<property name="title" translatable="yes">Search</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<signal name="key_press_event" handler="on_search_window_key_press_event" last_modification_time="Wed, 04 Apr 2007 18:39:27 GMT"/>
<signal name="destroy" handler="on_search_window_destroy" last_modification_time="Wed, 04 Apr 2007 18:39:35 GMT"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkVBox" id="search_vbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="label" translatable="yes">Please wait while retrieving search form...</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkProgressBar" id="progressbar">
<property name="visible">True</property>
<property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
<property name="fraction">0</property>
<property name="pulse_step">0.10000000149</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="spacing">6</property>
<child>
<widget class="GtkButton" id="search_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_search_button_clicked" last_modification_time="Thu, 19 Apr 2007 09:43:28 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-find</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label58">
<property name="visible">True</property>
<property name="label" translatable="yes">_Search</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkButton" id="close_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Mon, 25 Sep 2006 05:08:55 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -2,6 +2,7 @@
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkWindow" id="service_registration_window">
<property name="border_width">6</property>
<property name="title" translatable="yes">Register to</property>
@ -18,6 +19,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>
<child>
<widget class="GtkVBox" id="vbox12">
@ -25,31 +27,6 @@
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">4</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">4</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table">
<property name="visible">True</property>
@ -232,4 +209,5 @@
</widget>
</child>
</widget>
</glade-interface>

View File

@ -0,0 +1,131 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkDialog" id="synchronise_select_account_dialog">
<property name="border_width">12</property>
<property name="title" translatable="yes">Synchronise contacts</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">350</property>
<property name="default_height">300</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<property name="has_separator">True</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox7">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area6">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-6</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Thu, 01 Mar 2007 14:08:01 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
<signal name="clicked" handler="on_ok_button_clicked" last_modification_time="Thu, 01 Mar 2007 14:07:38 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label210">
<property name="visible">True</property>
<property name="label" translatable="yes">Select the account with which you want to synchronise</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow">
<property name="width_request">150</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="accounts_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -0,0 +1,131 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkDialog" id="synchronise_select_contacts_dialog">
<property name="border_width">12</property>
<property name="title" translatable="yes">Synchronise : select contacts</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">400</property>
<property name="default_height">300</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
<property name="has_separator">True</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox7">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area6">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-6</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Thu, 01 Mar 2007 14:08:01 GMT"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok_button">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
<signal name="clicked" handler="on_ok_button_clicked" last_modification_time="Thu, 01 Mar 2007 14:07:38 GMT"/>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label210">
<property name="visible">True</property>
<property name="label" translatable="yes">Select the contacts you want to synchronise</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow">
<property name="width_request">150</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="contacts_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -9,7 +9,7 @@
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
@ -392,7 +392,57 @@
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="label59">
<property name="visible">True</property>
<property name="label" translatable="yes">User avatar:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="no_user_avatar_label">
<property name="visible">True</property>
<property name="label" translatable="yes">None</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkEventBox" id="PHOTO_eventbox">
@ -417,6 +467,65 @@
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label60">
<property name="visible">True</property>
<property name="label" translatable="yes">Configured avatar:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="PHOTO_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="button_press_event" handler="on_PHOTO_button_press_event" last_modification_time="Sat, 21 Apr 2007 23:35:12 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="NOPHOTO_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Click to force avatar</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_NOPHOTO_button_clicked" last_modification_time="Sat, 21 Apr 2007 23:25:59 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
@ -432,25 +541,6 @@
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="log_history_checkbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">_Log conversation history</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">True</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkExpander" id="expander5">
<property name="visible">True</property>

View File

@ -9,28 +9,28 @@ ICONSET_FILES = **/{16x16,32x32,48x48}/{*.gif,*.png} \
transports/**/{16x16,32x32,48x48}/{*.gif,*.png}
install-data-local:
@for d in $(ICONSET_DIRS);do \
if test -d $$d;then \
@for d in $$(cd $(srcdir); echo $(ICONSET_DIRS));do \
if test -d $(srcdir)/$$d;then \
echo " $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/data/iconsets/$$d"; \
$(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/data/iconsets/$$d || exit 1; \
fi; \
done
for f in $(ICONSET_FILES);do \
if test -f $$f; then \
echo " $(INSTALL_DATA) $$f $(DESTDIR)$(pkgdatadir)/data/iconsets/$$f"; \
$(INSTALL_DATA) $$f $(DESTDIR)$(pkgdatadir)/data/iconsets/$$f || exit 1; \
for f in $$(cd $(srcdir); echo $(ICONSET_FILES));do \
if test -f $(srcdir)/$$f; then \
echo " $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(pkgdatadir)/data/iconsets/$$f"; \
$(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(pkgdatadir)/data/iconsets/$$f || exit 1; \
fi; \
done;
dist-hook:
@for d in $(ICONSET_DIRS);do \
if test -d $$d;then \
@for d in $$(cd $(srcdir); echo $(ICONSET_DIRS));do \
if test -d $(srcdir)/$$d;then \
echo " $(mkdir_p) $(distdir)/$$d"; \
$(mkdir_p) $(distdir)/$$d || exit 1; \
fi; \
done
for f in $(ICONSET_FILES);do \
if test -f $$f; then \
for f in $$(cd $(srcdir); echo $(ICONSET_FILES));do \
if test -f $(srcdir)/$$f; then \
echo " cp -pR $(srcdir)/$$f $(distdir)/$$f"; \
cp -pR $(srcdir)/$$f $(distdir)/$$f || exit 1; \
fi; \

Binary file not shown.

Before

Width:  |  Height:  |  Size: 975 B

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 B

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

After

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 964 B

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 B

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

After

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -70,6 +70,9 @@
<item jid="foxalpha.de" name="">
<active port="5222"/>
</item>
<item jid="fritalk.com" name="French Jabber Server">
<active port="5222"/>
</item>
<item jid="here.dk" name="Jabber server in Denmark">
<active port="5222"/>
</item>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,2 +1,2 @@
# available languages
fr pt el pl es ru bg de nb cs nl pt_BR sv it eu sk no zh_CN br eo hr en_GB be sr
fr pt el pl es ru bg de nb cs nl pt_BR sv it eu sk no zh_CN br eo hr en_GB be sr gl_ES

2924
po/de.po

File diff suppressed because it is too large Load Diff

View File

@ -1150,7 +1150,7 @@ msgstr "Rejoindre un salon de discussion"
#: ../data/glade/join_groupchat_window.glade.h:2
msgid "Join this room automatically when I connect"
msgstr "Joindre ce groupe de discution quand je me connecte"
msgstr "Joindre ce groupe de discussion quand je me connecte"
#: ../data/glade/join_groupchat_window.glade.h:3
#: ../data/glade/manage_bookmarks_window.glade.h:4
@ -2445,11 +2445,11 @@ msgstr "%s n'a pas envoyé de clé OpenPGP et vous ne lui en avez pas assigné u
#: ../src/chat_control.py:1263
msgid "Encryption enabled"
msgstr "Chiffrement activée"
msgstr "Chiffrement activé"
#: ../src/chat_control.py:1268
msgid "Encryption disabled"
msgstr "Chiffrement désactivée"
msgstr "Chiffrement désactivé"
#. add_to_roster_menuitem
#: ../src/chat_control.py:1412
@ -4218,7 +4218,7 @@ msgstr "Joindre un groupe de discussions"
#: ../src/gajim-remote.py:244
msgid "room"
msgstr "groupe de discution"
msgstr "groupe de discussion"
#: ../src/gajim-remote.py:245
msgid "nick"
@ -6504,6 +6504,39 @@ msgstr "[Ce message est chiffré]"
msgid "Error while adding service. %s"
msgstr "Erreur en ajoutant le service. %s"
msgid "_Blocked Contacts"
msgstr "Contacts _Bloqués"
msgid "Blocked Contacts for %s"
msgstr "Contacts Bloqués avec le compte %s"
msgid "Blocked Contacts"
msgstr "Contacts Bloqués"
msgid "_Block"
msgstr "_Bloquer"
msgid "_Unblock"
msgstr "_Débloquer"
msgid " [blocked]"
msgstr " [bloqué]"
msgid "_Maximize"
msgstr "_Maximiser"
msgid "_Minimize"
msgstr "_Minimiser"
msgid " [minimized]"
msgstr " [minimisé]"
msgid "Connected"
msgstr "Connecté"
msgid "Disconnected"
msgstr "Déconnecté"
#~ msgid "2003-12-13T18:30:02Z"
#~ msgstr "2003-12-13T18:30:02Z"
#~ msgid "<small>Romeo and Juliet</small>"

6633
po/gl_ES.po Normal file

File diff suppressed because it is too large Load Diff

3358
po/hr.po

File diff suppressed because it is too large Load Diff

1835
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -144,6 +144,11 @@ class ChatControlBase(MessageControl):
id = widget.connect('value-changed',
self.on_conversation_vadjustment_value_changed)
self.handlers[id] = widget
id = widget.connect('changed',
self.on_conversation_vadjustment_changed)
self.handlers[id] = widget
self.scroll_to_end_id = None
self.was_at_the_end = True
# add MessageTextView to UI and connect signals
self.msg_scrolledwindow = self.xml.get_widget('message_scrolledwindow')
self.msg_textview = MessageTextView()
@ -204,14 +209,7 @@ class ChatControlBase(MessageControl):
self.msg_textview.lang = lang
spell.set_language(lang)
except (gobject.GError, RuntimeError), msg:
dialogs.ErrorDialog(unicode(msg), _('If that is not your language '
'for which you want to highlight misspelled words, then please '
'set your $LANG as appropriate. Eg. for French do export '
'LANG=fr_FR or export LANG=fr_FR.UTF-8 in ~/.bash_profile or to '
'make it global in /etc/profile.\n\nHighlighting misspelled '
'words feature will not be used'))
gajim.config.set('use_speller', False)
dialogs.AspellDictError(lang)
self.style_event_id = 0
self.conv_textview.tv.show()
self._paint_banner()
@ -551,7 +549,7 @@ class ChatControlBase(MessageControl):
full_jid = self.get_full_jid()
textview = self.conv_textview
end = False
if textview.at_the_end() or kind == 'outgoing':
if self.was_at_the_end or kind == 'outgoing':
end = True
textview.print_conversation_line(text, jid, kind, name, tim,
other_tags_for_name, other_tags_for_time, other_tags_for_text,
@ -561,25 +559,36 @@ class ChatControlBase(MessageControl):
return
if kind == 'incoming':
gajim.last_message_time[self.account][full_jid] = time.time()
if (not self.parent_win.get_active_jid() or \
full_jid != self.parent_win.get_active_jid() or \
not self.parent_win.is_active() or not end) and \
kind in ('incoming', 'incoming_queue'):
if kind in ('incoming', 'incoming_queue'):
gc_message = False
if self.type_id == message_control.TYPE_GC:
gc_message = True
if not gc_message or \
(gc_message and (other_tags_for_text == ['marked'] or \
gajim.config.get('notify_on_all_muc_messages'))):
# we want to have save this message in events list
# other_tags_for_text == ['marked'] --> highlighted gc message
if ((self.parent_win and (not self.parent_win.get_active_jid() or \
full_jid != self.parent_win.get_active_jid() or \
not self.parent_win.is_active() or not end)) or \
(gc_message and \
gajim.interface.minimized_controls.has_key(self.account) and \
jid in gajim.interface.minimized_controls[self.account])) and \
kind in ('incoming', 'incoming_queue'):
# we want to have save this message in events list
# other_tags_for_text == ['marked'] --> highlighted gc message
type_ = 'printed_' + self.type_id
event = 'message_received'
show_in_roster = notify.get_show_in_roster(event,
self.account, self.contact)
show_in_systray = notify.get_show_in_systray(event,
self.account, self.contact)
if gc_message:
type_ = 'printed_gc_msg'
show_in_roster = notify.get_show_in_roster('message_received',
self.account, self.contact)
show_in_systray = notify.get_show_in_systray('message_received',
self.account, self.contact)
event = 'gc_message_received'
show_in_roster = True
show_in_systray = False
if gajim.config.get('notify_on_all_muc_messages'):
show_in_systray = True
if other_tags_for_text == ['marked']:
type_ = 'printed_marked_gc_msg'
event = gajim.events.create_event(type_, None,
show_in_roster = show_in_roster,
show_in_systray = show_in_systray)
@ -588,6 +597,14 @@ class ChatControlBase(MessageControl):
if show_in_roster:
gajim.interface.roster.draw_contact(self.contact.jid,
self.account)
if not self.parent_win:
return
if (not self.parent_win.get_active_jid() or \
full_jid != self.parent_win.get_active_jid() or \
not self.parent_win.is_active() or not end) and \
kind in ('incoming', 'incoming_queue'):
self.parent_win.redraw_tab(self)
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid, self.account)
if not self.parent_win.is_active():
@ -654,16 +671,38 @@ class ChatControlBase(MessageControl):
isactive = widget.get_active()
self.chat_buttons_set_visible(isactive)
def _on_minimize_menuitem_activate(self, widget):
'''When a grouchat is minimized, unparent the tab, put it in roster etc'''
win = gajim.interface.msg_win_mgr.get_window(self.contact.jid, self.account)
ctrl = win.get_control(self.contact.jid, self.account)
ctrl_page = win.notebook.page_num(ctrl.widget)
control = win.notebook.get_nth_page(ctrl_page)
win.notebook.remove_page(ctrl_page)
control.unparent()
ctrl.parent_win = None
if not gajim.interface.minimized_controls.has_key(self.account):
gajim.interface.minimized_controls[self.account] = {}
gajim.interface.minimized_controls[self.account][self.contact.jid] = ctrl
del win._controls[self.account][self.contact.jid]
win.check_tabs()
gajim.interface.roster.add_groupchat_to_roster(self.account,
self.contact.jid, status = self.subject)
def set_control_active(self, state):
if state:
jid = self.contact.jid
if self.conv_textview.at_the_end():
if self.was_at_the_end:
# we are at the end
type_ = 'printed_' + self.type_id
type_ = ['printed_' + self.type_id]
if self.type_id == message_control.TYPE_GC:
type_ = 'printed_gc_msg'
type_ = ['printed_gc_msg', 'printed_marked_gc_msg']
if not gajim.events.remove_events(self.account, self.get_full_jid(),
types = [type_]):
types = type_):
# There were events to remove
self.redraw_after_event_removed(jid)
self.msg_textview.grab_focus()
@ -672,18 +711,23 @@ class ChatControlBase(MessageControl):
def bring_scroll_to_end(self, textview, diff_y = 0):
''' scrolls to the end of textview if end is not visible '''
if self.scroll_to_end_id:
# a scroll is already planned
return
buffer = textview.get_buffer()
end_iter = buffer.get_end_iter()
end_rect = textview.get_iter_location(end_iter)
visible_rect = textview.get_visible_rect()
# scroll only if expected end is not visible
if end_rect.y >= (visible_rect.y + visible_rect.height + diff_y):
gobject.idle_add(self.scroll_to_end_iter, textview)
self.scroll_to_end_id = gobject.idle_add(self.scroll_to_end_iter,
textview)
def scroll_to_end_iter(self, textview):
buffer = textview.get_buffer()
end_iter = buffer.get_end_iter()
textview.scroll_to_iter(end_iter, 0, False, 1, 1)
self.scroll_to_end_id = None
return False
def size_request(self, msg_textview , requisition):
@ -741,7 +785,15 @@ class ChatControlBase(MessageControl):
return True
def on_conversation_vadjustment_value_changed(self, widget):
def on_conversation_vadjustment_changed(self, adjustment):
# used to stay at the end of the textview when we shrink conversation
# textview.
if self.was_at_the_end:
self.conv_textview.bring_scroll_to_end(-18)
self.was_at_the_end = (adjustment.upper - adjustment.value - adjustment.page_size) < 18
def on_conversation_vadjustment_value_changed(self, adjustment):
self.was_at_the_end = (adjustment.upper - adjustment.value - adjustment.page_size) < 18
if self.resource:
jid = self.contact.get_full_jid()
else:
@ -752,13 +804,12 @@ class ChatControlBase(MessageControl):
if not len(gajim.events.get_events(self.account, jid, ['printed_' + type_,
type_])):
return
if not self.parent_win:
return
if self.conv_textview.at_the_end() and \
self.parent_win.get_active_control() == self and \
self.parent_win.window.is_active():
self.parent_win.get_active_control() == self and \
self.parent_win.window.is_active():
# we are at the end
type_ = self.type_id
if type_ == message_control.TYPE_GC:
type_ = 'gc_msg'
if not gajim.events.remove_events(self.account, self.get_full_jid(),
types = ['printed_' + type_, type_]):
# There were events to remove
@ -774,9 +825,15 @@ class ChatControlBase(MessageControl):
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
groupchat_control = gajim.interface.msg_win_mgr.get_control(
room_jid, self.account)
if not groupchat_control and \
gajim.interface.minimized_controls.has_key(self.account) and \
room_jid in gajim.interface.minimized_controls[self.account]:
groupchat_control = \
gajim.interface.minimized_controls[self.account][room_jid]
groupchat_control.draw_contact(nick)
mw = gajim.interface.msg_win_mgr.get_window(room_jid, self.account)
mw.redraw_tab(groupchat_control)
if mw:
mw.redraw_tab(groupchat_control)
else:
gajim.interface.roster.draw_contact(jid, self.account)
gajim.interface.roster.show_title()
@ -846,6 +903,7 @@ class ChatControl(ChatControlBase):
'''A control for standard 1-1 chat'''
TYPE_ID = message_control.TYPE_CHAT
old_msg_kind = None # last kind of the printed message
CHAT_CMDS = ['clear', 'compact', 'help', 'ping']
def __init__(self, parent_win, contact, acct, resource = None):
ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
@ -1124,6 +1182,49 @@ class ChatControl(ChatControlBase):
self.contact.get_shown_name()
gtk.Tooltips().set_tip(self.xml.get_widget('gpg_eventbox'), tt)
def _process_command(self, message):
if message[0] != '/':
return False
# Handle common commands
if ChatControlBase._process_command(self, message):
return True
message = message[1:]
message_array = message.split(' ', 1)
command = message_array.pop(0).lower()
if message_array == ['']:
message_array = []
if command == 'help':
if len(message_array):
subcommand = message_array.pop(0)
self.get_command_help(subcommand)
else:
self.get_command_help(command)
self.clear(self.msg_textview)
return True
elif command == 'ping' and not len(message_array):
gajim.connections[self.account].sendPing(self.contact)
self.clear(self.msg_textview)
return True
return False
def get_command_help(self, command):
if command == 'help':
self.print_conversation(_('Commands: %s') % ChatControl.CHAT_CMDS,
'info')
elif command == 'clear':
self.print_conversation(_('Usage: /%s, clears the text window.'),
'info')
elif command == 'compact':
self.print_conversation(_('Usage: /%s, hide the chat buttons.'),
'info')
elif command == 'ping':
self.print_conversation(_(''), 'info')
else:
self.print_conversation(_('No help info for /%s') % command, 'info')
def send_message(self, message, keyID = '', chatstate = None):
'''Send a message to contact'''
if message in ('', None, '\n') or self._process_command(message):
@ -1153,13 +1254,9 @@ class ChatControl(ChatControlBase):
# because we want it sent with REAL message
# (not standlone) eg. one that has body
#FIXME:
# Enable 3 next lines after 0.11 release.
# Having this disabled violate xep85 5.1.2 but then we don't break
# notifications between 0.10.1 and 0.11 See #2637
# if contact.our_chatstate:
# # We already ask for xep 85, don't ask it twice
# composing_jep = 'asked_once'
if contact.our_chatstate:
# We already asked for xep 85, don't ask it twice
composing_jep = 'asked_once'
chatstate_to_send = 'active'
contact.our_chatstate = 'ask' # pseudo state
@ -1575,24 +1672,34 @@ class ChatControl(ChatControlBase):
if not gajim.config.get('show_avatar_in_chat'):
return
jid = self.contact.jid
jid_with_resource = jid
if resource:
jid_with_resource += '/' + resource
is_fake = False
if self.TYPE_ID == message_control.TYPE_PM:
is_fake = True
jid_with_resource = self.contact.jid # fake jid
else:
jid_with_resource = self.contact.jid
if resource:
jid_with_resource += '/' + resource
# we assume contact has no avatar
scaled_pixbuf = None
pixbuf = None
is_fake = False
if gajim.contacts.is_pm_from_jid(self.account, jid):
is_fake = True
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(jid_with_resource,
is_fake)
if pixbuf == 'ask':
# we don't have the vcard
gajim.connections[self.account].request_vcard(jid_with_resource,
is_fake)
if self.TYPE_ID == message_control.TYPE_PM:
if self.gc_contact.jid:
# We know the real jid of this contact
real_jid = self.gc_contact.jid
if self.gc_contact.resource:
real_jid += '/' + self.gc_contact.resource
else:
real_jid = jid_with_resource
gajim.connections[self.account].request_vcard(real_jid,
jid_with_resource)
else:
gajim.connections[self.account].request_vcard(jid_with_resource)
return
if pixbuf is not None:
scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'chat')

View File

@ -187,6 +187,7 @@ class Config:
'notification_avatar_width': [opt_int, 48],
'notification_avatar_height': [opt_int, 48],
'muc_highlight_words': [opt_str, '', _('A semicolon-separated list of words that will be highlighted in group chats.')],
'minimize_autojoined_rooms': [opt_bool, False, _('If True, autojoined bookmarked rooms will be minimized on startup.')],
'quit_on_roster_x_button': [opt_bool, False, _('If True, quits Gajim when X button of Window Manager is clicked. This setting is taken into account only if trayicon is used.')],
'check_if_gajim_is_default': [opt_bool, True, _('If True, Gajim will check if it\'s the default jabber client on each startup.')],
'show_unread_tab_icon': [opt_bool, False, _('If True, Gajim will display an icon on each tab containing unread messages. Depending on the theme, this icon may be animated.')],
@ -225,6 +226,8 @@ class Config:
'show_contacts_number': [opt_bool, True, _('If True, Gajim will show number of online and total contacts in account and group rows.')],
'treat_incoming_messages': [ opt_str, '', _('Can be empty, \'chat\' or \'normal\'. If not empty, treat all incoming messages as if they were of this type')],
'scroll_roster_to_last_message': [opt_bool, True, _('If True, Gajim will scroll and select the contact who sent you the last message, if chat window is not already opened.')],
'use_latex': [opt_bool, False, _('If True, Gajim will convert string between $$ and $$ to an image using dvips and convert before insterting it in chat window.')],
'change_status_window_timeout': [opt_int, 15, _('Time of inactivity needed before the change status window closes down.')],
}
__options_per_key = {

View File

@ -17,9 +17,9 @@
## GNU General Public License for more details.
##
import os
import random
import socket
try:
randomsource = random.SystemRandom()
@ -42,6 +42,7 @@ USE_GPG = GnuPG.USE_GPG
from common.rst_xhtml_generator import create_xhtml
from string import Template
import logging
log = logging.getLogger('gajim.c.connection')
@ -76,11 +77,19 @@ class Connection(ConnectionHandlers):
self.last_history_line = {}
self.password = passwords.get_password(name)
self.server_resource = gajim.config.get_per('accounts', name, 'resource')
# All valid resource substitution strings should be added to this hash.
if self.server_resource:
self.server_resource = Template(self.server_resource).safe_substitute({
'hostname': socket.gethostname()
})
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')
else:
self.keepalives = 0
self.privacy_rules_supported = False
self.blocked_list = []
self.blocked_contacts = []
self.blocked_groups = []
self.pep_supported = False
# Do we continue connection when we get roster (send presence,get vcard...)
self.continue_connect_info = None
@ -197,30 +206,15 @@ class Connection(ConnectionHandlers):
# it's a new account
if not data[1]: # wrong answer
self.dispatch('ACC_NOT_OK', (
_('Transport %s answered wrongly to register request: %s')\
_('Server %s answered wrongly to register request: %s')\
% (data[0], data[3])))
return
req = data[1].asDict()
req['username'] = self.new_account_info['name']
req['password'] = self.new_account_info['password']
def _on_register_result(result):
if not common.xmpp.isResultNode(result):
self.dispatch('ACC_NOT_OK', (result.getError()))
return
self.password = self.new_account_info['password']
if USE_GPG:
self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
gajim.config.set('usegpg', True)
else:
gajim.config.set('usegpg', False)
gajim.connections[self.name] = self
self.dispatch('ACC_OK', (self.new_account_info))
self.new_account_info = None
if self.connection:
self.connection.UnregisterDisconnectHandler(self._on_new_account)
self.disconnect(on_purpose=True)
common.xmpp.features_nb.register(self.connection, data[0],
req, _on_register_result)
is_form = data[2]
if is_form:
conf = data[1]
else:
conf = data[1].asDict()
self.dispatch('NEW_ACC_CONNECTED', (conf, is_form))
return
if not data[1]: # wrong answer
self.dispatch('ERROR', (_('Invalid answer'),
@ -293,16 +287,13 @@ class Connection(ConnectionHandlers):
def connect(self, data = None):
''' Start a connection to the Jabber server.
Returns connection, and connection type ('tls', 'ssl', 'tcp', '')
data MUST contain name, hostname, resource, usessl, proxy,
use_custom_host, custom_host (if use_custom_host), custom_port (if
use_custom_host), '''
data MUST contain hostname, usessl, proxy, use_custom_host,
custom_host (if use_custom_host), custom_port (if use_custom_host)'''
if self.connection:
return self.connection, ''
if data:
name = data['name']
hostname = data['hostname']
resource = data['resource']
usessl = data['usessl']
self.try_connecting_for_foo_secs = 45
p = data['proxy']
@ -312,9 +303,7 @@ class Connection(ConnectionHandlers):
custom_h = data['custom_host']
custom_p = data['custom_port']
else:
name = gajim.config.get_per('accounts', self.name, 'name')
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
resource = gajim.config.get_per('accounts', self.name, 'resource')
usessl = gajim.config.get_per('accounts', self.name, 'usessl')
self.try_connecting_for_foo_secs = gajim.config.get_per('accounts',
self.name, 'try_connecting_for_foo_secs')
@ -325,7 +314,7 @@ class Connection(ConnectionHandlers):
custom_h = gajim.config.get_per('accounts', self.name, 'custom_host')
custom_p = gajim.config.get_per('accounts', self.name, 'custom_port')
#create connection if it doesn't already exist
# create connection if it doesn't already exist
self.connected = 1
if p and p in gajim.config.get_per('proxies'):
proxy = {'host': gajim.config.get_per('proxies', p, 'host')}
@ -450,7 +439,6 @@ class Connection(ConnectionHandlers):
name = gajim.config.get_per('accounts', self.name, 'name')
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
resource = gajim.config.get_per('accounts', self.name, 'resource')
self.connection = con
fpr_good = self._check_fingerprint(con, con_type)
@ -471,7 +459,7 @@ class Connection(ConnectionHandlers):
if fpr_good == True:
log.info("Fingerprint found and matched for %s.", hostname)
con.auth(name, self.password, resource, 1, self.__on_auth)
con.auth(name, self.password, self.server_resource, 1, self.__on_auth)
return True
@ -572,6 +560,22 @@ class Connection(ConnectionHandlers):
return
common.xmpp.features_nb.getPrivacyLists(self.connection)
def sendPing(self, pingTo):
if not self.connection:
return
iq = common.xmpp.Iq('get', to = pingTo.get_full_jid())
iq.addChild(name = 'ping', namespace = common.xmpp.NS_PING)
def _on_response(resp):
timePong = time.time()
if not common.xmpp.isResultNode(resp):
self.dispatch('PING_ERROR', (pingTo))
return
timeDiff = round(timePong - timePing,2)
self.dispatch('PING_REPLY', (pingTo, timeDiff))
self.dispatch('PING_SENT', (pingTo))
timePing = time.time()
self.connection.SendAndCallForResponse(iq, _on_response)
def get_active_default_lists(self):
if not self.connection:
return
@ -628,6 +632,8 @@ class Connection(ConnectionHandlers):
self.connection.send(iq)
def send_invisible_presence(self, msg, signed, initial = False):
if not self.connection:
return
# try to set the privacy rule
iq = self.build_privacy_rule('invisible', 'deny')
self.connection.SendAndCallForResponse(iq, self._continue_invisible,
@ -715,6 +721,32 @@ class Connection(ConnectionHandlers):
self.awaiting_answers[id] = (PRIVACY_ARRIVED, )
self.connection.send(iq)
def send_custom_status(self, show, msg, jid):
if not show in STATUS_LIST:
return -1
if not self.connection:
return
sshow = helpers.get_xmpp_show(show)
if not msg:
msg = ''
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if show == 'offline':
p = common.xmpp.Presence(typ = 'unavailable', to = jid)
p = self.add_sha(p, False)
if msg:
p.setStatus(msg)
else:
signed = self.get_signed_msg(msg)
priority = unicode(gajim.get_priority(self.name, sshow))
p = common.xmpp.Presence(typ = None, priority = priority, show = sshow,
to = jid)
p = self.add_sha(p)
if msg:
p.setStatus(msg)
if signed:
p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
self.connection.send(p)
def change_status(self, show, msg, auto = False):
if not show in STATUS_LIST:
return -1
@ -731,9 +763,11 @@ class Connection(ConnectionHandlers):
# recconect before we auth to server
self.old_show = show
self.on_purpose = False
self.server_resource = gajim.config.get_per('accounts', self.name,
'resource')
self.connect_and_init(show, msg, signed)
elif show == 'offline' and self.connected:
elif show == 'offline':
self.connected = 0
if self.connection:
self.on_purpose = True
@ -967,6 +1001,41 @@ class Connection(ConnectionHandlers):
self.connection.getRoster().setItem(jid = jid, name = name,
groups = groups)
def send_new_account_infos(self, form, is_form):
def _on_register_result(result):
if not common.xmpp.isResultNode(result):
self.dispatch('ACC_NOT_OK', (result.getError()))
return
if USE_GPG:
self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
gajim.config.set('usegpg', True)
else:
gajim.config.set('usegpg', False)
gajim.connections[self.name] = self
self.dispatch('ACC_OK', (self.new_account_info))
self.new_account_info = None
if self.connection:
self.connection.UnregisterDisconnectHandler(self._on_new_account)
self.disconnect(on_purpose=True)
if is_form:
# Get username and password and put them in new_account_info
for field in self._data_form.iter_fields():
if field.var == 'username':
self.new_account_info['name'] = field.value
if field.var == 'password':
self.new_account_info['password'] = field.value
iq=Iq('set', NS_REGISTER, to = self._hostname)
iq.setTag('query').addChild(node = form)
self.connection.SendAndCallForResponse(iq, _on_register_result)
else:
# Get username and password and put them in new_account_info
if form.has_key('username'):
self.new_account_info['name'] = form['username']
if form.has_key('password'):
self.new_account_info['password'] = form['password']
common.xmpp.features_nb.register(self.connection, self._hostname,
form, _on_register_result)
def new_account(self, name, config, sync = False):
# If a connection already exist we cannot create a new account
if self.connection:
@ -980,13 +1049,11 @@ class Connection(ConnectionHandlers):
def _on_new_account(self, con = None, con_type = None):
if not con_type:
self.dispatch('ACC_NOT_OK',
self.dispatch('NEW_ACC_NOT_CONNECTED',
(_('Could not connect to "%s"') % self._hostname))
return
self.on_connect_failure = None
self.connection = con
#if con:
# con.RegisterDisconnectHandler(self._on_new_account)
common.xmpp.features_nb.getRegInfo(con, self._hostname)
def account_changed(self, new_name):
@ -1326,4 +1393,38 @@ class Connection(ConnectionHandlers):
else:
self.time_to_reconnect = None
def request_search_fields(self, jid):
iq = common.xmpp.Iq(typ = 'get', to = jid, queryNS = \
common.xmpp.NS_SEARCH)
self.connection.send(iq)
def send_search_form(self, jid, form, is_form):
iq = common.xmpp.Iq(typ = 'set', to = jid, queryNS = \
common.xmpp.NS_SEARCH)
item = iq.getTag('query')
if is_form:
item.addChild(node = form)
else:
for i in form.keys():
item.setTagData(i,form[i])
def _on_response(resp):
jid = jid = helpers.get_jid_from_iq(resp)
tag = resp.getTag('query', namespace = common.xmpp.NS_SEARCH)
if not tag:
self.dispatch('SEARCH_RESULT', (jid, None, False))
return
df = tag.getTag('x', namespace = common.xmpp.NS_DATA)
if df:
self.dispatch('SEARCH_RESULT', (jid, df, True))
return
df = []
for item in tag.getTags('item'):
f = {}
for i in item.getPayload():
f[i.getName()] = i.getData()
df.append(f)
self.dispatch('SEARCH_RESULT', (jid, df, False))
self.connection.SendAndCallForResponse(iq, _on_response)
# END Connection

View File

@ -818,6 +818,7 @@ class ConnectionVcard:
self.vcard_sha = None
self.vcard_shas = {} # sha of contacts
self.room_jids = [] # list of gc jids so that vcard are saved in a folder
self.groupchat_jids = {} # {ID : groupchat_jid}
def add_sha(self, p, send_caps = True):
c = p.setTag('x', namespace = common.xmpp.NS_VCARD_UPDATE)
@ -913,9 +914,10 @@ class ConnectionVcard:
vcard['resource'] = gajim.get_resource_from_jid(fjid)
return vcard
def request_vcard(self, jid = None, is_fake_jid = False):
'''request the VCARD. If is_fake_jid is True, it means we request a vcard
to a fake jid, like in private messages in groupchat'''
def request_vcard(self, jid = None, groupchat_jid = None):
'''request the VCARD. If groupchat_jid is not nul, it means we request a vcard
to a fake jid, like in private messages in groupchat. jid can be the
real jid of the contact, but we want to consider it comes from a fake jid'''
if not self.connection:
return
iq = common.xmpp.Iq(typ = 'get')
@ -928,13 +930,13 @@ class ConnectionVcard:
j = jid
if not j:
j = gajim.get_jid_from_account(self.name)
self.awaiting_answers[id] = (VCARD_ARRIVED, j)
if is_fake_jid:
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
self.awaiting_answers[id] = (VCARD_ARRIVED, j, groupchat_jid)
if groupchat_jid:
room_jid, nick = gajim.get_room_and_nick_from_fjid(groupchat_jid)
if not room_jid in self.room_jids:
self.room_jids.append(room_jid)
self.groupchat_jids[id] = groupchat_jid
self.connection.send(iq)
#('VCARD', {entry1: data, entry2: {entry21: data, ...}, ...})
def send_vcard(self, vcard):
if not self.connection:
@ -1017,17 +1019,22 @@ class ConnectionVcard:
# If vcard is empty, we send to the interface an empty vcard so that
# it knows it arrived
jid = self.awaiting_answers[id][1]
groupchat_jid = self.awaiting_answers[id][2]
frm = jid
if groupchat_jid:
# We do as if it comes from the fake_jid
frm = groupchat_jid
our_jid = gajim.get_jid_from_account(self.name)
if iq_obj.getType() == 'error' and jid == our_jid:
# our server doesn't support vcard
self.vcard_supported = False
if not iq_obj.getTag('vCard') or iq_obj.getType() == 'error':
if jid and jid != our_jid:
if frm and frm != our_jid:
# Write an empty file
self.save_vcard_to_hd(jid, '')
self.dispatch('VCARD', {'jid': jid})
elif jid == our_jid:
self.dispatch('MYVCARD', {'jid': jid})
self.save_vcard_to_hd(frm, '')
self.dispatch('VCARD', {'jid': frm})
elif frm == our_jid:
self.dispatch('MYVCARD', {'jid': frm})
elif self.awaiting_answers[id][0] == AGENT_REMOVED:
jid = self.awaiting_answers[id][1]
self.dispatch('AGENT_REMOVED', jid)
@ -1058,6 +1065,7 @@ class ConnectionVcard:
elif self.awaiting_answers[id][0] == PRIVACY_ARRIVED:
if iq_obj.getType() != 'error':
self.privacy_rules_supported = True
self.get_privacy_list('block')
# Ask metacontacts before roster
self.get_metacontacts()
@ -1070,10 +1078,15 @@ class ConnectionVcard:
return
if not vc.getTag('vCard').getNamespace() == common.xmpp.NS_VCARD:
return
id = vc.getID()
frm_iq = vc.getFrom()
our_jid = gajim.get_jid_from_account(self.name)
resource = ''
if frm_iq:
if id in self.groupchat_jids:
who = self.groupchat_jids[id]
frm, resource = gajim.get_room_and_nick_from_fjid(who)
del self.groupchat_jids[id]
elif frm_iq:
who = helpers.get_full_jid_from_iq(vc)
frm, resource = gajim.get_room_and_nick_from_fjid(who)
else:
@ -1145,6 +1158,7 @@ class ConnectionVcard:
p = self.add_sha(p)
self.connection.send(p)
else:
#('VCARD', {entry1: data, entry2: {entry21: data, ...}, ...})
self.dispatch('VCARD', vcard)
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub):
@ -1186,7 +1200,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
id = iq_obj.getTagAttr('confirm', 'id')
method = iq_obj.getTagAttr('confirm', 'method')
url = iq_obj.getTagAttr('confirm', 'url')
self.dispatch('HTTP_AUTH', (method, url, id, iq_obj));
msg = iq_obj.getTagData('body') # In case it's a message with a body
self.dispatch('HTTP_AUTH', (method, url, id, iq_obj, msg));
raise common.xmpp.NodeProcessed
def _ErrorCB(self, con, iq_obj):
@ -1390,6 +1405,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if msg.getTag('event') is not None:
self._pubsubEventCB(con, msg)
return
# check if the message is a xep70-confirmation-request
if msg.getTag('confirm') and msg.getTag('confirm').namespace == \
common.xmpp.NS_HTTP_AUTH:
self._HttpAuthCB(con, msg)
return
msgtxt = msg.getBody()
msghtml = msg.getXHTML()
mtype = msg.getType()
@ -1558,6 +1578,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
ptype = prs.getType()
if ptype == 'available':
ptype = None
rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed', 'unsubscribe', 'unsubscribed')
if ptype and not ptype in rfc_types:
ptype = None
gajim.log.debug('PresenceCB: %s' % ptype)
try:
who = helpers.get_full_jid_from_iq(prs)
@ -1580,6 +1603,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
user_nick = prs.getTagData('nick')
if not user_nick:
user_nick = ''
contact_nickname = None
transport_auto_auth = False
xtags = prs.getTags('x')
for x in xtags:
@ -1592,6 +1616,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
sigTag = x
elif namespace == common.xmpp.NS_VCARD_UPDATE:
avatar_sha = x.getTagData('photo')
contact_nickname = x.getTagData('nickname')
elif namespace == common.xmpp.NS_DELAY:
# JEP-0091
tim = prs.getTimestamp()
@ -1631,7 +1656,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
errcode = prs.getErrorCode()
if errcode == '502': # Internal Timeout:
self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource,
prio, keyID, timestamp))
prio, keyID, timestamp, None))
elif errcode == '401': # password required to join
self.dispatch('ERROR', (_('Unable to join group chat'),
_('A password is required to join this group chat.')))
@ -1664,7 +1689,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if not ptype or ptype == 'unavailable':
if gajim.config.get('log_contact_status_changes') and self.name\
not in no_log_for and jid_stripped not in no_log_for:
gc_c = gajim.contacts.get_gc_contact(self.name, jid_stripped, resource)
gc_c = gajim.contacts.get_gc_contact(self.name, jid_stripped,
resource)
st = status or ''
if gc_c:
jid = gc_c.jid
@ -1679,24 +1705,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# contact has no avatar
puny_nick = helpers.sanitize_filename(resource)
gajim.interface.remove_avatar_files(jid_stripped, puny_nick)
if self.vcard_shas.has_key(who): # Verify sha cached in mem
if avatar_sha != self.vcard_shas[who]:
# avatar has been updated
self.request_vcard(who, True)
else: # Verify sha cached in hdd
cached_vcard = self.get_cached_vcard(who, True)
if cached_vcard and cached_vcard.has_key('PHOTO') and \
cached_vcard['PHOTO'].has_key('SHA'):
cached_sha = cached_vcard['PHOTO']['SHA']
else:
cached_sha = ''
if cached_sha != avatar_sha:
# avatar has been updated
# sha in mem will be updated later
self.request_vcard(who, True)
else:
# save sha in mem NOW
self.vcard_shas[who] = avatar_sha
# if it's a gc presence, don't ask vcard here. We may ask it to
# real jid in gui part.
if ns_muc_user_x:
# Room has been destroyed. see
# http://www.xmpp.org/extensions/xep-0045.html#destroyroom
@ -1714,7 +1724,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
statusCode = prs.getStatusCode()
self.dispatch('GC_NOTIFY', (jid_stripped, show, status, resource,
prs.getRole(), prs.getAffiliation(), prs.getJid(),
reason, prs.getActor(), statusCode, prs.getNewNick()))
reason, prs.getActor(), statusCode, prs.getNewNick(),
avatar_sha))
return
if ptype == 'subscribe':
@ -1727,7 +1738,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.connection.send(p)
if who.find("@") <= 0 or transport_auto_auth:
self.dispatch('NOTIFY', (jid_stripped, 'offline', 'offline',
resource, prio, keyID, timestamp))
resource, prio, keyID, timestamp, None))
if transport_auto_auth:
self.automatically_added.append(jid_stripped)
self.request_subscription(jid_stripped, name = user_nick)
@ -1778,7 +1789,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
errcode = prs.getErrorCode()
if errcode == '502': # Internal Timeout:
self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource,
prio, keyID, timestamp))
prio, keyID, timestamp, None))
else: # print in the window the error
self.dispatch('ERROR_ANSWER', ('', jid_stripped,
errmsg, errcode))
@ -1799,7 +1810,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
not in no_log_for and jid_stripped not in no_log_for:
gajim.logger.write('status', jid_stripped, status, show)
self.dispatch('NOTIFY', (jid_stripped, show, status, resource, prio,
keyID, timestamp))
keyID, timestamp, contact_nickname))
# END presenceCB
def _StanzaArrivedCB(self, con, obj):
@ -1953,6 +1964,26 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('SIGNED_IN', ())
self.continue_connect_info = None
def _search_fields_received(self, con, iq_obj):
jid = jid = helpers.get_jid_from_iq(iq_obj)
tag = iq_obj.getTag('query', namespace = common.xmpp.NS_SEARCH)
if not tag:
self.dispatch('SEARCH_FORM', (jid, None, False))
return
df = tag.getTag('x', namespace = common.xmpp.NS_DATA)
if df:
self.dispatch('SEARCH_FORM', (jid, df, True))
return
df = {}
for i in iq_obj.getQueryPayload():
df[i.getName()] = i.getData()
self.dispatch('SEARCH_FORM', (jid, df, False))
def _StreamCB(self, con, obj):
if obj.getTag('conflict'):
# disconnected because of a resource conflict
self.dispatch('RESOURCE_CONFLICT', ())
def _register_handlers(self, con, con_type):
# try to find another way to register handlers in each class
# that defines handlers
@ -2018,6 +2049,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
common.xmpp.NS_DISCO_ITEMS)
con.RegisterHandler('iq', self._IqPingCB, 'get',
common.xmpp.NS_PING)
con.RegisterHandler('iq', self._search_fields_received, 'result',
common.xmpp.NS_SEARCH)
con.RegisterHandler('iq', self._PubSubCB, 'result')
con.RegisterHandler('iq', self._ErrorCB, 'error')
con.RegisterHandler('iq', self._IqCB)
@ -2025,3 +2058,4 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
con.RegisterHandler('iq', self._ResultCB, 'result')
con.RegisterHandler('presence', self._StanzaArrivedCB)
con.RegisterHandler('message', self._StanzaArrivedCB)
con.RegisterHandler('unknown', self._StreamCB, 'urn:ietf:params:xml:ns:xmpp-streams', xmlns='http://etherx.jabber.org/streams')

View File

@ -23,6 +23,7 @@ class Contact:
chatstate=None, last_status_time=None, msg_id = None, composing_jep = None):
self.jid = jid
self.name = name
self.contact_name = '' # nick choosen by contact
self.groups = groups
self.show = show
self.status = status
@ -61,6 +62,8 @@ class Contact:
def get_shown_name(self):
if self.name:
return self.name
if self.contact_name:
return self.contact_name
return self.jid.split('@')[0]
def is_hidden_from_roster(self):
@ -86,6 +89,11 @@ class Contact:
is_observer = True
return is_observer
def is_groupchat(self):
if _('Groupchats') in self.groups:
return True
return False
def is_transport(self):
# if not '@' or '@' starts the jid then contact is transport
if self.jid.find('@') <= 0:

View File

@ -34,7 +34,7 @@ def Field(typ, **attrs):
''' Helper function to create a field of given type. '''
f = {
'boolean': BooleanField,
'fixed': StringField,
'fixed': TextMultiField, # not editable, still can have multiple lines of text
'hidden': StringField,
'text-private': StringField,
'text-single': StringField,
@ -54,7 +54,7 @@ def ExtendField(node):
typ=node.getAttr('type')
f = {
'boolean': BooleanField,
'fixed': StringField,
'fixed': TextMultiField,
'hidden': StringField,
'text-private': StringField,
'text-single': StringField,
@ -63,12 +63,14 @@ def ExtendField(node):
'list-multi': ListMultiField,
'list-single': ListSingleField,
'text-multi': TextMultiField,
}[typ](extend=node)
return f
}
if typ not in f:
typ = 'text-single'
return f[typ](extend=node)
def ExtendForm(node):
''' Helper function to extend a node to form of appropriate type. '''
if node.getTag('recorded') is not None:
if node.getTag('reported') is not None:
return MultipleDataForm(extend=node)
else:
return SimpleDataForm(extend=node)
@ -195,7 +197,6 @@ class StringField(DataField):
pass
return locals()
class ListField(DataField):
''' Covers fields of types: jid-multi, jid-single, list-multi, list-single. '''
@nested_property
@ -385,9 +386,25 @@ class SimpleDataForm(DataForm, DataRecord):
DataRecord.__init__(self, fields=fields, extend=self, associated=self)
class MultipleDataForm(DataForm):
def __init__(self):
def __init__(self, type=None, title=None, instructions=None, items=None, extend=None):
DataForm.__init__(self, type=type, title=title, instructions=instructions, extend=extend)
# all records, recorded into DataRecords
pass
if extend is None:
# we have to build this object from scratch
xmpp.Node.__init__(self)
if items is not None: self.items = items
else:
# we already have xmpp.Node inside - try to convert all
# fields into DataField objects
if items is None:
self.items = list(self.iterTags('item'))
else:
for item in self.getTags('item'):
self.delChild(item)
self.items = items
reported_tag = self.getTag('reported')
self.reported = DataRecord(extend = reported_tag)
@nested_property
def items():
@ -401,7 +418,7 @@ class MultipleDataForm(DataForm):
DataRecord(extend=record)
self.addChild(node=record)
def fdel(self):
for record in self.getTags('record'):
for record in self.getTags('item'):
self.delChild(record)
return locals()
@ -409,18 +426,18 @@ class MultipleDataForm(DataForm):
for record in self.getTags('item'):
yield record
@nested_property
def recorded():
''' DataRecord that contains descriptions of fields in records.'''
def fget(self):
return self.getTag('recorded')
def fset(self, record):
try:
self.delChild('recorded')
except:
pass
record.setName('recorded')
self.addChild(node=record)
return locals()
# @nested_property
# def reported():
# ''' DataRecord that contains descriptions of fields in records.'''
# def fget(self):
# return self.getTag('reported')
# def fset(self, record):
# try:
# self.delChild('reported')
# except:
# pass
#
# record.setName('reported')
# self.addChild(node=record)
# return locals()

View File

@ -28,7 +28,7 @@ class Event:
show_in_systray = True):
''' type_ in chat, normal, file-request, file-error, file-completed,
file-request-error, file-send-error, file-stopped, gc_msg, pm,
printed_chat, printed_gc_msg, printed_pm
printed_chat, printed_gc_msg, printed_marked_gc_msg, printed_pm
parameters is (per type_):
chat, normal: [message, subject, kind, time, encrypted, resource,
msg_id]
@ -185,7 +185,8 @@ class Events:
first_event = event
return first_event
def _get_nb_events(self, account = None, jid = None, attribute = None, types = []):
def _get_nb_events(self, account = None, jid = None, attribute = None,
types = []):
'''return the number of pending events'''
nb = 0
if account:

View File

@ -124,6 +124,8 @@ status_before_autoaway = {}
# be online
transport_avatar = {} # {transport_jid: [jid_list]}
# Is Gnome configured to activate on single click ?
single_click = False
SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible', 'error']

View File

@ -40,7 +40,7 @@ try:
except:
pass
special_groups = (_('Transports'), _('Not in Roster'), _('Observers'))
special_groups = (_('Transports'), _('Not in Roster'), _('Observers'), _('Groupchats'))
class InvalidFormat(Exception):
pass
@ -548,6 +548,10 @@ def get_icon_name_to_show(contact, account = None):
if account and gajim.events.get_nb_roster_events(account,
contact.get_full_jid()):
return 'message'
if account and gajim.interface.minimized_controls.has_key(account) and \
contact.jid in gajim.interface.minimized_controls[account] and gajim.interface.\
minimized_controls[account][contact.jid].get_nb_unread_pm() > 0:
return 'message'
if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
return contact.show
if contact.sub in ('both', 'to'):
@ -869,7 +873,7 @@ def get_notification_icon_tooltip_text():
'chat'])
unread_single_chat = gajim.events.get_nb_events(types = ['normal'])
unread_gc = gajim.events.get_nb_events(types = ['printed_gc_msg',
'gc_msg'])
'printed_marked_gc_msg', 'gc_msg'])
unread_pm = gajim.events.get_nb_events(types = ['printed_pm', 'pm'])
accounts = get_accounts_info()
@ -942,3 +946,20 @@ def get_accounts_info():
accounts.append({'name': account, 'status_line': single_line,
'show': status, 'message': message})
return accounts
def get_avatar_path(prefix):
'''Returns the filename of the avatar, distinguishes between user- and
contact-provided one. Returns None if no avatar was found at all.
prefix is the path to the requested avatar just before the ".png" or
".jpeg".'''
# First, scan for a local, user-set avatar
for type_ in ('jpeg', 'png'):
file_ = prefix + '_local.' + type_
if os.path.exists(file_):
return file_
# If none available, scan for a contact-provided avatar
for type_ in ('jpeg', 'png'):
file_ = prefix + '.' + type_
if os.path.exists(file_):
return file_
return None

View File

@ -484,7 +484,7 @@ class Logger:
else: # user just typed something, we search in message column
where_sql = self._build_contact_where(account, jid)
like_sql = '%' + query + '%'
like_sql = '%' + query.replace("'", "''") + '%'
self.cur.execute('''
SELECT contact_name, time, kind, show, message, subject FROM logs
WHERE (%s) AND message LIKE '%s'

View File

@ -21,39 +21,70 @@ try:
from docutils import nodes,utils
from docutils.parsers.rst.roles import set_classes
except:
print "Requires docutils 0.4 for set_classes to be available"
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).
def pos_int_validator(text):
"""Validates that text can be evaluated as a positive integer."""
result = int(text)
if result < 0:
raise ValueError("Error: value '%(text)s' "
"must be a positive integer")
return result
Use as :JEP:`71` (or jep, or jep-reference).
Modeled after the sample in docutils documentation.
def generate_uri_role( role_name, aliases,
anchor_text, base_url,
interpret_url, validator):
'''Creates and register a uri based "interpreted role".
Those are similar to the RFC, and PEP ones, and take
role_name:
name that will be registered
aliases:
list of alternate names
anchor_text:
text that will be used, together with the role
base_url:
base url for the link
interpret_url:
this, modulo the validated text, will be added to it
validator:
should return the validated text, or raise ValueError
'''
def uri_reference_role(role, rawtext, text, lineno, inliner,
options={}, content=[]):
try:
valid_text = validator(text)
except ValueError, e:
msg = inliner.reporter.error( e.message % dict(text=text), line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
ref = base_url + interpret_url % valid_text
set_classes(options)
node = nodes.reference(rawtext, anchor_text + utils.unescape(text), refuri=ref,
**options)
return [node], []
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], []
uri_reference_role.__doc__ = """Role to make handy references to URIs.
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'
Use as :%(role_name)s:`71` (or any of %(aliases)s).
It will use %(base_url)s+%(interpret_url)s
validator should throw a ValueError, containing optionally
a %%(text)s format, if the interpreted text is not valid.
""" % locals()
roles.register_canonical_role(role_name, uri_reference_role)
from docutils.parsers.rst.languages import en
en.roles[role_name] = role_name
for alias in aliases:
en.roles[alias] = role_name
generate_uri_role('xep-reference', ('jep', 'xep'),
'XEP #', 'http://www.xmpp.org/extensions/', 'xep-%04d.html',
pos_int_validator)
generate_uri_role('gajim-ticket-reference', ('ticket','gtrack'),
'Gajim Ticket #', 'http://trac.gajim.org/ticket/', '%d',
pos_int_validator)
class HTMLGenerator:
'''Really simple HTMLGenerator starting from publish_parts.
@ -108,7 +139,7 @@ else:
if __name__ == '__main__':
print Generator.create_xhtml('''
print "test 1\n", Generator.create_xhtml("""
test::
>>> print 1
@ -118,9 +149,10 @@ test::
this `` should trigger`` should trigger the &nbsp; problem.
''')
print Generator.create_xhtml('''
""")
print "test 2\n", Generator.create_xhtml("""
*test1
test2_
''')
""")
print "test 3\n", Generator.create_xhtml(""":ticket:`316` implements :xep:`71`""")

View File

@ -143,7 +143,6 @@ def _ReceivedRegInfo(con, resp, agent):
def register(disp, host, info, cb):
""" Perform registration on remote server with provided info.
disp must be connected dispatcher instance.
Returns true or false depending on registration result.
If registration fails you can get additional info from the dispatcher's owner
attributes lastErrNode, lastErr and lastErrCode.
"""

View File

@ -158,10 +158,6 @@ class P2PClient(IdleObject):
self.Connection.PlugIn(self)
dispatcher_nb.Dispatcher().PlugIn(self)
self._register_handlers()
if self.sock_type == TYPE_CLIENT:
while self.stanzaqueue:
stanza, is_message = self.stanzaqueue.pop(0)
self.send(stanza, is_message)
def StreamInit(self):
''' Send an initial stream header. '''
@ -178,9 +174,11 @@ class P2PClient(IdleObject):
def send_stream_header(self):
self.Dispatcher._metastream = Node('stream:stream')
self.Dispatcher._metastream.setNamespace(self.Namespace)
# XXX TLS support
#~ self._metastream.setAttr('version', '1.0')
self.Dispatcher._metastream.setAttr('version', '1.0')
self.Dispatcher._metastream.setAttr('xmlns:stream', NS_STREAMS)
self.Dispatcher._metastream.setAttr('from', self.conn_holder.zeroconf.name)
if self.to:
self.Dispatcher._metastream.setAttr('to', self.to)
self.Dispatcher.send("<?xml version='1.0'?>%s>" % str(self.Dispatcher._metastream)[:-2])
def _check_stream_start(self, ns, tag, attrs):
@ -191,7 +189,17 @@ class P2PClient(IdleObject):
self.Connection.disconnect()
return
if self.sock_type == TYPE_SERVER:
if attrs.has_key('from'):
self.to = attrs['from']
self.send_stream_header()
if attrs.has_key('version') and attrs['version'] == '1.0':
# other part supports stream features
features = Node('stream:features')
self.Dispatcher.send(features)
while self.stanzaqueue:
stanza, is_message = self.stanzaqueue.pop(0)
self.send(stanza, is_message)
elif self.sock_type == TYPE_CLIENT:
while self.stanzaqueue:
stanza, is_message = self.stanzaqueue.pop(0)
self.send(stanza, is_message)

View File

@ -52,18 +52,21 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
# system username
self.username = None
self.name = name
self.server_resource = '' # zeroconf has no resource, fake an empty one
self.connected = 0 # offline
self.connection = None
self.gpg = None
self.is_zeroconf = True
self.privacy_rules_supported = False
self.blocked_contacts = []
self.blocked_groups = []
self.status = ''
self.old_show = ''
self.priority = 0
self.call_resolve_timeout = False
#self.time_to_reconnect = None
self.time_to_reconnect = None
#self.new_account_info = None
self.bookmarks = []
@ -127,10 +130,12 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
gajim.handlers[event](self.name, data)
def _reconnect(self):
# Do not try to reco while we are already trying
self.time_to_reconnect = None
gajim.log.debug('reconnect')
signed = self.get_signed_msg(self.status)
self.reconnect()
# signed = self.get_signed_msg(self.status)
self.connect(self.old_show, self.status)
def quit(self, kill_core):
if kill_core and self.connected > 1:
@ -174,7 +179,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
self.dispatch('ROSTER_INFO', (key, self.roster.getName(key),
'both', 'no', self.roster.getGroups(key)))
self.dispatch('NOTIFY', (key, self.roster.getStatus(key),
self.roster.getMessage(key), 'local', 0, None, 0))
self.roster.getMessage(key), 'local', 0, None, 0, None))
#XXX open chat windows don't get refreshed (full name), add that
return self.call_resolve_timeout
@ -182,13 +187,13 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
def _on_new_service(self,jid):
self.roster.setItem(jid)
self.dispatch('ROSTER_INFO', (jid, self.roster.getName(jid), 'both', 'no', self.roster.getGroups(jid)))
self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), self.roster.getMessage(jid), 'local', 0, None, 0))
self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), self.roster.getMessage(jid), 'local', 0, None, 0, None))
def _on_remove_service(self, jid):
self.roster.delItem(jid)
# 'NOTIFY' (account, (jid, status, status message, resource, priority,
# keyID, timestamp))
self.dispatch('NOTIFY', (jid, 'offline', '', 'local', 0, None, 0))
# keyID, timestamp, contact_nickname))
self.dispatch('NOTIFY', (jid, 'offline', '', 'local', 0, None, 0, None))
def _on_disconnected(self):
self.disconnect()
@ -199,6 +204,19 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
self.status = 'offline'
self.disconnect()
def _disconnectedReconnCB(self):
'''Called when we are disconnected. Comes from network manager for example
we don't try to reconnect, network manager will tell us when we can'''
if gajim.account_is_connected(self.name):
# we cannot change our status to offline or connecting
# after we auth to server
self.old_show = STATUS_LIST[self.connected]
self.connected = 0
self.dispatch('STATUS', 'offline')
# random number to show we wait network manager to send us a reconenct
self.time_to_reconnect = 5
self.on_purpose = False
def _on_name_conflictCB(self, alt_name):
self.disconnect()
self.dispatch('STATUS', 'offline')
@ -241,7 +259,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
#display contacts already detected and resolved
for jid in self.roster.keys():
self.dispatch('ROSTER_INFO', (jid, self.roster.getName(jid), 'both', 'no', self.roster.getGroups(jid)))
self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), self.roster.getMessage(jid), 'local', 0, None, 0))
self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), self.roster.getMessage(jid), 'local', 0, None, 0, None))
self.connected = STATUS_LIST.index(show)
@ -300,6 +318,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
# 'disconnect'
elif show == 'offline' and self.connected:
self.disconnect()
self.time_to_reconnect = None
# update status
elif show != 'offline' and self.connected:
@ -332,12 +351,12 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
chatstate = None, msg_id = None, composing_jep = None, resource = None,
user_nick = None):
fjid = jid
if not self.connection:
return
if not msg and chatstate is None:
return
if self.status in ('invisible', 'offline'):
self.dispatch('MSGERROR', [unicode(jid), '-1', _('You are not connected or not visible to others. Your message could not be sent.'), None, None])
return
@ -345,19 +364,23 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
msgtxt = msg
msgenc = ''
if keyID and USE_GPG:
#encrypt
msgenc = self.gpg.encrypt(msg, [keyID])
if msgenc:
# encrypt
msgenc, error = self.gpg.encrypt(msg, [keyID])
if msgenc and not error:
msgtxt = '[This message is encrypted]'
lang = os.getenv('LANG')
if lang is not None or lang != 'en': # we're not english
msgtxt = _('[This message is encrypted]') +\
' ([This message is encrypted])' # one in locale and one en
else:
# Encryption failed, do not send message
tim = time.localtime()
self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim))
return 3
if type == 'chat':
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt, typ = type)
else:
if subject:
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt,
@ -368,7 +391,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
if msgenc:
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
# chatstates - if peer supports jep85 or jep22, send chatstates
# please note that the only valid tag inside a message containing a <body>
# tag is the active event
@ -386,7 +409,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
# when msgtxt, requests JEP-0022 composing notification
if chatstate is 'composing' or msgtxt:
chatstate_node.addChild(name = 'composing')
if not self.connection.send(msg_iq, msg != None):
return

View File

@ -67,6 +67,8 @@ class Roster:
status = txt_dict['status']
else:
status = ''
if not status:
status = 'avail'
nm = ''
if txt_dict.has_key('1st'):
nm = txt_dict['1st']

View File

@ -1214,7 +1214,7 @@ class AccountModificationWindow:
self.xml.get_widget('save_password_checkbutton').set_active(
gajim.config.get_per('accounts', self.account, 'savepass'))
if gajim.config.get_per('accounts', self.account, 'savepass'):
passstr = passwords.get_password(self.account)
passstr = passwords.get_password(self.account) or ''
password_entry = self.xml.get_widget('password_entry')
password_entry.set_sensitive(True)
password_entry.set_text(passstr)
@ -1408,7 +1408,7 @@ class AccountModificationWindow:
config['keyname'] = self.xml.get_widget('gpg_name_label').get_text().\
decode('utf-8')
if config['keyname'] == '': #no key selected
if config['keyname'] == '': # no key selected
config['keyid'] = ''
config['savegpgpass'] = False
config['gpgpassword'] = ''
@ -1419,9 +1419,9 @@ class AccountModificationWindow:
'gpg_save_password_checkbutton').get_active()
config['gpgpassword'] = self.xml.get_widget('gpg_password_entry'
).get_text().decode('utf-8')
#if we modify the name of the account
# if we modify the name of the account
if name != self.account:
#update variables
# update variables
gajim.interface.instances[name] = gajim.interface.instances[
self.account]
gajim.nicks[name] = gajim.nicks[self.account]
@ -1446,7 +1446,7 @@ class AccountModificationWindow:
# change account variable for chat / gc controls
gajim.interface.msg_win_mgr.change_account_name(self.account, name)
# upgrade account variable in opened windows
for kind in ('infos', 'disco', 'gc_config'):
for kind in ('infos', 'disco', 'gc_config', 'search'):
for j in gajim.interface.instances[name][kind]:
gajim.interface.instances[name][kind][j].account = name
@ -1588,6 +1588,13 @@ class AccountModificationWindow:
gajim.interface.instances['manage_proxies'] = \
ManageProxiesWindow()
def on_synchronise_contacts_button_clicked(self, widget):
try:
dialog = dialogs.SynchroniseSelectAccountDialog(self.account)
except GajimGeneralException:
# If we showed ErrorDialog, there will not be dialog instance
return
def on_gpg_choose_button_clicked(self, widget, data = None):
if gajim.connections.has_key(self.account):
secret_keys = gajim.connections[self.account].ask_gpg_secrete_keys()
@ -2049,7 +2056,7 @@ class AccountsWindow:
connection_zeroconf.ConnectionZeroconf(gajim.ZEROCONF_ACC_NAME)
# update variables
gajim.interface.instances[gajim.ZEROCONF_ACC_NAME] = {'infos': {},
'disco': {}, 'gc_config': {}}
'disco': {}, 'gc_config': {}, 'search': {}}
gajim.connections[gajim.ZEROCONF_ACC_NAME].connected = 0
gajim.groups[gajim.ZEROCONF_ACC_NAME] = {}
gajim.contacts.add_account(gajim.ZEROCONF_ACC_NAME)
@ -2080,44 +2087,23 @@ class AccountsWindow:
self.on_checkbutton_toggled(widget, 'enable_zeroconf')
class ServiceRegistrationWindow:
'''Class for Service registration window:
Window that appears when we want to subscribe to a service
if is_form we use dataforms_widget else we use service_registarion_window'''
def __init__(self, service, infos, account, is_form):
self.service = service
class FakeDataForm(gtk.Table, object):
'''Class for forms that are in XML format <entry1>value1</entry1>
infos in a table {entry1: value1, }'''
def __init__(self, infos):
gtk.Table.__init__(self)
self.infos = infos
self.account = account
self.is_form = is_form
self.xml = gtkgui_helpers.get_glade('service_registration_window.glade')
self.window = self.xml.get_widget('service_registration_window')
self.window.set_transient_for(gajim.interface.roster.window)
if self.is_form:
dataform = dataforms.ExtendForm(node = self.infos)
self.data_form_widget = dataforms_widget.DataFormWidget(dataform)
if self.data_form_widget.title:
self.window.set_title("%s - Gajim" % self.data_form_widget.title)
table = self.xml.get_widget('table')
table.attach(self.data_form_widget, 0, 2, 0, 1)
else:
if infos.has_key('registered'):
self.window.set_title(_('Edit %s') % service)
else:
self.window.set_title(_('Register to %s') % service)
self.xml.get_widget('label').set_text(infos['instructions'])
self.entries = {}
self.draw_table()
self.entries = {}
self._draw_table()
self.xml.signal_autoconnect(self)
self.window.show_all()
def on_cancel_button_clicked(self, widget):
self.window.destroy()
def draw_table(self):
'''Draw the table in the window'''
def _draw_table(self):
'''Draw the table'''
nbrow = 0
table = self.xml.get_widget('table')
if self.infos.has_key('instructions'):
nbrow = 1
self.resize(rows = nbrow, columns = 2)
label = gtk.Label(self.infos['instructions'])
self.attach(label, 0, 2, 0, 1, 0, 0, 0, 0)
for name in self.infos.keys():
if name in ('key', 'instructions', 'x', 'registered'):
continue
@ -2125,40 +2111,73 @@ class ServiceRegistrationWindow:
continue
nbrow = nbrow + 1
table.resize(rows = nbrow, columns = 2)
self.resize(rows = nbrow, columns = 2)
label = gtk.Label(name.capitalize() + ':')
table.attach(label, 0, 1, nbrow - 1, nbrow, 0, 0, 0, 0)
self.attach(label, 0, 1, nbrow - 1, nbrow, 0, 0, 0, 0)
entry = gtk.Entry()
entry.set_activates_default(True)
if self.infos[name]:
entry.set_text(self.infos[name])
if name == 'password':
entry.set_visibility(False)
table.attach(entry, 1, 2, nbrow - 1, nbrow, 0, 0, 0, 0)
self.attach(entry, 1, 2, nbrow - 1, nbrow, 0, 0, 0, 0)
self.entries[name] = entry
if nbrow == 1:
entry.grab_focus()
table.show_all()
def get_infos(self):
for name in self.entries.keys():
self.infos[name] = self.entries[name].get_text().decode('utf-8')
return self.infos
class ServiceRegistrationWindow:
'''Class for Service registration window:
Window that appears when we want to subscribe to a service
if is_form we use dataforms_widget else we use service_registarion_window'''
def __init__(self, service, infos, account, is_form):
self.service = service
self.account = account
self.is_form = is_form
self.xml = gtkgui_helpers.get_glade('service_registration_window.glade')
self.window = self.xml.get_widget('service_registration_window')
self.window.set_transient_for(gajim.interface.roster.window)
if self.is_form:
dataform = dataforms.ExtendForm(node = infos)
self.data_form_widget = dataforms_widget.DataFormWidget(dataform)
if self.data_form_widget.title:
self.window.set_title('%s - Gajim' % self.data_form_widget.title)
table = self.xml.get_widget('table')
table.attach(self.data_form_widget, 0, 2, 0, 1)
else:
if infos.has_key('registered'):
self.window.set_title(_('Edit %s') % service)
else:
self.window.set_title(_('Register to %s') % service)
self.data_form_widget = FakeDataForm(infos)
table = self.xml.get_widget('table')
table.attach(self.data_form_widget, 0, 2, 0, 1)
self.xml.signal_autoconnect(self)
self.window.show_all()
def on_cancel_button_clicked(self, widget):
self.window.destroy()
def on_ok_button_clicked(self, widget):
# send registration info to the core
if self.is_form:
form = self.data_form_widget.data_form
gajim.connections[self.account].register_agent(self.service,
form, True) # True is for is_form
else:
# we pressed OK of service_registration_window
# send registration info to the core
for name in self.entries.keys():
self.infos[name] = self.entries[name].get_text().decode('utf-8')
if self.infos.has_key('instructions'):
del self.infos['instructions']
if self.infos.has_key('registered'):
del self.infos['registered']
gajim.connections[self.account].register_agent(self.service,
self.infos)
self.window.destroy()
infos = self.data_form_widget.get_infos()
if infos.has_key('instructions'):
del infos['instructions']
if infos.has_key('registered'):
del infos['registered']
gajim.connections[self.account].register_agent(self.service, infos)
self.window.destroy()
class GroupchatConfigWindow:
'''GroupchatConfigWindow class'''
@ -2759,13 +2778,21 @@ class AccountCreationWizardWindow:
'account_creation_wizard_window.glade')
self.window = self.xml.get_widget('account_creation_wizard_window')
completion = gtk.EntryCompletion()
# Connect events from comboboxentry.child
server_comboboxentry = self.xml.get_widget('server_comboboxentry')
entry = server_comboboxentry.child
entry.connect('key_press_event',
self.on_server_comboboxentry_key_press_event)
completion = gtk.EntryCompletion()
self.on_server_comboboxentry_key_press_event, server_comboboxentry)
entry.set_completion(completion)
# Do the same for the other server comboboxentry
server_comboboxentry1 = self.xml.get_widget('server_comboboxentry1')
entry = server_comboboxentry1.child
entry.connect('key_press_event',
self.on_server_comboboxentry_key_press_event, server_comboboxentry1)
entry.set_completion(completion)
self.update_proxy_list()
# parse servers.xml
servers_xml = os.path.join(gajim.DATA_DIR, 'other', 'servers.xml')
@ -2781,6 +2808,8 @@ class AccountCreationWizardWindow:
# Put servers into comboboxentries
server_comboboxentry.set_model(servers_model)
server_comboboxentry.set_text_column(0)
server_comboboxentry1.set_model(servers_model)
server_comboboxentry1.set_text_column(0)
# Generic widgets
self.notebook = self.xml.get_widget('notebook')
@ -2819,28 +2848,18 @@ class AccountCreationWizardWindow:
self.window.destroy()
def on_back_button_clicked(self, widget):
if self.notebook.get_current_page() == 1:
if self.notebook.get_current_page() in (1, 2):
self.notebook.set_current_page(0)
self.back_button.set_sensitive(False)
elif self.notebook.get_current_page() == 3: # finish page
elif self.notebook.get_current_page() == 3:
self.notebook.set_current_page(2)
self.xml.get_widget('form_vbox').remove(self.data_form_widget)
elif self.notebook.get_current_page() == 5: # finish page
self.forward_button.show()
self.notebook.set_current_page(1) # Goto parameters page
def get_widgets(self):
widgets = {}
for widget in (
'username_entry',
'server_comboboxentry',
'pass1_entry',
'pass2_entry',
'save_password_checkbutton',
'proxyhost_entry',
'proxyport_entry',
'proxyuser_entry',
'proxypass_entry',
'jid_label'):
widgets[widget] = self.xml.get_widget(widget)
return widgets
if self.modify:
self.notebook.set_current_page(1) # Go to parameters page
else:
self.notebook.set_current_page(2) # Go to server page
def on_forward_button_clicked(self, widget):
cur_page = self.notebook.get_current_page()
@ -2849,41 +2868,29 @@ class AccountCreationWizardWindow:
widget = self.xml.get_widget('use_existing_account_radiobutton')
if widget.get_active():
self.modify = True
self.xml.get_widget('server_features_button').hide()
self.xml.get_widget('pass2_entry').hide()
self.xml.get_widget('pass2_label').hide()
self.notebook.set_current_page(1)
else:
self.modify = False
self.xml.get_widget('server_features_button').show()
self.xml.get_widget('pass2_entry').show()
self.xml.get_widget('pass2_label').show()
self.notebook.set_current_page(1)
self.notebook.set_current_page(2)
self.back_button.set_sensitive(True)
return
else:
widgets = self.get_widgets()
username = widgets['username_entry'].get_text().decode('utf-8')
elif cur_page == 1:
# We are adding an existing account
username = self.xml.get_widget('username_entry').get_text().decode(
'utf-8')
if not username:
pritext = _('Invalid username')
sectext = _('You must provide a username to configure this account'
'.')
sectext = _(
'You must provide a username to configure this account.')
dialogs.ErrorDialog(pritext, sectext)
return
server = widgets['server_comboboxentry'].child.get_text().decode('utf-8')
savepass = widgets['save_password_checkbutton'].get_active()
password = widgets['pass1_entry'].get_text().decode('utf-8')
if not self.modify:
if password == '':
dialogs.ErrorDialog(_('Invalid password'),
_('You must enter a password for the new account.'))
return
if widgets['pass2_entry'].get_text() != password:
dialogs.ErrorDialog(_('Passwords do not match'),
_('The passwords typed in both fields must be identical.'))
return
server = self.xml.get_widget('server_comboboxentry').child.get_text().\
decode('utf-8')
savepass = self.xml.get_widget('save_password_checkbutton').\
get_active()
password = self.xml.get_widget('password_entry').get_text().decode(
'utf-8')
jid = username + '@' + server
# check if jid is conform to RFC and stringprep it
@ -2919,31 +2926,141 @@ class AccountCreationWizardWindow:
self.forward_button.hide()
if self.modify:
finish_text = '<big><b>%s</b></big>\n\n%s' % (
_('Account has been added successfully'),
_('You can set advanced account options by pressing the '
'Advanced button, or later by choosing the Accounts menuitem '
_('Account has been added successfully'),
_('You can set advanced account options by pressing the '
'Advanced button, or later by choosing the Accounts menuitem '
'under the Edit menu from the main window.'))
self.finish_label.set_markup(finish_text)
self.finish_button.show()
self.finish_button.set_property('has-default', True)
self.advanced_button.show()
self.go_online_checkbutton.show()
img = self.xml.get_widget('finish_image')
img.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_DIALOG)
self.notebook.set_current_page(3) # show finish page
self.show_vcard_checkbutton.set_active(False)
self.finish_label.set_markup(finish_text)
self.finish_button.show()
self.finish_button.set_property('has-default', True)
self.advanced_button.show()
self.go_online_checkbutton.show()
img = self.xml.get_widget('finish_image')
img.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_DIALOG)
self.notebook.set_current_page(5) # show finish page
self.show_vcard_checkbutton.set_active(False)
elif cur_page == 2:
server = self.xml.get_widget('server_comboboxentry1').child.get_text()\
.decode('utf-8')
if not server:
dialogs.ErrorDialog(_('Invalid server'),
_('Please provide a server on which you want to register.'))
return
self.account = server
i = 1
while self.account in gajim.connections:
self.account = server + str(i)
i += 1
config = self.get_config('', server, '', '')
# Get advanced options
proxies_combobox = self.xml.get_widget('proxies_combobox')
active = proxies_combobox.get_active()
proxy = proxies_combobox.get_model()[active][0].decode('utf-8')
if proxy == _('None'):
proxy = ''
config['proxy'] = proxy
config['use_custom_host'] = self.xml.get_widget(
'custom_host_port_checkbutton').get_active()
custom_port = self.xml.get_widget('custom_port_entry').get_text()
try:
custom_port = int(custom_port)
except:
dialogs.ErrorDialog(_('Invalid entry'),
_('Custom port must be a port number.'))
return
config['custom_port'] = custom_port
config['custom_host'] = self.xml.get_widget(
'custom_host_entry').get_text().decode('utf-8')
self.notebook.set_current_page(4) # show creating page
self.back_button.hide()
self.forward_button.hide()
self.update_progressbar_timeout_id = gobject.timeout_add(100,
self.update_progressbar)
# Get form from serveur
con = connection.Connection(self.account)
con.new_account(self.account, config)
gajim.connections[self.account] = con
elif cur_page == 3:
if self.is_form:
form = self.data_form_widget.data_form
else:
self.notebook.set_current_page(2) # show creating page
self.update_progressbar_timeout_id = gobject.timeout_add(100,
self.update_progressbar)
form = self.data_form_widget.get_infos()
gajim.connections[self.account].send_new_account_infos(form,
self.is_form)
self.xml.get_widget('form_vbox').remove(self.data_form_widget)
self.xml.get_widget('progressbar_label').set_markup('<b>Account is being created</b>\n\nPlease wait...')
self.notebook.set_current_page(4) # show creating page
self.back_button.hide()
self.forward_button.hide()
self.update_progressbar_timeout_id = gobject.timeout_add(100,
self.update_progressbar)
def update_proxy_list(self):
proxies_combobox = self.xml.get_widget('proxies_combobox')
model = gtk.ListStore(str)
proxies_combobox.set_model(model)
l = gajim.config.get_per('proxies')
l.insert(0, _('None'))
for i in xrange(len(l)):
model.append([l[i]])
proxies_combobox.set_active(0)
def on_manage_proxies_button_clicked(self, widget):
if gajim.interface.instances.has_key('manage_proxies'):
gajim.interface.instances['manage_proxies'].window.present()
else:
gajim.interface.instances['manage_proxies'] = \
ManageProxiesWindow()
def on_custom_host_port_checkbutton_toggled(self, widget):
self.xml.get_widget('custom_host_hbox').set_sensitive(widget.get_active())
def update_progressbar(self):
self.progressbar.pulse()
return True # loop forever
def new_acc_connected(self, form, is_form):
'''connection to server succeded, present the form to the user'''
if self.update_progressbar_timeout_id is not None:
gobject.source_remove(self.update_progressbar_timeout_id)
self.back_button.show()
self.forward_button.show()
self.notebook.set_current_page(3) # show form page
self.is_form = is_form
if is_form:
dataform = dataforms.ExtendForm(node = form)
self.data_form_widget = dataforms_widget.DataFormWidget(dataform)
else:
self.data_form_widget = FakeDataForm(form)
self.data_form_widget.show_all()
self.xml.get_widget('form_vbox').pack_start(self.data_form_widget)
def new_acc_not_connected(self, reason):
'''Account creation failed: connection to server failed'''
if self.update_progressbar_timeout_id is not None:
gobject.source_remove(self.update_progressbar_timeout_id)
del gajim.connections[self.account]
self.back_button.show()
self.cancel_button.show()
self.go_online_checkbutton.hide()
self.show_vcard_checkbutton.hide()
img = self.xml.get_widget('finish_image')
img.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
finish_text = '<big><b>%s</b></big>\n\n%s' % (
_('An error occurred during account creation') , reason)
self.finish_label.set_markup(finish_text)
self.notebook.set_current_page(5) # show finish page
def acc_is_ok(self, config):
'''Account creation succeeded'''
self.create_vars(config)
self.cancel_button.hide()
self.back_button.hide()
self.forward_button.hide()
self.finish_button.show()
self.finish_button.set_property('has-default', True)
self.advanced_button.show()
@ -2959,7 +3076,7 @@ class AccountCreationWizardWindow:
'button, or later by choosing the Accounts menuitem under the Edit '
'menu from the main window.'))
self.finish_label.set_markup(finish_text)
self.notebook.set_current_page(3) # show finish page
self.notebook.set_current_page(5) # show finish page
if self.update_progressbar_timeout_id is not None:
gobject.source_remove(self.update_progressbar_timeout_id)
@ -2970,12 +3087,16 @@ class AccountCreationWizardWindow:
self.cancel_button.show()
self.go_online_checkbutton.hide()
self.show_vcard_checkbutton.hide()
del gajim.connections[self.account]
img = self.xml.get_widget('finish_image')
img.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
finish_text = '<big><b>%s</b></big>\n\n%s' % (_('An error occurred during '
'account creation') , reason)
self.finish_label.set_markup(finish_text)
self.notebook.set_current_page(3) # show finish page
self.notebook.set_current_page(5) # show finish page
if self.update_progressbar_timeout_id is not None:
gobject.source_remove(self.update_progressbar_timeout_id)
def on_advanced_button_clicked(self, widget):
gajim.interface.instances[self.account]['account_modification'] = \
@ -3005,12 +3126,11 @@ class AccountCreationWizardWindow:
combobox.child.set_position(-1)
return True
def on_server_comboboxentry_key_press_event(self, widget, event):
def on_server_comboboxentry_key_press_event(self, widget, event, combobox):
# If backspace is pressed in empty field, return to the nick entry field
backspace = event.keyval == gtk.keysyms.BackSpace
combobox = self.xml.get_widget('server_comboboxentry')
empty = len(combobox.get_active_text()) == 0
if backspace and empty:
if backspace and empty and self.modify:
username_entry = self.xml.get_widget('username_entry')
username_entry.grab_focus()
username_entry.set_position(-1)
@ -3028,14 +3148,7 @@ class AccountCreationWizardWindow:
string = '<b>%s@%s</b>' % (name, server)
jid_label.set_label(string)
def save_account(self, login, server, savepass, password):
if self.account in gajim.connections:
dialogs.ErrorDialog(_('Account name is in use'),
_('You already have an account using this name.'))
return
con = connection.Connection(self.account)
con.password = password
def get_config(self, login, server, savepass, password):
config = {}
config['name'] = login
config['hostname'] = server
@ -3055,6 +3168,17 @@ class AccountCreationWizardWindow:
config['keyid'] = ''
config['savegpgpass'] = False
config['gpgpassword'] = ''
return config
def save_account(self, login, server, savepass, password):
if self.account in gajim.connections:
dialogs.ErrorDialog(_('Account name is in use'),
_('You already have an account using this name.'))
return
con = connection.Connection(self.account)
con.password = password
config = self.get_config(login, server, savepass, password)
if not self.modify:
con.new_account(self.account, config)
@ -3073,7 +3197,7 @@ class AccountCreationWizardWindow:
# update variables
gajim.interface.instances[self.account] = {'infos': {}, 'disco': {},
'gc_config': {}}
'gc_config': {}, 'search': {}}
gajim.connections[self.account].connected = 0
gajim.groups[self.account] = {}
gajim.contacts.add_account(self.account)

View File

@ -14,6 +14,10 @@
## GNU General Public License for more details.
##
import random
from tempfile import gettempdir
from subprocess import Popen
import gtk
import pango
import gobject
@ -572,6 +576,72 @@ class ConversationTextview:
return index # the position after *last* special text
def latex_to_image(self, str):
result = None
exitcode = 0
# some latex commands are really bad
blacklist = ["\\def", "\\let", "\\futurelet",
"\\newcommand", "\\renewcomment", "\\else", "\\fi", "\\write",
"\\input", "\\include", "\\chardef", "\\catcode", "\\makeatletter",
"\\noexpand", "\\toksdef", "\\every", "\\errhelp", "\\errorstopmode",
"\\scrollmode", "\\nonstopmode", "\\batchmode", "\\read", "\\csname",
"\\newhelp", "\\relax", "\\afterground", "\\afterassignment",
"\\expandafter", "\\noexpand", "\\special", "\\command", "\\loop",
"\\repeat", "\\toks", "\\output", "\\line", "\\mathcode", "\\name",
"\\item", "\\section", "\\mbox", "\\DeclareRobustCommand", "\\[",
"\\]"]
str = str[2:len(str)-2]
# filter latex code with bad commands
for word in blacklist:
if word in str:
exitcode = 1
break
if exitcode == 0:
random.seed()
tmpfile = os.path.join(gettempdir(), "gajimtex_" + random.randint(0,
100).__str__())
# build latex string
texstr = "\\documentclass[12pt]{article}\\usepackage[dvips]{graphicx}\\usepackage{amsmath}\\usepackage{amssymb}\\pagestyle{empty}"
texstr += "\\begin{document}\\begin{large}\\begin{gather*}"
texstr += str
texstr += "\\end{gather*}\\end{large}\\end{document}"
file = open(os.path.join(tmpfile + ".tex"), "w+")
file.write(texstr)
file.flush()
file.close()
p = Popen(['latex', '--interaction=nonstopmode', tmpfile + '.tex'],
cwd=gettempdir())
exitcode = p.wait()
if exitcode == 0:
p = Popen(['dvips', '-E', '-o', tmpfile + '.ps', tmpfile + '.dvi'],
cwd=gettempdir())
exitcode = p.wait()
if exitcode == 0:
p = Popen(['convert', tmpfile + '.ps', tmpfile + '.png'],
cwd=gettempdir())
exitcode = p.wait()
extensions = [".tex", ".log", ".aux", ".dvi", ".ps"]
for ext in extensions:
try:
os.remove(tmpfile + ext)
except Exception:
pass
if exitcode == 0:
result = tmpfile + '.png'
return result
def print_special_text(self, special_text, other_tags):
'''is called by detect_and_print_special_text and prints
special text (emots, links, formatting)'''
@ -656,6 +726,24 @@ class ConversationTextview:
else:
if not show_ascii_formatting_chars:
special_text = special_text[1:-1] # remove _ _
elif special_text.startswith('$$') and special_text.endswith('$$'):
imagepath = self.latex_to_image(special_text)
end_iter = buffer.get_end_iter()
anchor = buffer.create_child_anchor(end_iter)
if imagepath != None:
img = gtk.Image()
img.set_from_file(imagepath)
img.show()
# add
self.tv.add_child_at_anchor(img, anchor)
# delete old file
try:
os.remove(imagepath)
except Exception:
pass
else:
buffer.insert(end_iter, special_text)
use_other_tags = False
else:
#it's a url
tags.append('url')

View File

@ -16,8 +16,8 @@
##
""" This module contains widget that can display data form (JEP-0004).
Words single and multiple refers here to types of data forms:
single means these with one record of data (without <recorded/> element),
multiple - these which may contain more data (with <recorded/> element)."""
single means these with one record of data (without <reported/> element),
multiple - these which may contain more data (with <reported/> element)."""
import gtk
@ -41,7 +41,7 @@ class DataFormWidget(gtk.Alignment, object):
for name in ('instructions_label', 'instructions_hseparator',
'single_form_viewport', 'data_form_types_notebook',
'single_form_scrolledwindow', 'multiple_form_hbox',
'records_treeview', 'add_button', 'remove_button',
'records_treeview', 'buttons_vbox', 'add_button', 'remove_button',
'edit_button', 'up_button', 'down_button', 'clear_button'):
self.__dict__[name] = self.xml.get_widget(name)
@ -141,7 +141,7 @@ class DataFormWidget(gtk.Alignment, object):
# creating model for form...
fieldtypes = []
for field in self._data_form.recorded.iter_fields():
for field in self._data_form.reported.iter_fields():
# note: we store also text-private and hidden fields,
# we just do not display them.
# TODO: boolean fields
@ -157,8 +157,7 @@ class DataFormWidget(gtk.Alignment, object):
self.multiplemodel.append([field.value for field in item.iter_fields()])
# constructing columns...
for field, counter in zip(self._data_form.iter_fields(), itertools.count()):
print repr(field), repr(counter)
for field, counter in zip(self._data_form.reported.iter_fields(), itertools.count()):
self.records_treeview.append_column(
gtk.TreeViewColumn(field.label, gtk.CellRendererText(),
text=counter))
@ -172,8 +171,14 @@ class DataFormWidget(gtk.Alignment, object):
self.clean_data_form = self.clean_multiple_data_form
# refresh list look
self.refresh_multiple_buttons()
readwrite = self._data_form.type != 'result'
if not readwrite:
self.buttons_vbox.set_no_show_all(True)
self.buttons_vbox.hide()
else:
self.buttons_vbox.set_no_show_all(False)
# refresh list look
self.refresh_multiple_buttons()
def clean_multiple_data_form(self):
'''(Called as clean_data_form, read the docs of clean_data_form()).
@ -186,12 +191,12 @@ class DataFormWidget(gtk.Alignment, object):
selection = self.records_treeview.get_selection()
model = self.records_treeview.get_model()
count = selection.count_selected_rows()
if count==0:
if count == 0:
self.remove_button.set_sensitive(False)
self.edit_button.set_sensitive(False)
self.up_button.set_sensitive(False)
self.down_button.set_sensitive(False)
elif count==1:
elif count == 1:
self.remove_button.set_sensitive(True)
self.edit_button.set_sensitive(True)
_, (path,) = selection.get_selected_rows()
@ -199,7 +204,7 @@ class DataFormWidget(gtk.Alignment, object):
if model.iter_next(iter) is None:
self.up_button.set_sensitive(True)
self.down_button.set_sensitive(False)
elif path==(0,):
elif path == (0, ):
self.up_button.set_sensitive(False)
self.down_button.set_sensitive(True)
else:
@ -211,7 +216,7 @@ class DataFormWidget(gtk.Alignment, object):
self.up_button.set_sensitive(False)
self.down_button.set_sensitive(False)
if len(model)==0:
if len(model) == 0:
self.clear_button.set_sensitive(False)
else:
self.clear_button.set_sensitive(True)

View File

@ -7,6 +7,7 @@
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
@ -56,7 +57,7 @@ class EditGroupsDialog:
if len(list_) == 1:
contact = list_[0][0]
self.xml.get_widget('nickname_label').set_markup(
_("Contact name: <i>%s</i>") % contact.get_shown_name())
_('Contact name: <i>%s</i>') % contact.get_shown_name())
self.xml.get_widget('jid_label').set_markup(
_('Jabber ID: <i>%s</i>') % contact.jid)
else:
@ -434,10 +435,10 @@ class ChangeStatusMessageDialog:
self.window.set_transient_for(gajim.interface.roster.window)
if show:
uf_show = helpers.get_uf_show(show)
title_text = _('%s Status Message') % uf_show
self.title_text = _('%s Status Message') % uf_show
else:
title_text = _('Status Message')
self.window.set_title(title_text)
self.title_text = _('Status Message')
self.window.set_title(self.title_text)
message_textview = self.xml.get_widget('message_textview')
self.message_buffer = message_textview.get_buffer()
@ -459,6 +460,10 @@ class ChangeStatusMessageDialog:
self.preset_messages_dict[msg_name] = msg_text
sorted_keys_list = helpers.get_sorted_keys(self.preset_messages_dict)
self.countdown_time = gajim.config.get('change_status_window_timeout')
self.countdown_left = self.countdown_time
self.countdown_enabled = True
self.message_liststore = gtk.ListStore(str) # msg_name
self.message_combobox = self.xml.get_widget('message_combobox')
self.message_combobox.set_model(self.message_liststore)
@ -470,9 +475,25 @@ class ChangeStatusMessageDialog:
self.xml.signal_autoconnect(self)
self.window.show_all()
def countdown(self):
if self.countdown_enabled:
if self.countdown_left <= 0:
self.window.response(gtk.RESPONSE_OK)
return False
self.window.set_title('%s [%s]' % (self.title_text,
str(self.countdown_left)))
self.countdown_left -= 1
return True
else:
self.window.set_title(self.title_text)
return False
def run(self):
'''Wait for OK or Cancel button to be pressed and return status messsage
(None if users pressed Cancel or x button of WM'''
if self.countdown_time > 0:
self.countdown()
gobject.timeout_add(1000, self.countdown)
rep = self.window.run()
if rep == gtk.RESPONSE_OK:
beg, end = self.message_buffer.get_bounds()
@ -487,6 +508,7 @@ class ChangeStatusMessageDialog:
return message
def on_message_combobox_changed(self, widget):
self.countdown_enabled = False
model = widget.get_model()
active = widget.get_active()
if active < 0:
@ -495,10 +517,13 @@ class ChangeStatusMessageDialog:
self.message_buffer.set_text(self.preset_messages_dict[name])
def on_change_status_message_dialog_key_press_event(self, widget, event):
self.countdown_enabled = False
if event.keyval == gtk.keysyms.Return or \
event.keyval == gtk.keysyms.KP_Enter: # catch CTRL+ENTER
if (event.state & gtk.gdk.CONTROL_MASK):
self.window.response(gtk.RESPONSE_OK)
# Stop the event
return True
def toggle_sensitiviy_of_save_as_preset(self, widget):
btn = self.xml.get_widget('save_as_preset_button')
@ -508,6 +533,7 @@ class ChangeStatusMessageDialog:
btn.set_sensitive(True)
def on_save_as_preset_button_clicked(self, widget):
self.countdown_enabled = False
start_iter, finish_iter = self.message_buffer.get_bounds()
status_message_to_save_as_preset = self.message_buffer.get_text(
start_iter, finish_iter)
@ -582,21 +608,21 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
self.available_types = []
for acct in accounts:
for j in gajim.contacts.get_jid_list(acct):
contact = gajim.contacts.get_first_contact_from_jid(acct, j)
if gajim.jid_is_transport(j):
type_ = gajim.get_transport_name_from_jid(j)
type_ = gajim.get_transport_name_from_jid(j, False)
if self.agents.has_key(type_):
self.agents[type_].append(j)
else:
self.agents[type_] = [j]
# Now add the one to which we can register
for acct in accounts:
for type_ in gajim.connections[account].available_transports:
for type_ in gajim.connections[acct].available_transports:
if type_ in self.agents:
continue
self.agents[type_] = []
for jid_ in gajim.connections[account].available_transports[type_]:
self.agents[type_].append(jid_)
for jid_ in gajim.connections[acct].available_transports[type_]:
if not jid_ in self.agents[type_]:
self.agents[type_].append(jid_)
self.available_types.append(type_)
liststore = gtk.ListStore(str)
self.group_comboboxentry.set_model(liststore)
@ -835,7 +861,7 @@ class AboutDialog:
dlg.set_transient_for(gajim.interface.roster.window)
dlg.set_name('Gajim')
dlg.set_version(gajim.version)
s = u'Copyright © 2003-2006 Gajim Team'
s = u'Copyright © 2003-2007 Gajim Team'
dlg.set_copyright(s)
copying_file_path = None
if os.path.isfile(os.path.join(gajim.defs.docdir, 'COPYING')):
@ -1028,6 +1054,15 @@ class BindPortError(HigDialog):
_('Maybe you have another running instance of Gajim. '
'File Transfer will be cancelled.'))
class AspellDictError(HigDialog):
def __init__(self, lang):
ErrorDialog(
_('Dictionary for lang %s not available') % lang,
_('You have to install %s dictionary to use spellchecking, or '
'choose another language by setting the speller_language option.'
'\n\nHighlighting misspelled words feature will not be used') % lang)
gajim.config.set('use_speller', False)
class ConfirmationDialog(HigDialog):
'''HIG compliant confirmation dialog.'''
def __init__(self, pritext, sectext='', on_response_ok = None,
@ -1438,6 +1473,136 @@ class JoinGroupchatWindow:
self.window.destroy()
class SynchroniseSelectAccountDialog:
def __init__(self, account):
# 'account' can be None if we are about to create our first one
if not account or gajim.connections[account].connected < 2:
ErrorDialog(_('You are not connected to the server'),
_('Without a connection, you can not synchronise your contacts.'))
raise GajimGeneralException, 'You are not connected to the server'
self.account = account
self.xml = gtkgui_helpers.get_glade('synchronise_select_account_dialog.glade')
self.dialog = self.xml.get_widget('synchronise_select_account_dialog')
self.accounts_treeview = self.xml.get_widget('accounts_treeview')
model = gtk.ListStore(str, str, bool)
self.accounts_treeview.set_model(model)
# columns
renderer = gtk.CellRendererText()
self.accounts_treeview.insert_column_with_attributes(-1,
_('Name'), renderer, text = 0)
renderer = gtk.CellRendererText()
self.accounts_treeview.insert_column_with_attributes(-1,
_('Server'), renderer, text = 1)
self.xml.signal_autoconnect(self)
self.init_accounts()
self.dialog.show_all()
def on_accounts_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
self.window.destroy()
def init_accounts(self):
'''initialize listStore with existing accounts'''
model = self.accounts_treeview.get_model()
model.clear()
for remote_account in gajim.connections:
if remote_account == self.account:
# Do not show the account we're sync'ing
continue
iter = model.append()
model.set(iter, 0, remote_account, 1, gajim.get_hostname_from_account(
remote_account))
def on_cancel_button_clicked(self, widget):
self.dialog.destroy()
def on_ok_button_clicked(self, widget):
sel = self.accounts_treeview.get_selection()
(model, iter) = sel.get_selected()
if not iter:
return
remote_account = model.get_value(iter, 0).decode('utf-8')
if gajim.connections[remote_account].connected < 2:
ErrorDialog(_('This account is not connected to the server'),
_('You cannot synchronize with an account unless it is connected.'))
return
else:
try:
dialog = SynchroniseSelectContactsDialog(self.account, remote_account)
except GajimGeneralException:
# if we showed ErrorDialog, there will not be dialog instance
return
self.dialog.destroy()
class SynchroniseSelectContactsDialog:
def __init__(self, account, remote_account):
self.local_account = account
self.remote_account = remote_account
self.xml = gtkgui_helpers.get_glade('synchronise_select_contacts_dialog.glade')
self.dialog = self.xml.get_widget('synchronise_select_contacts_dialog')
self.contacts_treeview = self.xml.get_widget('contacts_treeview')
model = gtk.ListStore(bool, str)
self.contacts_treeview.set_model(model)
# columns
renderer1 = gtk.CellRendererToggle()
renderer1.set_property('activatable', True)
renderer1.connect('toggled', self.toggled_callback)
self.contacts_treeview.insert_column_with_attributes(-1,
_('Synchronise'), renderer1, active = 0)
renderer2 = gtk.CellRendererText()
self.contacts_treeview.insert_column_with_attributes(-1,
_('Name'), renderer2, text = 1)
self.xml.signal_autoconnect(self)
self.init_contacts()
self.dialog.show_all()
def toggled_callback(self, cell, path):
model = self.contacts_treeview.get_model()
iter = model.get_iter(path)
model[iter][0] = not cell.get_active()
def on_contacts_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
self.window.destroy()
def init_contacts(self):
'''initialize listStore with existing accounts'''
model = self.contacts_treeview.get_model()
model.clear()
# recover local contacts
local_jid_list = gajim.contacts.get_jid_list(self.local_account)
remote_jid_list = gajim.contacts.get_jid_list(self.remote_account)
for remote_jid in remote_jid_list:
if remote_jid not in local_jid_list:
iter = model.append()
model.set(iter, 0, True, 1, remote_jid)
def on_cancel_button_clicked(self, widget):
self.dialog.destroy()
def on_ok_button_clicked(self, widget):
model = self.contacts_treeview.get_model()
iter = model.get_iter_root()
while iter:
if model[iter][0]:
# it is selected
remote_jid = model[iter][1].decode('utf-8')
message = 'I\'m synchronizing my contacts from my %s account, could you please add this address to your contact list?' % \
gajim.get_hostname_from_account(self.remote_account)
remote_contact = gajim.contacts.get_first_contact_from_jid(
self.remote_account, remote_jid)
# keep same groups and same nickname
gajim.interface.roster.req_sub(self, remote_jid, message,
self.local_account, groups = remote_contact.groups,
nickname = remote_contact.name, auto_auth = True)
iter = model.iter_next(iter)
self.dialog.destroy()
class NewChatDialog(InputDialog):
def __init__(self, account):
self.account = account
@ -1692,9 +1857,7 @@ class SingleMessageWindow:
spell1.set_language(lang)
spell2.set_language(lang)
except gobject.GError, msg:
ErrorDialog(unicode(msg), _('If that is not your language for which you want to highlight misspelled words, then please set your $LANG as appropriate. Eg. for French do export LANG=fr_FR or export LANG=fr_FR.UTF-8 in ~/.bash_profile or to make it global in /etc/profile.\n\nHighlighting misspelled words feature will not be used'))
gajim.config.set('use_speller', False)
dialogs.AspellDictError(lang)
self.send_button.set_no_show_all(True)
self.reply_button.set_no_show_all(True)
self.send_and_close_button.set_no_show_all(True)
@ -2046,6 +2209,16 @@ class PrivacyListWindow:
self.privacy_list_default_checkbutton.set_sensitive(False)
self.list_of_rules_combobox.set_sensitive(False)
# set jabber id completion
jids_list_store = gtk.ListStore(gobject.TYPE_STRING)
for jid in gajim.contacts.get_jid_list(self.account):
jids_list_store.append([jid])
jid_entry_completion = gtk.EntryCompletion()
jid_entry_completion.set_text_column(0)
jid_entry_completion.set_model(jids_list_store)
jid_entry_completion.set_popup_completion(True)
self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
if action == 'EDIT':
self.refresh_rules()
@ -2265,7 +2438,7 @@ class PrivacyListWindow:
gajim.connections[self.account].set_privacy_list(
self.privacy_list_name, tags)
self.privacy_list_received(tags)
self.refresh_rules()
self.add_edit_vbox.hide()
if 'privacy_lists' in gajim.interface.instances[self.account]:
win = gajim.interface.instances[self.account]['privacy_lists']
@ -2288,6 +2461,101 @@ class PrivacyListWindow:
def on_close_button_clicked(self, widget):
self.window.destroy()
class BlockedContactsWindow:
'''Window that is the main window for ContactWindows;'''
def __init__(self, account):
self.account = account
self.xml = gtkgui_helpers.get_glade('blocked_contacts_window.glade')
self.window = self.xml.get_widget('blocked_contacts_window')
self.remove_button = self.xml.get_widget('remove_button')
self.contacts_treeview = self.xml.get_widget('contacts_treeview')
renderer = gtk.CellRendererText()
self.store = gtk.ListStore(str)
self.contacts_treeview.set_model(self.store)
column = gtk.TreeViewColumn('Contact', renderer, text=0)
self.contacts_treeview.append_column(column)
if len(gajim.connections) > 1:
title = _('Blocked Contacts for %s') % self.account
else:
title = _('Blocked Contacts')
self.window.set_title(title)
self.window.show_all()
self.xml.signal_autoconnect(self)
gajim.connections[self.account].get_privacy_list('block')
def on_blocked_contacts_window_destroy(self, widget):
key_name = 'blocked_contacts'
if key_name in gajim.interface.instances[self.account]:
del gajim.interface.instances[self.account][key_name]
def on_remove_button_clicked(self, widget):
if self.contacts_treeview.get_selection().get_selected()[1] == None:
return
tags = []
rule_selected = self.store.get_path(
self.contacts_treeview.get_selection().get_selected()[1])[0]
for i in range(0, len(self.global_rules)):
if i != rule_selected:
tags.append(self.global_rules[i])
else:
deleted_rule = self.global_rules[i]
for rule in self.global_rules_to_append:
tags.append(rule)
gajim.connections[self.account].set_privacy_list(
'block', tags)
gajim.connections[self.account].get_privacy_list('block')
if len(tags) == 0:
self.privacy_list_received([])
gajim.connections[self.account].blocked_contacts = []
gajim.connections[self.account].blocked_groups = []
gajim.connections[self.account].blocked_list = []
gajim.connections[self.account].set_default_list('')
gajim.connections[self.account].set_active_list('')
gajim.connections[self.account].del_privacy_list('block')
status = gajim.connections[self.account].connected
msg = gajim.connections[self.account].status
show = gajim.SHOW_LIST[gajim.connections[self.account].connected]
if deleted_rule['type'] == 'jid':
jid = deleted_rule['value']
gajim.connections[self.account].send_custom_status(show, msg, jid)
# needed for draw_contact:
if jid in gajim.connections[self.account].blocked_contacts:
gajim.connections[self.account].blocked_contacts.remove(jid)
gajim.interface.roster.draw_contact(jid, self.account)
else:
group = deleted_rule['value']
# needed for draw_group:
if group in gajim.connections[self.account].blocked_groups:
gajim.connections[self.account].blocked_groups.remove(group)
gajim.interface.roster.draw_group(group, self.account)
for jid in gajim.contacts.get_jid_list(self.account):
contact = gajim.contacts.get_contact_with_highest_priority(
self.account, jid)
if group in contact.groups:
gajim.connections[self.account].send_custom_status(show, msg,
contact.jid)
gajim.interface.roster.draw_contact(contact.jid, self.account)
def privacy_list_received(self, rules):
self.store.clear()
self.global_rules = []
self.global_rules_to_append = []
for rule in rules:
if rule['type'] == 'jid' and rule['action'] == 'deny':
#self.global_rules[text_item] = rule
self.store.append([rule['value']])
self.global_rules.append(rule)
elif rule['type'] == 'group' and rule['action'] == 'deny':
text_item = _('Group %s') % rule['value']
self.store.append([text_item])
self.global_rules.append(rule)
else:
self.global_rules_to_append.append(rule)
class PrivacyListsWindow:
'''Window that is the main window for Privacy Lists;
we can list there the privacy lists and ask to create a new one

View File

@ -49,6 +49,7 @@ import tooltips
import gtkgui_helpers
import groups
import adhoc_commands
import search_window
from common import gajim
from common import xmpp
@ -986,6 +987,7 @@ class ToplevelAgentBrowser(AgentBrowser):
self.register_button = None
self.join_button = None
self.execute_button = None
self.search_button = None
# Keep track of our treeview signals
self._view_signals = []
self._scroll_signal = None
@ -1142,7 +1144,7 @@ class ToplevelAgentBrowser(AgentBrowser):
AgentBrowser._add_actions(self)
self.execute_button = gtk.Button()
image = gtk.image_new_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_BUTTON)
label = gtk.Label(_('_Execute Command...'))
label = gtk.Label(_('_Execute Command'))
label.set_use_underline(True)
hbox = gtk.HBox()
hbox.pack_start(image, False, True, 6)
@ -1170,6 +1172,18 @@ class ToplevelAgentBrowser(AgentBrowser):
self.window.action_buttonbox.add(self.join_button)
self.join_button.show_all()
self.search_button = gtk.Button()
image = gtk.image_new_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON)
label = gtk.Label(_('_Search'))
label.set_use_underline(True)
hbox = gtk.HBox()
hbox.pack_start(image, False, True, 6)
hbox.pack_end(label, True, True)
self.search_button.add(hbox)
self.search_button.connect('clicked', self.on_search_button_clicked)
self.window.action_buttonbox.add(self.search_button)
self.search_button.show_all()
def _clean_actions(self):
if self.execute_button:
self.execute_button.destroy()
@ -1180,6 +1194,56 @@ class ToplevelAgentBrowser(AgentBrowser):
if self.join_button:
self.join_button.destroy()
self.join_button = None
if self.search_button:
self.search_button.destroy()
self.search_button = None
AgentBrowser._clean_actions(self)
def cleanup(self):
self.tooltip.hide_tooltip()
AgentBrowser.cleanup(self)
def update_theme(self):
theme = gajim.config.get('roster_theme')
bgcolor = gajim.config.get_per('themes', theme, 'groupbgcolor')
if bgcolor:
self._renderer.set_property('cell-background', bgcolor)
self.window.services_treeview.queue_draw()
def on_execute_button_clicked(self, widget = None):
'''When we want to execute a command:
open adhoc command window'''
model, iter = self.window.services_treeview.get_selection().get_selected()
if not iter:
return
service = model[iter][0].decode('utf-8')
adhoc_commands.CommandWindow(self.account, service)
def on_search_button_clicked(self, widget = None):
'''When we want to search something:
open search window'''
model, iter = self.window.services_treeview.get_selection().get_selected()
if not iter:
return
service = model[iter][0].decode('utf-8')
if gajim.interface.instances[self.account]['search'].has_key(service):
gajim.interface.instances[self.account]['search'][service].present()
else:
gajim.interface.instances[self.account]['search'][service] = \
search_window.SearchWindow(self.account, service)
def on_register_button_clicked(self, widget = None):
'''When we want to register an agent:
request information about registering with the agent and close the
window.'''
model, iter = self.window.services_treeview.get_selection().get_selected()
if not iter:
return
jid = model[iter][0].decode('utf-8')
if jid:
gajim.connections[self.account].request_register_agent_info(jid)
self.window.destroy(chain = True)
AgentBrowser._clean_actions(self)
def cleanup(self):
@ -1239,6 +1303,9 @@ class ToplevelAgentBrowser(AgentBrowser):
self.browse_button.set_sensitive(False)
if self.join_button:
self.join_button.set_sensitive(False)
if self.search_button:
self.search_button.set_sensitive(False)
model, iter = self.window.services_treeview.get_selection().get_selected()
model, iter = self.window.services_treeview.get_selection().get_selected()
if not iter:
return
@ -1271,6 +1338,8 @@ class ToplevelAgentBrowser(AgentBrowser):
AgentBrowser._update_actions(self, jid, node, identities, features, data)
if self.execute_button and xmpp.NS_COMMANDS in features:
self.execute_button.set_sensitive(True)
if self.search_button and xmpp.NS_SEARCH in features:
self.search_button.set_sensitive(True)
if self.register_button and xmpp.NS_REGISTER in features:
# We can register this agent
registered_transports = []

View File

@ -400,13 +400,16 @@ class Interface:
gc_control.show_change_nick_input_dialog(title, prompt, proposed_nick)
def handle_event_http_auth(self, account, data):
#('HTTP_AUTH', account, (method, url, transaction_id, iq_obj))
#('HTTP_AUTH', account, (method, url, transaction_id, iq_obj, msg))
def response(widget, account, iq_obj, answer):
self.dialog.destroy()
gajim.connections[account].build_http_auth_answer(iq_obj, answer)
sec_msg = _('Do you accept this request?')
if data[4]:
sec_msg = data[4] + '\n' + sec_msg
self.dialog = dialogs.YesNoDialog(_('HTTP (%s) Authorization for %s (id: %s)') \
% (data[0], data[1], data[2]), _('Do you accept this request?'),
% (data[0], data[1], data[2]), sec_msg,
on_response_yes = (response, account, data[3], 'yes'),
on_response_no = (response, account, data[3], 'no'))
@ -502,7 +505,7 @@ class Interface:
def handle_event_notify(self, account, array):
# 'NOTIFY' (account, (jid, status, status message, resource, priority,
# keyID, timestamp))
# keyID, timestamp, contact_nickname))
# if we're here it means contact changed show
statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible']
@ -514,6 +517,7 @@ class Interface:
status_message = array[2]
jid = array[0].split('/')[0]
keyID = array[5]
contact_nickname = array[7]
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if jid in attached_keys:
@ -542,8 +546,12 @@ class Interface:
if contact1:
if contact1.show in statuss:
old_show = statuss.index(contact1.show)
if contact_nickname is not None and \
contact1.contact_name != contact_nickname:
contact1.contact_name = contact_nickname
self.roster.draw_contact(jid, account)
if old_show == new_show and contact1.status == status_message and \
contact1.priority == priority: # no change
contact1.priority == priority: # no change
return
else:
contact1 = gajim.contacts.get_first_contact_from_jid(account, ji)
@ -596,6 +604,7 @@ class Interface:
elif not gajim.block_signed_in_notifications[account]:
# We're connected since more that 30 seconds
contact1.last_status_time = time.localtime()
contact1.contact_nickname = contact_nickname
if gajim.jid_is_transport(jid):
# It must be an agent
if ji in jid_list:
@ -651,7 +660,7 @@ class Interface:
# remove in 2007
# It's maybe a GC_NOTIFY (specialy for MSN gc)
self.handle_event_gc_notify(account, (jid, array[1], status_message,
array[3], None, None, None, None, None, None, None))
array[3], None, None, None, None, None, None, None, None))
def handle_event_msg(self, account, array):
@ -677,6 +686,10 @@ class Interface:
jid = jid.replace('@', '')
groupchat_control = self.msg_win_mgr.get_control(jid, account)
if not groupchat_control and \
gajim.interface.minimized_controls.has_key(account) and \
jid in gajim.interface.minimized_controls[account]:
groupchat_control = gajim.interface.minimized_controls[account][jid]
pm = False
if groupchat_control and groupchat_control.type_id == \
message_control.TYPE_GC:
@ -801,8 +814,7 @@ class Interface:
show = 'offline'
gc_c = gajim.contacts.create_gc_contact(room_jid = jid,
name = nick, show = show)
c = gajim.contacts.contact_from_gc_contact(gc_c)
self.roster.new_chat(c, account, private_chat = True)
self.roster.new_private_chat(gc_c, account)
ctrl = self.msg_win_mgr.get_control(full_jid_with_resource, account)
ctrl.print_conversation('Error %s: %s' % (array[1], array[2]),
'status')
@ -936,6 +948,17 @@ class Interface:
except AttributeError:
return
def handle_event_new_acc_connected(self, account, array):
#('NEW_ACC_CONNECTED', account, (infos, is_form))
if self.instances.has_key('account_creation_wizard'):
self.instances['account_creation_wizard'].new_acc_connected(array[0],
array[1])
def handle_event_new_acc_not_connected(self, account, array):
#('NEW_ACC_NOT_CONNECTED', account, (reason))
if self.instances.has_key('account_creation_wizard'):
self.instances['account_creation_wizard'].new_acc_not_connected(array)
def handle_event_acc_ok(self, account, array):
#('ACC_OK', account, (config))
if self.instances.has_key('account_creation_wizard'):
@ -954,10 +977,10 @@ class Interface:
def handle_event_myvcard(self, account, array):
nick = ''
if array.has_key('NICKNAME'):
nick = array['NICKNAME']
if nick:
gajim.nicks[account] = nick
if array.has_key('NICKNAME') and array['NICKNAME']:
gajim.nicks[account] = array['NICKNAME']
elif array.has_key('FN') and array['FN']:
gajim.nicks[account] = array['FN']
if self.instances[account].has_key('profile'):
win = self.instances[account]['profile']
win.set_values(array)
@ -1040,7 +1063,7 @@ class Interface:
def handle_event_gc_notify(self, account, array):
#'GC_NOTIFY' (account, (room_jid, show, status, nick,
# role, affiliation, jid, reason, actor, statusCode, newNick))
# role, affiliation, jid, reason, actor, statusCode, newNick, avatar_sha))
nick = array[3]
if not nick:
return
@ -1052,19 +1075,26 @@ class Interface:
# Get the window and control for the updated status, this may be a
# PrivateChatControl
control = self.msg_win_mgr.get_control(room_jid, account)
if not control and \
self.minimized_controls.has_key(account) and \
room_jid in self.minimized_controls[account]:
control = self.minimized_controls[account][room_jid]
if control and control.type_id != message_control.TYPE_GC:
return
if control:
control.chg_contact_status(nick, show, status, array[4], array[5],
array[6], array[7], array[8], array[9], array[10])
array[6], array[7], array[8], array[9], array[10], array[11])
if control and not control.parent_win:
gajim.interface.roster.draw_contact(room_jid, account)
ctrl = self.msg_win_mgr.get_control(fjid, account)
# print status in chat window and update status/GPG image
if self.msg_win_mgr.has_window(fjid, account):
ctrl = self.msg_win_mgr.get_control(fjid, account)
if ctrl:
contact = ctrl.contact
contact.show = show
contact.status = status
ctrl.update_ui()
uf_show = helpers.get_uf_show(show)
if status:
ctrl.print_conversation(_('%s is now %s (%s)') % (nick, uf_show,
@ -1073,6 +1103,7 @@ class Interface:
ctrl.print_conversation(_('%s is now %s') % (nick, uf_show),
'status')
ctrl.parent_win.redraw_tab(ctrl)
ctrl.update_ui()
if self.remote_ctrl:
self.remote_ctrl.raise_signal('GCPresence', (account, array))
@ -1080,10 +1111,17 @@ class Interface:
# ('GC_MSG', account, (jid, msg, time, has_timestamp, htmlmsg))
jids = array[0].split('/', 1)
room_jid = jids[0]
gc_control = self.msg_win_mgr.get_control(room_jid, account)
if not gc_control and \
self.minimized_controls.has_key(account) and \
room_jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][room_jid]
if not gc_control:
return
xhtml = array[4]
if gajim.config.get('ignore_incoming_xhtml'):
xhtml = None
if len(jids) == 1:
@ -1092,7 +1130,14 @@ class Interface:
else:
# message from someone
nick = jids[1]
gc_control.on_message(nick, array[1], array[2], array[3], xhtml)
contact = gajim.contacts.\
get_contact_with_highest_priority(account, room_jid)
if contact:
gajim.interface.roster.draw_contact(room_jid, account)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('GCMessage', (account, array))
@ -1100,7 +1145,20 @@ class Interface:
#('GC_SUBJECT', account, (jid, subject, body, has_timestamp))
jids = array[0].split('/', 1)
jid = jids[0]
gc_control = self.msg_win_mgr.get_control(jid, account)
if not gc_control and \
self.minimized_controls.has_key(account) and \
jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][jid]
contact = gajim.contacts.\
get_contact_with_highest_priority(account, jid)
if contact:
contact.status = array[1]
gajim.interface.roster.draw_contact(jid, account)
if not gc_control:
return
gc_control.set_subject(array[1])
@ -1243,7 +1301,8 @@ class Interface:
for bm in bms:
if bm['autojoin'] in ('1', 'true'):
self.roster.join_gc_room(account, bm['jid'], bm['nick'],
bm['password'])
bm['password'],
minimize = gajim.config.get('minimize_autojoined_rooms'))
def handle_event_file_send_error(self, account, array):
jid = array[0]
@ -1620,6 +1679,25 @@ class Interface:
if self.instances[account].has_key('privacy_list_%s' % name):
self.instances[account]['privacy_list_%s' % name].\
privacy_list_received(rules)
if name == 'block':
gajim.connections[account].blocked_contacts = []
gajim.connections[account].blocked_groups = []
gajim.connections[account].blocked_list = []
for rule in rules:
if rule['type'] == 'jid' and rule['action'] == 'deny':
gajim.connections[account].blocked_contacts.append(rule['value'])
if rule['type'] == 'group' and rule['action'] == 'deny':
gajim.connections[account].blocked_groups.append(rule['value'])
gajim.connections[account].blocked_list.append(rule)
#elif rule['type'] == "group" and action == "deny":
# text_item = _('%s group "%s"') % _(rule['action']), rule['value']
# self.store.append([text_item])
# self.global_rules.append(rule)
#else:
# self.global_rules_to_append.append(rule)
if self.instances[account].has_key('blocked_contacts'):
self.instances[account]['blocked_contacts'].\
privacy_list_received(rules)
def handle_event_privacy_lists_active_default(self, account, data):
if not data:
@ -1651,6 +1729,53 @@ class Interface:
else:
gajim.connections[account].change_status('offline','')
def handle_event_ping_sent(self, account, contact):
ctrl = self.msg_win_mgr.get_control(contact.get_full_jid(), account)
if ctrl == None:
ctrl = self.msg_win_mgr.get_control(contact.jid, account)
ctrl.print_conversation(_('Ping?'), 'status')
def handle_event_ping_reply(self, account, data):
contact = data[0]
seconds = data[1]
ctrl = self.msg_win_mgr.get_control(contact.get_full_jid(), account)
if ctrl == None:
ctrl = self.msg_win_mgr.get_control(contact.jid, account)
ctrl.print_conversation(_('Pong! (%s s.)') % seconds, 'status')
def handle_event_ping_error(self, account, contact):
ctrl = self.msg_win_mgr.get_control(contact.get_full_jid(), account)
if ctrl == None:
ctrl = self.msg_win_mgr.get_control(contact.jid, account)
ctrl.print_conversation(_('Error.'), 'status')
def handle_event_search_form(self, account, data):
# ('SEARCH_FORM', account, (jid, dataform, is_dataform))
if not self.instances[account]['search'].has_key(data[0]):
return
self.instances[account]['search'][data[0]].on_form_arrived(data[1],
data[2])
def handle_event_search_result(self, account, data):
# ('SEARCH_RESULT', account, (jid, dataform, is_dataform))
if not self.instances[account]['search'].has_key(data[0]):
return
self.instances[account]['search'][data[0]].on_result_arrived(data[1],
data[2])
def handle_event_resource_conflict(self, account, data):
# ('RESOURCE_CONFLICT', account, ())
# First we go offline, but we don't overwrite status message
self.roster.send_status(account, 'offline',
gajim.connections[account].status)
def on_ok(new_resource):
gajim.config.set_per('accounts', account, 'resource', new_resource)
self.roster.send_status(account, gajim.connections[account].old_show,
gajim.connections[account].status)
dlg = dialogs.InputDialog(_('Resource Conflict'),
_('You are already connected to this account with the same resource. Please type a new one'), input_str = gajim.connections[account].server_resource,
is_modal = False, ok_handler = on_ok)
def read_sleepy(self):
'''Check idle status and change that status if needed'''
if not self.sleeper.poll():
@ -1753,20 +1878,14 @@ class Interface:
# \S*[^\s\W] --> in the matching string don't match ? or ) etc.. if at the end
# so http://be) will match http://be and http://be)be) will match http://be)be
prefixes = (r'http://', r'https://', r'gopher://', r'news://', r'ftp://',
r'ed2k://', r'irc://', r'magnet:', r'sip:', r'www\.', r'ftp\.')
prefixes = '|'.join((r'http://', r'https://', r'gopher://', r'news://',
r'ftp://', r'ed2k://', r'irc://', r'magnet:', r'sip:', r'www\.',
r'ftp\.'))
# NOTE: it's ok to catch www.gr such stuff exist!
#FIXME: recognize xmpp: and treat it specially
prefix_pattern = ''
for prefix in prefixes:
prefix_pattern += prefix + '|'
prefix_pattern = prefix_pattern[:-1] # remove last |
prefix_pattern = '(' + prefix_pattern + ')'
links = r'\b' + prefix_pattern + r'\S*[\w\/\=]|'
links = r'\b(%s)\S*[\w\/\=]|' % prefixes
#2nd one: at_least_one_char@at_least_one_char.at_least_one_char
mail = r'\bmailto:\S*[^\s\W]|' r'\b\S+@\S+\.\S*[^\s\W]'
@ -1776,7 +1895,13 @@ class Interface:
r'(?<!\w|\<)' r'/[^\s/]' r'([^/]*[^\s/])?' r'/(?!\w)|'\
r'(?<!\w)' r'_[^\s_]' r'([^_]*[^\s_])?' r'_(?!\w)'
latex = r'|\$\$.*\$\$'
basic_pattern = links + mail
if gajim.config.get('use_latex'):
basic_pattern += latex
if gajim.config.get('ascii_formatting'):
basic_pattern += formatting
self.basic_pattern_re = re.compile(basic_pattern, re.IGNORECASE)
@ -1933,6 +2058,8 @@ class Interface:
'AGENT_INFO_ITEMS': self.handle_event_agent_info_items,
'AGENT_INFO_INFO': self.handle_event_agent_info_info,
'QUIT': self.handle_event_quit,
'NEW_ACC_CONNECTED': self.handle_event_new_acc_connected,
'NEW_ACC_NOT_CONNECTED': self.handle_event_new_acc_not_connected,
'ACC_OK': self.handle_event_acc_ok,
'ACC_NOT_OK': self.handle_event_acc_not_ok,
'MYVCARD': self.handle_event_myvcard,
@ -1969,6 +2096,12 @@ class Interface:
self.handle_event_privacy_lists_active_default,
'PRIVACY_LIST_REMOVED': self.handle_event_privacy_list_removed,
'ZC_NAME_CONFLICT': self.handle_event_zc_name_conflict,
'PING_SENT': self.handle_event_ping_sent,
'PING_REPLY': self.handle_event_ping_reply,
'PING_ERROR': self.handle_event_ping_error,
'SEARCH_FORM': self.handle_event_search_form,
'SEARCH_RESULT': self.handle_event_search_result,
'RESOURCE_CONFLICT': self.handle_event_resource_conflict,
}
gajim.handlers = self.handlers
@ -1992,7 +2125,7 @@ class Interface:
w = None
resource = gajim.get_resource_from_jid(fjid)
jid = gajim.get_jid_without_resource(fjid)
if type_ in ('printed_gc_msg', 'gc_msg'):
if type_ in ('printed_gc_msg', 'printed_marked_gc_msg', 'gc_msg'):
w = self.msg_win_mgr.get_window(jid, account)
elif type_ in ('printed_chat', 'chat', ''):
# '' is for log in/out notifications
@ -2072,6 +2205,7 @@ class Interface:
self.emoticons_menu = None
# handler when an emoticon is clicked in emoticons_menu
self.emoticon_menuitem_clicked = None
self.minimized_controls = {}
self.default_colors = {
'inmsgcolor': gajim.config.get('inmsgcolor'),
'outmsgcolor': gajim.config.get('outmsgcolor'),
@ -2088,7 +2222,17 @@ class Interface:
if os.name != 'nt' and gajim.config.get('check_if_gajim_is_default'):
gtkgui_helpers.possibly_set_gajim_as_xmpp_handler()
#add default status messages if there is not in the config file
# Is gnome configured to activate row on single click ?
try:
import gconf
client = gconf.client_get_default()
click_policy = client.get_string(
'/apps/nautilus/preferences/click_policy')
if click_policy == 'single':
gajim.single_click = True
except:
pass
# add default status messages if there is not in the config file
if len(gajim.config.get_per('statusmsg')) == 0:
for msg in gajim.config.statusmsg_default:
gajim.config.add_per('statusmsg', msg)
@ -2152,7 +2296,8 @@ class Interface:
self.instances = {'logs': {}}
for a in gajim.connections:
self.instances[a] = {'infos': {}, 'disco': {}, 'gc_config': {}}
self.instances[a] = {'infos': {}, 'disco': {}, 'gc_config': {},
'search': {}}
gajim.contacts.add_account(a)
gajim.groups[a] = {}
gajim.gc_connected[a] = {}
@ -2233,13 +2378,7 @@ class Interface:
import gtkspell
spell = gtkspell.Spell(tv, lang)
except:
dialogs.ErrorDialog(
_('Dictionary for lang %s not available') % lang,
_('You have to install %s dictionary to use spellchecking, or '
'choose another language by setting the speller_language option.'
) % lang)
gajim.config.set('use_speller', False)
dialogs.AspellDictError(lang)
self.last_ftwindow_update = 0
gobject.timeout_add(100, self.autoconnect)

View File

@ -97,10 +97,11 @@ def tree_cell_data_func(column, renderer, model, iter, tv=None):
class PrivateChatControl(ChatControl):
TYPE_ID = message_control.TYPE_PM
def __init__(self, parent_win, contact, acct):
def __init__(self, parent_win, gc_contact, contact, acct):
room_jid = contact.jid.split('/')[0]
room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid, acct)
self.room_name = room_ctrl.name
self.gc_contact = gc_contact
ChatControl.__init__(self, parent_win, contact, acct)
self.TYPE_ID = 'pm'
@ -195,6 +196,9 @@ class GroupchatControl(ChatControlBase):
# muc attention flag (when we are mentioned in a muc)
# if True, the room has mentioned us
self.attention_flag = False
# sorted list of nicks who mentioned us (last at the end)
self.attention_list = []
self.room_creation = int(time.time()) # Use int to reduce mem usage
self.nick_hits = []
self.cmd_hits = []
@ -213,33 +217,39 @@ class GroupchatControl(ChatControlBase):
self._on_bookmark_room_menuitem_activate)
self.handlers[id] = widget
widget = xm.get_widget('change_nick_menuitem')
id = widget.connect('activate', self._on_change_nick_menuitem_activate)
self.handlers[id] = widget
self.change_nick_menuitem = xm.get_widget('change_nick_menuitem')
id = self.change_nick_menuitem.connect('activate',
self._on_change_nick_menuitem_activate)
self.handlers[id] = self.change_nick_menuitem
widget = xm.get_widget('configure_room_menuitem')
id = widget.connect('activate',
self.configure_room_menuitem = xm.get_widget('configure_room_menuitem')
id = self.configure_room_menuitem.connect('activate',
self._on_configure_room_menuitem_activate)
self.handlers[id] = widget
self.handlers[id] = self.configure_room_menuitem
widget = xm.get_widget('destroy_room_menuitem')
id = widget.connect('activate',
self.destroy_room_menuitem = xm.get_widget('destroy_room_menuitem')
id = self.destroy_room_menuitem.connect('activate',
self._on_destroy_room_menuitem_activate)
self.handlers[id] = widget
self.handlers[id] = self.destroy_room_menuitem
widget = xm.get_widget('change_subject_menuitem')
id = widget.connect('activate',
self.change_subject_menuitem = xm.get_widget('change_subject_menuitem')
id = self.change_subject_menuitem.connect('activate',
self._on_change_subject_menuitem_activate)
self.handlers[id] = widget
self.handlers[id] = self.change_subject_menuitem
widget = xm.get_widget('compact_view_menuitem')
id = widget.connect('activate', self._on_compact_view_menuitem_activate)
self.handlers[id] = widget
self.compact_view_menuitem = xm.get_widget('compact_view_menuitem')
id = self.compact_view_menuitem.connect('activate',
self._on_compact_view_menuitem_activate)
self.handlers[id] = self.compact_view_menuitem
widget = xm.get_widget('history_menuitem')
id = widget.connect('activate', self._on_history_menuitem_activate)
self.handlers[id] = widget
widget = xm.get_widget('minimize_menuitem')
id = widget.connect('activate', self._on_minimize_menuitem_activate)
self.handlers[id] = widget
self.gc_popup_menu = xm.get_widget('gc_control_popup_menu')
self.name_label = self.xml.get_widget('banner_name_label')
@ -471,28 +481,32 @@ class GroupchatControl(ChatControlBase):
self.subject_tooltip.set_tip(self.event_box, self.subject)
self.name_label.set_markup(text)
def prepare_context_menu(self):
'''sets compact view menuitem active state
sets sensitivity state for configure_room'''
menu = self.gc_popup_menu
childs = menu.get_children()
# Check compact view menuitem
childs[6].set_active(self.hide_chat_buttons_current)
self.compact_view_menuitem.set_active(self.hide_chat_buttons_current)
if gajim.gc_connected[self.account][self.room_jid]:
c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
self.nick)
if c.affiliation not in ('owner', 'admin'):
childs[1].set_sensitive(False)
self.configure_room_menuitem.set_sensitive(False)
else:
self.configure_room_menuitem.set_sensitive(True)
if c.affiliation != 'owner':
childs[2].set_sensitive(False)
self.destroy_room_menuitem.set_sensitive(False)
else:
self.destroy_room_menuitem.set_sensitive(True)
self.change_subject_menuitem.set_sensitive(True)
self.change_nick_menuitem.set_sensitive(True)
else:
# We are not connected to this groupchat, disable unusable menuitems
childs[1].set_sensitive(False)
childs[2].set_sensitive(False)
childs[3].set_sensitive(False)
childs[4].set_sensitive(False)
return menu
self.configure_room_menuitem.set_sensitive(False)
self.destroy_room_menuitem.set_sensitive(False)
self.change_subject_menuitem.set_sensitive(False)
self.change_nick_menuitem.set_sensitive(False)
return self.gc_popup_menu
def on_message(self, nick, msg, tim, has_timestamp = False, xhtml = None):
if not nick:
@ -533,14 +547,16 @@ class GroupchatControl(ChatControlBase):
self.room_jid, icon_name = 'message')
image = state_images['message']
model[iter][C_IMG] = image
self.parent_win.show_title()
self.parent_win.redraw_tab(self)
if self.parent_win:
self.parent_win.show_title()
self.parent_win.redraw_tab(self)
else:
self._start_private_message(nick)
# Scroll to line
self.list_treeview.expand_row(path[0:1], False)
self.list_treeview.scroll_to_cell(path)
self.list_treeview.set_cursor(path)
gajim.interface.roster.draw_contact(self.room_jid, self.account)
def get_contact_iter(self, nick):
model = self.list_treeview.get_model()
@ -584,7 +600,7 @@ class GroupchatControl(ChatControlBase):
small_attr = []
ChatControlBase.print_conversation_line(self, text, kind, contact, tim,
small_attr, small_attr + ['restored_message'],
small_attr + ['restored_message'], xhtml = xhtml)
small_attr + ['restored_message'], count_as_new = False, xhtml = xhtml)
def print_conversation(self, text, contact = '', tim = None, xhtml = None):
'''Print a line in the conversation:
@ -604,7 +620,8 @@ class GroupchatControl(ChatControlBase):
else:
kind = 'incoming'
# muc-specific chatstate
self.parent_win.redraw_tab(self, 'newmsg')
if self.parent_win:
self.parent_win.redraw_tab(self, 'newmsg')
else:
kind = 'status'
@ -626,9 +643,17 @@ class GroupchatControl(ChatControlBase):
str(self.gc_count_nicknames_colors))
if highlight:
# muc-specific chatstate
self.parent_win.redraw_tab(self, 'attention')
if self.parent_win:
self.parent_win.redraw_tab(self, 'attention')
other_tags_for_name.append('bold')
other_tags_for_text.append('marked')
if contact in self.attention_list:
self.attention_list.remove(contact)
elif len(self.attention_list) > 6:
self.attention_list.pop(0) # remove older
self.attention_list.append(contact)
if sound == 'received':
helpers.play_sound('muc_message_received')
elif sound == 'highlight':
@ -637,14 +662,18 @@ class GroupchatControl(ChatControlBase):
other_tags_for_text.append('gc_nickname_color_' + \
str(self.gc_custom_colors[contact]))
self.check_and_possibly_add_focus_out_line()
if self.parent_win:
self.check_and_possibly_add_focus_out_line()
ChatControlBase.print_conversation_line(self, text, kind, contact, tim,
other_tags_for_name, [], other_tags_for_text, xhtml = xhtml)
def get_nb_unread(self):
type_events = ['printed_marked_gc_msg']
if gajim.config.get('notify_on_all_muc_messages'):
type_events.append('printed_gc_msg')
nb = len(gajim.events.get_events(self.account, self.room_jid,
['printed_gc_msg']))
type_events))
nb += self.get_nb_unread_pm()
return nb
@ -827,7 +856,7 @@ class GroupchatControl(ChatControlBase):
model[iter][C_AVATAR] = scaled_pixbuf
def chg_contact_status(self, nick, show, status, role, affiliation, jid,
reason, actor, statusCode, new_nick):
reason, actor, statusCode, new_nick, avatar_sha, tim = None):
'''When an occupant changes his or her status'''
if show == 'invisible':
return
@ -836,7 +865,7 @@ class GroupchatControl(ChatControlBase):
role = 'visitor'
if not affiliation:
affiliation = 'none'
fake_jid = self.room_jid + '/' + nick
newly_created = False
if show in ('offline', 'error'):
if statusCode == '307':
@ -849,7 +878,7 @@ class GroupchatControl(ChatControlBase):
'nick': nick,
'who': actor,
'reason': reason }
self.print_conversation(s, 'info')
self.print_conversation(s, 'info', tim = tim)
elif statusCode == '301':
if actor is None: # do not print 'banned by None'
s = _('%(nick)s has been banned: %(reason)s') % {
@ -860,7 +889,7 @@ class GroupchatControl(ChatControlBase):
'nick': nick,
'who': actor,
'reason': reason }
self.print_conversation(s, 'info')
self.print_conversation(s, 'info', tim = tim)
elif statusCode == '303': # Someone changed his or her nick
if nick == self.nick: # We changed our nick
self.nick = new_nick
@ -895,12 +924,11 @@ class GroupchatControl(ChatControlBase):
# Windows require this
os.remove(files[old_file])
os.rename(old_file, files[old_file])
self.print_conversation(s, 'info')
self.print_conversation(s, 'info', tim)
elif statusCode == 'destroyed': # Room has been destroyed
self.print_conversation(reason, 'info')
self.print_conversation(reason, 'info', tim)
if len(gajim.events.get_events(self.account,
self.room_jid + '/' + nick)) == 0:
if len(gajim.events.get_events(self.account, fake_jid)) == 0:
self.remove_contact(nick)
else:
c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
@ -908,7 +936,12 @@ class GroupchatControl(ChatControlBase):
c.status = status
if nick == self.nick and statusCode != '303': # We became offline
self.got_disconnected()
self.parent_win.redraw_tab(self)
contact = gajim.contacts.\
get_contact_with_highest_priority(self.account, self.room_jid)
if contact:
gajim.interface.roster.draw_contact(self.room_jid, self.account)
if self.parent_win:
self.parent_win.redraw_tab(self)
else:
iter = self.get_contact_iter(nick)
if not iter:
@ -918,23 +951,51 @@ class GroupchatControl(ChatControlBase):
if statusCode == '201': # We just created the room
gajim.connections[self.account].request_gc_config(self.room_jid)
else:
gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
nick)
# Re-get vcard if avatar has changed
# We do that here because we may request it to the real JID if we
# knows it. connections.py doesn't know it.
con = gajim.connections[self.account]
if gc_c.jid:
real_jid = gc_c.jid
if gc_c.resource:
real_jid += '/' + gc_c.resource
else:
real_jid = fake_jid
if con.vcard_shas.has_key(fake_jid):
if avatar_sha != con.vcard_shas[fake_jid]:
con.request_vcard(real_jid, fake_jid)
else:
cached_vcard = con.get_cached_vcard(fake_jid, True)
if cached_vcard and cached_vcard.has_key('PHOTO') and \
cached_vcard['PHOTO'].has_key('SHA'):
cached_sha = cached_vcard['PHOTO']['SHA']
else:
cached_sha = ''
if cached_sha != avatar_sha:
# avatar has been updated
# sha in mem will be updated later
con.request_vcard(real_jid, fake_jid)
else:
# save sha in mem NOW
con.vcard_shas[fake_jid] = avatar_sha
actual_role = self.get_role(nick)
if role != actual_role:
self.remove_contact(nick)
self.add_contact_to_roster(nick, show, role,
affiliation, status, jid)
else:
c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
nick)
if c.show == show and c.status == status and \
c.affiliation == affiliation: #no change
if gc_c.show == show and gc_c.status == status and \
gc_c.affiliation == affiliation: # no change
return
c.show = show
c.affiliation = affiliation
c.status = status
gc_c.show = show
gc_c.affiliation = affiliation
gc_c.status = status
self.draw_contact(nick)
self.parent_win.redraw_tab(self)
if self.parent_win:
self.parent_win.redraw_tab(self)
if (time.time() - self.room_creation) > 30 and \
nick != self.nick and statusCode != '303':
st = ''
@ -963,7 +1024,7 @@ class GroupchatControl(ChatControlBase):
if st:
if status:
st += ' (' + status + ')'
self.print_conversation(st)
self.print_conversation(st, tim = tim)
def add_contact_to_roster(self, nick, show, role, affiliation, status,
jid = ''):
@ -998,10 +1059,16 @@ class GroupchatControl(ChatControlBase):
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
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(fjid, True)
fake_jid = self.room_jid + '/' + nick
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(fake_jid, True)
if pixbuf == 'ask':
gajim.connections[self.account].request_vcard(fjid, True)
if j:
fjid = j
if resource:
fjid += '/' + resource
gajim.connections[self.account].request_vcard(fjid, fake_jid)
else:
gajim.connections[self.account].request_vcard(fake_jid, fake_jid)
if nick == self.nick: # we became online
self.got_connected()
self.list_treeview.expand_row((model.get_path(role_iter)), False)
@ -1341,7 +1408,6 @@ class GroupchatControl(ChatControlBase):
nick = instance.input_entry.get_text().decode('utf-8')
nick = helpers.parse_resource(nick)
gajim.connections[self.account].join_gc(nick, self.room_jid, None)
self.nick = nick
instance = dialogs.InputDialog(title, prompt, proposed_nick,
is_modal = False, ok_handler = on_ok)
@ -1466,7 +1532,7 @@ class GroupchatControl(ChatControlBase):
try:
jid = helpers.parse_jid(jid)
except:
ErrorDialog(_('Invalid group chat Jabber ID'),
dialogs.ErrorDialog(_('Invalid group chat Jabber ID'),
_('The group chat Jabber ID has not allowed characters.'))
return
else:
@ -1575,6 +1641,13 @@ class GroupchatControl(ChatControlBase):
self.nick_hits = [] # clear the hit list
list_nick = gajim.contacts.get_nick_list(self.account,
self.room_jid)
if begin == '':
# empty message, show lasts nicks that highlighted us first
for nick in self.attention_list:
if nick in list_nick:
list_nick.remove(nick)
list_nick.insert(0, nick)
list_nick.remove(self.nick) # Skip self
for nick in list_nick:
if nick.lower().startswith(begin.lower()):
@ -1741,18 +1814,18 @@ class GroupchatControl(ChatControlBase):
def _start_private_message(self, nick):
gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
c = gajim.contacts.contact_from_gc_contact(gc_c)
nick_jid = c.jid
nick_jid = gc_c.get_full_jid()
win = gajim.interface.msg_win_mgr.get_window(nick_jid, self.account)
if not win:
gajim.interface.roster.new_chat(c, self.account, private_chat = True)
gajim.interface.roster.new_private_chat(gc_c, self.account)
win = gajim.interface.msg_win_mgr.get_window(nick_jid, self.account)
win.set_active_tab(nick_jid, self.account)
win.window.present()
def on_list_treeview_row_activated(self, widget, path, col = 0):
'''When an iter is double clicked: open the chat window'''
def on_row_activated(self, widget, path):
'''When an iter is activated (dubblick or single click if gnome is set
this way'''
model = widget.get_model()
if len(path) == 1: # It's a group
if (widget.row_expanded(path)):
@ -1762,6 +1835,11 @@ class GroupchatControl(ChatControlBase):
else: # We want to send a private message
nick = model[path][C_NICK].decode('utf-8')
self._start_private_message(nick)
def on_list_treeview_row_activated(self, widget, path, col = 0):
'''When an iter is double clicked: open the chat window'''
if not gajim.single_click:
self.on_row_activated(widget, path)
def on_list_treeview_button_press_event(self, widget, event):
'''popup user's group's or agent menu'''
@ -1802,26 +1880,30 @@ class GroupchatControl(ChatControlBase):
widget.get_selection().unselect_all()
return
model = widget.get_model()
iter = model.get_iter(path)
nick = model[iter][C_NICK].decode('utf-8')
if not nick in gajim.contacts.get_nick_list(self.account,
self.room_jid):
#it's a group
col = widget.get_column(0)
avatar_cell = col.get_cell_renderers()[0]
(pos, avatar_size) = col.cell_get_position(avatar_cell)
status_cell = col.get_cell_renderers()[1]
(pos, status_size) = col.cell_get_position(status_cell)
if x > avatar_size and x < avatar_size + status_size:
if (widget.row_expanded(path)):
widget.collapse_row(path)
else:
widget.expand_row(path, False)
elif event.state & gtk.gdk.SHIFT_MASK:
self.append_nick_in_msg_textview(self.msg_textview, nick)
self.msg_textview.grab_focus()
if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK:
self.on_row_activated(widget, path)
return True
else:
model = widget.get_model()
iter = model.get_iter(path)
nick = model[iter][C_NICK].decode('utf-8')
if not nick in gajim.contacts.get_nick_list(self.account,
self.room_jid):
# it's a group
col = widget.get_column(0)
avatar_cell = col.get_cell_renderers()[0]
(pos, avatar_size) = col.cell_get_position(avatar_cell)
status_cell = col.get_cell_renderers()[1]
(pos, status_size) = col.cell_get_position(status_cell)
if x > avatar_size and x < avatar_size + status_size:
if (widget.row_expanded(path)):
widget.collapse_row(path)
else:
widget.expand_row(path, False)
elif event.state & gtk.gdk.SHIFT_MASK:
self.append_nick_in_msg_textview(self.msg_textview, nick)
self.msg_textview.grab_focus()
return True
def append_nick_in_msg_textview(self, widget, nick):
message_buffer = self.msg_textview.get_buffer()

View File

@ -528,7 +528,7 @@ def get_scaled_pixbuf(pixbuf, kind):
scaled_buf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_HYPER)
return scaled_buf
def get_avatar_pixbuf_from_cache(fjid, is_fake_jid = False):
def get_avatar_pixbuf_from_cache(fjid, is_fake_jid = False, use_local = True):
'''checks if jid has cached avatar and if that avatar is valid image
(can be shown)
returns None if there is no image in vcard
@ -545,8 +545,21 @@ def get_avatar_pixbuf_from_cache(fjid, is_fake_jid = False):
if is_fake_jid:
puny_nick = helpers.sanitize_filename(nick)
path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
local_avatar_basepath = os.path.join(gajim.AVATAR_PATH, puny_jid,
puny_nick) + '_local'
else:
path = os.path.join(gajim.VCARD_PATH, puny_jid)
local_avatar_basepath = os.path.join(gajim.AVATAR_PATH, puny_jid) + \
'_local'
if use_local:
for extension in ('.png', '.jpeg'):
local_avatar_path = local_avatar_basepath + extension
if os.path.isfile(local_avatar_path):
avatar_file = open(local_avatar_path, 'rb')
avatar_data = avatar_file.read()
avatar_file.close()
return get_pixbuf_from_data(avatar_data)
if not os.path.isfile(path):
return 'ask'
@ -591,6 +604,10 @@ def get_path_to_generic_or_avatar(generic, jid = None, suffix = None):
if jid:
puny_jid = helpers.sanitize_filename(jid)
path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + suffix
filepath, extension = os.path.splitext(path_to_file)
path_to_local_file = filepath + '_local' + extension
if os.path.exists(path_to_local_file):
return path_to_local_file
if os.path.exists(path_to_file):
return path_to_file
return os.path.abspath(generic)
@ -773,7 +790,7 @@ default_name = ''):
is_fake = False
if account and gajim.contacts.is_pm_from_jid(account, jid):
is_fake = True
pixbuf = get_avatar_pixbuf_from_cache(jid, is_fake)
pixbuf = get_avatar_pixbuf_from_cache(jid, is_fake, False)
ext = file_path.split('.')[-1]
type_ = ''
if not ext:

View File

@ -126,6 +126,14 @@ class HistoryWindow:
self.calendar.select_month(gtk_month, y)
self.calendar.select_day(d)
self.add_lines_for_date(y, m, d)
log = True
if self.jid in gajim.config.get_per('accounts', self.account,
'no_log_for').split(' '):
log = False
checkbutton = xml.get_widget('log_history_checkbutton')
checkbutton.set_active(log)
checkbutton.connect('toggled', self.on_log_history_checkbutton_toggled)
self.window.show_all()
@ -391,3 +399,20 @@ class HistoryWindow:
match_start_mark = self.history_buffer.create_mark('match_start',
match_start_iter, True)
self.history_textview.tv.scroll_to_mark(match_start_mark, 0, True)
def on_log_history_checkbutton_toggled(self, widget):
# log conversation history?
oldlog = True
no_log_for = gajim.config.get_per('accounts', self.account,
'no_log_for').split()
if self.jid in no_log_for:
oldlog = False
log = widget.get_active()
if not log and not self.jid in no_log_for:
no_log_for.append(self.jid)
if log and self.jid in no_log_for:
no_log_for.remove(self.jid)
if oldlog != log:
gajim.config.set_per('accounts', self.account, 'no_log_for',
' '.join(no_log_for))

View File

@ -40,9 +40,9 @@ import time
import urllib2
import operator
if __name__ == '__main__':
from common import i18n
from common import gajim
#from common import i18n
import tooltips
@ -56,13 +56,13 @@ allwhitespace_rx = re.compile('^\\s*$')
display_resolution = 0.3514598*(gtk.gdk.screen_height() /
float(gtk.gdk.screen_height_mm()))
#embryo of CSS classes
# embryo of CSS classes
classes = {
#'system-message':';display: none',
'problematic':';color: red',
}
#styles for elemens
# styles for elements
element_styles = {
'u' : ';text-decoration: underline',
'em' : ';font-style: oblique',
@ -83,9 +83,6 @@ element_styles['tt'] = element_styles['kbd']
element_styles['i'] = element_styles['em']
element_styles['b'] = element_styles['strong']
class_styles = {
}
'''
==========
JEP-0071
@ -182,40 +179,43 @@ for name in BLOCK_HEAD:
def build_patterns(view, config, interface):
#extra, rst does not mark _underline_ or /it/ up
#actually <b>, <i> or <u> are not in the JEP-0071, but are seen in the wild
# extra, rst does not mark _underline_ or /it/ up
# actually <b>, <i> or <u> are not in the JEP-0071, but are seen in the wild
basic_pattern = r'(?<!\w|\<|/|:)' r'/[^\s/]' r'([^/]*[^\s/])?' r'/(?!\w|/|:)|'\
r'(?<!\w)' r'_[^\s_]' r'([^_]*[^\s_])?' r'_(?!\w)'
view.basic_pattern_re = re.compile(basic_pattern)
#TODO: emoticons
# emoticons
emoticons_pattern = ''
if config.get('emoticons_theme'):
# When an emoticon is bordered by an alpha-numeric character it is NOT
# expanded. e.g., foo:) NO, foo :) YES, (brb) NO, (:)) YES, etc.
# We still allow multiple emoticons side-by-side like :P:P:P
# sort keys by length so :qwe emot is checked before :q
keys = interface.emoticons.keys()
keys.sort(interface.on_emoticon_sort)
emoticons_pattern_prematch = ''
emoticons_pattern_postmatch = ''
emoticon_length = 0
for emoticon in keys: # travel thru emoticons list
emoticon_escaped = re.escape(emoticon) # espace regexp metachars
emoticons_pattern += emoticon_escaped + '|'# | means or in regexp
if (emoticon_length != len(emoticon)):
# Build up expressions to match emoticons next to other emoticons
emoticons_pattern_prematch = emoticons_pattern_prematch[:-1] + ')|(?<='
emoticons_pattern_postmatch = emoticons_pattern_postmatch[:-1] + ')|(?='
emoticon_length = len(emoticon)
emoticons_pattern_prematch += emoticon_escaped + '|'
emoticons_pattern_postmatch += emoticon_escaped + '|'
# We match from our list of emoticons, but they must either have
# whitespace, or another emoticon next to it to match successfully
# [\w.] alphanumeric and dot (for not matching 8) in (2.8))
emoticons_pattern = '|' + \
try:
if config.get('emoticons_theme'):
# When an emoticon is bordered by an alpha-numeric character it is NOT
# expanded. e.g., foo:) NO, foo :) YES, (brb) NO, (:)) YES, etc.
# We still allow multiple emoticons side-by-side like :P:P:P
# sort keys by length so :qwe emot is checked before :q
keys = interface.emoticons.keys()
keys.sort(interface.on_emoticon_sort)
emoticons_pattern_prematch = ''
emoticons_pattern_postmatch = ''
emoticon_length = 0
for emoticon in keys: # travel thru emoticons list
emoticon_escaped = re.escape(emoticon) # espace regexp metachars
emoticons_pattern += emoticon_escaped + '|'# | means or in regexp
if (emoticon_length != len(emoticon)):
# Build up expressions to match emoticons next to other emoticons
emoticons_pattern_prematch = emoticons_pattern_prematch[:-1] + ')|(?<='
emoticons_pattern_postmatch = emoticons_pattern_postmatch[:-1] + ')|(?='
emoticon_length = len(emoticon)
emoticons_pattern_prematch += emoticon_escaped + '|'
emoticons_pattern_postmatch += emoticon_escaped + '|'
# We match from our list of emoticons, but they must either have
# whitespace, or another emoticon next to it to match successfully
# [\w.] alphanumeric and dot (for not matching 8) in (2.8))
emoticons_pattern = '|' + \
'(?:(?<![\w.]' + emoticons_pattern_prematch[:-1] + '))' + \
'(?:' + emoticons_pattern[:-1] + ')' + \
'(?:(?![\w.]' + emoticons_pattern_postmatch[:-1] + '))'
except:
pass
# because emoticons match later (in the string) they need to be after
# basic matches that may occur earlier
@ -230,10 +230,17 @@ def _parse_css_color(color):
return gtk.gdk.Color(r, g, b)
else:
return gtk.gdk.color_parse(color)
def style_iter(style):
return (map(lambda x:x.strip(),item.split(':', 1)) for item in style.split(';') if len(item.strip()))
class HtmlHandler(xml.sax.handler.ContentHandler):
"""A handler to display html to a gtk textview.
It keeps a stack of "style spans" (start/end element pairs)
and a stack of list counters, for nested lists.
"""
def __init__(self, textview, startiter):
xml.sax.handler.ContentHandler.__init__(self)
self.textbuf = textview.get_buffer()
@ -303,16 +310,20 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
frac, callback, args):
callback(allocation.width*frac, *args)
def _parse_length(self, value, font_relative, callback, *args):
def _parse_length(self, value, font_relative, block_relative, minl, maxl, callback, *args):
'''Parse/calc length, converting to pixels, calls callback(length, *args)
when the length is first computed or changes'''
if value.endswith('%'):
frac = float(value[:-1])/100
val = float(value[:-1])
sign = cmp(val,0)
# limits: 1% to 500%
val = sign*max(1,min(abs(val),500))
frac = val/100
if font_relative:
attrs = self._get_current_attributes()
font_size = attrs.font.get_size() / pango.SCALE
callback(frac*display_resolution*font_size, *args)
else:
elif block_relative:
# CSS says 'Percentage values: refer to width of the closest
# block-level ancestor'
# This is difficult/impossible to implement, so we use
@ -323,27 +334,42 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
self.textview.connect('size-allocate',
self.__parse_length_frac_size_allocate,
frac, callback, args)
else:
callback(frac, *args)
return
elif value.endswith('pt'): # points
callback(float(value[:-2])*display_resolution, *args)
val = float(value[:-2])
sign = cmp(val,0)
# validate length
val = sign*max(minl,min(abs(val*display_resolution),maxl))
if value.endswith('pt'): # points
callback(val*display_resolution, *args)
elif value.endswith('em'): # ems, the height of the element's font
elif value.endswith('em'): # ems, the width of the element's font
attrs = self._get_current_attributes()
font_size = attrs.font.get_size() / pango.SCALE
callback(float(value[:-2])*display_resolution*font_size, *args)
callback(val*display_resolution*font_size, *args)
elif value.endswith('ex'): # x-height, ~ the height of the letter 'x'
# FIXME: figure out how to calculate this correctly
# for now 'em' size is used as approximation
attrs = self._get_current_attributes()
font_size = attrs.font.get_size() / pango.SCALE
callback(float(value[:-2])*display_resolution*font_size, *args)
callback(val*display_resolution*font_size, *args)
elif value.endswith('px'): # pixels
callback(int(value[:-2]), *args)
callback(val, *args)
else:
warnings.warn('Unable to parse length value "%s"' % value)
try:
# TODO: isn't "no units" interpreted as pixels?
val = int(value)
sign = cmp(val,0)
# validate length
val = sign*max(minl,min(abs(val),maxl))
callback(val, *args)
except:
warnings.warn('Unable to parse length value "%s"' % value)
def __parse_font_size_cb(length, tag):
tag.set_property('size-points', length/display_resolution)
@ -352,7 +378,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
def _parse_style_display(self, tag, value):
if value == 'none':
tag.set_property('invisible','true')
#Fixme: display: block, inline
# FIXME: display: block, inline
def _parse_style_font_size(self, tag, value):
try:
@ -377,7 +403,8 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
if value == 'larger':
tag.set_property('scale', pango.SCALE_LARGE)
return
self._parse_length(value, True, self.__parse_font_size_cb, tag)
# font relative (5 ~ 4pt, 110 ~ 72pt)
self._parse_length(value, True, False, 5, 110, self.__parse_font_size_cb, tag)
def _parse_style_font_style(self, tag, value):
try:
@ -399,11 +426,13 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
#__frac_length_tag_cb = staticmethod(__frac_length_tag_cb)
def _parse_style_margin_left(self, tag, value):
self._parse_length(value, False, self.__frac_length_tag_cb,
# block relative
self._parse_length(value, False, True, 1, 1000, self.__frac_length_tag_cb,
tag, 'left-margin')
def _parse_style_margin_right(self, tag, value):
self._parse_length(value, False, self.__frac_length_tag_cb,
# block relative
self._parse_length(value, False, True, 1, 1000, self.__frac_length_tag_cb,
tag, 'right-margin')
def _parse_style_font_weight(self, tag, value):
@ -469,13 +498,32 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
tag.set_property('wrap_mode', gtk.WRAP_WORD)
elif value == 'nowrap':
tag.set_property('wrap_mode', gtk.WRAP_NONE)
def __length_tag_cb(self, value, tag, propname):
try:
tag.set_property(propname, value)
except:
gajim.log.warn( "Error with prop: " + propname + " for tag: " + str(tag))
def _parse_style_width(self, tag, value):
if value == 'auto':
return
self._parse_length(value, False, False, 1, 1000, self.__length_tag_cb,
tag, "width")
def _parse_style_height(self, tag, value):
if value == 'auto':
return
self._parse_length(value, False, False, 1, 1000, self.__length_tag_cb,
tag, "height")
# build a dictionary mapping styles to methods, for greater speed
__style_methods = dict()
for style in ['background-color', 'color', 'font-family', 'font-size',
'font-style', 'font-weight', 'margin-left', 'margin-right',
'text-align', 'text-decoration', 'white-space', 'display' ]:
'text-align', 'text-decoration', 'white-space', 'display',
'width', 'height' ]:
try:
method = locals()['_parse_style_%s' % style.replace('-', '_')]
except KeyError:
@ -489,6 +537,8 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
return [tag for tag in self.styles if tag is not None]
def _create_url(self, href, title, type_, id_):
'''Process a url tag.
'''
tag = self.textbuf.create_tag(id_)
if href and href[0] != '#':
tag.href = href
@ -501,6 +551,125 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
tag.title = title
return tag
def _process_img(self, attrs):
'''Process a img tag.
'''
try:
# Wait maximum 1s for connection
socket.setdefaulttimeout(1)
try:
f = urllib2.urlopen(attrs['src'])
except Exception, ex:
gajim.log.debug(str('Error loading image %s ' % attrs['src'] + ex))
pixbuf = None
alt = attrs.get('alt', 'Broken image')
else:
# Wait 0.1s between each byte
try:
f.fp._sock.fp._sock.settimeout(0.5)
except:
pass
# Max image size = 2 MB (to try to prevent DoS)
mem = ''
deadline = time.time() + 3
while True:
if time.time() > deadline:
gajim.log.debug(str('Timeout loading image %s ' % \
attrs['src'] + ex))
mem = ''
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Timeout loading image')
break
try:
temp = f.read(100)
except socket.timeout, ex:
gajim.log.debug('Timeout loading image %s ' % attrs['src'] + \
str(ex))
mem = ''
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Timeout loading image')
break
if temp:
mem += temp
else:
break
if len(mem) > 2*1024*1024:
alt = attrs.get('alt', '')
if alt:
alt += '\n'
alt += _('Image is too big')
break
pixbuf = None
if mem:
# Caveat: GdkPixbuf is known not to be safe to load
# images from network... this program is now potentially
# hackable ;)
loader = gtk.gdk.PixbufLoader()
dims = [0,0]
def height_cb(length):
dims[1] = length
def width_cb(length):
dims[0] = length
# process width and height attributes
w = attrs.get('width')
h = attrs.get('height')
# override with width and height styles
for attr, val in style_iter(attrs.get('style', '')):
if attr == 'width':
w = val
elif attr == 'height':
h = val
if w:
self._parse_length(w, False, False, 1, 1000, width_cb)
if h:
self._parse_length(h, False, False, 1, 1000, height_cb)
def set_size(pixbuf, w, h, dims):
'''FIXME: floats should be relative to the whole
textview, and resize with it. This needs new
pifbufs for every resize, gtk.gdk.Pixbuf.scale_simple
or similar.
'''
if type(dims[0]) == float:
dims[0] = int(dims[0]*w)
elif not dims[0]:
dims[0] = w
if type(dims[1]) == float:
dims[1] = int(dims[1]*h)
if not dims[1]:
dims[1] = h
loader.set_size(*dims)
if w or h:
loader.connect('size-prepared', set_size, dims)
loader.write(mem)
loader.close()
pixbuf = loader.get_pixbuf()
alt = attrs.get('alt', '')
if pixbuf is not None:
tags = self._get_style_tags()
if tags:
tmpmark = self.textbuf.create_mark(None, self.iter, True)
self.textbuf.insert_pixbuf(self.iter, pixbuf)
self.starting = False
if tags:
start = self.textbuf.get_iter_at_mark(tmpmark)
for tag in tags:
self.textbuf.apply_tag(tag, start, self.iter)
self.textbuf.delete_mark(tmpmark)
else:
self._insert_text('[IMG: %s]' % alt)
except Exception, ex:
gajim.log.error('Error loading image ' + str(ex))
pixbuf = None
alt = attrs.get('alt', 'Broken image')
try:
loader.close()
except:
pass
return pixbuf
def _begin_span(self, style, tag=None, id_=None):
if style is None:
@ -511,9 +680,9 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
tag = self.textbuf.create_tag(id_)
else:
tag = self.textbuf.create_tag() # we create anonymous tag
for attr, val in [item.split(':', 1) for item in style.split(';') if len(item.strip())]:
attr = attr.strip().lower()
val = val.strip()
for attr, val in style_iter(style):
attr = attr.lower()
val = val
try:
method = self.__style_methods[attr]
except KeyError:
@ -603,6 +772,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
self.text += content
return
if allwhitespace_rx.match(content) is not None and self._starts_line():
self.text += ' '
return
self.text += content
self.starting = False
@ -611,9 +781,8 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
def startElement(self, name, attrs):
self._flush_text()
klass = [i for i in attrs.get('class',' ').split(' ') if i]
style = attrs.get('style','')
style = ''
#Add styles defined for classes
#TODO: priority between class and style elements?
for k in klass:
if k in classes:
style += classes[k]
@ -641,9 +810,13 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
tag.is_anchor = True
elif name in LIST_ELEMS:
style += ';margin-left: 2em'
elif name == 'img':
tag = self._process_img(attrs)
if name in element_styles:
style += element_styles[name]
# so that explicit styles override implicit ones,
# we add the attribute last
style += ";"+attrs.get('style','')
if style == '':
style = None
self._begin_span(style, tag, id_)
@ -681,64 +854,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
elif name == 'dt':
if not self.starting:
self._jump_line()
elif name == 'img':
# Wait maximum 1s for connection
socket.setdefaulttimeout(1)
try:
f = urllib2.urlopen(attrs['src'])
except Exception, ex:
gajim.log.debug(str('Error loading image %s ' % attrs['src'] + ex))
pixbuf = None
alt = attrs.get('alt', 'Broken image')
else:
# Wait 10ms between each byte
try:
f.fp._sock.fp._sock.settimeout(0.01)
except:
pass
# Max image size = 2 MB (to try to prevent DoS) in Max 3s
mem = ''
deadline = time.time() + 3
while True:
if time.time() > deadline:
gajim.log.debug(str('Timeout loading image %s ' % \
attrs['src'] + ex))
pixbuf = None
alt = attrs.get('alt', 'Timeout loading image')
break
temp = f.read(100)
if temp:
mem += temp
else:
break
if len(mem) > 2*1024*1024:
alt = attrs.get('alt', 'Image is too big')
break
# Caveat: GdkPixbuf is known not to be safe to load
# images from network... this program is now potentially
# hackable ;)
loader = gtk.gdk.PixbufLoader()
loader.write(mem)
loader.close()
pixbuf = loader.get_pixbuf()
if pixbuf is not None:
tags = self._get_style_tags()
if tags:
tmpmark = self.textbuf.create_mark(None, self.iter, True)
self.textbuf.insert_pixbuf(self.iter, pixbuf)
if tags:
start = self.textbuf.get_iter_at_mark(tmpmark)
for tag in tags:
self.textbuf.apply_tag(tag, start, self.iter)
self.textbuf.delete_mark(tmpmark)
else:
self._insert_text('[IMG: %s]' % alt)
elif name == 'body' or name == 'html':
pass
elif name == 'a':
elif name in ('a', 'img', 'body', 'html'):
pass
elif name in INLINE:
pass
@ -788,10 +904,6 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
# self.text = ' '
class HtmlTextView(gtk.TextView):
__gtype_name__ = 'HtmlTextView'
__gsignals__ = {
'url-clicked': (gobject.SIGNAL_RUN_LAST, None, (str, str)), # href, type
}
def __init__(self):
gobject.GObject.__init__(self)
@ -867,16 +979,39 @@ class HtmlTextView(gtk.TextView):
parser.setContentHandler(HtmlHandler(self, eob))
parser.parse(StringIO(html))
# too much space after :)
#if not eob.starts_line():
# buffer.insert(eob, '\n')
change_cursor = None
if __name__ == '__main__':
import os
from common import gajim
class log(object):
def debug(self, text):
print "debug:", text
def warn(self, text):
print "warn;", text
def error(self,text):
print "error;", text
gajim.log=log()
if gajim.config.get('emoticons_theme'):
print "emoticons"
htmlview = HtmlTextView()
path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'muc_separator.png')
# use this for hr
htmlview.focus_out_line_pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
tooltip = tooltips.BaseTooltip()
def on_textview_motion_notify_event(widget, event):
'''change the cursor to a hand when we are over a mail or an url'''
@ -946,9 +1081,9 @@ if __name__ == '__main__':
<body xmlns='http://www.w3.org/1999/xhtml'>
<p style='text-align:center'>Hey, are you licensed to <a href='http://www.jabber.org/'>Jabber</a>?</p>
<p style='text-align:right'><img src='http://www.jabber.org/images/psa-license.jpg'
alt='A License to Jabber'
height='261'
width='537'/></p>
alt='A License to Jabber'
width='50%' height='50%'
/></p>
</body>
''')
htmlview.display_html('<hr />')
@ -973,8 +1108,9 @@ if __name__ == '__main__':
<li> One </li>
<li> Two is nested: <ul style='background-color:rgb(200,200,100)'>
<li> One </li>
<li> Two </li>
<li> Three </li>
<li style='font-size:50%'> Two </li>
<li style='font-size:200%'> Three </li>
<li style='font-size:9999pt'> Four </li>
</ul></li>
<li> Three </li></ol>
</body>

View File

@ -235,6 +235,11 @@ class MessageWindow:
def show_title(self, urgent = True, control = None):
'''redraw the window's title'''
if not control:
control = self.get_active_control()
if not control:
# No more control in this window
return
unread = 0
for ctrl in self.controls():
if ctrl.type_id == message_control.TYPE_GC and not \
@ -253,8 +258,6 @@ class MessageWindow:
else:
urgent = False
if not control:
control = self.get_active_control()
if control.type_id == message_control.TYPE_GC:
name = control.room_jid.split('@')[0]
urgent = control.attention_flag
@ -318,6 +321,10 @@ class MessageWindow:
if len(self._controls[ctrl.account]) == 0:
del self._controls[ctrl.account]
self.check_tabs()
self.show_title()
def check_tabs(self):
if self.get_num_controls() == 0:
# These are not called when the window is destroyed like this, fake it
gajim.interface.msg_win_mgr._on_window_delete(self.window, None)
@ -332,9 +339,8 @@ class MessageWindow:
self.notebook.set_show_tabs(show_tabs_if_one_tab)
if not show_tabs_if_one_tab:
self.alignment.set_property('top-padding', 0)
self.show_title()
def redraw_tab(self, ctrl, chatstate = None):
hbox = self.notebook.get_tab_label(ctrl.widget).get_children()[0]
status_img = hbox.get_children()[0]

View File

@ -42,6 +42,8 @@ except ImportError:
def get_show_in_roster(event, account, contact):
'''Return True if this event must be shown in roster, else False'''
if event == 'gc_message_received':
return True
num = get_advanced_notification(event, account, contact)
if num != None:
if gajim.config.get_per('notifications', str(num), 'roster') == 'yes':
@ -316,8 +318,8 @@ def popup(event_type, jid, account, msg_type = '', path_to_image = None,
notification.set_data('event_type', event_type)
notification.set_data('jid', jid)
notification.set_data('account', account)
notification.set_data('msg_type', event_type)
notification.set_data('path_to_image', path_to_image)
notification.set_data('msg_type', msg_type)
notification.set_property('icon-name', path_to_image)
notification.add_action('default', 'Default Action',
on_pynotify_notification_clicked)

View File

@ -118,20 +118,21 @@ class ProfileWindow:
def on_ok(widget, path_to_file):
must_delete = False
filesize = os.path.getsize(path_to_file) # in bytes
#FIXME: use messages for invalid file for 0.11
invalid_file = False
msg = ''
if os.path.isfile(path_to_file):
stat = os.stat(path_to_file)
if stat[6] == 0:
invalid_file = True
msg = _('File is empty')
else:
invalid_file = True
msg = _('File does not exist')
if not invalid_file and filesize > 16384: # 16 kb
try:
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
# get the image at 'notification size'
# and use that user did not specify in ACE crazy size
# and hope that user did not specify in ACE crazy size
scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf,
'tooltip')
except gobject.GError, msg: # unknown format
@ -148,13 +149,17 @@ class ProfileWindow:
'avatar_scaled.png')
scaled_pixbuf.save(path_to_file, 'png')
must_delete = True
self.dialog.destroy()
fd = open(path_to_file, 'rb')
data = fd.read()
pixbuf = gtkgui_helpers.get_pixbuf_from_data(data)
# rescale it
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
try:
# rescale it
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
except AttributeError: # unknown format
dialogs.ErrorDialog(_('Could not load image'))
return
self.dialog.destroy()
button = self.xml.get_widget('PHOTO_button')
image = button.get_image()
image.set_from_pixbuf(pixbuf)
@ -183,7 +188,8 @@ class ProfileWindow:
menu = gtk.Menu()
# Try to get pixbuf
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(self.jid)
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(self.jid,
use_local = False)
if pixbuf:
nick = gajim.config.get_per('accounts', self.account, 'name')

View File

@ -48,6 +48,7 @@ from groupchat_control import PrivateChatControl
from common import dbus_support
if dbus_support.supported:
from music_track_listener import MusicTrackListener
import dbus
#(icon, name, type, jid, account, editable, second pixbuf)
(
@ -279,6 +280,7 @@ class RosterWindow:
if hide and contact.sub != 'from':
return
observer = contact.is_observer()
groupchat = contact.is_groupchat()
if observer:
# if he has a tag, remove it
@ -368,6 +370,8 @@ class RosterWindow:
typestr = 'contact'
if group == _('Transports'):
typestr = 'agent'
if group == _('Groupchats'):
typestr = 'groupchat'
name = contact.get_shown_name()
# we add some values here. see draw_contact for more
@ -391,13 +395,15 @@ class RosterWindow:
accounts = []
else:
accounts = [account]
text = group
text = gobject.markup_escape_text(group)
if group in gajim.connections[account].blocked_groups:
text = '<span strikethrough="true">%s</span>' % text
if gajim.config.get('show_contacts_number'):
nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
accounts = accounts, groups = [group])
text += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
model = self.tree.get_model()
model.set_value(iter, 1 , gobject.markup_escape_text(text))
model.set_value(iter, 1 , text)
def add_to_not_in_the_roster(self, account, jid, nick = '', resource = ''):
''' add jid to group "not in the roster", he MUST not be in roster yet,
@ -414,6 +420,20 @@ class RosterWindow:
self.add_contact_to_roster(contact.jid, account)
return contact
def add_groupchat_to_roster(self, account, jid, nick = '', resource = '',
status = ''):
''' add groupchat to roster '''
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if contact == None:
contact = gajim.contacts.create_contact(jid = jid, name = jid,
groups = [_('Groupchats')], show = 'muc_active',
status = status, sub = 'none',
resource = resource)
gajim.contacts.add_contact(account, contact)
self.add_contact_to_roster(jid, account)
self.draw_group(_('Groupchats'), account)
return contact
def get_self_contact_iter(self, account):
model = self.tree.get_model()
iterAcct = self.get_account_iter(account)
@ -560,6 +580,35 @@ class RosterWindow:
return
name = gobject.markup_escape_text(contact.get_shown_name())
# gets number of unread gc marked messages
if gajim.interface.minimized_controls.has_key(account) and \
jid in gajim.interface.minimized_controls[account]:
nb_unread = len(gajim.events.get_events(account, jid,
['printed_marked_gc_msg']))
nb_unread += \
gajim.interface.minimized_controls[account][jid].get_nb_unread_pm()
if nb_unread == 1:
name = '%s *' % name
elif nb_unread > 1:
name = '%s [%s]' % (name, str(nb_unread))
strike = False
if jid in gajim.connections[account].blocked_contacts:
strike = True
else:
groups = contact.groups
if contact.is_observer():
groups = [_('Observers')]
elif not groups:
groups = [_('General')]
for group in groups:
if group in gajim.connections[account].blocked_groups:
strike = True
break
if strike:
name = '<span strikethrough="true">%s</span>' % name
nb_connected_contact = 0
for c in contact_instances:
if c.show not in ('error', 'offline'):
@ -643,6 +692,13 @@ class RosterWindow:
state_images = self.get_appropriate_state_images(jid,
icon_name = icon_name)
if icon_name != 'message' and gajim.gc_connected[account].\
has_key(jid):
if gajim.gc_connected[account][jid]:
icon_name = 'muc_active'
else:
icon_name = 'muc_inactive'
img = state_images[icon_name]
for iter in iters:
@ -677,7 +733,7 @@ class RosterWindow:
for iter in iters:
model[iter][C_SECPIXBUF] = scaled_pixbuf
def join_gc_room(self, account, room_jid, nick, password):
def join_gc_room(self, account, room_jid, nick, password, minimize = False):
'''joins the room immediatelly'''
if gajim.interface.msg_win_mgr.has_window(room_jid, account) and \
gajim.gc_connected[account][room_jid]:
@ -686,11 +742,28 @@ class RosterWindow:
win.set_active_tab(room_jid, account)
dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
return
if gajim.interface.minimized_controls.has_key(account) and \
room_jid in gajim.interface.minimized_controls[account]:
self.on_groupchat_maximized(None, room_jid, account)
return
invisible_show = gajim.SHOW_LIST.index('invisible')
if gajim.connections[account].connected == invisible_show:
dialogs.ErrorDialog(
_('You cannot join a group chat while you are invisible'))
return
if minimize:
contact = gajim.contacts.create_contact(jid = room_jid, name = nick)
gc_control = GroupchatControl(None, contact, account)
if not gajim.interface.minimized_controls.has_key(account):
gajim.interface.minimized_controls[account] = {}
gajim.interface.minimized_controls[account][room_jid] = gc_control
self.add_groupchat_to_roster(account, room_jid)
gajim.connections[account].join_gc(nick, room_jid, password)
if password:
gajim.gc_passwords[room_jid] = password
return
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)
@ -729,6 +802,13 @@ class RosterWindow:
else:
gajim.interface.instances[account]['privacy_lists'] = \
dialogs.PrivacyListsWindow(account)
def on_blocked_contacts_menuitem_activate(self, widget, account):
if gajim.interface.instances[account].has_key('blocked_contacts'):
gajim.interface.instances[account]['blocked_contacts'].window.present()
else:
gajim.interface.instances[account]['blocked_contacts'] = \
dialogs.BlockedContactsWindow(account)
def on_set_motd_menuitem_activate(self, widget, account):
server = gajim.config.get_per('accounts', account, 'hostname')
@ -762,6 +842,7 @@ class RosterWindow:
send_single_message_menuitem = xml.get_widget(
'send_single_message_menuitem')
xml_console_menuitem = xml.get_widget('xml_console_menuitem')
blocked_contacts_menuitem = xml.get_widget('blocked_contacts_menuitem')
privacy_lists_menuitem = xml.get_widget('privacy_lists_menuitem')
administrator_menuitem = xml.get_widget('administrator_menuitem')
send_server_message_menuitem = xml.get_widget(
@ -775,9 +856,12 @@ class RosterWindow:
if gajim.connections[account] and gajim.connections[account].\
privacy_rules_supported:
blocked_contacts_menuitem.connect('activate',
self.on_blocked_contacts_menuitem_activate, account)
privacy_lists_menuitem.connect('activate',
self.on_privacy_lists_menuitem_activate, account)
else:
blocked_contacts_menuitem.set_sensitive(False)
privacy_lists_menuitem.set_sensitive(False)
if gajim.connections[account].is_zeroconf:
@ -1303,14 +1387,25 @@ class RosterWindow:
if not connected_contacts:
# no connected contacts, show the ofline one
connected_contacts = contacts
self.tooltip.account = account
self.tooltip.timeout = gobject.timeout_add(500,
self.show_tooltip, connected_contacts)
elif model[iter][C_TYPE] == 'groupchat':
account = model[iter][C_ACCOUNT].decode('utf-8')
jid = model[iter][C_JID].decode('utf-8')
if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
self.tooltip.id = row
contact = gajim.contacts.get_contact(account, jid)
self.tooltip.account = account
self.tooltip.timeout = gobject.timeout_add(500,
self.show_tooltip, contact)
elif model[iter][C_TYPE] == 'account':
# we're on an account entry in the roster
account = model[iter][C_ACCOUNT].decode('utf-8')
if account == 'all':
if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
self.tooltip.id = row
self.tooltip.account = None
self.tooltip.timeout = gobject.timeout_add(500,
self.show_tooltip, [])
return
@ -1327,8 +1422,7 @@ class RosterWindow:
contact = gajim.contacts.create_contact(jid = jid,
name = account_name, show = connection.get_status(), sub = '',
status = connection.status,
resource = gajim.config.get_per('accounts', connection.name,
'resource'),
resource = connection.server_resource,
priority = connection.priority,
keyID = gajim.config.get_per('accounts', connection.name,
'keyid'))
@ -1353,6 +1447,7 @@ class RosterWindow:
contacts.append(contact)
if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
self.tooltip.id = row
self.tooltip.account = None
self.tooltip.timeout = gobject.timeout_add(500,
self.show_tooltip, contacts)
@ -1413,6 +1508,152 @@ class RosterWindow:
self.dialog = dialogs.ConfirmationDialog(pritext, sectext,
on_response_ok = (remove, list_))
def on_block(self, widget, iter, group_list):
''' When clicked on the 'block' button in context menu. '''
model = self.tree.get_model()
accounts = []
msg = self.get_status_message('offline')
if group_list == None:
jid = model[iter][C_JID].decode('utf-8')
account = model[iter][C_ACCOUNT].decode('utf-8')
accounts.append(account)
self.send_status(account, 'offline', msg, to = jid)
new_rule = {'order': u'1', 'type': u'jid', 'action': u'deny',
'value' : jid, 'child': [u'message', u'iq', u'presence-out']}
gajim.connections[account].blocked_list.append(new_rule)
# needed for draw_contact:
gajim.connections[account].blocked_contacts.append(jid)
self.draw_contact(jid, account)
else:
if iter == None:
for (contact, account) in group_list:
if account not in accounts:
if not gajim.connections[account].privacy_rules_supported:
continue
accounts.append(account)
self.send_status(account, 'offline', msg, to=contact.jid)
new_rule = {'order': u'1', 'type': u'jid',
'action': u'deny', 'value' : contact.jid,
'child': [u'message', u'iq', u'presence-out']}
gajim.connections[account].blocked_list.append(new_rule)
# needed for draw_contact:
gajim.connections[account].blocked_contacts.append(contact.jid)
self.draw_contact(contact.jid, account)
else:
group = model[iter][C_JID].decode('utf-8')
for (contact, account) in group_list:
if account not in accounts:
if not gajim.connections[account].privacy_rules_supported:
continue
accounts.append(account)
# needed for draw_group:
gajim.connections[account].blocked_groups.append(group)
self.draw_group(group, account)
self.send_status(account, 'offline', msg, to=contact.jid)
self.draw_contact(contact.jid, account)
new_rule = {'order': u'1', 'type': u'group', 'action': u'deny',
'value' : group, 'child': [u'message', u'iq', u'presence-out']}
gajim.connections[account].blocked_list.append(new_rule)
for account in accounts:
gajim.connections[account].set_privacy_list(
'block', gajim.connections[account].blocked_list)
if len(gajim.connections[account].blocked_list) == 1:
gajim.connections[account].set_active_list('block')
gajim.connections[account].set_default_list('block')
gajim.connections[account].get_privacy_list('block')
def on_unblock(self, widget, iter, group_list):
''' When clicked on the 'unblock' button in context menu. '''
model = self.tree.get_model()
accounts = []
if group_list == None:
jid = model[iter][C_JID].decode('utf-8')
jid_account = model[iter][C_ACCOUNT].decode('utf-8')
accounts.append(jid_account)
gajim.connections[jid_account].new_blocked_list = []
for rule in gajim.connections[jid_account].blocked_list:
if rule['action'] != 'deny' or rule['type'] != 'jid' \
or rule['value'] != jid:
gajim.connections[jid_account].new_blocked_list.append(rule)
# needed for draw_contact:
if jid in gajim.connections[jid_account].blocked_contacts:
gajim.connections[jid_account].blocked_contacts.remove(jid)
self.draw_contact(jid, jid_account)
else:
if iter == None:
for (contact, account) in group_list:
if account not in accounts:
if gajim.connections[account].privacy_rules_supported:
accounts.append(account)
gajim.connections[account].new_blocked_list = []
gajim.connections[account].to_unblock = []
gajim.connections[account].to_unblock.append(contact.jid)
else:
gajim.connections[account].to_unblock.append(contact.jid)
# needed for draw_contact:
if contact.jid in gajim.connections[account].blocked_contacts:
gajim.connections[account].blocked_contacts.remove(
contact.jid)
self.draw_contact(contact.jid, account)
for account in accounts:
for rule in gajim.connections[account].blocked_list:
if rule['action'] != 'deny' or rule['type'] != 'jid' \
or rule['value'] not in gajim.connections[account].to_unblock:
gajim.connections[account].new_blocked_list.append(rule)
else:
group = model[iter][C_JID].decode('utf-8')
for (contact, account) in group_list:
if account not in accounts:
if gajim.connections[account].privacy_rules_supported:
accounts.append(account)
# needed for draw_group:
if group in gajim.connections[account].blocked_groups:
gajim.connections[account].blocked_groups.remove(group)
self.draw_group(group, account)
gajim.connections[account].new_blocked_list = []
for rule in gajim.connections[account].blocked_list:
if rule['action'] != 'deny' or rule['type'] != 'group' \
or rule['value'] != group:
gajim.connections[account].new_blocked_list.append(
rule)
self.draw_contact(contact.jid, account)
for account in accounts:
gajim.connections[account].set_privacy_list(
'block', gajim.connections[account].new_blocked_list)
gajim.connections[account].get_privacy_list('block')
if len(gajim.connections[account].new_blocked_list) == 0:
gajim.connections[account].blocked_list = []
gajim.connections[account].blocked_contacts = []
gajim.connections[account].blocked_groups = []
gajim.connections[account].set_default_list('')
gajim.connections[account].set_active_list('')
gajim.connections[account].del_privacy_list('block')
if gajim.interface.instances[account].has_key('blocked_contacts'):
gajim.interface.instances[account]['blocked_contacts'].\
privacy_list_received([])
if group_list == None:
status = gajim.connections[jid_account].connected
msg = gajim.connections[jid_account].status
if not self.regroup:
show = gajim.SHOW_LIST[status]
else: # accounts merged
show = helpers.get_global_show()
self.send_status(jid_account, show, msg, to=jid)
else:
for (contact, account) in group_list:
if not self.regroup:
show = gajim.SHOW_LIST[gajim.connections[account].connected]
else: # accounts merged
show = helpers.get_global_show()
if account not in accounts:
if gajim.connections[account].privacy_rules_supported:
accounts.append(account)
self.send_status(account, show,
gajim.connections[account].status, to=contact.jid)
else:
self.send_status(account, show,
gajim.connections[account].status, to=contact.jid)
def on_rename(self, widget, iter, path):
# this function is called either by F2 or by Rename menuitem
if gajim.interface.instances.has_key('rename'):
@ -1734,9 +1975,13 @@ class RosterWindow:
'roster_contact_context_menu')
start_chat_menuitem = xml.get_widget('start_chat_menuitem')
send_custom_status_menuitem = xml.get_widget(
'send_custom_status_menuitem')
send_single_message_menuitem = xml.get_widget(
'send_single_message_menuitem')
invite_menuitem = xml.get_widget('invite_menuitem')
block_menuitem = xml.get_widget('block_menuitem')
unblock_menuitem = xml.get_widget('unblock_menuitem')
rename_menuitem = xml.get_widget('rename_menuitem')
edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
# separator has with send file, assign_openpgp_key_menuitem, etc..
@ -1803,6 +2048,20 @@ class RosterWindow:
item = gtk.SeparatorMenuItem() # separator
invite_to_submenu.append(item)
# One or several resource, we do the same for send_custom_status
status_menuitems = gtk.Menu()
send_custom_status_menuitem.set_submenu(status_menuitems)
iconset = gajim.config.get('iconset')
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
# icon MUST be different instance for every item
state_images = self.load_iconset(path)
status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
status_menuitem.connect('activate', self.on_send_custom_status,
[(contact, account)], s)
icon = state_images[s]
status_menuitem.set_image(icon)
status_menuitems.append(status_menuitem)
if len(contacts) > 1: # several resources
def resources_submenu(action, room_jid = None, room_account = None):
''' Build a submenu with contact's resources.
@ -1952,6 +2211,22 @@ class RosterWindow:
remove_from_roster_menuitem, execute_command_menuitem]:
widget.set_sensitive(False)
if gajim.connections[account] and gajim.connections[account].\
privacy_rules_supported:
if jid in gajim.connections[account].blocked_contacts:
block_menuitem.set_no_show_all(True)
unblock_menuitem.connect('activate', self.on_unblock, iter, None)
block_menuitem.hide()
else:
unblock_menuitem.set_no_show_all(True)
block_menuitem.connect('activate', self.on_block, iter, None)
unblock_menuitem.hide()
else:
block_menuitem.set_no_show_all(True)
unblock_menuitem.set_no_show_all(True)
block_menuitem.hide()
unblock_menuitem.hide()
event_button = gtkgui_helpers.get_possible_button_event(event)
roster_contact_context_menu.attach_to_widget(self.tree, None)
@ -2010,6 +2285,7 @@ class RosterWindow:
connected_accounts = []
contacts_transport = -1
# -1 is at start, False when not from the same, None when jabber
is_blocked = True
for iter in iters:
jid = model[iter][C_JID].decode('utf-8')
account = model[iter][C_ACCOUNT].decode('utf-8')
@ -2024,6 +2300,8 @@ class RosterWindow:
contacts_transport = transport
if contacts_transport != transport:
contacts_transport = False
if jid not in gajim.connections[account].blocked_contacts:
is_blocked = False
list_.append((contact, account))
menu = gtk.Menu()
@ -2093,6 +2371,18 @@ class RosterWindow:
send_group_message_item.connect('activate',
self.on_send_single_message_menuitem_activate, account, list_)
if is_blocked:
unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
icon = gtk.image_new_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
unblock_menuitem.set_image(icon)
unblock_menuitem.connect('activate', self.on_unblock, None, list_)
menu.append(unblock_menuitem)
else:
block_menuitem = gtk.ImageMenuItem(_('_Block'))
icon = gtk.image_new_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
block_menuitem.set_image(icon)
block_menuitem.connect('activate', self.on_block, None, list_)
menu.append(block_menuitem)
# unsensitive if one account is not connected
if one_account_offline:
remove_item.set_sensitive(False)
@ -2104,6 +2394,54 @@ class RosterWindow:
menu.show_all()
menu.popup(None, None, None, event_button, event.time)
def make_groupchat_menu(self, event, iter):
model = self.tree.get_model()
path = model.get_path(iter)
jid = model[iter][C_JID].decode('utf-8')
account = model[iter][C_ACCOUNT].decode('utf-8')
menu = gtk.Menu()
maximize_menuitem = gtk.ImageMenuItem(_('_Maximize'))
icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
maximize_menuitem.set_image(icon)
maximize_menuitem.connect('activate', self.on_groupchat_maximized, \
jid, account)
menu.append(maximize_menuitem)
event_button = gtkgui_helpers.get_possible_button_event(event)
menu.attach_to_widget(self.tree, None)
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
menu.show_all()
menu.popup(None, None, None, event_button, event.time)
def on_groupchat_maximized(self, widget, jid, account):
'''When a groupshat is maximised'''
if not gajim.interface.minimized_controls.has_key(account):
return
if not gajim.interface.minimized_controls[account].has_key(jid):
return
ctrl = gajim.interface.minimized_controls[account][jid]
mw = gajim.interface.msg_win_mgr.get_window(ctrl.contact.jid, ctrl.account)
if not mw:
mw = gajim.interface.msg_win_mgr.create_window(ctrl.contact, \
ctrl.account, ctrl.type_id)
ctrl.parent_win = mw
mw.new_tab(ctrl)
mw.set_active_tab(jid, account)
mw.window.present()
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
self.remove_contact(contact, account)
gajim.contacts.remove_contact(account, contact)
self.draw_group(_('Groupchats'), account)
del gajim.interface.minimized_controls[account][jid]
def make_group_menu(self, event, iter):
'''Make group's popup menu'''
model = self.tree.get_model()
@ -2164,6 +2502,47 @@ class RosterWindow:
group_message_to_all_item.connect('activate',
self.on_send_single_message_menuitem_activate, account, list_)
send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
# add a special img for this menuitem
icon = gtk.image_new_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_MENU)
send_custom_status_menuitem.set_image(icon)
status_menuitems = gtk.Menu()
send_custom_status_menuitem.set_submenu(status_menuitems)
iconset = gajim.config.get('iconset')
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
# icon MUST be different instance for every item
state_images = self.load_iconset(path)
status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
status_menuitem.connect('activate', self.on_send_custom_status, list_,
s)
icon = state_images[s]
status_menuitem.set_image(icon)
status_menuitems.append(status_menuitem)
menu.append(send_custom_status_menuitem)
is_blocked = False
if self.regroup:
for g_account in gajim.connections:
if group in gajim.connections[g_account].blocked_groups:
is_blocked = True
else:
if group in gajim.connections[account].blocked_groups:
is_blocked = True
if group not in helpers.special_groups + (_('General'),):
if is_blocked:
unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
icon = gtk.image_new_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
unblock_menuitem.set_image(icon)
unblock_menuitem.connect('activate', self.on_unblock, iter, list_)
menu.append(unblock_menuitem)
else:
block_menuitem = gtk.ImageMenuItem(_('_Block'))
icon = gtk.image_new_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
block_menuitem.set_image(icon)
block_menuitem.connect('activate', self.on_block, iter, list_)
menu.append(block_menuitem)
event_button = gtkgui_helpers.get_possible_button_event(event)
menu.attach_to_widget(self.tree, None)
@ -2564,6 +2943,8 @@ class RosterWindow:
return
if type_ == 'group' and len(iters) == 1:
self.make_group_menu(event, iters[0])
if type_ == 'groupchat' and len(iters) == 1:
self.make_groupchat_menu(event, iters[0])
elif type_ == 'agent' and len(iters) == 1:
self.make_transport_menu(event, iters[0])
elif type_ in ('contact', 'self_contact') and len(iters) == 1:
@ -2624,8 +3005,8 @@ class RosterWindow:
self.tree.get_selection().unselect_all()
self.tree.get_selection().select_path(path)
type_ = model[path][C_TYPE]
if type_ in ('agent', 'contact', 'self_contact'):
self.on_roster_treeview_row_activated(widget, path)
if type_ in ('agent', 'contact', 'self_contact', 'groupchat'):
self.on_row_activated(widget, path)
elif type_ == 'account':
account = model[path][C_ACCOUNT].decode('utf-8')
if account != 'all':
@ -2651,24 +3032,28 @@ class RosterWindow:
elif event.button == 1: # Left click
model = self.tree.get_model()
type_ = model[path][C_TYPE]
if type_ == 'group' and x < 27:
# first cell in 1st column (the arrow SINGLE clicked)
if (self.tree.row_expanded(path)):
self.tree.collapse_row(path)
else:
self.tree.expand_row(path, False)
elif type_ == 'contact' and x < 27:
account = model[path][C_ACCOUNT].decode('utf-8')
jid = model[path][C_JID].decode('utf-8')
# first cell in 1st column (the arrow SINGLE clicked)
iters = self.get_contact_iter(jid, account)
for iter in iters:
path = model.get_path(iter)
if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK and \
not event.state & gtk.gdk.CONTROL_MASK:
self.on_row_activated(widget, path)
else:
if type_ == 'group' and x < 27:
# first cell in 1st column (the arrow SINGLE clicked)
if (self.tree.row_expanded(path)):
self.tree.collapse_row(path)
else:
self.tree.expand_row(path, False)
elif type_ == 'contact' and x < 27:
account = model[path][C_ACCOUNT].decode('utf-8')
jid = model[path][C_JID].decode('utf-8')
# first cell in 1st column (the arrow SINGLE clicked)
iters = self.get_contact_iter(jid, account)
for iter in iters:
path = model.get_path(iter)
if (self.tree.row_expanded(path)):
self.tree.collapse_row(path)
else:
self.tree.expand_row(path, False)
def on_req_usub(self, widget, list_):
'''Remove a contact. list_ is a list of (contact, account) tuples'''
@ -2683,7 +3068,7 @@ class RosterWindow:
gajim.connections[account].unsubscribe(contact.jid, remove_auth)
for c in gajim.contacts.get_contact(account, contact.jid):
self.remove_contact(c, account)
gajim.contacts.remove_jid(account, c.jid)
gajim.contacts.remove_jid(account, contact.jid)
# redraw group rows for contact numbers
for group in c.groups:
self.draw_group(group, account)
@ -2742,7 +3127,7 @@ class RosterWindow:
if gajim.interface.systray_enabled:
gajim.interface.systray.change_status('connecting')
def send_status(self, account, status, txt, auto = False):
def send_status(self, account, status, txt, auto = False, to = None):
model = self.tree.get_model()
accountIter = self.get_account_iter(account)
if status != 'offline':
@ -2827,18 +3212,42 @@ class RosterWindow:
passphrase)
gajim.connections[account].gpg_passphrase(passphrase)
for gc_control in gajim.interface.msg_win_mgr.get_controls(
message_control.TYPE_GC):
if gc_control.account == account:
gajim.connections[account].send_gc_status(gc_control.nick,
gc_control.room_jid, status, txt)
if gajim.account_is_connected(account):
if status == 'online' and gajim.interface.sleeper.getState() != \
common.sleepy.STATE_UNKNOWN:
gajim.sleeper_state[account] = 'online'
elif gajim.sleeper_state[account] not in ('autoaway', 'autoxa'):
gajim.sleeper_state[account] = 'off'
gajim.connections[account].change_status(status, txt, auto)
if to:
gajim.connections[account].send_custom_status(status, txt, to)
else:
was_invisible = gajim.connections[account].connected == \
gajim.SHOW_LIST.index('invisible')
gajim.connections[account].change_status(status, txt, auto)
if not gajim.interface.minimized_controls.has_key(account):
gajim.interface.minimized_controls[account] = {}
for gc_control in gajim.interface.msg_win_mgr.get_controls(
message_control.TYPE_GC) + \
gajim.interface.minimized_controls[account].values():
if gc_control.account == account:
if gajim.gc_connected[account][gc_control.room_jid]:
gajim.connections[account].send_gc_status(gc_control.nick,
gc_control.room_jid, status, txt)
else:
# for some reason, we are not connected to the room even if
# tab is opened, send initial join_gc()
gajim.connections[account].join_gc(gc_control.nick,
gc_control.room_jid, None)
if was_invisible:
# We come back from invisible, join bookmarks
for bm in gajim.connections[account].bookmarks:
room_jid = bm['jid']
if room_jid in gajim.gc_connected[account] and \
gajim.gc_connected[account][room_jid]:
continue
self.join_gc_room(account, room_jid, bm['nick'], bm['password'],
minimize = gajim.config.get('minimize_autojoined_rooms'))
def get_status_message(self, show):
if show in gajim.config.get_per('defaultstatusmsg'):
@ -2878,6 +3287,14 @@ class RosterWindow:
else:
change(None, account, status)
def on_send_custom_status(self, widget, contact_list, show):
'''send custom status'''
dlg = dialogs.ChangeStatusMessageDialog(show)
message = dlg.run()
if message is not None: # None if user pressed Cancel
for (contact, account) in contact_list:
self.send_status(account, show, message, to = contact.jid)
def on_status_combobox_changed(self, widget):
'''When we change our status via the combobox'''
model = self.status_combobox.get_model()
@ -2957,7 +3374,7 @@ class RosterWindow:
# status
if not global_sync_connected_accounts > 0 or \
gajim.account_is_connected(account):
gajim.connections[account].connected > 0:
self.send_status(account, status, message)
self.update_status_combobox()
@ -2983,6 +3400,26 @@ class RosterWindow:
self._music_track_changed_signal = None
self._music_track_changed(None, None)
def _change_awn_icon_status(self, status):
if not dbus_support.supported:
# do nothing if user doesn't have D-Bus bindings
return
iconset = gajim.config.get('iconset')
prefix = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '32x32')
if status in ('chat', 'away', 'xa', 'dnd', 'invisible', 'offline'):
status = status + '.png'
elif status == 'online':
prefix = os.path.join(gajim.DATA_DIR, 'pixmaps')
status = 'gajim.png'
path = os.path.join(prefix, status)
try:
bus = dbus.SessionBus()
obj = bus.get_object('com.google.code.Awn', '/com/google/code/Awn')
awn = dbus.Interface(obj, 'com.google.code.Awn')
awn.SetTaskIconByName('Gajim', os.path.abspath(path))
except Exception, e:
pass
def _music_track_changed(self, unused_listener, music_track_info):
from common import pep
accounts = gajim.connections.keys()
@ -3026,6 +3463,7 @@ class RosterWindow:
# in the combobox
self.combobox_callback_active = False
self.status_combobox.set_active(table[show])
self._change_awn_icon_status(show)
self.combobox_callback_active = True
if gajim.interface.systray_enabled:
gajim.interface.systray.change_status(show)
@ -3068,12 +3506,23 @@ class RosterWindow:
self.actions_menu_needs_rebuild = True
self.update_status_combobox()
def new_chat(self, contact, account, private_chat = False, resource = None):
def new_private_chat(self, gc_contact, account):
contact = gajim.contacts.contact_from_gc_contact(gc_contact)
type_ = message_control.TYPE_PM
fjid = gc_contact.room_jid + '/' + gc_contact.name
mw = gajim.interface.msg_win_mgr.get_window(fjid, account)
if not mw:
mw = gajim.interface.msg_win_mgr.create_window(contact, account, type_)
chat_control = PrivateChatControl(mw, gc_contact, contact, account)
mw.new_tab(chat_control)
if len(gajim.events.get_events(account, fjid)):
# We call this here to avoid race conditions with widget validation
chat_control.read_queue()
def new_chat(self, contact, account, resource = None):
# Get target window, create a control, and associate it with the window
if not private_chat:
type_ = message_control.TYPE_CHAT
else:
type_ = message_control.TYPE_PM
type_ = message_control.TYPE_CHAT
fjid = contact.jid
if resource:
@ -3082,10 +3531,7 @@ class RosterWindow:
if not mw:
mw = gajim.interface.msg_win_mgr.create_window(contact, account, type_)
if not private_chat:
chat_control = ChatControl(mw, contact, account, resource)
else:
chat_control = PrivateChatControl(mw, contact, account)
chat_control = ChatControl(mw, contact, account, resource)
mw.new_tab(chat_control)
@ -3311,7 +3757,8 @@ class RosterWindow:
'''
self.close_all_from_dict(gajim.interface.instances[account])
for ctrl in gajim.interface.msg_win_mgr.get_controls(acct = account):
ctrl.parent_win.remove_tab(ctrl, force = force)
ctrl.parent_win.remove_tab(ctrl, ctrl.parent_win.CLOSE_CLOSE_BUTTON,
force = force)
def on_roster_window_delete_event(self, widget, event):
'''When we want to close the window'''
@ -3525,8 +3972,9 @@ class RosterWindow:
win.window.present()
def on_roster_treeview_row_activated(self, widget, path, col = 0):
'''When an iter is double clicked: open the first event window'''
def on_row_activated(self, widget, path):
'''When an iter is activated (dubblick or single click if gnome is set
this way'''
model = self.tree.get_model()
account = model[path][C_ACCOUNT].decode('utf-8')
type_ = model[path][C_TYPE]
@ -3538,6 +3986,9 @@ class RosterWindow:
self.tree.collapse_row(path)
else:
self.tree.expand_row(path, False)
elif gajim.interface.minimized_controls.has_key(account) and \
gajim.interface.minimized_controls[account].has_key(jid):
self.on_groupchat_maximized(None, jid, account)
else:
first_ev = gajim.events.get_first_event(account, jid)
if not first_ev:
@ -3570,6 +4021,11 @@ class RosterWindow:
resource = c.resource
self.on_open_chat_window(widget, c, account, resource = resource)
def on_roster_treeview_row_activated(self, widget, path, col = 0):
'''When an iter is double clicked: open the first event window'''
if not gajim.single_click:
self.on_row_activated(widget, path)
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()
@ -3685,7 +4141,7 @@ class RosterWindow:
pixbuf2.composite(pixbuf1, 0, 0,
pixbuf2.get_property('width'),
pixbuf2.get_property('height'), 0, 0, 1.0, 1.0,
gtk.gdk.INTERP_HYPER, 255)
gtk.gdk.INTERP_NEAREST, 255)
image.set_from_pixbuf(pixbuf1)
break
return imgs

195
src/search_window.py Normal file
View File

@ -0,0 +1,195 @@
# -*- coding: utf-8 -*-
## search_window.py
##
## Copyright (C) 2007 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
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
import gobject
import gtk
from common import xmpp, gajim, dataforms
import gtkgui_helpers
import dialogs
import dataforms_widget
class SearchWindow:
def __init__(self, account, jid):
'''Create new window.'''
# an account object
self.account = account
self.jid = jid
# retrieving widgets from xml
self.xml = gtkgui_helpers.get_glade('search_window.glade')
self.window = self.xml.get_widget('search_window')
for name in ('label', 'progressbar', 'search_vbox', 'search_button'):
self.__dict__[name] = self.xml.get_widget(name)
self.data_form_widget = dataforms_widget.DataFormWidget()
self.table = None
# displaying the window
self.xml.signal_autoconnect(self)
self.window.show_all()
self.request_form()
self.pulse_id = gobject.timeout_add(80, self.pulse_callback)
self.is_form = None
# for non-dataform forms
self.entries = {}
self.info = {}
def request_form(self):
gajim.connections[self.account].request_search_fields(self.jid)
def pulse_callback(self):
self.progressbar.pulse()
return True
def on_search_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
self.window.destroy()
def on_search_window_destroy(self, widget):
if self.pulse_id:
gobject.source_remove(self.pulse_id)
del gajim.interface.instances[self.account]['search'][self.jid]
def on_close_button_clicked(self, button):
self.window.destroy()
def on_search_button_clicked(self, button):
if self.is_form:
self.data_form_widget.data_form.type = 'submit'
gajim.connections[self.account].send_search_form(self.jid,
self.data_form_widget.data_form, True)
self.search_vbox.remove(self.data_form_widget)
else:
for name in self.entries.keys():
self.infos[name] = self.entries[name].get_text().decode('utf-8')
if self.infos.has_key('instructions'):
del self.infos['instructions']
gajim.connections[self.account].send_search_form(self.jid, self.infos,
False)
self.search_vbox.remove(self.table)
self.progressbar.show()
self.label.set_text(_('Waiting for results'))
self.label.show()
self.pulse_id = gobject.timeout_add(80, self.pulse_callback)
self.search_button.hide()
def on_form_arrived(self, form, is_form):
if self.pulse_id:
gobject.source_remove(self.pulse_id)
self.progressbar.hide()
self.label.hide()
if not is_form:
self.is_form = False
self.infos = form
nbrow = 0
if self.infos.has_key('instructions'):
self.label.set_text(self.infos['instructions'])
self.label.show()
self.table = gtk.Table()
for name in self.infos.keys():
if not name:
continue
if name == 'instructions':
continue
nbrow = nbrow + 1
self.table.resize(rows = nbrow, columns = 2)
label = gtk.Label(name.capitalize() + ':')
self.table.attach(label, 0, 1, nbrow - 1, nbrow, 0, 0, 0, 0)
entry = gtk.Entry()
entry.set_activates_default(True)
if self.infos[name]:
entry.set_text(self.infos[name])
if name == 'password':
entry.set_visibility(False)
self.table.attach(entry, 1, 2, nbrow - 1, nbrow, 0, 0, 0, 0)
self.entries[name] = entry
self.table.show_all()
self.search_vbox.pack_start(self.table)
return
self.dataform = dataforms.ExtendForm(node = form)
self.data_form_widget.set_sensitive(True)
try:
self.data_form_widget.data_form = self.dataform
except dataforms.Error:
self.label.set_text(_('Error in received dataform'))
self.label.show()
return
self.is_form = True
self.search_vbox.pack_start(self.data_form_widget)
self.data_form_widget.show()
if self.data_form_widget.title:
self.window.set_title('%s - Search - Gajim' % \
self.data_form_widget.title)
def on_result_arrived(self, form, is_form):
if self.pulse_id:
gobject.source_remove(self.pulse_id)
self.progressbar.hide()
self.label.hide()
if not is_form:
if not form:
self.label.set_text(_('No result'))
self.label.show()
return
# We suppose all items have the same fields
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
treeview = gtk.TreeView()
sw.add(treeview)
# Create model
fieldtypes = [str]*len(form[0])
model = gtk.ListStore(*fieldtypes)
# Copy data to model
for item in form:
model.append(item.values())
# Create columns
counter = 0
for field in form[0].keys():
treeview.append_column(
gtk.TreeViewColumn(field, gtk.CellRendererText(),
text = counter))
counter += 1
treeview.set_model(model)
sw.show_all()
self.search_vbox.pack_start(sw)
return
self.dataform = dataforms.ExtendForm(node = form)
self.data_form_widget.set_sensitive(True)
try:
self.data_form_widget.data_form = self.dataform
except dataforms.Error:
self.label.set_text(_('Error in received dataform'))
self.label.show()
return
self.search_vbox.pack_start(self.data_form_widget)
self.data_form_widget.show()
if self.data_form_widget.title:
self.window.set_title('%s - Search - Gajim' % \
self.data_form_widget.title)

View File

@ -322,15 +322,13 @@ class GCTooltip(BaseTooltip):
# Add avatar
puny_name = helpers.sanitize_filename(contact.name)
puny_room = helpers.sanitize_filename(contact.room_jid)
for type_ in ('jpeg', 'png'):
file = os.path.join(gajim.AVATAR_PATH, puny_room,
puny_name + '.' + type_)
if os.path.exists(file):
self.avatar_image.set_from_file(file)
pix = self.avatar_image.get_pixbuf()
pix = gtkgui_helpers.get_scaled_pixbuf(pix, 'tooltip')
self.avatar_image.set_from_pixbuf(pix)
break
file = helpers.get_avatar_path(os.path.join(gajim.AVATAR_PATH, puny_room,
puny_name))
if file:
self.avatar_image.set_from_file(file)
pix = self.avatar_image.get_pixbuf()
pix = gtkgui_helpers.get_scaled_pixbuf(pix, 'tooltip')
self.avatar_image.set_from_pixbuf(pix)
else:
self.avatar_image.set_from_pixbuf(None)
while properties:
@ -375,7 +373,7 @@ class RosterTooltip(NotificationAreaTooltip):
def populate(self, contacts):
self.create_window()
self.create_table()
if not contacts or len(contacts) == 0:
# Tooltip for merged accounts row
@ -393,16 +391,14 @@ class RosterTooltip(NotificationAreaTooltip):
puny_jid = helpers.sanitize_filename(prim_contact.jid)
table_size = 3
for type_ in ('jpeg', 'png'):
file = os.path.join(gajim.AVATAR_PATH, puny_jid + '.' + type_)
if os.path.exists(file):
self.avatar_image.set_from_file(file)
pix = self.avatar_image.get_pixbuf()
pix = gtkgui_helpers.get_scaled_pixbuf(pix, 'tooltip')
self.avatar_image.set_from_pixbuf(pix)
table_size = 4
break
file = helpers.get_avatar_path(os.path.join(gajim.AVATAR_PATH, puny_jid))
if file:
self.avatar_image.set_from_file(file)
pix = self.avatar_image.get_pixbuf()
pix = gtkgui_helpers.get_scaled_pixbuf(pix, 'tooltip')
self.avatar_image.set_from_pixbuf(pix)
table_size = 4
else:
self.avatar_image.set_from_pixbuf(None)
vcard_table = gtk.Table(table_size, 1)
@ -414,8 +410,15 @@ class RosterTooltip(NotificationAreaTooltip):
name_markup = u'<span weight="bold">' + \
gobject.markup_escape_text(prim_contact.get_shown_name())\
+ '</span>'
if self.account and prim_contact.jid in gajim.connections[
self.account].blocked_contacts:
name_markup += _(' [blocked]')
if self.account and \
gajim.interface.minimized_controls.has_key(self.account) and \
prim_contact.jid in gajim.interface.minimized_controls[self.account]:
name_markup += _(' [minimized]')
properties.append((name_markup, None))
num_resources = 0
# put contacts in dict, where key is priority
contacts_dict = {}
@ -476,6 +479,12 @@ class RosterTooltip(NotificationAreaTooltip):
locale.getpreferredencoding())
text = text % local_time
show += text
if self.account and \
gajim.gc_connected[self.account].has_key(prim_contact.jid):
if gajim.gc_connected[self.account][prim_contact.jid]:
show = _('Connected')
else:
show = _('Disconnected')
show = '<i>' + show + '</i>'
# we append show below
@ -519,8 +528,9 @@ class RosterTooltip(NotificationAreaTooltip):
status = contact.status.strip()
if status:
# reduce long status
# (no more than 100 chars on line and no more than 5 lines)
status = helpers.reduce_chars_newlines(status, 100, 5)
# (no more than 300 chars on line and no more than 5 lines)
# status is wrapped
status = helpers.reduce_chars_newlines(status, 300, 5)
# escape markup entities.
status = gobject.markup_escape_text(status)
properties.append(('<i>%s</i>' % status, None))
@ -534,7 +544,8 @@ class RosterTooltip(NotificationAreaTooltip):
gobject.markup_escape_text(contact.resource) +\
' (' + unicode(contact.priority) + ')'))
if prim_contact.sub and prim_contact.sub != 'both':
if self.account and prim_contact.sub and prim_contact.sub != 'both' and\
not gajim.gc_connected[self.account].has_key(prim_contact.jid):
# ('both' is the normal sub so we don't show it)
properties.append(( _('Subscription: '),
gobject.markup_escape_text(helpers.get_uf_sub(prim_contact.sub))))
@ -571,6 +582,7 @@ class RosterTooltip(NotificationAreaTooltip):
else:
if isinstance(property[0], (unicode, str)): #FIXME: rm unicode?
label.set_markup(property[0])
label.set_line_wrap(True)
else:
label = property[0]
vcard_table.attach(label, 1, 3, vcard_current_row,

View File

@ -21,8 +21,11 @@ import gobject
import base64
import time
import locale
import os
import gtkgui_helpers
import dialogs
import message_control
from common import helpers
from common import gajim
@ -67,6 +70,27 @@ class VcardWindow:
self.account = account
self.gc_contact = gc_contact
self.xml.get_widget('no_user_avatar_label').set_no_show_all(True)
self.xml.get_widget('no_user_avatar_label').hide()
self.xml.get_widget('PHOTO_image').set_no_show_all(True)
self.xml.get_widget('PHOTO_image').hide()
image = gtk.Image()
self.photo_button = self.xml.get_widget('PHOTO_button')
self.photo_button.set_image(image)
self.nophoto_button = self.xml.get_widget('NOPHOTO_button')
puny_jid = helpers.sanitize_filename(contact.jid)
local_avatar_basepath = os.path.join(gajim.AVATAR_PATH, puny_jid) + \
'_local'
for extension in ('.png', '.jpeg'):
local_avatar_path = local_avatar_basepath + extension
if os.path.isfile(local_avatar_path):
image.set_from_file(local_avatar_path)
self.nophoto_button.set_no_show_all(True)
self.nophoto_button.hide()
break
else:
self.photo_button.set_no_show_all(True)
self.photo_button.hide()
self.avatar_mime_type = None
self.avatar_encoded = None
self.vcard_arrived = False
@ -88,6 +112,105 @@ class VcardWindow:
self.progressbar.pulse()
return True # loop forever
def update_avatar_in_gui(self):
jid = self.contact.jid
# Update roster
gajim.interface.roster.draw_avatar(jid, self.account)
# Update chat window
if gajim.interface.msg_win_mgr.has_window(jid, self.account):
win = gajim.interface.msg_win_mgr.get_window(jid, self.account)
ctrl = win.get_control(jid, self.account)
if win and ctrl.type_id != message_control.TYPE_GC:
ctrl.show_avatar()
def on_NOPHOTO_button_clicked(self, button):
def on_ok(widget, path_to_file):
filesize = os.path.getsize(path_to_file) # in bytes
invalid_file = False
msg = ''
if os.path.isfile(path_to_file):
stat = os.stat(path_to_file)
if stat[6] == 0:
invalid_file = True
msg = _('File is empty')
else:
invalid_file = True
msg = _('File does not exist')
if invalid_file:
dialogs.ErrorDialog(_('Could not load image'), msg)
return
try:
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
if filesize > 16384: # 16 kb
# get the image at 'notification size'
# and hope that user did not specify in ACE crazy size
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'tooltip')
except gobject.GError, msg: # unknown format
# msg should be string, not object instance
msg = str(msg)
dialogs.ErrorDialog(_('Could not load image'), msg)
return
puny_jid = helpers.sanitize_filename(self.contact.jid)
path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + '_local.png'
pixbuf.save(path_to_file, 'png')
self.dialog.destroy()
self.update_avatar_in_gui()
# rescale it
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
image = self.photo_button.get_image()
image.set_from_pixbuf(pixbuf)
self.photo_button.show()
self.nophoto_button.hide()
def on_clear(widget):
self.dialog.destroy()
self.on_clear_button_clicked(widget)
self.dialog = dialogs.AvatarChooserDialog(on_response_ok = on_ok,
on_response_clear = on_clear)
def on_clear_button_clicked(self, widget):
# empty the image
image = self.photo_button.get_image()
image.set_from_pixbuf(None)
self.photo_button.hide()
self.nophoto_button.show()
# Delete file:
puny_jid = helpers.sanitize_filename(self.contact.jid)
path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + '_local.png'
try:
os.remove(path_to_file)
except OSError:
gajim.log.debug('Cannot remove %s' % path_to_file)
self.update_avatar_in_gui()
def on_PHOTO_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3 and self.avatar_encoded: # right click
menu = gtk.Menu()
# Try to get pixbuf
# pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(self.jid)
# if pixbuf:
# nick = self.contact.get_shown_name()
# menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
# menuitem.connect('activate',
# gtkgui_helpers.on_avatar_save_as_menuitem_activate, self.jid,
# None, nick + '.jpeg')
# menu.append(menuitem)
# show clear
menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
menuitem.connect('activate', self.on_clear_button_clicked)
menu.append(menuitem)
menu.connect('selection-done', lambda w:w.destroy())
# show the menu
menu.show_all()
menu.popup(None, None, None, event.button, event.time)
elif event.button == 1: # left click
self.on_NOPHOTO_button_clicked(widget)
def on_vcard_information_window_destroy(self, widget):
if self.update_progressbar_timeout_id is not None:
gobject.source_remove(self.update_progressbar_timeout_id)
@ -100,27 +223,10 @@ class VcardWindow:
connection.annotations[self.contact.jid] = annotation
connection.store_annotations()
def on_vcard_information_window_key_press_event(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
self.window.destroy()
def on_log_history_checkbutton_toggled(self, widget):
#log conversation history?
oldlog = True
no_log_for = gajim.config.get_per('accounts', self.account,
'no_log_for').split()
if self.contact.jid in no_log_for:
oldlog = False
log = widget.get_active()
if not log and not self.contact.jid in no_log_for:
no_log_for.append(self.contact.jid)
if log and self.contact.jid in no_log_for:
no_log_for.remove(self.contact.jid)
if oldlog != log:
gajim.config.set_per('accounts', self.account, 'no_log_for',
' '.join(no_log_for))
def on_PHOTO_eventbox_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3: # right click
@ -153,12 +259,15 @@ class VcardWindow:
pass
def set_values(self, vcard):
if not 'PHOTO' in vcard:
self.xml.get_widget('no_user_avatar_label').show()
for i in vcard.keys():
if i == 'PHOTO' and self.xml.get_widget('information_notebook').\
get_n_pages() > 4:
pixbuf, self.avatar_encoded, self.avatar_mime_type = \
get_avatar_pixbuf_encoded_mime(vcard[i])
image = self.xml.get_widget('PHOTO_image')
image.show()
if not pixbuf:
image.set_from_icon_name('stock_person',
gtk.ICON_SIZE_DIALOG)
@ -298,14 +407,6 @@ class VcardWindow:
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(' '):
log = False
checkbutton = self.xml.get_widget('log_history_checkbutton')
checkbutton.set_active(log)
checkbutton.connect('toggled', self.on_log_history_checkbutton_toggled)
resources = '%s (%s)' % (self.contact.resource, unicode(
self.contact.priority))
uf_resources = self.contact.resource + _(' resource with priority ')\
@ -352,8 +453,11 @@ class VcardWindow:
self.fill_status_label()
gajim.connections[self.account].request_vcard(self.contact.jid,
self.gc_contact is not None)
if self.gc_contact:
gajim.connections[self.account].request_vcard(self.contact.jid,
self.gc_contact.get_full_jid())
else:
gajim.connections[self.account].request_vcard(self.contact.jid)
def on_close_button_clicked(self, widget):
self.window.destroy()
@ -385,22 +489,6 @@ class ZeroconfVcardWindow:
if event.keyval == gtk.keysyms.Escape:
self.window.destroy()
def on_log_history_checkbutton_toggled(self, widget):
#log conversation history?
oldlog = True
no_log_for = gajim.config.get_per('accounts', self.account,
'no_log_for').split()
if self.contact.jid in no_log_for:
oldlog = False
log = widget.get_active()
if not log and not self.contact.jid in no_log_for:
no_log_for.append(self.contact.jid)
if log and self.contact.jid in no_log_for:
no_log_for.remove(self.contact.jid)
if oldlog != log:
gajim.config.set_per('accounts', self.account, 'no_log_for',
' '.join(no_log_for))
def on_PHOTO_eventbox_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3: # right click
@ -469,14 +557,6 @@ class ZeroconfVcardWindow:
'</span></b>')
self.xml.get_widget('local_jid_label').set_text(self.contact.jid)
log = True
if self.contact.jid in gajim.config.get_per('accounts', self.account,
'no_log_for').split(' '):
log = False
checkbutton = self.xml.get_widget('log_history_checkbutton')
checkbutton.set_active(log)
checkbutton.connect('toggled', self.on_log_history_checkbutton_toggled)
resources = '%s (%s)' % (self.contact.resource, unicode(
self.contact.priority))
uf_resources = self.contact.resource + _(' resource with priority ')\