Merge with trunk
|
@ -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]
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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; \
|
||||
|
|
Before Width: | Height: | Size: 975 B After Width: | Height: | Size: 887 B |
Before Width: | Height: | Size: 1010 B After Width: | Height: | Size: 941 B |
Before Width: | Height: | Size: 983 B After Width: | Height: | Size: 936 B |
Before Width: | Height: | Size: 964 B After Width: | Height: | Size: 964 B |
Before Width: | Height: | Size: 1014 B After Width: | Height: | Size: 941 B |
Before Width: | Height: | Size: 1005 B After Width: | Height: | Size: 945 B |
Before Width: | Height: | Size: 969 B After Width: | Height: | Size: 896 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.1 KiB |
|
@ -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>
|
||||
|
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -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
|
||||
|
|
41
po/fr.po
|
@ -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>"
|
||||
|
|
4479
po/pt_BR.po
|
@ -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')
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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 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`""")
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
|
390
src/config.py
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
296
src/dialogs.py
|
@ -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
|
||||
|
|
71
src/disco.py
|
@ -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 = []
|
||||
|
|
213
src/gajim.py
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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,
|
||||
|
|
182
src/vcard.py
|
@ -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 ')\
|
||||
|
|