merge from trunk
This commit is contained in:
commit
a3863703f0
47
ChangeLog
47
ChangeLog
|
@ -1,4 +1,47 @@
|
|||
Gajim 0.13 (XX November 2009)
|
||||
Gajim 0.14 (XX)
|
||||
|
||||
* Jingle audio / video chat
|
||||
* Improve Startup time
|
||||
* Copy emoticons, LaTeX expressions when they are selected
|
||||
* Fix status icon transparency by using gtk.statusicon
|
||||
* Groupchat auto-rejoin
|
||||
* geolocation (with geoclue)
|
||||
* use XDG standards
|
||||
* SCRAM-SHA-1 and SASL EXTERNAL authentication
|
||||
* MUC captcha
|
||||
* Lots of refactoring
|
||||
|
||||
Gajim 0.13.4 (02 April 2010)
|
||||
|
||||
* Add japanese translation
|
||||
* Fix some TLS connection
|
||||
* Don't raise a lot of "DB Error" dialog
|
||||
* Fix contact synchronisation
|
||||
* Minor fixes
|
||||
|
||||
Gajim 0.13.3 (23 February 2010)
|
||||
|
||||
* Fix facebook xmpp server connection
|
||||
* Fix copy / paste with Ctrl+C on non-latin keyboard
|
||||
* Fix sending PEP information when connecting
|
||||
* Fix parsing HTML messages that have ascii markup
|
||||
|
||||
Gajim 0.13.2 (14 January 2010)
|
||||
|
||||
* Fix some translations
|
||||
* Fix string comparison according to locales
|
||||
* Fix resizing of groupchat occupant treeview
|
||||
* Fix some gnomekeyring glitches
|
||||
* better SRV usage with libasyncns
|
||||
* copy emoticons when we copy / paste in conversations
|
||||
|
||||
Gajim 0.13.1 (28 November 2009)
|
||||
|
||||
* Fix a bug when no account exists and bonjour is not available
|
||||
* Fix a bug when opening advanced option in MUC
|
||||
* Fix a bug when using non-BOSH proxies
|
||||
|
||||
Gajim 0.13 (24 November 2009)
|
||||
|
||||
* Improve gtkspell (fix memleak)
|
||||
* BOSH connection
|
||||
|
@ -44,7 +87,7 @@ Gajim 0.12.2 (07 June 2009)
|
|||
* Improve error messages handling
|
||||
* Totem support for played music
|
||||
* Fix SSL with some servers
|
||||
* Handle Xfce notification-daemon
|
||||
* Handle XFCE notification-daemon
|
||||
* Restore old behaviour of click on systray: left click to open events
|
||||
* Network manager 0.7 support
|
||||
* Move logs file under windows to $APPDATA/gajim
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
SUBDIRS = src data po icons
|
||||
SUBDIRS = src data plugins po icons
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
|
||||
<p>To specify where to install do:</p>
|
||||
<pre>
|
||||
su -c make PREFIX=custom_path install
|
||||
./configure --prefix=custom_path
|
||||
</pre>
|
||||
|
||||
<h2>Running Gajim</h2>
|
||||
|
@ -98,7 +98,8 @@ or if you didn't 'make install' you can also run from gajim folder with<em>./lau
|
|||
If you want to remove it from custom directory provide it as:
|
||||
</p>
|
||||
<pre>
|
||||
make PREFIX=custom_path uninstall
|
||||
./configure --prefix=custom_path
|
||||
make uninstall
|
||||
</pre>
|
||||
|
||||
<h2>Miscellaneous</h2>
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
gajimversion="0.13.90.1"
|
||||
if [ -d ".hg" ]; then
|
||||
hgversion="-$(hexdump -n6 -e'6/1 "%02x"' .hg/dirstate)"
|
||||
else
|
||||
hgversion=""
|
||||
fi
|
||||
echo "define([AC_PACKAGE_VERSION], [${gajimversion}${hgversion}])" > m4/hgversion.m4
|
||||
|
||||
AM_ARGS="--add-missing --gnu --copy"
|
||||
CONF_ARGS=""
|
||||
if test x`uname -s 2>/dev/null` = 'xDarwin' -a -f /Library/Frameworks/GTK+.framework/Versions/Current/env; then
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
AC_INIT([Gajim - A Jabber Instant Messager],
|
||||
[0.13.10.2-dev],[http://trac.gajim.org/],[gajim])
|
||||
["version-set-in-hgversion"],[http://trac.gajim.org/],[gajim])
|
||||
AC_PREREQ([2.59])
|
||||
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
|
@ -65,14 +65,17 @@ AC_ARG_ENABLE(site-packages,
|
|||
instead of DATADIR/gajim/src.])]
|
||||
,
|
||||
AC_SUBST([gajim_srcdir], [\${pkgpythondir}])
|
||||
AC_SUBST([gajim_pluginsdir], [\${pkgpythondir}])
|
||||
,
|
||||
AC_SUBST([gajim_srcdir], [\${datadir}/\${PACKAGE}/src])
|
||||
AC_SUBST([gajim_pluginsdir], [\${datadir}/\${PACKAGE}/plugins])
|
||||
)
|
||||
|
||||
AS_AC_EXPAND(GAJIM_SRCDIR, "${gajim_srcdir}")
|
||||
AS_AC_EXPAND(PKGDATADIR, "${datadir}/${PACKAGE}")
|
||||
AS_AC_EXPAND(DOCDIR, "${docdir}")
|
||||
AS_AC_EXPAND(LOCALEDIR, "${localedir}")
|
||||
AS_AC_EXPAND(GAJIM_PLUGINSDIR, "${gajim_pluginsdir}")
|
||||
|
||||
AC_SUBST(VERSION)
|
||||
AC_SUBST(PACKAGE)
|
||||
|
@ -94,6 +97,7 @@ AC_CONFIG_FILES([
|
|||
scripts/gajim-remote:scripts/gajim.in
|
||||
scripts/gajim-history-manager:scripts/gajim.in
|
||||
po/Makefile.in
|
||||
plugins/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
echo "
|
||||
|
@ -101,6 +105,7 @@ echo "
|
|||
Installation:
|
||||
Prefix ........... ${prefix}
|
||||
Python modules ... ${GAJIM_SRCDIR}
|
||||
Plugins .. ....... ${GAJIM_PLUGINSDIR}
|
||||
Documentation .... ${DOCDIR}
|
||||
Others ........... ${PKGDATADIR}
|
||||
*****************************"
|
||||
|
|
|
@ -14,7 +14,7 @@ sounds_DATA = $(srcdir)/sounds/*.wav
|
|||
otherdir = $(pkgdatadir)/data/other
|
||||
other_DATA = other/servers.xml other/cacerts.pem
|
||||
|
||||
man_MANS = gajim.1 gajim-remote.1
|
||||
man_MANS = gajim.1 gajim-remote.1 gajim-history-manager.1
|
||||
|
||||
|
||||
EXTRA_DIST = $(desktop_in_files) \
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
.\" 20050901
|
||||
.TH "Gajim-history-manager" "1" "September 01, 2005" "Gajim dev team" ""
|
||||
.SH "NAME"
|
||||
Gajim-history-manager \- Tool to manage gajim logs
|
||||
.SH "SYNOPSIS"
|
||||
.B gajim-history-manager [\-c config-path] [\-h]
|
||||
.SH "DESCRIPTION"
|
||||
.B Gajim-history-manager
|
||||
is a tool to manage (do some cleanup) log file of Gajim jabber client.
|
||||
.PP
|
||||
.SH "OPTIONS"
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-config-path\fR path
|
||||
Path where logs.db is located. ~/.gajim by default.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print this help.
|
||||
.SH "FEEDBACK"
|
||||
You can report bugs or feature requests in http://trac.gajim.org or in the mailing list: http://lists.gajim.org/cgi\-bin/listinfo/gajim\-devel. You can also find us in our room gajim@conference.gajim.org
|
||||
.SH "AUTHORS"
|
||||
Written by Yann Le Boulanger <asterix@lagaule.org>, Nikos Kouremenos <kourem@gmail.com> and Dimitur Kirov <dkirov@gmail.com>.
|
|
@ -217,7 +217,7 @@ to the Jabber network.</property>
|
|||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">@</property>
|
||||
<property name="label">@</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
|
|
|
@ -422,6 +422,21 @@
|
|||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="finish_button">
|
||||
<property name="label" translatable="yes">F_inish</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_finish_button_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
|
@ -435,7 +450,7 @@
|
|||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">4</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -9,6 +9,13 @@
|
|||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="archiving_preferences_menuitem">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Edit Archi_ving Preferences</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="privacy_lists_menuitem">
|
||||
<property name="label" translatable="yes">Edit _Privacy Lists...</property>
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<!-- column-name item text -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">No</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Yes</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore2">
|
||||
<columns>
|
||||
<!-- column-name item text -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Prefer</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Concede</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Forbid</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore3">
|
||||
<columns>
|
||||
<!-- column-name item text -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Prefer</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Concede</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Forbid</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore4">
|
||||
<columns>
|
||||
<!-- column-name item text -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Prefer</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Concede</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Forbid</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkWindow" id="archiving_preferences_window">
|
||||
<property name="border_width">12</property>
|
||||
<signal name="destroy" handler="on_archiving_preferences_window_destroy"/>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">4</property>
|
||||
<property name="n_columns">2</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><i>Method Manual</i></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><i>Method Local</i></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><i>Method Auto</i></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="method_manual_combobox">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore4</property>
|
||||
<signal name="changed" handler="on_method_foo_combobox_changed"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext4"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="method_local_combobox">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore3</property>
|
||||
<signal name="changed" handler="on_method_foo_combobox_changed"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext3"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<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>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="method_auto_combobox">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore2</property>
|
||||
<signal name="changed" handler="on_method_foo_combobox_changed"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext2"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Auto</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="auto_combobox">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore1</property>
|
||||
<signal name="changed" handler="on_auto_combobox_changed"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext1"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="item_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<signal name="cursor_changed" handler="on_item_treeview_cursor_changed"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHButtonBox" id="hbuttonbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">spread</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="add_button">
|
||||
<property name="label">gtk-add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_add_item_button_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="remove_button">
|
||||
<property name="label">gtk-remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_remove_item_button_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="edit_button">
|
||||
<property name="label">gtk-edit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_edit_item_button_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHButtonBox" id="hbuttonbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_close_button_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
File diff suppressed because it is too large
Load Diff
|
@ -75,6 +75,85 @@
|
|||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHPaned" id="hpaned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="border_width">3</property>
|
||||
<property name="position">495</property>
|
||||
<property name="position_set">True</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="gc_textviews_vbox">
|
||||
<property name="width_request">0</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="conversation_scrolledwindow">
|
||||
<property name="width_request">200</property>
|
||||
<property name="height_request">60</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="message_scrolledwindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="vscrollbar_policy">never</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="list_scrolledwindow">
|
||||
<property name="width_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="list_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="border_width">1</property>
|
||||
<property name="headers_visible">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="actions_hbox">
|
||||
<property name="visible">True</property>
|
||||
|
@ -133,7 +212,6 @@
|
|||
<object class="GtkVSeparator" id="vseparator2">
|
||||
<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="orientation">vertical</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -233,7 +311,6 @@
|
|||
<object class="GtkVSeparator" id="vseparator4">
|
||||
<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="orientation">vertical</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -269,6 +346,14 @@
|
|||
<property name="position">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="label_selector">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
|
@ -278,7 +363,7 @@
|
|||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">9</property>
|
||||
<property name="position">10</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -326,7 +411,7 @@
|
|||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">10</property>
|
||||
<property name="position">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -335,94 +420,5 @@
|
|||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHPaned" id="hpaned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="border_width">3</property>
|
||||
<property name="position">495</property>
|
||||
<property name="position_set">True</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox108">
|
||||
<property name="width_request">0</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox109">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="conversation_scrolledwindow">
|
||||
<property name="width_request">200</property>
|
||||
<property name="height_request">60</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="message_scrolledwindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="vscrollbar_policy">never</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="list_scrolledwindow">
|
||||
<property name="width_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="list_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="border_width">1</property>
|
||||
<property name="headers_visible">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">From</property>
|
||||
<property name="label" translatable="yes">From:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
|
@ -31,7 +31,7 @@
|
|||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Subject</property>
|
||||
<property name="label" translatable="yes">Subject:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkWindow" id="item_archiving_preferences_window">
|
||||
<property name="border_width">12</property>
|
||||
<signal name="destroy" handler="on_item_archiving_preferences_window_destroy"/>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table3">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">4</property>
|
||||
<property name="n_columns">2</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">expire</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="expire_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="otr_combobox">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore2</property>
|
||||
<signal name="changed" handler="on_otr_combobox_changed"/>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext2"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<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>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="save_combobox">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext1"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">save</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label9">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">otr</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">jid</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="jid_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkProgressBar" id="progressbar"/>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHButtonBox" id="hbuttonbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_cancel_button_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="ok_button">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_ok_button_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<!-- column-name item text -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">body</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">false</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">message</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">stream</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore2">
|
||||
<columns>
|
||||
<!-- column-name item text -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">approve</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">concede</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">forbid</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">oppose</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">prefer</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">require</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
</interface>
|
|
@ -44,6 +44,7 @@
|
|||
<property name="xalign">0</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="max_width_chars">9</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
|
|
|
@ -0,0 +1,644 @@
|
|||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkWindow" id="plugins_window">
|
||||
<property name="width_request">650</property>
|
||||
<property name="height_request">500</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="title" translatable="yes">Plugins</property>
|
||||
<property name="default_width">650</property>
|
||||
<property name="default_height">500</property>
|
||||
<signal name="destroy" handler="on_plugins_window_destroy"/>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="plugins_notebook">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkHPaned" id="hpaned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="position">250</property>
|
||||
<property name="position_set">True</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="installed_plugins_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="plugin_name_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">&lt;empty&gt;</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Version:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="plugin_version_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><empty></property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="yalign">0</property>
|
||||
<property name="label" translatable="yes">Authors:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="plugin_authors_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0</property>
|
||||
<property name="label" translatable="yes"><empty></property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
<property name="selectable">True</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox3">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Homepage:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLinkButton" id="plugin_homepage_linkbutton">
|
||||
<property name="label" translatable="yes">homepage url</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox5">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Descrition:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTextView" id="plugin_description_textview">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="pixels_above_lines">6</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="wrap_mode">word</property>
|
||||
<property name="left_margin">6</property>
|
||||
<property name="right_margin">6</property>
|
||||
<property name="indent">1</property>
|
||||
<property name="buffer">textbuffer1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHButtonBox" id="hbuttonbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="uninstall_plugin_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_uninstall_plugin_button_clicked"/>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox11">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-cancel</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="uninstall_plugin_button_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Uninstall</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="configure_plugin_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_configure_plugin_button_clicked"/>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox12">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image2">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-preferences</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="configure_plugin_button_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Configure</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Installed</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHPaned" id="hpaned2">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="treeview2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox6">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="plugin_name_label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><empty></property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment3">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox7">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Version:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="plugin_version_label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><empty></property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox8">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Authors:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="plugin_authors_label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><empty></property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox9">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label9">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Homepage:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLinkButton" id="plugin_homepage_linkbutton1">
|
||||
<property name="label" translatable="yes">button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox5">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox10">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Descrition:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment4">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTextView" id="plugin_description_textview1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHButtonBox" id="hbuttonbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="uninstall_plugin_button1">
|
||||
<property name="label" translatable="yes">button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="configure_plugin_button1">
|
||||
<property name="label" translatable="yes">button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label" translatable="yes">Available</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHButtonBox" id="hbuttonbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">15</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_close_button_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="textbuffer1">
|
||||
<property name="text" translatable="yes">Plug-in decription should be displayed here. This text will be erased during PluginsWindow initialization.</property>
|
||||
</object>
|
||||
</interface>
|
|
@ -2136,7 +2136,7 @@ $T will be replaced by auto-not-available timeout</property>
|
|||
<child>
|
||||
<object class="GtkTable" id="table8">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">2</property>
|
||||
<property name="n_rows">4</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<property name="row_spacing">6</property>
|
||||
|
@ -2184,6 +2184,52 @@ $T will be replaced by auto-not-available timeout</property>
|
|||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label26">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Video framerate</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label27">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Video size</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="video_framerate_combobox">
|
||||
<property name="visible">True</property>
|
||||
<signal name="changed" handler="on_video_framerate_combobox_changed"/>
|
||||
</object>
|
||||
<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>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="video_size_combobox">
|
||||
<property name="visible">True</property>
|
||||
<signal name="changed" handler="on_video_size_combobox_changed"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -54,8 +54,8 @@
|
|||
<child>
|
||||
<object class="GtkImageMenuItem" id="join_gc_menuitem">
|
||||
<property name="label" translatable="yes">Join _Group Chat...</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="image">image3</property>
|
||||
<property name="use_stock">False</property>
|
||||
<property name="accel_group">accelgroup1</property>
|
||||
|
@ -69,8 +69,8 @@
|
|||
<child>
|
||||
<object class="GtkImageMenuItem" id="add_new_contact_menuitem">
|
||||
<property name="label" translatable="yes">Add _Contact...</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="image">image4</property>
|
||||
<property name="use_stock">False</property>
|
||||
<property name="accel_group">accelgroup1</property>
|
||||
|
@ -159,6 +159,16 @@
|
|||
<signal name="activate" handler="on_preferences_menuitem_activate"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="plugins_menuitem">
|
||||
<property name="label" translatable="yes">P_lugins</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="image">image13</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_plugins_menuitem_activate"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -437,4 +447,9 @@
|
|||
<property name="stock">gtk-properties</property>
|
||||
<property name="icon-size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image13">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-disconnect</property>
|
||||
<property name="icon-size">1</property>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
gajim (0.13.90-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Yann Leboulanger <asterix@lagaule.org> Sat, 24 Jul 2010 10:25:26 +0200
|
||||
|
||||
gajim (0.13.4-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
* Fix flood when trying to join a full MUC. Closes: #575688
|
||||
|
||||
-- Yann Leboulanger <asterix@lagaule.org> Fri, 02 Apr 2010 10:19:59 +0200
|
||||
|
||||
gajim (0.13.3-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
|
|
@ -2,21 +2,21 @@ Source: gajim
|
|||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Yann Leboulanger <asterix@lagaule.org>
|
||||
Build-Depends: debhelper (>= 7), cdbs (>= 0.4.43), python-support (>= 0.7.1), python-dev, libgtk2.0-dev, python-gtk2-dev, gettext (>= 0.17-4), intltool (>= 0.40.1), imagemagick, python-central (>= 0.5)
|
||||
Build-Depends: debhelper (>= 7), cdbs (>= 0.4.43), python-support (>= 0.7.1), python-dev, libgtk2.0-dev, python-gtk2-dev, gettext (>= 0.17-4), intltool (>= 0.40.1), imagemagick
|
||||
Build-Conflicts: python2.3
|
||||
XS-Python-Version: >= 2.4
|
||||
XS-Python-Version: >= 2.5
|
||||
Standards-Version: 3.8.3
|
||||
Homepage: http://www.gajim.org
|
||||
Vcs-Hg: http://hg.gajim.org/gajim/
|
||||
Vcs-Browser: http://hg.gajim.org/gajim/file
|
||||
|
||||
Package: gajim
|
||||
Architecture: any
|
||||
Architecture: all
|
||||
XB-Python-Version: ${python:Versions}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}, ${python:Depends}, python-support (>= 0.7.1), python-glade2 (>= 2.12.0), python-gtk2 (>= 2.12.0), dnsutils
|
||||
Recommends: dbus, python-dbus, notification-daemon, python-gnupginterface, python-openssl, python-crypto
|
||||
Suggests: python-gconf, python-gnome2, nautilus-sendto, avahi-daemon, python-avahi, network-manager, libgtkspell0, aspell-en, python-gnomekeyring, gnome-keyring, python-sexy, python-kerberos (>= 1.1), texlive-latex-base, dvipng
|
||||
Suggests: python-gconf, python-gnome2, nautilus-sendto, avahi-daemon, python-avahi, network-manager, libgtkspell0, aspell-en, python-gnomekeyring, gnome-keyring, python-sexy, python-kerberos (>= 1.1), texlive-latex-base, dvipng, python-farsight, gstreamer0.10-plugins-ugly
|
||||
Description: Jabber client written in PyGTK
|
||||
Gajim is a Jabber client. It has a tabbed user interface with normal chats,
|
||||
group chats, and has many features such as, TLS, GPG, SSL, multiple accounts,
|
||||
avatars, file transfers, D-Bus and Metacontacts.
|
||||
avatars, file transfers, audio/video call, D-Bus and Metacontacts.
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
data/gajim.1
|
||||
data/gajim-remote.1
|
||||
debian/gajim-history-manager.1
|
||||
data/gajim-history-manager.1
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
2
|
|
@ -1 +1 @@
|
|||
2.4
|
||||
2.5-
|
||||
|
|
|
@ -7,12 +7,11 @@ include /usr/share/cdbs/1/rules/debhelper.mk
|
|||
include /usr/share/cdbs/1/rules/simple-patchsys.mk
|
||||
include /usr/share/cdbs/1/class/autotools.mk
|
||||
|
||||
PYTHONVER = 2.5
|
||||
DEB_CONFIGURE_EXTRA_FLAGS := --prefix=/usr
|
||||
DEB_MAKE_BUILD_TARGET := all PYTHON=python$(PYTHONVER)
|
||||
DEB_MAKE_INSTALL_TARGET = install PYTHON=python$(PYTHONVER) DESTDIR=$(DEB_DESTDIR)
|
||||
DEB_MAKE_BUILD_TARGET := all
|
||||
DEB_MAKE_INSTALL_TARGET = install DESTDIR=$(DEB_DESTDIR)
|
||||
|
||||
binary-install/gajim::
|
||||
rm $(DEB_DESTDIR)/usr/share/gajim/src/common/GnuPGInterface.py*
|
||||
dh_pysupport -pgajim
|
||||
convert $(DEB_DESTDIR)/usr/share/pixmaps/gajim.png -resize 32x32 $(DEB_DESTDIR)/usr/share/pixmaps/gajim.xpm
|
||||
convert $(DEB_DESTDIR)/usr/share/icons/hicolor/64x64/apps/gajim.png -resize 32x32 $(DEB_DESTDIR)/usr/share/pixmaps/gajim.xpm
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# Control file for uscan
|
||||
# Run the "uscan" command to check for upstream updates and more.
|
||||
# See uscan(1) for format
|
||||
|
||||
# Compulsory line, this is a version 3 file
|
||||
version=3
|
||||
|
||||
http://www.gajim.org/downloads/([\d.]*)/gajim-([\d\.]*)\.tar\.gz
|
Binary file not shown.
After Width: | Height: | Size: 828 B |
Binary file not shown.
After Width: | Height: | Size: 706 B |
Binary file not shown.
After Width: | Height: | Size: 767 B |
Binary file not shown.
After Width: | Height: | Size: 760 B |
|
@ -47,3 +47,8 @@ AC_DEFUN([AS_AC_EXPAND],
|
|||
prefix=$prefix_save
|
||||
exec_prefix=$exec_prefix_save
|
||||
])
|
||||
|
||||
# Fix autoconf: They don't allow shell variables in AC_INIT
|
||||
# So we have to define them via m4 to be accepted....
|
||||
|
||||
m4_include(m4/hgversion.m4)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
INCLUDES = \
|
||||
$(PYTHON_INCLUDES)
|
||||
|
||||
gajimpluginsdir = $(gajim_pluginsdir)
|
||||
nobase_dist_gajimplugins_PYTHON = \
|
||||
$(srcdir)/*.py \
|
||||
$(srcdir)/*/*.py \
|
||||
$(srcdir)/*/*.ui
|
||||
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
|
@ -0,0 +1,102 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
Acronyms expander plugin.
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 9th June 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log, log_calls
|
||||
|
||||
class AcronymsExpanderPlugin(GajimPlugin):
|
||||
name = u'Acronyms Expander'
|
||||
short_name = u'acronyms_expander'
|
||||
version = u'0.1'
|
||||
description = u'''Replaces acronyms (or other strings) with given expansions/substitutes.'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('AcronymsExpanderPlugin')
|
||||
def init(self):
|
||||
self.config_dialog = None
|
||||
|
||||
self.gui_extension_points = {
|
||||
'chat_control_base': (self.connect_with_chat_control_base,
|
||||
self.disconnect_from_chat_control_base)
|
||||
}
|
||||
|
||||
self.config_default_values = {
|
||||
'INVOKER': (' ', _('')),
|
||||
'ACRONYMS': ({'RTFM': 'Read The Friendly Manual',
|
||||
'/slap': '/me slaps',
|
||||
'PS-': 'plug-in system',
|
||||
'G-': 'Gajim',
|
||||
'GNT-': 'http://trac.gajim.org/newticket',
|
||||
'GW-': 'http://trac.gajim.org/',
|
||||
'GTS-': 'http://trac.gajim.org/report',
|
||||
},
|
||||
_('')),
|
||||
}
|
||||
|
||||
@log_calls('AcronymsExpanderPlugin')
|
||||
def textbuffer_live_acronym_expander(self, tb):
|
||||
"""
|
||||
@param tb gtk.TextBuffer
|
||||
"""
|
||||
#assert isinstance(tb,gtk.TextBuffer)
|
||||
ACRONYMS = self.config['ACRONYMS']
|
||||
INVOKER = self.config['INVOKER']
|
||||
t = tb.get_text(tb.get_start_iter(), tb.get_end_iter())
|
||||
#log.debug('%s %d'%(t, len(t)))
|
||||
if t and t[-1] == INVOKER:
|
||||
#log.debug('changing msg text')
|
||||
base, sep, head=t[:-1].rpartition(INVOKER)
|
||||
log.debug('%s | %s | %s'%(base, sep, head))
|
||||
if head in ACRONYMS:
|
||||
head = ACRONYMS[head]
|
||||
#log.debug('head: %s'%(head))
|
||||
t = ''.join((base, sep, head, INVOKER))
|
||||
#log.debug("setting text: '%s'"%(t))
|
||||
gobject.idle_add(tb.set_text, t)
|
||||
|
||||
@log_calls('AcronymsExpanderPlugin')
|
||||
def connect_with_chat_control_base(self, chat_control):
|
||||
d = {}
|
||||
tv = chat_control.msg_textview
|
||||
tb = tv.get_buffer()
|
||||
h_id = tb.connect('changed', self.textbuffer_live_acronym_expander)
|
||||
d['h_id'] = h_id
|
||||
|
||||
chat_control.acronyms_expander_plugin_data = d
|
||||
|
||||
return True
|
||||
|
||||
@log_calls('AcronymsExpanderPlugin')
|
||||
def disconnect_from_chat_control_base(self, chat_control):
|
||||
d = chat_control.acronyms_expander_plugin_data
|
||||
tv = chat_control.msg_textview
|
||||
tv.get_buffer().disconnect(d['h_id'])
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
from plugin import BannerTweaksPlugin
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkWindow" id="window1">
|
||||
<child>
|
||||
<object class="GtkVBox" id="banner_tweaks_config_vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">9</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="show_banner_image_checkbutton">
|
||||
<property name="label" translatable="yes">Display status icon</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">If checked, status icon will be displayed in chat window banner.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_show_banner_image_checkbutton_toggled"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="show_banner_online_msg_checkbutton">
|
||||
<property name="label" translatable="yes">Display status message of contact</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">If checked, status message of contact will be displayed in chat window banner.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_show_banner_online_msg_checkbutton_toggled"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="show_banner_resource_checkbutton">
|
||||
<property name="label" translatable="yes">Display resource name of contact</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">If checked, resource name of contact will be displayed in chat window banner.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_show_banner_resource_checkbutton_toggled"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="banner_small_fonts_checkbutton">
|
||||
<property name="label" translatable="yes">Use small fonts for contact name and resource name</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">If checked, smaller font will be used to display resource name and contact name in chat window banner.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_banner_small_fonts_checkbutton_toggled"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,205 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
Adjustable chat window banner.
|
||||
|
||||
Includes tweaks to make it compact.
|
||||
|
||||
Based on patch by pb in ticket #4133:
|
||||
http://trac.gajim.org/attachment/ticket/4133/gajim-chatbanneroptions-svn10008.patch
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 30 July 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import message_control
|
||||
from common import i18n
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log, log_calls
|
||||
from plugins.gui import GajimPluginConfigDialog
|
||||
|
||||
class BannerTweaksPlugin(GajimPlugin):
|
||||
name = u'Banner Tweaks'
|
||||
short_name = u'banner_tweaks'
|
||||
version = u'0.1'
|
||||
description = u'''Allows user to tweak chat window banner appearance (eg. make it compact).
|
||||
|
||||
Based on patch by pb in ticket #4133:
|
||||
http://trac.gajim.org/attachment/ticket/4133'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('BannerTweaksPlugin')
|
||||
def init(self):
|
||||
self.config_dialog = BannerTweaksPluginConfigDialog(self)
|
||||
|
||||
self.gui_extension_points = {
|
||||
'chat_control_base_draw_banner': (self.chat_control_base_draw_banner_called,
|
||||
self.chat_control_base_draw_banner_deactivation)
|
||||
}
|
||||
|
||||
self.config_default_values = {
|
||||
'show_banner_image': (True, _('If True, Gajim will display a status icon in the banner of chat windows.')),
|
||||
'show_banner_online_msg': (True, _('If True, Gajim will display the status message of the contact in the banner of chat windows.')),
|
||||
'show_banner_resource': (False, _('If True, Gajim will display the resource name of the contact in the banner of chat windows.')),
|
||||
'banner_small_fonts': (False, _('If True, Gajim will use small fonts for contact name and resource name in the banner of chat windows.')),
|
||||
'old_chat_avatar_height': (52, _('chat_avatar_height value before plugin was activated')),
|
||||
}
|
||||
|
||||
@log_calls('BannerTweaksPlugin')
|
||||
def activate(self):
|
||||
self.config['old_chat_avatar_height'] = gajim.config.get('chat_avatar_height')
|
||||
#gajim.config.set('chat_avatar_height', 28)
|
||||
|
||||
@log_calls('BannerTweaksPlugin')
|
||||
def deactivate(self):
|
||||
gajim.config.set('chat_avatar_height', self.config['old_chat_avatar_height'])
|
||||
|
||||
@log_calls('BannerTweaksPlugin')
|
||||
def chat_control_base_draw_banner_called(self, chat_control):
|
||||
if not self.config['show_banner_online_msg']:
|
||||
chat_control.banner_status_label.hide()
|
||||
chat_control.banner_status_label.set_no_show_all(True)
|
||||
status_text = ''
|
||||
chat_control.banner_status_label.set_markup(status_text)
|
||||
|
||||
if not self.config['show_banner_image']:
|
||||
banner_status_img = chat_control.xml.get_object('banner_status_image')
|
||||
banner_status_img.clear()
|
||||
|
||||
# TODO: part below repeats a lot of code from ChatControl.draw_banner_text()
|
||||
# This could be rewritten using re module: getting markup text from
|
||||
# banner_name_label and replacing some elements based on plugin config.
|
||||
# Would it be faster?
|
||||
if self.config['show_banner_resource'] or self.config['banner_small_fonts']:
|
||||
banner_name_label = chat_control.xml.get_object('banner_name_label')
|
||||
label_text = banner_name_label.get_label()
|
||||
|
||||
contact = chat_control.contact
|
||||
jid = contact.jid
|
||||
|
||||
name = contact.get_shown_name()
|
||||
if chat_control.resource:
|
||||
name += '/' + chat_control.resource
|
||||
elif contact.resource and self.config['show_banner_resource']:
|
||||
name += '/' + contact.resource
|
||||
|
||||
if chat_control.TYPE_ID == message_control.TYPE_PM:
|
||||
name = _('%(nickname)s from group chat %(room_name)s') %\
|
||||
{'nickname': name, 'room_name': chat_control.room_name}
|
||||
name = gobject.markup_escape_text(name)
|
||||
|
||||
# We know our contacts nick, but if another contact has the same nick
|
||||
# in another account we need to also display the account.
|
||||
# except if we are talking to two different resources of the same contact
|
||||
acct_info = ''
|
||||
for account in gajim.contacts.get_accounts():
|
||||
if account == chat_control.account:
|
||||
continue
|
||||
if acct_info: # We already found a contact with same nick
|
||||
break
|
||||
for jid in gajim.contacts.get_jid_list(account):
|
||||
other_contact_ = \
|
||||
gajim.contacts.get_first_contact_from_jid(account, jid)
|
||||
if other_contact_.get_shown_name() == chat_control.contact.get_shown_name():
|
||||
acct_info = ' (%s)' % \
|
||||
gobject.markup_escape_text(chat_control.account)
|
||||
break
|
||||
|
||||
font_attrs, font_attrs_small = chat_control.get_font_attrs()
|
||||
if self.config['banner_small_fonts']:
|
||||
font_attrs = font_attrs_small
|
||||
|
||||
st = gajim.config.get('displayed_chat_state_notifications')
|
||||
cs = contact.chatstate
|
||||
if cs and st in ('composing_only', 'all'):
|
||||
if contact.show == 'offline':
|
||||
chatstate = ''
|
||||
elif contact.composing_xep == 'XEP-0085':
|
||||
if st == 'all' or cs == 'composing':
|
||||
chatstate = helpers.get_uf_chatstate(cs)
|
||||
else:
|
||||
chatstate = ''
|
||||
elif contact.composing_xep == 'XEP-0022':
|
||||
if cs in ('composing', 'paused'):
|
||||
# only print composing, paused
|
||||
chatstate = helpers.get_uf_chatstate(cs)
|
||||
else:
|
||||
chatstate = ''
|
||||
else:
|
||||
# When does that happen ? See [7797] and [7804]
|
||||
chatstate = helpers.get_uf_chatstate(cs)
|
||||
|
||||
label_text = '<span %s>%s</span><span %s>%s %s</span>' % \
|
||||
(font_attrs, name, font_attrs_small, acct_info, chatstate)
|
||||
else:
|
||||
# weight="heavy" size="x-large"
|
||||
label_text = '<span %s>%s</span><span %s>%s</span>' % \
|
||||
(font_attrs, name, font_attrs_small, acct_info)
|
||||
|
||||
banner_name_label.set_markup(label_text)
|
||||
|
||||
@log_calls('BannerTweaksPlugin')
|
||||
def chat_control_base_draw_banner_deactivation(self, chat_control):
|
||||
pass
|
||||
#chat_control.draw_banner()
|
||||
|
||||
class BannerTweaksPluginConfigDialog(GajimPluginConfigDialog):
|
||||
def init(self):
|
||||
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
|
||||
'config_dialog.ui')
|
||||
self.xml = gtk.Builder()
|
||||
self.xml.set_translation_domain(i18n.APP)
|
||||
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
|
||||
['banner_tweaks_config_vbox'])
|
||||
self.config_vbox = self.xml.get_object('banner_tweaks_config_vbox')
|
||||
self.child.pack_start(self.config_vbox)
|
||||
|
||||
self.show_banner_image_checkbutton = self.xml.get_object('show_banner_image_checkbutton')
|
||||
self.show_banner_online_msg_checkbutton = self.xml.get_object('show_banner_online_msg_checkbutton')
|
||||
self.show_banner_resource_checkbutton = self.xml.get_object('show_banner_resource_checkbutton')
|
||||
self.banner_small_fonts_checkbutton = self.xml.get_object('banner_small_fonts_checkbutton')
|
||||
|
||||
self.xml.connect_signals(self)
|
||||
|
||||
def on_run(self):
|
||||
self.show_banner_image_checkbutton.set_active(self.plugin.config['show_banner_image'])
|
||||
self.show_banner_online_msg_checkbutton.set_active(self.plugin.config['show_banner_online_msg'])
|
||||
self.show_banner_resource_checkbutton.set_active(self.plugin.config['show_banner_resource'])
|
||||
self.banner_small_fonts_checkbutton.set_active(self.plugin.config['banner_small_fonts'])
|
||||
|
||||
def on_show_banner_image_checkbutton_toggled(self, button):
|
||||
self.plugin.config['show_banner_image'] = button.get_active()
|
||||
|
||||
def on_show_banner_online_msg_checkbutton_toggled(self, button):
|
||||
self.plugin.config['show_banner_online_msg'] = button.get_active()
|
||||
|
||||
def on_show_banner_resource_checkbutton_toggled(self, button):
|
||||
self.plugin.config['show_banner_resource'] = button.get_active()
|
||||
|
||||
def on_banner_small_fonts_checkbutton_toggled(self, button):
|
||||
self.plugin.config['banner_small_fonts'] = button.get_active()
|
|
@ -0,0 +1 @@
|
|||
from plugin import DBusPlugin
|
|
@ -0,0 +1,738 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
## Copyright (C) 2005-2006 Yann Leboulanger <asterix@lagaule.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
|
||||
## Copyright (C) 2005-2006 Andrew Sayman <lorien420@myrealbox.com>
|
||||
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
|
||||
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
|
||||
## Copyright (C) 2007 Travis Shirk <travis@pobox.com>
|
||||
## Copyright (C) 2008 Mateusz Biliński <mateusz@bilinski.it>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
'''
|
||||
D-BUS Support plugin.
|
||||
|
||||
Based on src/remote_control.py
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 8th August 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
import os
|
||||
import new
|
||||
|
||||
import gobject
|
||||
|
||||
|
||||
from common import dbus_support
|
||||
if dbus_support.supported:
|
||||
import dbus
|
||||
if dbus_support:
|
||||
INTERFACE = 'org.gajim.dbusplugin.RemoteInterface'
|
||||
OBJ_PATH = '/org/gajim/dbusplugin/RemoteObject'
|
||||
SERVICE = 'org.gajim.dbusplugin'
|
||||
|
||||
import dbus.service
|
||||
import dbus.glib
|
||||
# type mapping
|
||||
|
||||
# in most cases it is a utf-8 string
|
||||
DBUS_STRING = dbus.String
|
||||
|
||||
# general type (for use in dicts, where all values should have the same type)
|
||||
DBUS_BOOLEAN = dbus.Boolean
|
||||
DBUS_DOUBLE = dbus.Double
|
||||
DBUS_INT32 = dbus.Int32
|
||||
# dictionary with string key and binary value
|
||||
DBUS_DICT_SV = lambda : dbus.Dictionary({}, signature="sv")
|
||||
# dictionary with string key and value
|
||||
DBUS_DICT_SS = lambda : dbus.Dictionary({}, signature="ss")
|
||||
# empty type (there is no equivalent of None on D-Bus, but historically gajim
|
||||
# used 0 instead)
|
||||
DBUS_NONE = lambda : dbus.Int32(0)
|
||||
|
||||
def get_dbus_struct(obj):
|
||||
''' recursively go through all the items and replace
|
||||
them with their casted dbus equivalents
|
||||
'''
|
||||
if obj is None:
|
||||
return DBUS_NONE()
|
||||
if isinstance(obj, (unicode, str)):
|
||||
return DBUS_STRING(obj)
|
||||
if isinstance(obj, int):
|
||||
return DBUS_INT32(obj)
|
||||
if isinstance(obj, float):
|
||||
return DBUS_DOUBLE(obj)
|
||||
if isinstance(obj, bool):
|
||||
return DBUS_BOOLEAN(obj)
|
||||
if isinstance(obj, (list, tuple)):
|
||||
result = dbus.Array([get_dbus_struct(i) for i in obj],
|
||||
signature='v')
|
||||
if result == []:
|
||||
return DBUS_NONE()
|
||||
return result
|
||||
if isinstance(obj, dict):
|
||||
result = DBUS_DICT_SV()
|
||||
for key, value in obj.items():
|
||||
result[DBUS_STRING(key)] = get_dbus_struct(value)
|
||||
if result == {}:
|
||||
return DBUS_NONE()
|
||||
return result
|
||||
# unknown type
|
||||
return DBUS_NONE()
|
||||
|
||||
class SignalObject(dbus.service.Object):
|
||||
''' Local object definition for /org/gajim/dbus/RemoteObject.
|
||||
(This docstring is not be visible, because the clients can access only the remote object.)'''
|
||||
|
||||
def __init__(self, bus_name):
|
||||
self.first_show = True
|
||||
self.vcard_account = None
|
||||
|
||||
# register our dbus API
|
||||
dbus.service.Object.__init__(self, bus_name, OBJ_PATH)
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def Roster(self, account_and_data):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def AccountPresence(self, status_and_account):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def ContactPresence(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def ContactAbsence(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def ContactStatus(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def NewMessage(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def Subscribe(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def Subscribed(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def Unsubscribed(self, account_and_jid):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def NewAccount(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def VcardInfo(self, account_and_vcard):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def LastStatusTime(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def OsInfo(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def GCPresence(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def GCMessage(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def RosterInfo(self, account_and_array):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(INTERFACE, signature='av')
|
||||
def NewGmail(self, account_and_array):
|
||||
pass
|
||||
|
||||
def raise_signal(self, signal, arg):
|
||||
'''raise a signal, with a single argument of unspecified type
|
||||
Instead of obj.raise_signal("Foo", bar), use obj.Foo(bar).'''
|
||||
getattr(self, signal)(arg)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='s', out_signature='s')
|
||||
def get_status(self, account):
|
||||
'''Returns status (show to be exact) which is the global one
|
||||
unless account is given'''
|
||||
if not account:
|
||||
# If user did not ask for account, returns the global status
|
||||
return DBUS_STRING(helpers.get_global_show())
|
||||
# return show for the given account
|
||||
index = gajim.connections[account].connected
|
||||
return DBUS_STRING(gajim.SHOW_LIST[index])
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='s', out_signature='s')
|
||||
def get_status_message(self, account):
|
||||
'''Returns status which is the global one
|
||||
unless account is given'''
|
||||
if not account:
|
||||
# If user did not ask for account, returns the global status
|
||||
return DBUS_STRING(str(helpers.get_global_status()))
|
||||
# return show for the given account
|
||||
status = gajim.connections[account].status
|
||||
return DBUS_STRING(status)
|
||||
|
||||
def _get_account_and_contact(self, account, jid):
|
||||
'''get the account (if not given) and contact instance from jid'''
|
||||
connected_account = None
|
||||
contact = None
|
||||
accounts = gajim.contacts.get_accounts()
|
||||
# if there is only one account in roster, take it as default
|
||||
# if user did not ask for account
|
||||
if not account and len(accounts) == 1:
|
||||
account = accounts[0]
|
||||
if account:
|
||||
if gajim.connections[account].connected > 1: # account is connected
|
||||
connected_account = account
|
||||
contact = gajim.contacts.get_contact_with_highest_priority(account,
|
||||
jid)
|
||||
else:
|
||||
for account in accounts:
|
||||
contact = gajim.contacts.get_contact_with_highest_priority(account,
|
||||
jid)
|
||||
if contact and gajim.connections[account].connected > 1:
|
||||
# account is connected
|
||||
connected_account = account
|
||||
break
|
||||
if not contact:
|
||||
contact = jid
|
||||
|
||||
return connected_account, contact
|
||||
|
||||
def _get_account_for_groupchat(self, account, room_jid):
|
||||
'''get the account which is connected to groupchat (if not given)
|
||||
or check if the given account is connected to the groupchat'''
|
||||
connected_account = None
|
||||
accounts = gajim.contacts.get_accounts()
|
||||
# if there is only one account in roster, take it as default
|
||||
# if user did not ask for account
|
||||
if not account and len(accounts) == 1:
|
||||
account = accounts[0]
|
||||
if account:
|
||||
if gajim.connections[account].connected > 1 and \
|
||||
room_jid in gajim.gc_connected[account] and \
|
||||
gajim.gc_connected[account][room_jid]:
|
||||
# account and groupchat are connected
|
||||
connected_account = account
|
||||
else:
|
||||
for account in accounts:
|
||||
if gajim.connections[account].connected > 1 and \
|
||||
room_jid in gajim.gc_connected[account] and \
|
||||
gajim.gc_connected[account][room_jid]:
|
||||
# account and groupchat are connected
|
||||
connected_account = account
|
||||
break
|
||||
return connected_account
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
|
||||
def send_file(self, file_path, jid, account):
|
||||
'''send file, located at 'file_path' to 'jid', using account
|
||||
(optional) 'account' '''
|
||||
jid = self._get_real_jid(jid, account)
|
||||
connected_account, contact = self._get_account_and_contact(account, jid)
|
||||
|
||||
if connected_account:
|
||||
if file_path[:7] == 'file://':
|
||||
file_path=file_path[7:]
|
||||
if os.path.isfile(file_path): # is it file?
|
||||
gajim.interface.instances['file_transfers'].send_file(
|
||||
connected_account, contact, file_path)
|
||||
return DBUS_BOOLEAN(True)
|
||||
return DBUS_BOOLEAN(False)
|
||||
|
||||
def _send_message(self, jid, message, keyID, account, type = 'chat',
|
||||
subject = None):
|
||||
'''can be called from send_chat_message (default when send_message)
|
||||
or send_single_message'''
|
||||
if not jid or not message:
|
||||
return DBUS_BOOLEAN(False)
|
||||
if not keyID:
|
||||
keyID = ''
|
||||
|
||||
connected_account, contact = self._get_account_and_contact(account, jid)
|
||||
if connected_account:
|
||||
connection = gajim.connections[connected_account]
|
||||
connection.send_message(jid, message, keyID, type, subject)
|
||||
return DBUS_BOOLEAN(True)
|
||||
return DBUS_BOOLEAN(False)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='ssss', out_signature='b')
|
||||
def send_chat_message(self, jid, message, keyID, account):
|
||||
'''Send chat 'message' to 'jid', using account (optional) 'account'.
|
||||
if keyID is specified, encrypt the message with the pgp key '''
|
||||
jid = self._get_real_jid(jid, account)
|
||||
return self._send_message(jid, message, keyID, account)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='sssss', out_signature='b')
|
||||
def send_single_message(self, jid, subject, message, keyID, account):
|
||||
'''Send single 'message' to 'jid', using account (optional) 'account'.
|
||||
if keyID is specified, encrypt the message with the pgp key '''
|
||||
jid = self._get_real_jid(jid, account)
|
||||
return self._send_message(jid, message, keyID, account, type, subject)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
|
||||
def send_groupchat_message(self, room_jid, message, account):
|
||||
'''Send 'message' to groupchat 'room_jid',
|
||||
using account (optional) 'account'.'''
|
||||
if not room_jid or not message:
|
||||
return DBUS_BOOLEAN(False)
|
||||
connected_account = self._get_account_for_groupchat(account, room_jid)
|
||||
if connected_account:
|
||||
connection = gajim.connections[connected_account]
|
||||
connection.send_gc_message(room_jid, message)
|
||||
return DBUS_BOOLEAN(True)
|
||||
return DBUS_BOOLEAN(False)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='ss', out_signature='b')
|
||||
def open_chat(self, jid, account):
|
||||
'''Shows the tabbed window for new message to 'jid', using account
|
||||
(optional) 'account' '''
|
||||
if not jid:
|
||||
raise MissingArgument
|
||||
return DBUS_BOOLEAN(False)
|
||||
jid = self._get_real_jid(jid, account)
|
||||
try:
|
||||
jid = helpers.parse_jid(jid)
|
||||
except:
|
||||
# Jid is not conform, ignore it
|
||||
return DBUS_BOOLEAN(False)
|
||||
|
||||
if account:
|
||||
accounts = [account]
|
||||
else:
|
||||
accounts = gajim.connections.keys()
|
||||
if len(accounts) == 1:
|
||||
account = accounts[0]
|
||||
connected_account = None
|
||||
first_connected_acct = None
|
||||
for acct in accounts:
|
||||
if gajim.connections[acct].connected > 1: # account is online
|
||||
contact = gajim.contacts.get_first_contact_from_jid(acct, jid)
|
||||
if gajim.interface.msg_win_mgr.has_window(jid, acct):
|
||||
connected_account = acct
|
||||
break
|
||||
# jid is in roster
|
||||
elif contact:
|
||||
connected_account = acct
|
||||
break
|
||||
# we send the message to jid not in roster, because account is
|
||||
# specified, or there is only one account
|
||||
elif account:
|
||||
connected_account = acct
|
||||
elif first_connected_acct is None:
|
||||
first_connected_acct = acct
|
||||
|
||||
# if jid is not a conntact, open-chat with first connected account
|
||||
if connected_account is None and first_connected_acct:
|
||||
connected_account = first_connected_acct
|
||||
|
||||
if connected_account:
|
||||
gajim.interface.new_chat_from_jid(connected_account, jid)
|
||||
# preserve the 'steal focus preservation'
|
||||
win = gajim.interface.msg_win_mgr.get_window(jid,
|
||||
connected_account).window
|
||||
if win.get_property('visible'):
|
||||
win.window.focus()
|
||||
return DBUS_BOOLEAN(True)
|
||||
return DBUS_BOOLEAN(False)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
|
||||
def change_status(self, status, message, account):
|
||||
''' change_status(status, message, account). account is optional -
|
||||
if not specified status is changed for all accounts. '''
|
||||
if status not in ('offline', 'online', 'chat',
|
||||
'away', 'xa', 'dnd', 'invisible'):
|
||||
return DBUS_BOOLEAN(False)
|
||||
if account:
|
||||
gobject.idle_add(gajim.interface.roster.send_status, account,
|
||||
status, message)
|
||||
else:
|
||||
# account not specified, so change the status of all accounts
|
||||
for acc in gajim.contacts.get_accounts():
|
||||
if not gajim.config.get_per('accounts', acc,
|
||||
'sync_with_global_status'):
|
||||
continue
|
||||
gobject.idle_add(gajim.interface.roster.send_status, acc,
|
||||
status, message)
|
||||
return DBUS_BOOLEAN(False)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='', out_signature='')
|
||||
def show_next_pending_event(self):
|
||||
'''Show the window(s) with next pending event in tabbed/group chats.'''
|
||||
if gajim.events.get_nb_events():
|
||||
gajim.interface.systray.handle_first_event()
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='s', out_signature='a{sv}')
|
||||
def contact_info(self, jid):
|
||||
'''get vcard info for a contact. Return cached value of the vcard.
|
||||
'''
|
||||
if not isinstance(jid, unicode):
|
||||
jid = unicode(jid)
|
||||
if not jid:
|
||||
raise MissingArgument
|
||||
return DBUS_DICT_SV()
|
||||
jid = self._get_real_jid(jid)
|
||||
|
||||
cached_vcard = gajim.connections.values()[0].get_cached_vcard(jid)
|
||||
if cached_vcard:
|
||||
return get_dbus_struct(cached_vcard)
|
||||
|
||||
# return empty dict
|
||||
return DBUS_DICT_SV()
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='', out_signature='as')
|
||||
def list_accounts(self):
|
||||
'''list register accounts'''
|
||||
result = gajim.contacts.get_accounts()
|
||||
result_array = dbus.Array([], signature='s')
|
||||
if result and len(result) > 0:
|
||||
for account in result:
|
||||
result_array.append(DBUS_STRING(account))
|
||||
return result_array
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='s', out_signature='a{ss}')
|
||||
def account_info(self, account):
|
||||
'''show info on account: resource, jid, nick, prio, message'''
|
||||
result = DBUS_DICT_SS()
|
||||
if gajim.connections.has_key(account):
|
||||
# account is valid
|
||||
con = gajim.connections[account]
|
||||
index = con.connected
|
||||
result['status'] = DBUS_STRING(gajim.SHOW_LIST[index])
|
||||
result['name'] = DBUS_STRING(con.name)
|
||||
result['jid'] = DBUS_STRING(gajim.get_jid_from_account(con.name))
|
||||
result['message'] = DBUS_STRING(con.status)
|
||||
result['priority'] = DBUS_STRING(unicode(con.priority))
|
||||
result['resource'] = DBUS_STRING(unicode(gajim.config.get_per(
|
||||
'accounts', con.name, 'resource')))
|
||||
return result
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='s', out_signature='aa{sv}')
|
||||
def list_contacts(self, account):
|
||||
'''list all contacts in the roster. If the first argument is specified,
|
||||
then return the contacts for the specified account'''
|
||||
result = dbus.Array([], signature='aa{sv}')
|
||||
accounts = gajim.contacts.get_accounts()
|
||||
if len(accounts) == 0:
|
||||
return result
|
||||
if account:
|
||||
accounts_to_search = [account]
|
||||
else:
|
||||
accounts_to_search = accounts
|
||||
for acct in accounts_to_search:
|
||||
if acct in accounts:
|
||||
for jid in gajim.contacts.get_jid_list(acct):
|
||||
item = self._contacts_as_dbus_structure(
|
||||
gajim.contacts.get_contacts(acct, jid))
|
||||
if item:
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='', out_signature='')
|
||||
def toggle_roster_appearance(self):
|
||||
''' shows/hides the roster window '''
|
||||
win = gajim.interface.roster.window
|
||||
if win.get_property('visible'):
|
||||
gobject.idle_add(win.hide)
|
||||
else:
|
||||
win.present()
|
||||
# preserve the 'steal focus preservation'
|
||||
if self._is_first():
|
||||
win.window.focus()
|
||||
else:
|
||||
win.window.focus(long(time()))
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='', out_signature='')
|
||||
def toggle_ipython(self):
|
||||
''' shows/hides the ipython window '''
|
||||
win = gajim.ipython_window
|
||||
if win:
|
||||
if win.window.is_visible():
|
||||
gobject.idle_add(win.hide)
|
||||
else:
|
||||
win.show_all()
|
||||
win.present()
|
||||
else:
|
||||
gajim.interface.create_ipython_window()
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='', out_signature='a{ss}')
|
||||
def prefs_list(self):
|
||||
prefs_dict = DBUS_DICT_SS()
|
||||
def get_prefs(data, name, path, value):
|
||||
if value is None:
|
||||
return
|
||||
key = ''
|
||||
if path is not None:
|
||||
for node in path:
|
||||
key += node + '#'
|
||||
key += name
|
||||
prefs_dict[DBUS_STRING(key)] = DBUS_STRING(value[1])
|
||||
gajim.config.foreach(get_prefs)
|
||||
return prefs_dict
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='', out_signature='b')
|
||||
def prefs_store(self):
|
||||
try:
|
||||
gajim.interface.save_config()
|
||||
except Exception, e:
|
||||
return DBUS_BOOLEAN(False)
|
||||
return DBUS_BOOLEAN(True)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
|
||||
def prefs_del(self, key):
|
||||
if not key:
|
||||
return DBUS_BOOLEAN(False)
|
||||
key_path = key.split('#', 2)
|
||||
if len(key_path) != 3:
|
||||
return DBUS_BOOLEAN(False)
|
||||
if key_path[2] == '*':
|
||||
gajim.config.del_per(key_path[0], key_path[1])
|
||||
else:
|
||||
gajim.config.del_per(key_path[0], key_path[1], key_path[2])
|
||||
return DBUS_BOOLEAN(True)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
|
||||
def prefs_put(self, key):
|
||||
if not key:
|
||||
return DBUS_BOOLEAN(False)
|
||||
key_path = key.split('#', 2)
|
||||
if len(key_path) < 3:
|
||||
subname, value = key.split('=', 1)
|
||||
gajim.config.set(subname, value)
|
||||
return DBUS_BOOLEAN(True)
|
||||
subname, value = key_path[2].split('=', 1)
|
||||
gajim.config.set_per(key_path[0], key_path[1], subname, value)
|
||||
return DBUS_BOOLEAN(True)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='ss', out_signature='b')
|
||||
def add_contact(self, jid, account):
|
||||
if account:
|
||||
if account in gajim.connections and \
|
||||
gajim.connections[account].connected > 1:
|
||||
# if given account is active, use it
|
||||
AddNewContactWindow(account = account, jid = jid)
|
||||
else:
|
||||
# wrong account
|
||||
return DBUS_BOOLEAN(False)
|
||||
else:
|
||||
# if account is not given, show account combobox
|
||||
AddNewContactWindow(account = None, jid = jid)
|
||||
return DBUS_BOOLEAN(True)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='ss', out_signature='b')
|
||||
def remove_contact(self, jid, account):
|
||||
jid = self._get_real_jid(jid, account)
|
||||
accounts = gajim.contacts.get_accounts()
|
||||
|
||||
# if there is only one account in roster, take it as default
|
||||
if account:
|
||||
accounts = [account]
|
||||
contact_exists = False
|
||||
for account in accounts:
|
||||
contacts = gajim.contacts.get_contacts(account, jid)
|
||||
if contacts:
|
||||
gajim.connections[account].unsubscribe(jid)
|
||||
for contact in contacts:
|
||||
gajim.interface.roster.remove_contact(contact, account)
|
||||
gajim.contacts.remove_jid(account, jid)
|
||||
contact_exists = True
|
||||
return DBUS_BOOLEAN(contact_exists)
|
||||
|
||||
def _is_first(self):
|
||||
if self.first_show:
|
||||
self.first_show = False
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_real_jid(self, jid, account = None):
|
||||
'''get the real jid from the given one: removes xmpp: or get jid from nick
|
||||
if account is specified, search only in this account
|
||||
'''
|
||||
if account:
|
||||
accounts = [account]
|
||||
else:
|
||||
accounts = gajim.connections.keys()
|
||||
if jid.startswith('xmpp:'):
|
||||
return jid[5:] # len('xmpp:') = 5
|
||||
nick_in_roster = None # Is jid a nick ?
|
||||
for account in accounts:
|
||||
# Does jid exists in roster of one account ?
|
||||
if gajim.contacts.get_contacts(account, jid):
|
||||
return jid
|
||||
if not nick_in_roster:
|
||||
# look in all contact if one has jid as nick
|
||||
for jid_ in gajim.contacts.get_jid_list(account):
|
||||
c = gajim.contacts.get_contacts(account, jid_)
|
||||
if c[0].name == jid:
|
||||
nick_in_roster = jid_
|
||||
break
|
||||
if nick_in_roster:
|
||||
# We have not found jid in roster, but we found is as a nick
|
||||
return nick_in_roster
|
||||
# We have not found it as jid nor as nick, probably a not in roster jid
|
||||
return jid
|
||||
|
||||
def _contacts_as_dbus_structure(self, contacts):
|
||||
''' get info from list of Contact objects and create dbus dict '''
|
||||
if not contacts:
|
||||
return None
|
||||
prim_contact = None # primary contact
|
||||
for contact in contacts:
|
||||
if prim_contact is None or contact.priority > prim_contact.priority:
|
||||
prim_contact = contact
|
||||
contact_dict = DBUS_DICT_SV()
|
||||
contact_dict['name'] = DBUS_STRING(prim_contact.name)
|
||||
contact_dict['show'] = DBUS_STRING(prim_contact.show)
|
||||
contact_dict['jid'] = DBUS_STRING(prim_contact.jid)
|
||||
if prim_contact.keyID:
|
||||
keyID = None
|
||||
if len(prim_contact.keyID) == 8:
|
||||
keyID = prim_contact.keyID
|
||||
elif len(prim_contact.keyID) == 16:
|
||||
keyID = prim_contact.keyID[8:]
|
||||
if keyID:
|
||||
contact_dict['openpgp'] = keyID
|
||||
contact_dict['resources'] = dbus.Array([], signature='(sis)')
|
||||
for contact in contacts:
|
||||
resource_props = dbus.Struct((DBUS_STRING(contact.resource),
|
||||
dbus.Int32(contact.priority), DBUS_STRING(contact.status)))
|
||||
contact_dict['resources'].append(resource_props)
|
||||
contact_dict['groups'] = dbus.Array([], signature='(s)')
|
||||
for group in prim_contact.groups:
|
||||
contact_dict['groups'].append((DBUS_STRING(group),))
|
||||
return contact_dict
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='', out_signature='s')
|
||||
def get_unread_msgs_number(self):
|
||||
return DBUS_STRING(str(gajim.events.get_nb_events()))
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
|
||||
def start_chat(self, account):
|
||||
if not account:
|
||||
# error is shown in gajim-remote check_arguments(..)
|
||||
return DBUS_BOOLEAN(False)
|
||||
NewChatDialog(account)
|
||||
return DBUS_BOOLEAN(True)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='ss', out_signature='')
|
||||
def send_xml(self, xml, account):
|
||||
if account:
|
||||
gajim.connections[account].send_stanza(xml)
|
||||
else:
|
||||
for acc in gajim.contacts.get_accounts():
|
||||
gajim.connections[acc].send_stanza(xml)
|
||||
|
||||
@dbus.service.method(INTERFACE, in_signature='ssss', out_signature='')
|
||||
def join_room(self, room_jid, nick, password, account):
|
||||
if not account:
|
||||
# get the first connected account
|
||||
accounts = gajim.connections.keys()
|
||||
for acct in accounts:
|
||||
if gajim.account_is_connected(acct):
|
||||
account = acct
|
||||
break
|
||||
if not account:
|
||||
return
|
||||
if not nick:
|
||||
nick = ''
|
||||
gajim.interface.instances[account]['join_gc'] = \
|
||||
JoinGroupchatWindow(account, room_jid, nick)
|
||||
else:
|
||||
gajim.interface.join_gc_room(account, room_jid, nick, password)
|
||||
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
from time import time
|
||||
from dialogs import AddNewContactWindow, NewChatDialog, JoinGroupchatWindow
|
||||
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log_calls, log
|
||||
from common import ged
|
||||
|
||||
class DBusPlugin(GajimPlugin):
|
||||
name = u'D-Bus Support'
|
||||
short_name = u'dbus'
|
||||
version = u'0.1'
|
||||
description = u'''D-Bus support. Based on remote_control module from
|
||||
Gajim core but uses new events handling system.'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('DBusPlugin')
|
||||
def init(self):
|
||||
self.config_dialog = None
|
||||
#self.gui_extension_points = {}
|
||||
#self.config_default_values = {}
|
||||
|
||||
self.events_names = ['Roster', 'AccountPresence', 'ContactPresence',
|
||||
'ContactAbsence', 'ContactStatus', 'NewMessage',
|
||||
'Subscribe', 'Subscribed', 'Unsubscribed',
|
||||
'NewAccount', 'VcardInfo', 'LastStatusTime',
|
||||
'OsInfo', 'GCPresence', 'GCMessage', 'RosterInfo',
|
||||
'NewGmail']
|
||||
|
||||
self.signal_object = None
|
||||
|
||||
self.events_handlers = {}
|
||||
self._set_handling_methods()
|
||||
|
||||
@log_calls('DBusPlugin')
|
||||
def activate(self):
|
||||
session_bus = dbus_support.session_bus.SessionBus()
|
||||
|
||||
bus_name = dbus.service.BusName(SERVICE, bus=session_bus)
|
||||
self.signal_object = SignalObject(bus_name)
|
||||
|
||||
@log_calls('DBusPlugin')
|
||||
def deactivate(self):
|
||||
self.signal_object.remove_from_connection()
|
||||
self.signal_object = None
|
||||
|
||||
@log_calls('DBusPlugin')
|
||||
def _set_handling_methods(self):
|
||||
for event_name in self.events_names:
|
||||
setattr(self, event_name,
|
||||
new.instancemethod(
|
||||
self._generate_handling_method(event_name),
|
||||
self,
|
||||
DBusPlugin))
|
||||
self.events_handlers[event_name] = (ged.POSTCORE,
|
||||
getattr(self, event_name))
|
||||
|
||||
def _generate_handling_method(self, event_name):
|
||||
def handler(self, arg):
|
||||
#print "Handler of event %s called"%(event_name)
|
||||
if self.signal_object:
|
||||
getattr(self.signal_object, event_name)(get_dbus_struct(arg))
|
||||
|
||||
return handler
|
|
@ -0,0 +1 @@
|
|||
from plugin import EventsDumpPlugin
|
|
@ -0,0 +1,129 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
'''
|
||||
Events Dump plugin.
|
||||
|
||||
Dumps info about selected events to console.
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 10th August 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import new
|
||||
from pprint import pformat
|
||||
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log_calls, log
|
||||
from common import ged
|
||||
|
||||
class EventsDumpPlugin(GajimPlugin):
|
||||
name = u'Events Dump'
|
||||
short_name = u'events_dump'
|
||||
version = u'0.1'
|
||||
description = u'''Dumps info about selected events to console.'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('EventsDumpPlugin')
|
||||
def init(self):
|
||||
self.config_dialog = None
|
||||
#self.gui_extension_points = {}
|
||||
#self.config_default_values = {}
|
||||
events_from_old_dbus_support = [
|
||||
'Roster', 'AccountPresence', 'ContactPresence',
|
||||
'ContactAbsence', 'ContactStatus', 'NewMessage',
|
||||
'Subscribe', 'Subscribed', 'Unsubscribed',
|
||||
'NewAccount', 'VcardInfo', 'LastStatusTime',
|
||||
'OsInfo', 'GCPresence', 'GCMessage', 'RosterInfo',
|
||||
'NewGmail']
|
||||
|
||||
events_from_src_gajim = [
|
||||
'ROSTER', 'WARNING', 'ERROR',
|
||||
'INFORMATION', 'ERROR_ANSWER', 'STATUS',
|
||||
'NOTIFY', 'MSGERROR', 'MSGSENT', 'MSGNOTSENT',
|
||||
'SUBSCRIBED', 'UNSUBSCRIBED', 'SUBSCRIBE',
|
||||
'AGENT_ERROR_INFO', 'AGENT_ERROR_ITEMS',
|
||||
'AGENT_REMOVED', 'REGISTER_AGENT_INFO',
|
||||
'AGENT_INFO_ITEMS', 'AGENT_INFO_INFO',
|
||||
'QUIT', 'NEW_ACC_CONNECTED',
|
||||
'NEW_ACC_NOT_CONNECTED', 'ACC_OK', 'ACC_NOT_OK',
|
||||
'MYVCARD', 'VCARD', 'LAST_STATUS_TIME', 'OS_INFO',
|
||||
'GC_NOTIFY', 'GC_MSG', 'GC_SUBJECT', 'GC_CONFIG',
|
||||
'GC_CONFIG_CHANGE', 'GC_INVITATION',
|
||||
'GC_AFFILIATION', 'GC_PASSWORD_REQUIRED',
|
||||
'BAD_PASSPHRASE', 'ROSTER_INFO', 'BOOKMARKS',
|
||||
'CON_TYPE', 'CONNECTION_LOST', 'FILE_REQUEST',
|
||||
'GMAIL_NOTIFY', 'FILE_REQUEST_ERROR',
|
||||
'FILE_SEND_ERROR', 'STANZA_ARRIVED', 'STANZA_SENT',
|
||||
'HTTP_AUTH', 'VCARD_PUBLISHED',
|
||||
'VCARD_NOT_PUBLISHED', 'ASK_NEW_NICK', 'SIGNED_IN',
|
||||
'METACONTACTS', 'ATOM_ENTRY', 'FAILED_DECRYPT',
|
||||
'PRIVACY_LISTS_RECEIVED', 'PRIVACY_LIST_RECEIVED',
|
||||
'PRIVACY_LISTS_ACTIVE_DEFAULT',
|
||||
'PRIVACY_LIST_REMOVED', 'ZC_NAME_CONFLICT',
|
||||
'PING_SENT', 'PING_REPLY', 'PING_ERROR',
|
||||
'SEARCH_FORM', 'SEARCH_RESULT',
|
||||
'RESOURCE_CONFLICT', 'PEP_CONFIG',
|
||||
'UNIQUE_ROOM_ID_UNSUPPORTED',
|
||||
'UNIQUE_ROOM_ID_SUPPORTED', 'SESSION_NEG',
|
||||
'GPG_PASSWORD_REQUIRED', 'SSL_ERROR',
|
||||
'FINGERPRINT_ERROR', 'PLAIN_CONNECTION',
|
||||
'PUBSUB_NODE_REMOVED', 'PUBSUB_NODE_NOT_REMOVED']
|
||||
|
||||
network_events_from_core = ['raw-message-received',
|
||||
'raw-iq-received',
|
||||
'raw-pres-received']
|
||||
|
||||
network_events_generated_in_nec = [
|
||||
'customized-message-received',
|
||||
'more-customized-message-received',
|
||||
'modify-only-message-received',
|
||||
'enriched-chat-message-received']
|
||||
|
||||
self.events_names = []
|
||||
self.events_names += network_events_from_core
|
||||
self.events_names += network_events_generated_in_nec
|
||||
|
||||
self.events_handlers = {}
|
||||
self._set_handling_methods()
|
||||
|
||||
@log_calls('EventsDumpPlugin')
|
||||
def activate(self):
|
||||
pass
|
||||
|
||||
@log_calls('EventsDumpPlugin')
|
||||
def deactivate(self):
|
||||
pass
|
||||
|
||||
@log_calls('EventsDumpPlugin')
|
||||
def _set_handling_methods(self):
|
||||
for event_name in self.events_names:
|
||||
setattr(self, event_name,
|
||||
new.instancemethod(
|
||||
self._generate_handling_method(event_name),
|
||||
self,
|
||||
EventsDumpPlugin))
|
||||
self.events_handlers[event_name] = (ged.POSTCORE,
|
||||
getattr(self, event_name))
|
||||
|
||||
def _generate_handling_method(self, event_name):
|
||||
def handler(self, *args):
|
||||
print "Event '%s' occured. Arguments: %s\n\n===\n"%(event_name, pformat(args))
|
||||
|
||||
return handler
|
|
@ -0,0 +1 @@
|
|||
from plugin import GoogleTranslationPlugin
|
|
@ -0,0 +1,118 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
'''
|
||||
Google Translation plugin.
|
||||
|
||||
Translates (currently only incoming) messages using Google Translate.
|
||||
|
||||
:note: consider this as proof-of-concept
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 25th August 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import re
|
||||
import urllib2
|
||||
import new
|
||||
from pprint import pformat
|
||||
|
||||
from common import helpers
|
||||
from common import gajim
|
||||
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log_calls, log
|
||||
from common import ged
|
||||
from common import nec
|
||||
|
||||
class GoogleTranslationPlugin(GajimPlugin):
|
||||
name = u'Google Translation'
|
||||
short_name = u'google_translation'
|
||||
version = u'0.1'
|
||||
description = u'''Translates (currently only incoming) messages using Google Translate.'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('GoogleTranslationPlugin')
|
||||
def init(self):
|
||||
self.config_dialog = None
|
||||
#self.gui_extension_points = {}
|
||||
self.config_default_values = {'from_lang' : (u'en', _(u'Language of text to be translated')),
|
||||
'to_lang' : (u'fr', _(u'Language to which translation will be made')),
|
||||
'user_agent' : (u'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080213 Firefox/2.0.0.11',
|
||||
_(u'User Agent data to be used with urllib2 when connecting to Google Translate service'))}
|
||||
|
||||
#self.events_handlers = {}
|
||||
|
||||
self.events = [GoogleTranslateMessageReceivedEvent]
|
||||
|
||||
self.translated_text_re = \
|
||||
re.compile(r'google.language.callbacks.id100\(\'22\',{"translatedText":"(?P<text>[^"]*)"}, 200, null, 200\)')
|
||||
|
||||
@log_calls('GoogleTranslationPlugin')
|
||||
def translate_text(self, text, from_lang, to_lang):
|
||||
text = self.prepare_text_for_url(text)
|
||||
headers = { 'User-Agent' : self.config['user_agent'] }
|
||||
translation_url = u'http://www.google.com/uds/Gtranslate?callback=google.language.callbacks.id100&context=22&q=%(text)s&langpair=%(from_lang)s%%7C%(to_lang)s&key=notsupplied&v=1.0'%locals()
|
||||
|
||||
request = urllib2.Request(translation_url, headers=headers)
|
||||
response = urllib2.urlopen(request)
|
||||
results = response.read()
|
||||
|
||||
translated_text = self.translated_text_re.search(results).group('text')
|
||||
|
||||
return translated_text
|
||||
|
||||
@log_calls('GoogleTranslationPlugin')
|
||||
def prepare_text_for_url(self, text):
|
||||
'''
|
||||
Converts text so it can be used within URL as query to Google Translate.
|
||||
'''
|
||||
|
||||
# There should be more replacements for plugin to work in any case:
|
||||
char_replacements = { ' ' : '%20',
|
||||
'+' : '%2B'}
|
||||
|
||||
for char, replacement in char_replacements.iteritems():
|
||||
text = text.replace(char, replacement)
|
||||
|
||||
return text
|
||||
|
||||
@log_calls('GoogleTranslationPlugin')
|
||||
def activate(self):
|
||||
pass
|
||||
|
||||
@log_calls('GoogleTranslationPlugin')
|
||||
def deactivate(self):
|
||||
pass
|
||||
|
||||
class GoogleTranslateMessageReceivedEvent(nec.NetworkIncomingEvent):
|
||||
name = 'google-translate-message-received'
|
||||
base_network_events = ['raw-message-received']
|
||||
|
||||
def generate(self):
|
||||
msg_type = self.base_event.xmpp_msg.attrs.get('type', None)
|
||||
if msg_type == u'chat':
|
||||
msg_text = "".join(self.base_event.xmpp_msg.kids[0].data)
|
||||
if msg_text:
|
||||
from_lang = self.plugin.config['from_lang']
|
||||
to_lang = self.plugin.config['to_lang']
|
||||
self.base_event.xmpp_msg.kids[0].setData(
|
||||
self.plugin.translate_text(msg_text, from_lang, to_lang))
|
||||
|
||||
return False # We only want to modify old event, not emit another,
|
||||
# so we return False here.
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
from length_notifier import LengthNotifierPlugin
|
|
@ -0,0 +1,152 @@
|
|||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkWindow" id="window1">
|
||||
<child>
|
||||
<object class="GtkTable" id="length_notifier_config_table">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">9</property>
|
||||
<property name="n_rows">3</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="column_spacing">7</property>
|
||||
<property name="row_spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Message length at which notification is invoked.</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Message length:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Background color of text entry field in chat window when notification is invoked.</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Notification color:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="tooltip_text" translatable="yes">JabberIDs that plugin should be used with (eg. restrict only to one microblogging bot). Use comma (without space) as separator. If empty plugin is used with every JID. </property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">JabberIDs to include:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="jids_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">JabberIDs that plugin should be used with (eg. restrict only to one microblogging bot). Use comma (without space) as separator. If empty plugin is used with every JID. </property>
|
||||
<signal name="editing_done" handler="on_jids_entry_editing_done"/>
|
||||
<signal name="changed" handler="on_jids_entry_changed"/>
|
||||
</object>
|
||||
<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="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkColorButton" id="notification_colorbutton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Background color of text entry field in chat window when notification is invoked.</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="title" translatable="yes">Pick a color for notification</property>
|
||||
<property name="color">#000000000000</property>
|
||||
<signal name="color_set" handler="on_notification_colorbutton_color_set"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="message_length_spinbutton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Message length at which notification is invoked.</property>
|
||||
<property name="width_chars">6</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="value_changed" handler="on_message_length_spinbutton_value_changed"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,161 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
Message length notifier plugin.
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 1st June 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
import gtk
|
||||
from common import i18n
|
||||
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log, log_calls
|
||||
from plugins.gui import GajimPluginConfigDialog
|
||||
|
||||
class LengthNotifierPlugin(GajimPlugin):
|
||||
name = u'Message Length Notifier'
|
||||
short_name = u'length_notifier'
|
||||
version = u'0.1'
|
||||
description = u'''Highlights message entry field in chat window when given length of message is exceeded.'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('LengthNotifierPlugin')
|
||||
def init(self):
|
||||
self.config_dialog = LengthNotifierPluginConfigDialog(self)
|
||||
|
||||
self.gui_extension_points = {
|
||||
'chat_control' : (self.connect_with_chat_control,
|
||||
self.disconnect_from_chat_control)
|
||||
}
|
||||
|
||||
self.config_default_values = {'MESSAGE_WARNING_LENGTH' : (140, _('Message length at which notification is invoked.')),
|
||||
'WARNING_COLOR' : ('#F0DB3E', _('Background color of text entry field in chat window when notification is invoked.')),
|
||||
'JIDS' : ([], _('JabberIDs that plugin should be used with (eg. restrict only to one microblogging bot). If empty plugin is used with every JID. [not implemented]'))
|
||||
}
|
||||
|
||||
@log_calls('LengthNotifierPlugin')
|
||||
def textview_length_warning(self, tb, chat_control):
|
||||
tv = chat_control.msg_textview
|
||||
d = chat_control.length_notifier_plugin_data
|
||||
t = tb.get_text(tb.get_start_iter(), tb.get_end_iter())
|
||||
if t:
|
||||
len_t = len(t)
|
||||
#print("len_t: %d"%(len_t))
|
||||
if len_t>self.config['MESSAGE_WARNING_LENGTH']:
|
||||
if not d['prev_color']:
|
||||
d['prev_color'] = tv.style.copy().base[gtk.STATE_NORMAL]
|
||||
tv.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.config['WARNING_COLOR']))
|
||||
elif d['prev_color']:
|
||||
tv.modify_base(gtk.STATE_NORMAL, d['prev_color'])
|
||||
d['prev_color'] = None
|
||||
|
||||
@log_calls('LengthNotifierPlugin')
|
||||
def connect_with_chat_control(self, chat_control):
|
||||
jid = chat_control.contact.jid
|
||||
if self.jid_is_ok(jid):
|
||||
d = {'prev_color' : None}
|
||||
tv = chat_control.msg_textview
|
||||
tb = tv.get_buffer()
|
||||
h_id = tb.connect('changed', self.textview_length_warning, chat_control)
|
||||
d['h_id'] = h_id
|
||||
|
||||
t = tb.get_text(tb.get_start_iter(), tb.get_end_iter())
|
||||
if t:
|
||||
len_t = len(t)
|
||||
if len_t>self.config['MESSAGE_WARNING_LENGTH']:
|
||||
d['prev_color'] = tv.style.copy().base[gtk.STATE_NORMAL]
|
||||
tv.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.config['WARNING_COLOR']))
|
||||
|
||||
chat_control.length_notifier_plugin_data = d
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@log_calls('LengthNotifierPlugin')
|
||||
def disconnect_from_chat_control(self, chat_control):
|
||||
try:
|
||||
d = chat_control.length_notifier_plugin_data
|
||||
tv = chat_control.msg_textview
|
||||
tv.get_buffer().disconnect(d['h_id'])
|
||||
if d['prev_color']:
|
||||
tv.modify_base(gtk.STATE_NORMAL, d['prev_color'])
|
||||
except AttributeError, error:
|
||||
pass
|
||||
#log.debug('Length Notifier Plugin was (probably) never connected with this chat window.\n Error: %s' % (error))
|
||||
|
||||
@log_calls('LengthNotifierPlugin')
|
||||
def jid_is_ok(self, jid):
|
||||
if jid in self.config['JIDS'] or not self.config['JIDS']:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
class LengthNotifierPluginConfigDialog(GajimPluginConfigDialog):
|
||||
def init(self):
|
||||
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
|
||||
'config_dialog.ui')
|
||||
self.xml = gtk.Builder()
|
||||
self.xml.set_translation_domain(i18n.APP)
|
||||
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
|
||||
['length_notifier_config_table'])
|
||||
self.config_table = self.xml.get_object('length_notifier_config_table')
|
||||
self.child.pack_start(self.config_table)
|
||||
|
||||
self.message_length_spinbutton = self.xml.get_object(
|
||||
'message_length_spinbutton')
|
||||
self.message_length_spinbutton.get_adjustment().set_all(140, 0, 500, 1,
|
||||
10, 0)
|
||||
self.notification_colorbutton = self.xml.get_object(
|
||||
'notification_colorbutton')
|
||||
self.jids_entry = self.xml.get_object('jids_entry')
|
||||
|
||||
self.xml.connect_signals(self)
|
||||
|
||||
def on_run(self):
|
||||
self.message_length_spinbutton.set_value(self.plugin.config['MESSAGE_WARNING_LENGTH'])
|
||||
self.notification_colorbutton.set_color(gtk.gdk.color_parse(self.plugin.config['WARNING_COLOR']))
|
||||
#self.jids_entry.set_text(self.plugin.config['JIDS'])
|
||||
self.jids_entry.set_text(','.join(self.plugin.config['JIDS']))
|
||||
|
||||
@log_calls('LengthNotifierPluginConfigDialog')
|
||||
def on_message_length_spinbutton_value_changed(self, spinbutton):
|
||||
self.plugin.config['MESSAGE_WARNING_LENGTH'] = spinbutton.get_value()
|
||||
|
||||
@log_calls('LengthNotifierPluginConfigDialog')
|
||||
def on_notification_colorbutton_color_set(self, colorbutton):
|
||||
self.plugin.config['WARNING_COLOR'] = colorbutton.get_color().to_string()
|
||||
|
||||
@log_calls('LengthNotifierPluginConfigDialog')
|
||||
def on_jids_entry_changed(self, entry):
|
||||
text = entry.get_text()
|
||||
if len(text)>0:
|
||||
self.plugin.config['JIDS'] = entry.get_text().split(',')
|
||||
else:
|
||||
self.plugin.config['JIDS'] = []
|
||||
|
||||
@log_calls('LengthNotifierPluginConfigDialog')
|
||||
def on_jids_entry_editing_done(self, entry):
|
||||
pass
|
|
@ -0,0 +1 @@
|
|||
from plugin import NewEventsExamplePlugin
|
|
@ -0,0 +1,147 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
'''
|
||||
New Events Example plugin.
|
||||
|
||||
Demonstrates how to use Network Events Controller to generate new events
|
||||
based on existing one.
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 15th August 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import new
|
||||
from pprint import pformat
|
||||
|
||||
from common import helpers
|
||||
from common import gajim
|
||||
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log_calls, log
|
||||
from common import ged
|
||||
from common import nec
|
||||
|
||||
class NewEventsExamplePlugin(GajimPlugin):
|
||||
name = u'New Events Example'
|
||||
short_name = u'new_events_example'
|
||||
version = u'0.1'
|
||||
description = u'''Shows how to generate new network events based on existing one using Network Events Controller.'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('NewEventsExamplePlugin')
|
||||
def init(self):
|
||||
self.config_dialog = None
|
||||
#self.gui_extension_points = {}
|
||||
#self.config_default_values = {}
|
||||
|
||||
self.events_handlers = {'raw-message-received' :
|
||||
(ged.POSTCORE,
|
||||
self.raw_message_received),
|
||||
'customized-message-received' :
|
||||
(ged.POSTCORE,
|
||||
self.customized_message_received),
|
||||
'enriched-chat-message-received' :
|
||||
(ged.POSTCORE,
|
||||
self.enriched_chat_message_received)}
|
||||
|
||||
self.events = [CustomizedMessageReceivedEvent,
|
||||
MoreCustomizedMessageReceivedEvent,
|
||||
ModifyOnlyMessageReceivedEvent,
|
||||
EnrichedChatMessageReceivedEvent]
|
||||
|
||||
def enriched_chat_message_received(self, event_object):
|
||||
pass
|
||||
#print "Event '%s' occured. Event object: %s\n\n===\n"%(event_object.name,
|
||||
#event_object)
|
||||
|
||||
def raw_message_received(self, event_object):
|
||||
pass
|
||||
#print "Event '%s' occured. Event object: %s\n\n===\n"%(event_object.name,
|
||||
#event_object)
|
||||
|
||||
def customized_message_received(self, event_object):
|
||||
pass
|
||||
#print "Event '%s' occured. Event object: %s\n\n===\n"%(event_object.name,
|
||||
#event_object
|
||||
|
||||
@log_calls('NewEventsExamplePlugin')
|
||||
def activate(self):
|
||||
pass
|
||||
|
||||
@log_calls('NewEventsExamplePlugin')
|
||||
def deactivate(self):
|
||||
pass
|
||||
|
||||
class CustomizedMessageReceivedEvent(nec.NetworkIncomingEvent):
|
||||
name = 'customized-message-received'
|
||||
base_network_events = ['raw-message-received']
|
||||
|
||||
def generate(self):
|
||||
return True
|
||||
|
||||
class MoreCustomizedMessageReceivedEvent(nec.NetworkIncomingEvent):
|
||||
'''
|
||||
Shows chain of custom created events.
|
||||
|
||||
This one is based on custom 'customized-messsage-received'.
|
||||
'''
|
||||
name = 'more-customized-message-received'
|
||||
base_network_events = ['customized-message-received']
|
||||
|
||||
def generate(self):
|
||||
return True
|
||||
|
||||
class ModifyOnlyMessageReceivedEvent(nec.NetworkIncomingEvent):
|
||||
name = 'modify-only-message-received'
|
||||
base_network_events = ['raw-message-received']
|
||||
|
||||
def generate(self):
|
||||
msg_type = self.base_event.xmpp_msg.attrs.get('type', None)
|
||||
if msg_type == u'chat':
|
||||
msg_text = "".join(self.base_event.xmpp_msg.kids[0].data)
|
||||
self.base_event.xmpp_msg.kids[0].setData(
|
||||
u'%s [MODIFIED BY CUSTOM NETWORK EVENT]'%(msg_text))
|
||||
|
||||
return False
|
||||
|
||||
class EnrichedChatMessageReceivedEvent(nec.NetworkIncomingEvent):
|
||||
'''
|
||||
Generates more friendly (in use by handlers) network event for
|
||||
received chat message.
|
||||
'''
|
||||
name = 'enriched-chat-message-received'
|
||||
base_network_events = ['raw-message-received']
|
||||
|
||||
def generate(self):
|
||||
msg_type = self.base_event.xmpp_msg.attrs.get('type', None)
|
||||
if msg_type == u'chat':
|
||||
self.xmpp_msg = self.base_event.xmpp_msg
|
||||
self.conn = self.base_event.conn
|
||||
self.from_jid = helpers.get_full_jid_from_iq(self.xmpp_msg)
|
||||
self.from_jid_without_resource = gajim.get_jid_without_resource(self.from_jid)
|
||||
self.account = self.base_event.account
|
||||
self.from_nickname = gajim.get_contact_name_from_jid(
|
||||
self.account,
|
||||
self.from_jid_without_resource)
|
||||
self.msg_text = "".join(self.xmpp_msg.kids[0].data)
|
||||
|
||||
return True
|
||||
|
||||
return False
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
__all__ = ['RosterButtonsPlugin']
|
||||
|
||||
from plugin import RosterButtonsPlugin
|
|
@ -0,0 +1,86 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
Roster buttons plug-in.
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 14th June 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
import gtk
|
||||
from common import i18n
|
||||
from common import gajim
|
||||
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log, log_calls
|
||||
|
||||
class RosterButtonsPlugin(GajimPlugin):
|
||||
name = u'Roster Buttons'
|
||||
short_name = u'roster_buttons'
|
||||
version = u'0.1'
|
||||
description = u'''Adds quick action buttons to roster window.'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('RosterButtonsPlugin')
|
||||
def init(self):
|
||||
self.GTK_BUILDER_FILE_PATH = self.local_file_path('roster_buttons.ui')
|
||||
self.roster_vbox = gajim.interface.roster.xml.get_object('roster_vbox2')
|
||||
self.show_offline_contacts_menuitem = gajim.interface.roster.xml.get_object('show_offline_contacts_menuitem')
|
||||
|
||||
self.config_dialog = None
|
||||
|
||||
@log_calls('RosterButtonsPlugin')
|
||||
def activate(self):
|
||||
self.xml = gtk.Builder()
|
||||
self.xml.set_translation_domain(i18n.APP)
|
||||
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
|
||||
['roster_buttons_buttonbox'])
|
||||
self.buttonbox = self.xml.get_object('roster_buttons_buttonbox')
|
||||
|
||||
self.roster_vbox.pack_start(self.buttonbox, expand=False)
|
||||
self.roster_vbox.reorder_child(self.buttonbox, 0)
|
||||
self.xml.connect_signals(self)
|
||||
|
||||
@log_calls('RosterButtonsPlugin')
|
||||
def deactivate(self):
|
||||
self.roster_vbox.remove(self.buttonbox)
|
||||
|
||||
self.buttonbox = None
|
||||
self.xml = None
|
||||
|
||||
@log_calls('RosterButtonsPlugin')
|
||||
def on_roster_button_1_clicked(self, button):
|
||||
#gajim.interface.roster.on_show_offline_contacts_menuitem_activate(None)
|
||||
self.show_offline_contacts_menuitem.set_active(not self.show_offline_contacts_menuitem.get_active())
|
||||
|
||||
@log_calls('RosterButtonsPlugin')
|
||||
def on_roster_button_2_clicked(self, button):
|
||||
pass
|
||||
|
||||
@log_calls('RosterButtonsPlugin')
|
||||
def on_roster_button_3_clicked(self, button):
|
||||
pass
|
||||
|
||||
@log_calls('RosterButtonsPlugin')
|
||||
def on_roster_button_4_clicked(self, button):
|
||||
pass
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkWindow" id="window1">
|
||||
<child>
|
||||
<object class="GtkHButtonBox" id="roster_buttons_buttonbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<property name="layout_style">spread</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="roster_button_1">
|
||||
<property name="label" translatable="yes">1</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_roster_button_1_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="roster_button_2">
|
||||
<property name="label" translatable="yes">2</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_roster_button_2_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="roster_button_3">
|
||||
<property name="label" translatable="yes">3</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_roster_button_3_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="roster_button_4">
|
||||
<property name="label" translatable="yes">4</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_roster_button_4_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,772 @@
|
|||
"""
|
||||
A python version of the main functions to use Snarl
|
||||
(http://www.fullphat.net/snarl)
|
||||
|
||||
Version 1.0
|
||||
|
||||
This module can be used in two ways. One is the normal way
|
||||
the other snarl interfaces work. This means you can call snShowMessage
|
||||
and get an ID back for manipulations.
|
||||
|
||||
The other way is there is a class this module exposes called SnarlMessage.
|
||||
This allows you to keep track of the message as a python object. If you
|
||||
use the send without specifying False as the argument it will set the ID
|
||||
to what the return of the last SendMessage was. This is of course only
|
||||
useful for the SHOW message.
|
||||
|
||||
Requires one of:
|
||||
pywin32 extensions from http://pywin32.sourceforge.net
|
||||
ctypes (included in Python 2.5, downloadable for earlier versions)
|
||||
|
||||
Creator: Sam Listopad II (samlii@users.sourceforge.net)
|
||||
|
||||
Copyright 2006-2008 Samuel Listopad II
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
use this file except in compliance with the License. You may obtain a copy
|
||||
of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
|
||||
by applicable law or agreed to in writing, software distributed under the
|
||||
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
||||
OF ANY KIND, either express or implied. See the License for the specific
|
||||
language governing permissions and limitations under the License.
|
||||
"""
|
||||
|
||||
import array, struct
|
||||
|
||||
def LOWORD(dword):
|
||||
"""Return the low WORD of the passed in integer"""
|
||||
return dword & 0x0000ffff
|
||||
#get the hi word
|
||||
def HIWORD(dword):
|
||||
"""Return the high WORD of the passed in integer"""
|
||||
return dword >> 16
|
||||
|
||||
class Win32FuncException(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class Win32Funcs:
|
||||
"""Just a little class to hide the details of finding and using the
|
||||
correct win32 functions. The functions may throw a UnicodeEncodeError if
|
||||
there is not a unicode version and it is sent a unicode string that cannot
|
||||
be converted to ASCII."""
|
||||
WM_USER = 0x400
|
||||
WM_COPYDATA = 0x4a
|
||||
#Type of String the functions are expecting.
|
||||
#Used like function(myWin32Funcs.strType(param)).
|
||||
__strType = str
|
||||
#FindWindow function to use
|
||||
__FindWindow = None
|
||||
#FindWindow function to use
|
||||
__FindWindowEx = None
|
||||
#SendMessage function to use
|
||||
__SendMessage = None
|
||||
#SendMessageTimeout function to use
|
||||
__SendMessageTimeout = None
|
||||
#IsWindow function to use
|
||||
__IsWindow = None
|
||||
#RegisterWindowMessage to use
|
||||
__RegisterWindowMessage = None
|
||||
#GetWindowText to use
|
||||
__GetWindowText = None
|
||||
|
||||
def FindWindow(self, lpClassName, lpWindowName):
|
||||
"""Wraps the windows API call of FindWindow"""
|
||||
if lpClassName is not None:
|
||||
lpClassName = self.__strType(lpClassName)
|
||||
if lpWindowName is not None:
|
||||
lpWindowName = self.__strType(lpWindowName)
|
||||
return self.__FindWindow(lpClassName, lpWindowName)
|
||||
|
||||
def FindWindowEx(self, hwndParent, hwndChildAfter, lpClassName, lpWindowName):
|
||||
"""Wraps the windows API call of FindWindow"""
|
||||
if lpClassName is not None:
|
||||
lpClassName = self.__strType(lpClassName)
|
||||
if lpWindowName is not None:
|
||||
lpWindowName = self.__strType(lpWindowName)
|
||||
return self.__FindWindowEx(hwndParent, hwndChildAfter, lpClassName, lpWindowName)
|
||||
|
||||
def SendMessage(self, hWnd, Msg, wParam, lParam):
|
||||
"""Wraps the windows API call of SendMessage"""
|
||||
return self.__SendMessage(hWnd, Msg, wParam, lParam)
|
||||
|
||||
def SendMessageTimeout(self, hWnd, Msg,
|
||||
wParam, lParam, fuFlags,
|
||||
uTimeout, lpdwResult = None):
|
||||
"""Wraps the windows API call of SendMessageTimeout"""
|
||||
idToRet = None
|
||||
try:
|
||||
idFromMsg = array.array('I', [0])
|
||||
result = idFromMsg.buffer_info()[0]
|
||||
response = self.__SendMessageTimeout(hWnd, Msg, wParam,
|
||||
lParam, fuFlags,
|
||||
uTimeout, result)
|
||||
if response == 0:
|
||||
raise Win32FuncException, "SendMessageTimeout TimedOut"
|
||||
|
||||
idToRet = idFromMsg[0]
|
||||
except TypeError:
|
||||
idToRet = self.__SendMessageTimeout(hWnd, Msg, wParam,
|
||||
lParam, fuFlags,
|
||||
uTimeout)
|
||||
|
||||
if lpdwResult is not None and lpdwResult.typecode == 'I':
|
||||
lpdwResult[0] = idToRet
|
||||
|
||||
return idToRet
|
||||
|
||||
def IsWindow(self, hWnd):
|
||||
"""Wraps the windows API call of IsWindow"""
|
||||
return self.__IsWindow(hWnd)
|
||||
|
||||
def RegisterWindowMessage(self, lpString):
|
||||
"""Wraps the windows API call of RegisterWindowMessage"""
|
||||
return self.__RegisterWindowMessage(self.__strType(lpString))
|
||||
|
||||
def GetWindowText(self, hWnd, lpString = None, nMaxCount = None):
|
||||
"""Wraps the windows API call of SendMessageTimeout"""
|
||||
text = ''
|
||||
if hWnd == 0:
|
||||
return text
|
||||
|
||||
if nMaxCount is None:
|
||||
nMaxCount = 1025
|
||||
|
||||
try:
|
||||
arrayType = 'c'
|
||||
if self.__strType == unicode:
|
||||
arrayType = 'u'
|
||||
path_string = array.array(arrayType, self.__strType('\x00') * nMaxCount)
|
||||
path_buffer = path_string.buffer_info()[0]
|
||||
result = self.__GetWindowText(hWnd,
|
||||
path_buffer,
|
||||
nMaxCount)
|
||||
if result > 0:
|
||||
if self.__strType == unicode:
|
||||
text = path_string[0:result].tounicode()
|
||||
else:
|
||||
text = path_string[0:result].tostring()
|
||||
except TypeError:
|
||||
text = self.__GetWindowText(hWnd)
|
||||
|
||||
if lpString is not None and lpString.typecode == 'c':
|
||||
lpdwResult[0:len(text)] = array.array('c', str(text));
|
||||
|
||||
if lpString is not None and lpString.typecode == 'u':
|
||||
lpdwResult[0:len(text)] = array.array('u', unicode(text));
|
||||
|
||||
return text
|
||||
|
||||
def __init__(self):
|
||||
"""Load up my needed functions"""
|
||||
# First see if they already have win32gui imported. If so use it.
|
||||
# This has to be checked first since the auto check looks for ctypes
|
||||
# first.
|
||||
try:
|
||||
self.__FindWindow = win32gui.FindWindow
|
||||
self.__FindWindowEx = win32gui.FindWindowEx
|
||||
self.__GetWindowText = win32gui.GetWindowText
|
||||
self.__IsWindow = win32gui.IsWindow
|
||||
self.__SendMessage = win32gui.SendMessage
|
||||
self.__SendMessageTimeout = win32gui.SendMessageTimeout
|
||||
self.__RegisterWindowMessage = win32gui.RegisterWindowMessage
|
||||
self.__strType = unicode
|
||||
|
||||
#Something threw a NameError, most likely the win32gui lines
|
||||
#so do auto check
|
||||
except NameError:
|
||||
try:
|
||||
from ctypes import windll
|
||||
self.__FindWindow = windll.user32.FindWindowW
|
||||
self.__FindWindowEx = windll.user32.FindWindowExW
|
||||
self.__GetWindowText = windll.user32.GetWindowTextW
|
||||
self.__IsWindow = windll.user32.IsWindow
|
||||
self.__SendMessage = windll.user32.SendMessageW
|
||||
self.__SendMessageTimeout = windll.user32.SendMessageTimeoutW
|
||||
self.__RegisterWindowMessage = windll.user32.RegisterWindowMessageW
|
||||
self.__strType = unicode
|
||||
|
||||
#FindWindowW wasn't found, look for FindWindowA
|
||||
except AttributeError:
|
||||
try:
|
||||
self.__FindWindow = windll.user32.FindWindowA
|
||||
self.__FindWindowEx = windll.user32.FindWindowExA
|
||||
self.__GetWindowText = windll.user32.GetWindowTextA
|
||||
self.__IsWindow = windll.user32.IsWindow
|
||||
self.__SendMessage = windll.user32.SendMessageA
|
||||
self.__SendMessageTimeout = windll.user32.SendMessageTimeoutA
|
||||
self.__RegisterWindowMessage = windll.user32.RegisterWindowMessageA
|
||||
# Couldn't find either so Die and tell user why.
|
||||
except AttributeError:
|
||||
import sys
|
||||
sys.stderr.write("Your Windows TM setup seems to be corrupt."+
|
||||
" No FindWindow found in user32.\n")
|
||||
sys.stderr.flush()
|
||||
sys.exit(3)
|
||||
|
||||
except ImportError:
|
||||
try:
|
||||
import win32gui
|
||||
self.__FindWindow = win32gui.FindWindow
|
||||
self.__FindWindowEx = win32gui.FindWindowEx
|
||||
self.__GetWindowText = win32gui.GetWindowText
|
||||
self.__IsWindow = win32gui.IsWindow
|
||||
self.__SendMessage = win32gui.SendMessage
|
||||
self.__SendMessageTimeout = win32gui.SendMessageTimeout
|
||||
self.__RegisterWindowMessage = win32gui.RegisterWindowMessage
|
||||
self.__strType = unicode
|
||||
|
||||
except ImportError:
|
||||
import sys
|
||||
sys.stderr.write("You need to have either"+
|
||||
" ctypes or pywin32 installed.\n")
|
||||
sys.stderr.flush()
|
||||
#sys.exit(2)
|
||||
|
||||
|
||||
myWin32Funcs = Win32Funcs()
|
||||
|
||||
|
||||
SHOW = 1
|
||||
HIDE = 2
|
||||
UPDATE = 3
|
||||
IS_VISIBLE = 4
|
||||
GET_VERSION = 5
|
||||
REGISTER_CONFIG_WINDOW = 6
|
||||
REVOKE_CONFIG_WINDOW = 7
|
||||
REGISTER_ALERT = 8
|
||||
REVOKE_ALERT = 9
|
||||
REGISTER_CONFIG_WINDOW_2 = 10
|
||||
GET_VERSION_EX = 11
|
||||
SET_TIMEOUT = 12
|
||||
|
||||
EX_SHOW = 32
|
||||
|
||||
GLOBAL_MESSAGE = "SnarlGlobalMessage"
|
||||
GLOBAL_MSG = "SnarlGlobalEvent"
|
||||
|
||||
#Messages That may be received from Snarl
|
||||
SNARL_LAUNCHED = 1
|
||||
SNARL_QUIT = 2
|
||||
SNARL_ASK_APPLET_VER = 3
|
||||
SNARL_SHOW_APP_UI = 4
|
||||
|
||||
SNARL_NOTIFICATION_CLICKED = 32 #notification was right-clicked by user
|
||||
SNARL_NOTIFICATION_CANCELLED = SNARL_NOTIFICATION_CLICKED #Name clarified
|
||||
SNARL_NOTIFICATION_TIMED_OUT = 33
|
||||
SNARL_NOTIFICATION_ACK = 34 #notification was left-clicked by user
|
||||
|
||||
#Snarl Test Message
|
||||
WM_SNARLTEST = myWin32Funcs.WM_USER + 237
|
||||
|
||||
M_ABORTED = 0x80000007L
|
||||
M_ACCESS_DENIED = 0x80000009L
|
||||
M_ALREADY_EXISTS = 0x8000000CL
|
||||
M_BAD_HANDLE = 0x80000006L
|
||||
M_BAD_POINTER = 0x80000005L
|
||||
M_FAILED = 0x80000008L
|
||||
M_INVALID_ARGS = 0x80000003L
|
||||
M_NO_INTERFACE = 0x80000004L
|
||||
M_NOT_FOUND = 0x8000000BL
|
||||
M_NOT_IMPLEMENTED = 0x80000001L
|
||||
M_OK = 0x00000000L
|
||||
M_OUT_OF_MEMORY = 0x80000002L
|
||||
M_TIMED_OUT = 0x8000000AL
|
||||
|
||||
ErrorCodeRev = {
|
||||
0x80000007L : "M_ABORTED",
|
||||
0x80000009L : "M_ACCESS_DENIED",
|
||||
0x8000000CL : "M_ALREADY_EXISTS",
|
||||
0x80000006L : "M_BAD_HANDLE",
|
||||
0x80000005L : "M_BAD_POINTER",
|
||||
0x80000008L : "M_FAILED",
|
||||
0x80000003L : "M_INVALID_ARGS",
|
||||
0x80000004L : "M_NO_INTERFACE",
|
||||
0x8000000BL : "M_NOT_FOUND",
|
||||
0x80000001L : "M_NOT_IMPLEMENTED",
|
||||
0x00000000L : "M_OK",
|
||||
0x80000002L : "M_OUT_OF_MEMORY",
|
||||
0x8000000AL : "M_TIMED_OUT"
|
||||
}
|
||||
|
||||
class SnarlMessage(object):
|
||||
"""The main Snarl interface object.
|
||||
|
||||
ID = Snarl Message ID for most operations. See SDK for more info
|
||||
as to other values to put here.
|
||||
type = Snarl Message Type. Valid values are : SHOW, HIDE, UPDATE,
|
||||
IS_VISIBLE, GET_VERSION, REGISTER_CONFIG_WINDOW, REVOKE_CONFIG_WINDOW
|
||||
all which are constants in the PySnarl module.
|
||||
timeout = Timeout in seconds for the Snarl Message
|
||||
data = Snarl Message data. This is dependant upon message type. See SDK
|
||||
title = Snarl Message title.
|
||||
text = Snarl Message text.
|
||||
icon = Path to the icon to display in the Snarl Message.
|
||||
"""
|
||||
__msgType = 0
|
||||
__msgID = 0
|
||||
__msgTimeout = 0
|
||||
__msgData = 0
|
||||
__msgTitle = ""
|
||||
__msgText = ""
|
||||
__msgIcon = ""
|
||||
__msgClass = ""
|
||||
__msgExtra = ""
|
||||
__msgExtra2 = ""
|
||||
__msgRsvd1 = 0
|
||||
__msgRsvd2 = 0
|
||||
__msgHWnd = 0
|
||||
|
||||
lastKnownHWnd = 0
|
||||
|
||||
def getType(self):
|
||||
"""Type Attribute getter."""
|
||||
return self.__msgType
|
||||
def setType(self, value):
|
||||
"""Type Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgType = value
|
||||
type = property(getType, setType, doc="The Snarl Message Type")
|
||||
|
||||
def getID(self):
|
||||
"""ID Attribute getter."""
|
||||
return self.__msgID
|
||||
def setID(self, value):
|
||||
"""ID Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgID = value
|
||||
ID = property(getID, setID, doc="The Snarl Message ID")
|
||||
|
||||
def getTimeout(self):
|
||||
"""Timeout Attribute getter."""
|
||||
return self.__msgTimeout
|
||||
def updateTimeout(self, value):
|
||||
"""Timeout Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgTimeout = value
|
||||
timeout = property(getTimeout, updateTimeout,
|
||||
doc="The Snarl Message Timeout")
|
||||
|
||||
def getData(self):
|
||||
"""Data Attribute getter."""
|
||||
return self.__msgData
|
||||
def setData(self, value):
|
||||
"""Data Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgData = value
|
||||
data = property(getData, setData, doc="The Snarl Message Data")
|
||||
|
||||
def getTitle(self):
|
||||
"""Title Attribute getter."""
|
||||
return self.__msgTitle
|
||||
def setTitle(self, value):
|
||||
"""Title Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgTitle = value
|
||||
title = property(getTitle, setTitle, doc="The Snarl Message Title")
|
||||
|
||||
def getText(self):
|
||||
"""Text Attribute getter."""
|
||||
return self.__msgText
|
||||
def setText(self, value):
|
||||
"""Text Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgText = value
|
||||
text = property(getText, setText, doc="The Snarl Message Text")
|
||||
|
||||
def getIcon(self):
|
||||
"""Icon Attribute getter."""
|
||||
return self.__msgIcon
|
||||
def setIcon(self, value):
|
||||
"""Icon Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgIcon = value
|
||||
icon = property(getIcon, setIcon, doc="The Snarl Message Icon")
|
||||
|
||||
def getClass(self):
|
||||
"""Class Attribute getter."""
|
||||
return self.__msgClass
|
||||
def setClass(self, value):
|
||||
"""Class Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgClass = value
|
||||
msgclass = property(getClass, setClass, doc="The Snarl Message Class")
|
||||
|
||||
def getExtra(self):
|
||||
"""Extra Attribute getter."""
|
||||
return self.__msgExtra
|
||||
def setExtra(self, value):
|
||||
"""Extra Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgExtra = value
|
||||
extra = property(getExtra, setExtra, doc="Extra Info for the Snarl Message")
|
||||
|
||||
def getExtra2(self):
|
||||
"""Extra2 Attribute getter."""
|
||||
return self.__msgExtra2
|
||||
def setExtra2(self, value):
|
||||
"""Extra2 Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgExtra2 = value
|
||||
extra2 = property(getExtra2, setExtra2,
|
||||
doc="More Extra Info for the Snarl Message")
|
||||
|
||||
def getRsvd1(self):
|
||||
"""Rsvd1 Attribute getter."""
|
||||
return self.__msgRsvd1
|
||||
def setRsvd1(self, value):
|
||||
"""Rsvd1 Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgRsvd1 = value
|
||||
rsvd1 = property(getRsvd1, setRsvd1, doc="The Snarl Message Field Rsvd1")
|
||||
|
||||
def getRsvd2(self):
|
||||
"""Rsvd2 Attribute getter."""
|
||||
return self.__msgRsvd2
|
||||
def setRsvd2(self, value):
|
||||
"""Rsvd2 Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgRsvd2 = value
|
||||
rsvd2 = property(getRsvd2, setRsvd2, doc="The Snarl Message Field Rsvd2")
|
||||
|
||||
def getHwnd(self):
|
||||
"""hWnd Attribute getter."""
|
||||
return self.__msgHWnd
|
||||
def setHwnd(self, value):
|
||||
"""hWnd Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgHWnd = value
|
||||
|
||||
hWnd = property(getHwnd, setHwnd, doc="The hWnd of the window this message is being sent from")
|
||||
|
||||
|
||||
def __init__(self, title="", text="", icon="", msg_type=1, msg_id=0):
|
||||
self.__msgTimeout = 0
|
||||
self.__msgData = 0
|
||||
self.__msgClass = ""
|
||||
self.__msgExtra = ""
|
||||
self.__msgExtra2 = ""
|
||||
self.__msgRsvd1 = 0
|
||||
self.__msgRsvd2 = 0
|
||||
self.__msgType = msg_type
|
||||
self.__msgText = text
|
||||
self.__msgTitle = title
|
||||
self.__msgIcon = icon
|
||||
self.__msgID = msg_id
|
||||
|
||||
def createCopyStruct(self):
|
||||
"""Creates the struct to send as the copyData in the message."""
|
||||
return struct.pack("ILLL1024s1024s1024s1024s1024s1024sLL",
|
||||
self.__msgType,
|
||||
self.__msgID,
|
||||
self.__msgTimeout,
|
||||
self.__msgData,
|
||||
self.__msgTitle.encode('utf-8'),
|
||||
self.__msgText.encode('utf-8'),
|
||||
self.__msgIcon.encode('utf-8'),
|
||||
self.__msgClass.encode('utf-8'),
|
||||
self.__msgExtra.encode('utf-8'),
|
||||
self.__msgExtra2.encode('utf-8'),
|
||||
self.__msgRsvd1,
|
||||
self.__msgRsvd2
|
||||
)
|
||||
__lpData = None
|
||||
__cds = None
|
||||
|
||||
def packData(self, dwData):
|
||||
"""This packs the data in the necessary format for a
|
||||
WM_COPYDATA message."""
|
||||
self.__lpData = None
|
||||
self.__cds = None
|
||||
item = self.createCopyStruct()
|
||||
self.__lpData = array.array('c', item)
|
||||
lpData_ad = self.__lpData.buffer_info()[0]
|
||||
cbData = self.__lpData.buffer_info()[1]
|
||||
self.__cds = array.array('c',
|
||||
struct.pack("IIP",
|
||||
dwData,
|
||||
cbData,
|
||||
lpData_ad)
|
||||
)
|
||||
cds_ad = self.__cds.buffer_info()[0]
|
||||
return cds_ad
|
||||
|
||||
def reset(self):
|
||||
"""Reset this SnarlMessage to the default state."""
|
||||
self.__msgType = 0
|
||||
self.__msgID = 0
|
||||
self.__msgTimeout = 0
|
||||
self.__msgData = 0
|
||||
self.__msgTitle = ""
|
||||
self.__msgText = ""
|
||||
self.__msgIcon = ""
|
||||
self.__msgClass = ""
|
||||
self.__msgExtra = ""
|
||||
self.__msgExtra2 = ""
|
||||
self.__msgRsvd1 = 0
|
||||
self.__msgRsvd2 = 0
|
||||
|
||||
|
||||
def send(self, setid=True):
|
||||
"""Send this SnarlMessage to the Snarl window.
|
||||
Args:
|
||||
setid - Boolean defining whether or not to set the ID
|
||||
of this SnarlMessage to the return value of
|
||||
the SendMessage call. Default is True to
|
||||
make simple case of SHOW easy.
|
||||
"""
|
||||
hwnd = myWin32Funcs.FindWindow(None, "Snarl")
|
||||
if myWin32Funcs.IsWindow(hwnd):
|
||||
if self.type == REGISTER_CONFIG_WINDOW or self.type == REGISTER_CONFIG_WINDOW_2:
|
||||
self.hWnd = self.data
|
||||
try:
|
||||
response = myWin32Funcs.SendMessageTimeout(hwnd,
|
||||
myWin32Funcs.WM_COPYDATA,
|
||||
self.hWnd, self.packData(2),
|
||||
2, 500)
|
||||
except Win32FuncException:
|
||||
return False
|
||||
|
||||
idFromMsg = response
|
||||
if setid:
|
||||
self.ID = idFromMsg
|
||||
return True
|
||||
else:
|
||||
return idFromMsg
|
||||
print "No snarl window found"
|
||||
return False
|
||||
|
||||
def hide(self):
|
||||
"""Hide this message. Type will revert to type before calling hide
|
||||
to allow for better reuse of object."""
|
||||
oldType = self.__msgType
|
||||
self.__msgType = HIDE
|
||||
retVal = bool(self.send(False))
|
||||
self.__msgType = oldType
|
||||
return retVal
|
||||
|
||||
def isVisible(self):
|
||||
"""Is this message visible. Type will revert to type before calling
|
||||
hide to allow for better reuse of object."""
|
||||
oldType = self.__msgType
|
||||
self.__msgType = IS_VISIBLE
|
||||
retVal = bool(self.send(False))
|
||||
self.__msgType = oldType
|
||||
return retVal
|
||||
|
||||
def update(self, title=None, text=None, icon=None):
|
||||
"""Update this message with given title and text. Type will revert
|
||||
to type before calling hide to allow for better reuse of object."""
|
||||
oldType = self.__msgType
|
||||
self.__msgType = UPDATE
|
||||
if text:
|
||||
self.__msgText = text
|
||||
if title:
|
||||
self.__msgTitle = title
|
||||
if icon:
|
||||
self.__msgIcon = icon
|
||||
retVal = self.send(False)
|
||||
self.__msgType = oldType
|
||||
return retVal
|
||||
|
||||
def setTimeout(self, timeout):
|
||||
"""Set the timeout in seconds of the message"""
|
||||
oldType = self.__msgType
|
||||
oldData = self.__msgData
|
||||
self.__msgType = SET_TIMEOUT
|
||||
#self.timeout = timeout
|
||||
#self.__msgData = self.__msgTimeout
|
||||
self.__msgData = timeout
|
||||
retVal = self.send(False)
|
||||
self.__msgType = oldType
|
||||
self.__msgData = oldData
|
||||
return retVal
|
||||
|
||||
def show(self, timeout=None, title=None,
|
||||
text=None, icon=None,
|
||||
replyWindow=None, replyMsg=None, msgclass=None, soundPath=None):
|
||||
"""Show a message"""
|
||||
oldType = self.__msgType
|
||||
oldTimeout = self.__msgTimeout
|
||||
self.__msgType = SHOW
|
||||
if text:
|
||||
self.__msgText = text
|
||||
if title:
|
||||
self.__msgTitle = title
|
||||
if timeout:
|
||||
self.__msgTimeout = timeout
|
||||
if icon:
|
||||
self.__msgIcon = icon
|
||||
if replyWindow:
|
||||
self.__msgID = replyMsg
|
||||
if replyMsg:
|
||||
self.__msgData = replyWindow
|
||||
if soundPath:
|
||||
self.__msgExtra = soundPath
|
||||
if msgclass:
|
||||
self.__msgClass = msgclass
|
||||
|
||||
if ((self.__msgClass and self.__msgClass != "") or
|
||||
(self.__msgExtra and self.__msgExtra != "")):
|
||||
self.__msgType = EX_SHOW
|
||||
|
||||
|
||||
retVal = bool(self.send())
|
||||
self.__msgType = oldType
|
||||
self.__msgTimeout = oldTimeout
|
||||
return retVal
|
||||
|
||||
|
||||
def snGetVersion():
|
||||
""" Get the version of Snarl that is running as a tuple. (Major, Minor)
|
||||
|
||||
If Snarl is not running or there was an error it will
|
||||
return False."""
|
||||
msg = SnarlMessage(msg_type=GET_VERSION)
|
||||
version = msg.send(False)
|
||||
if not version:
|
||||
return False
|
||||
return (HIWORD(version), LOWORD(version))
|
||||
|
||||
def snGetVersionEx():
|
||||
""" Get the internal version of Snarl that is running.
|
||||
|
||||
If Snarl is not running or there was an error it will
|
||||
return False."""
|
||||
sm = SnarlMessage(msg_type=GET_VERSION_EX)
|
||||
verNum = sm.send(False)
|
||||
if not verNum:
|
||||
return False
|
||||
return verNum
|
||||
|
||||
def snGetGlobalMessage():
|
||||
"""Get the Snarl global message id from windows."""
|
||||
return myWin32Funcs.RegisterWindowMessage(GLOBAL_MSG)
|
||||
|
||||
def snShowMessage(title, text, timeout=0, iconPath="",
|
||||
replyWindow=0, replyMsg=0):
|
||||
"""Show a message using Snarl and return its ID. See SDK for arguments."""
|
||||
sm = SnarlMessage( title, text, iconPath, msg_id=replyMsg)
|
||||
sm.data = replyWindow
|
||||
if sm.show(timeout):
|
||||
return sm.ID
|
||||
else:
|
||||
return False
|
||||
|
||||
def snShowMessageEx(msgClass, title, text, timeout=0, iconPath="",
|
||||
replyWindow=0, replyMsg=0, soundFile=None, hWndFrom=None):
|
||||
"""Show a message using Snarl and return its ID. See SDK for arguments.
|
||||
One added argument is hWndFrom that allows one to make the messages appear
|
||||
to come from a specific window. This window should be the one you registered
|
||||
earlier with RegisterConfig"""
|
||||
sm = SnarlMessage( title, text, iconPath, msg_id=replyMsg)
|
||||
sm.data = replyWindow
|
||||
if hWndFrom is not None:
|
||||
sm.hWnd = hWndFrom
|
||||
else:
|
||||
sm.hWnd = SnarlMessage.lastKnownHWnd
|
||||
if sm.show(timeout, msgclass=msgClass, soundPath=soundFile):
|
||||
return sm.ID
|
||||
else:
|
||||
return False
|
||||
|
||||
def snUpdateMessage(msgId, msgTitle, msgText, icon=None):
|
||||
"""Update a message"""
|
||||
sm = SnarlMessage(msg_id=msgId)
|
||||
if icon:
|
||||
sm.icon = icon
|
||||
return sm.update(msgTitle, msgText)
|
||||
|
||||
def snHideMessage(msgId):
|
||||
"""Hide a message"""
|
||||
return SnarlMessage(msg_id=msgId).hide()
|
||||
|
||||
def snSetTimeout(msgId, timeout):
|
||||
"""Update the timeout of a message already shown."""
|
||||
sm = SnarlMessage(msg_id=msgId)
|
||||
return sm.setTimeout(timeout)
|
||||
|
||||
def snIsMessageVisible(msgId):
|
||||
"""Returns True if the message is visible False otherwise."""
|
||||
return SnarlMessage(msg_id=msgId).isVisible()
|
||||
|
||||
def snRegisterConfig(replyWnd, appName, replyMsg):
|
||||
"""Register a config window. See SDK for more info."""
|
||||
global lastRegisteredSnarlMsg
|
||||
sm = SnarlMessage(msg_type=REGISTER_CONFIG_WINDOW,
|
||||
title=appName,
|
||||
msg_id=replyMsg)
|
||||
sm.data = replyWnd
|
||||
SnarlMessage.lastKnownHWnd = replyWnd
|
||||
|
||||
return sm.send(False)
|
||||
|
||||
def snRegisterConfig2(replyWnd, appName, replyMsg, icon):
|
||||
"""Register a config window. See SDK for more info."""
|
||||
global lastRegisteredSnarlMsg
|
||||
sm = SnarlMessage(msg_type=REGISTER_CONFIG_WINDOW_2,
|
||||
title=appName,
|
||||
msg_id=replyMsg,
|
||||
icon=icon)
|
||||
sm.data = replyWnd
|
||||
SnarlMessage.lastKnownHWnd = replyWnd
|
||||
return sm.send(False)
|
||||
|
||||
def snRegisterAlert(appName, classStr) :
|
||||
"""Register an alert for an already registered config. See SDK for more info."""
|
||||
sm = SnarlMessage(msg_type=REGISTER_ALERT,
|
||||
title=appName,
|
||||
text=classStr)
|
||||
return sm.send(False)
|
||||
|
||||
def snRevokeConfig(replyWnd):
|
||||
"""Revoke a config window"""
|
||||
sm = SnarlMessage(msg_type=REVOKE_CONFIG_WINDOW)
|
||||
sm.data = replyWnd
|
||||
if replyWnd == SnarlMessage.lastKnownHWnd:
|
||||
SnarlMessage.lastKnownHWnd = 0
|
||||
return sm.send(False)
|
||||
|
||||
def snGetSnarlWindow():
|
||||
"""Returns the hWnd of the snarl window"""
|
||||
return myWin32Funcs.FindWindow(None, "Snarl")
|
||||
|
||||
def snGetAppPath():
|
||||
"""Returns the application path of the currently running snarl window"""
|
||||
app_path = None
|
||||
snarl_handle = snGetSnarlWindow()
|
||||
if snarl_handle != 0:
|
||||
pathwin_handle = myWin32Funcs.FindWindowEx(snarl_handle,
|
||||
0,
|
||||
"static",
|
||||
None)
|
||||
if pathwin_handle != 0:
|
||||
try:
|
||||
result = myWin32Funcs.GetWindowText(pathwin_handle)
|
||||
app_path = result
|
||||
except Win32FuncException:
|
||||
pass
|
||||
|
||||
|
||||
return app_path
|
||||
|
||||
def snGetIconsPath():
|
||||
"""Returns the path to the icons of the program"""
|
||||
s = snGetAppPath()
|
||||
if s is None:
|
||||
return ""
|
||||
else:
|
||||
return s + "etc\\icons\\"
|
||||
|
||||
def snSendTestMessage(data=None):
|
||||
"""Sends a test message to Snarl. Used to make sure the
|
||||
api is connecting"""
|
||||
param = 0
|
||||
command = 0
|
||||
if data:
|
||||
param = struct.pack("I", data)
|
||||
command = 1
|
||||
myWin32Funcs.SendMessage(snGetSnarlWindow(), WM_SNARLTEST, command, param)
|
|
@ -0,0 +1 @@
|
|||
from plugin import SnarlNotificationsPlugin
|
|
@ -0,0 +1,90 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
## Gajim 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 3 only.
|
||||
##
|
||||
## Gajim 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.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
'''
|
||||
Events notifications using Snarl
|
||||
|
||||
Fancy events notifications under Windows using Snarl infrastructure.
|
||||
|
||||
:note: plugin is at proof-of-concept state.
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 15th August 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import new
|
||||
from pprint import pformat
|
||||
|
||||
#import PySnarl
|
||||
|
||||
from common import gajim
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log_calls, log
|
||||
from common import ged
|
||||
|
||||
class SnarlNotificationsPlugin(GajimPlugin):
|
||||
name = u'Snarl Notifications'
|
||||
short_name = u'snarl_notifications'
|
||||
version = u'0.1'
|
||||
description = u'''Shows events notification using Snarl (http://www.fullphat.net/) under Windows. Snarl needs to be installed in system.
|
||||
PySnarl bindings are used (http://code.google.com/p/pysnarl/).'''
|
||||
authors = [u'Mateusz Biliński <mateusz@bilinski.it>']
|
||||
homepage = u'http://blog.bilinski.it'
|
||||
|
||||
@log_calls('SnarlNotificationsPlugin')
|
||||
def init(self):
|
||||
self.config_dialog = None
|
||||
#self.gui_extension_points = {}
|
||||
#self.config_default_values = {}
|
||||
|
||||
self.events_handlers = {'NewMessage' : (ged.POSTCORE, self.newMessage)}
|
||||
|
||||
@log_calls('SnarlNotificationsPlugin')
|
||||
def activate(self):
|
||||
pass
|
||||
|
||||
@log_calls('SnarlNotificationsPlugin')
|
||||
def deactivate(self):
|
||||
pass
|
||||
|
||||
@log_calls('SnarlNotificationsPlugin')
|
||||
def newMessage(self, args):
|
||||
event_name = "NewMessage"
|
||||
data = args
|
||||
account = data[0]
|
||||
jid = data[1][0]
|
||||
jid_without_resource = gajim.get_jid_without_resource(jid)
|
||||
msg = data[1][1]
|
||||
msg_type = data[1][4]
|
||||
if msg_type == 'chat':
|
||||
nickname = gajim.get_contact_name_from_jid(account,
|
||||
jid_without_resource)
|
||||
elif msg_type == 'pm':
|
||||
nickname = gajim.get_resource_from_jid(jid)
|
||||
|
||||
print "Event '%s' occured. Arguments: %s\n\n===\n"%(event_name, pformat(args))
|
||||
print "Event '%s' occured. Arguments: \naccount = %s\njid = %s\nmsg = %s\nnickname = %s"%(
|
||||
event_name, account, jid, msg, nickname)
|
||||
|
||||
|
||||
#if PySnarl.snGetVersion() != False:
|
||||
#(major, minor) = PySnarl.snGetVersion()
|
||||
#print "Found Snarl version",str(major)+"."+str(minor),"running."
|
||||
#PySnarl.snShowMessage(nickname, msg[:20]+'...')
|
||||
#else:
|
||||
#print "Sorry Snarl does not appear to be running"
|
2623
po/be@latin.po
2623
po/be@latin.po
File diff suppressed because it is too large
Load Diff
2740
po/en_GB.po
2740
po/en_GB.po
File diff suppressed because it is too large
Load Diff
2615
po/pt_BR.po
2615
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
2589
po/sr@Latn.po
2589
po/sr@Latn.po
File diff suppressed because it is too large
Load Diff
2615
po/zh_CN.po
2615
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
|
@ -10,7 +10,8 @@ nobase_dist_gajimsrc_PYTHON = \
|
|||
$(srcdir)/common/xmpp/*.py \
|
||||
$(srcdir)/common/zeroconf/*.py \
|
||||
$(srcdir)/command_system/*.py \
|
||||
$(srcdir)/command_system/implementation/*.py
|
||||
$(srcdir)/command_system/implementation/*.py \
|
||||
$(srcdir)/plugins/*.py
|
||||
|
||||
dist-hook:
|
||||
rm -f $(distdir)/ipython_view.py
|
||||
|
|
|
@ -64,13 +64,12 @@ class CommandWindow:
|
|||
self.window.connect('delete-event',
|
||||
self.on_adhoc_commands_window_delete_event)
|
||||
for name in ('restart_button', 'back_button', 'forward_button',
|
||||
'execute_button', 'close_button', 'stages_notebook',
|
||||
'retrieving_commands_stage_vbox',
|
||||
'command_list_stage_vbox', 'command_list_vbox',
|
||||
'sending_form_stage_vbox', 'sending_form_progressbar',
|
||||
'notes_label', 'no_commands_stage_vbox', 'error_stage_vbox',
|
||||
'error_description_label'):
|
||||
self.__dict__[name] = self.xml.get_object(name)
|
||||
'execute_button', 'finish_button', 'close_button', 'stages_notebook',
|
||||
'retrieving_commands_stage_vbox', 'command_list_stage_vbox',
|
||||
'command_list_vbox', 'sending_form_stage_vbox',
|
||||
'sending_form_progressbar', 'notes_label', 'no_commands_stage_vbox',
|
||||
'error_stage_vbox', 'error_description_label'):
|
||||
setattr(self, name, self.xml.get_object(name))
|
||||
|
||||
self.initiate()
|
||||
|
||||
|
@ -100,6 +99,7 @@ class CommandWindow:
|
|||
self.stage1()
|
||||
|
||||
# displaying the window
|
||||
self.window.set_title('Ad-hoc Commands - Gajim')
|
||||
self.xml.connect_signals(self)
|
||||
self.window.show_all()
|
||||
|
||||
|
@ -121,6 +121,9 @@ class CommandWindow:
|
|||
def stage_close_button_clicked(self, *anything):
|
||||
assert False
|
||||
|
||||
def stage_restart_button_clicked(self, *anything):
|
||||
assert False
|
||||
|
||||
def stage_adhoc_commands_window_delete_event(self, *anything):
|
||||
assert False
|
||||
|
||||
|
@ -137,9 +140,15 @@ class CommandWindow:
|
|||
def on_execute_button_clicked(self, *anything):
|
||||
return self.stage_execute_button_clicked(*anything)
|
||||
|
||||
def on_finish_button_clicked(self, *anything):
|
||||
return self.stage_finish_button_clicked(*anything)
|
||||
|
||||
def on_close_button_clicked(self, *anything):
|
||||
return self.stage_close_button_clicked(*anything)
|
||||
|
||||
def on_restart_button_clicked(self, *anything):
|
||||
return self.stage_restart_button_clicked(*anything)
|
||||
|
||||
def on_adhoc_commands_window_destroy(self, *anything):
|
||||
# TODO: do all actions that are needed to remove this object from memory
|
||||
self.remove_pulsing()
|
||||
|
@ -169,15 +178,17 @@ class CommandWindow:
|
|||
self.back_button.set_sensitive(False)
|
||||
self.forward_button.set_sensitive(False)
|
||||
self.execute_button.set_sensitive(False)
|
||||
self.finish_button.set_sensitive(False)
|
||||
|
||||
# request command list
|
||||
self.request_command_list()
|
||||
self.setup_pulsing(
|
||||
self.xml.get_object('retrieving_commands_progressbar'))
|
||||
self.xml.get_object('retrieving_commands_progressbar'))
|
||||
|
||||
# setup the callbacks
|
||||
self.stage_finish = self.stage1_finish
|
||||
self.stage_close_button_clicked = self.stage1_close_button_clicked
|
||||
self.stage_restart_button_clicked = self.stage1_restart_button_clicked
|
||||
self.stage_adhoc_commands_window_delete_event = \
|
||||
self.stage1_adhoc_commands_window_delete_event
|
||||
|
||||
|
@ -187,9 +198,13 @@ class CommandWindow:
|
|||
def stage1_close_button_clicked(self, widget):
|
||||
# cancelling in this stage is not critical, so we don't
|
||||
# show any popups to user
|
||||
self.stage1_finish()
|
||||
self.stage_finish()
|
||||
self.window.destroy()
|
||||
|
||||
def stage1_restart_button_clicked(self, widget):
|
||||
self.stage_finish()
|
||||
self.restart()
|
||||
|
||||
def stage1_adhoc_commands_window_delete_event(self, widget):
|
||||
self.stage1_finish()
|
||||
return True
|
||||
|
@ -214,6 +229,7 @@ class CommandWindow:
|
|||
self.back_button.set_sensitive(False)
|
||||
self.forward_button.set_sensitive(True)
|
||||
self.execute_button.set_sensitive(False)
|
||||
self.finish_button.set_sensitive(False)
|
||||
|
||||
# build the commands list radiobuttons
|
||||
first_radio = None
|
||||
|
@ -229,6 +245,7 @@ class CommandWindow:
|
|||
|
||||
self.stage_finish = self.stage2_finish
|
||||
self.stage_close_button_clicked = self.stage2_close_button_clicked
|
||||
self.stage_restart_button_clicked = self.stage2_restart_button_clicked
|
||||
self.stage_forward_button_clicked = self.stage2_forward_button_clicked
|
||||
self.stage_adhoc_commands_window_delete_event = self.do_nothing
|
||||
|
||||
|
@ -244,6 +261,10 @@ class CommandWindow:
|
|||
self.stage_finish()
|
||||
self.window.destroy()
|
||||
|
||||
def stage2_restart_button_clicked(self, widget):
|
||||
self.stage_finish()
|
||||
self.restart()
|
||||
|
||||
def stage2_forward_button_clicked(self, widget):
|
||||
self.stage3()
|
||||
|
||||
|
@ -266,10 +287,12 @@ class CommandWindow:
|
|||
self.stages_notebook.page_num(
|
||||
self.sending_form_stage_vbox))
|
||||
|
||||
self.restart_button.set_sensitive(True)
|
||||
self.close_button.set_sensitive(True)
|
||||
self.back_button.set_sensitive(False)
|
||||
self.forward_button.set_sensitive(False)
|
||||
self.execute_button.set_sensitive(False)
|
||||
self.finish_button.set_sensitive(False)
|
||||
|
||||
self.stage3_submit_form()
|
||||
|
||||
|
@ -277,13 +300,31 @@ class CommandWindow:
|
|||
self.stage_back_button_clicked = self.stage3_back_button_clicked
|
||||
self.stage_forward_button_clicked = self.stage3_forward_button_clicked
|
||||
self.stage_execute_button_clicked = self.stage3_execute_button_clicked
|
||||
self.stage_finish_button_clicked = self.stage3_finish_button_clicked
|
||||
self.stage_close_button_clicked = self.stage3_close_button_clicked
|
||||
self.stage_restart_button_clicked = self.stage3_restart_button_clicked
|
||||
self.stage_adhoc_commands_window_delete_event = \
|
||||
self.stage3_close_button_clicked
|
||||
|
||||
def stage3_finish(self):
|
||||
pass
|
||||
|
||||
def stage3_can_close(self, cb):
|
||||
if self.form_status == 'completed':
|
||||
cb()
|
||||
return
|
||||
|
||||
def on_yes(button):
|
||||
self.send_cancel()
|
||||
dialog.destroy()
|
||||
cb()
|
||||
|
||||
dialog = dialogs.HigDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT \
|
||||
| gtk.DIALOG_MODAL, gtk.BUTTONS_YES_NO, _('Cancel confirmation'),
|
||||
_('You are in process of executing command. Do you really want to '
|
||||
'cancel it?'), on_response_yes=on_yes)
|
||||
dialog.popup()
|
||||
|
||||
def stage3_close_button_clicked(self, widget):
|
||||
"""
|
||||
We are in the middle of executing command. Ask user if he really want to
|
||||
|
@ -291,26 +332,23 @@ class CommandWindow:
|
|||
"""
|
||||
# this works also as a handler for window_delete_event, so we have to
|
||||
# return appropriate values
|
||||
if self.form_status == 'completed':
|
||||
if widget != self.window:
|
||||
self.window.destroy()
|
||||
return False
|
||||
|
||||
if self.allow_stage3_close:
|
||||
return False
|
||||
|
||||
def on_yes(button):
|
||||
self.send_cancel()
|
||||
def on_ok():
|
||||
self.allow_stage3_close = True
|
||||
self.window.destroy()
|
||||
|
||||
dialog = dialogs.HigDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT \
|
||||
| gtk.DIALOG_MODAL, gtk.BUTTONS_YES_NO, _('Cancel confirmation'),
|
||||
_('You are in process of executing command. Do you really want to '
|
||||
'cancel it?'), on_response_yes=on_yes)
|
||||
dialog.popup()
|
||||
self.stage3_can_close(on_ok)
|
||||
|
||||
return True # Block event, don't close window
|
||||
|
||||
def stage3_restart_button_clicked(self, widget):
|
||||
def on_ok():
|
||||
self.restart()
|
||||
|
||||
self.stage3_can_close(on_ok)
|
||||
|
||||
def stage3_back_button_clicked(self, widget):
|
||||
self.stage3_submit_form('prev')
|
||||
|
||||
|
@ -320,9 +358,19 @@ class CommandWindow:
|
|||
def stage3_execute_button_clicked(self, widget):
|
||||
self.stage3_submit_form('execute')
|
||||
|
||||
def stage3_finish_button_clicked(self, widget):
|
||||
self.stage3_submit_form('complete')
|
||||
|
||||
def stage3_submit_form(self, action='execute'):
|
||||
self.data_form_widget.set_sensitive(False)
|
||||
|
||||
if self.data_form_widget.get_data_form():
|
||||
df = self.data_form_widget.get_data_form()
|
||||
if not df.is_valid():
|
||||
dialogs.ErrorDialog(_('Invalid Form'),
|
||||
_('The form is not filled correctly.'))
|
||||
self.data_form_widget.set_sensitive(True)
|
||||
return
|
||||
self.data_form_widget.data_form.type = 'submit'
|
||||
else:
|
||||
self.data_form_widget.hide()
|
||||
|
@ -331,6 +379,7 @@ class CommandWindow:
|
|||
self.back_button.set_sensitive(False)
|
||||
self.forward_button.set_sensitive(False)
|
||||
self.execute_button.set_sensitive(False)
|
||||
self.finish_button.set_sensitive(False)
|
||||
|
||||
self.sending_form_progressbar.show()
|
||||
self.setup_pulsing(self.sending_form_progressbar)
|
||||
|
@ -379,18 +428,21 @@ class CommandWindow:
|
|||
self.forward_button.set_sensitive(
|
||||
actions.getTag('next') is not None)
|
||||
self.execute_button.set_sensitive(True)
|
||||
self.finish_button.set_sensitive(actions.getTag('complete') is not \
|
||||
None)
|
||||
else:
|
||||
self.close_button.set_sensitive(True)
|
||||
self.back_button.set_sensitive(False)
|
||||
self.forward_button.set_sensitive(False)
|
||||
self.execute_button.set_sensitive(True)
|
||||
self.finish_button.set_sensitive(False)
|
||||
|
||||
if self.form_status == 'completed':
|
||||
self.close_button.set_sensitive(True)
|
||||
self.restart_button.set_sensitive(True)
|
||||
self.back_button.hide()
|
||||
self.forward_button.hide()
|
||||
self.execute_button.hide()
|
||||
self.finish_button.hide()
|
||||
self.close_button.show()
|
||||
self.stage_adhoc_commands_window_delete_event = \
|
||||
self.stage3_close_button_clicked
|
||||
|
@ -404,7 +456,7 @@ class CommandWindow:
|
|||
self.notes_label.set_no_show_all(True)
|
||||
self.notes_label.hide()
|
||||
|
||||
def on_restart_button_clicked(self, widget):
|
||||
def restart(self):
|
||||
self.commandnode = None
|
||||
self.initiate()
|
||||
|
||||
|
@ -423,14 +475,19 @@ class CommandWindow:
|
|||
self.back_button.set_sensitive(False)
|
||||
self.forward_button.set_sensitive(False)
|
||||
self.execute_button.set_sensitive(False)
|
||||
self.finish_button.set_sensitive(False)
|
||||
|
||||
self.stage_finish = self.do_nothing
|
||||
self.stage_close_button_clicked = self.stage4_close_button_clicked
|
||||
self.stage_restart_button_clicked = self.stage4_restart_button_clicked
|
||||
self.stage_adhoc_commands_window_delete_event = self.do_nothing
|
||||
|
||||
def stage4_close_button_clicked(self, widget):
|
||||
self.window.destroy()
|
||||
|
||||
def stage4_restart_button_clicked(self, widget):
|
||||
self.restart()
|
||||
|
||||
def on_check_commands_2_button_clicked(self, widget):
|
||||
self.stage1()
|
||||
|
||||
|
@ -468,16 +525,21 @@ class CommandWindow:
|
|||
self.back_button.hide()
|
||||
self.forward_button.hide()
|
||||
self.execute_button.hide()
|
||||
self.finish_button.hide()
|
||||
|
||||
self.error_description_label.set_text(error)
|
||||
|
||||
self.stage_finish = self.do_nothing
|
||||
self.stage_close_button_clicked = self.stage5_close_button_clicked
|
||||
self.stage_restart_button_clicked = self.stage5_restart_button_clicked
|
||||
self.stage_adhoc_commands_window_delete_event = self.do_nothing
|
||||
|
||||
def stage5_close_button_clicked(self, widget):
|
||||
self.window.destroy()
|
||||
|
||||
def stage5_restart_button_clicked(self, widget):
|
||||
self.restart()
|
||||
|
||||
# helpers to handle pulsing in progressbar
|
||||
def setup_pulsing(self, progressbar):
|
||||
"""
|
||||
|
@ -554,7 +616,7 @@ class CommandWindow:
|
|||
cmdnode.setAttr('sessionid', self.sessionid)
|
||||
|
||||
if self.data_form_widget.data_form:
|
||||
cmdnode.addChild(node=self.data_form_widget.data_form)
|
||||
cmdnode.addChild(node=self.data_form_widget.data_form.get_purged())
|
||||
|
||||
def callback(response):
|
||||
# FIXME: move to connection_handlers.py
|
||||
|
|
|
@ -53,15 +53,16 @@ class AtomWindow:
|
|||
"""
|
||||
Create new window... only if we have anything to show
|
||||
"""
|
||||
assert len(self.__class__.entries)>0
|
||||
assert len(self.__class__.entries)
|
||||
|
||||
self.entry = None # the entry actually displayed
|
||||
self.entry = None # the entry actually displayed
|
||||
|
||||
self.xml = gtkgui_helpers.get_gtk_builder('atom_entry_window.ui')
|
||||
self.window = self.xml.get_object('atom_entry_window')
|
||||
for name in ('new_entry_label', 'feed_title_label', 'feed_title_eventbox',
|
||||
'feed_tagline_label', 'entry_title_label', 'entry_title_eventbox',
|
||||
'last_modified_label', 'close_button', 'next_button'):
|
||||
for name in ('new_entry_label', 'feed_title_label',
|
||||
'feed_title_eventbox', 'feed_tagline_label', 'entry_title_label',
|
||||
'entry_title_eventbox', 'last_modified_label', 'close_button',
|
||||
'next_button'):
|
||||
self.__dict__[name] = self.xml.get_object(name)
|
||||
|
||||
self.displayNextEntry()
|
||||
|
@ -83,23 +84,26 @@ class AtomWindow:
|
|||
# fill the fields
|
||||
if newentry.feed_link is not None:
|
||||
self.feed_title_label.set_markup(
|
||||
u'<span foreground="blue" underline="single">%s</span>' % \
|
||||
gobject.markup_escape_text(newentry.feed_title))
|
||||
u'<span foreground="blue" underline="single">%s</span>' % \
|
||||
gobject.markup_escape_text(newentry.feed_title))
|
||||
else:
|
||||
self.feed_title_label.set_markup(
|
||||
gobject.markup_escape_text(newentry.feed_title))
|
||||
gobject.markup_escape_text(newentry.feed_title))
|
||||
|
||||
self.feed_tagline_label.set_markup(
|
||||
u'<small>%s</small>' % \
|
||||
gobject.markup_escape_text(newentry.feed_tagline))
|
||||
u'<small>%s</small>' % \
|
||||
gobject.markup_escape_text(newentry.feed_tagline))
|
||||
|
||||
if newentry.uri is not None:
|
||||
self.entry_title_label.set_markup(
|
||||
if newentry.title:
|
||||
if newentry.uri is not None:
|
||||
self.entry_title_label.set_markup(
|
||||
u'<span foreground="blue" underline="single">%s</span>' % \
|
||||
gobject.markup_escape_text(newentry.title))
|
||||
else:
|
||||
self.entry_title_label.set_markup(
|
||||
else:
|
||||
self.entry_title_label.set_markup(
|
||||
gobject.markup_escape_text(newentry.title))
|
||||
else:
|
||||
self.entry_title_label.set_markup('')
|
||||
|
||||
self.last_modified_label.set_text(newentry.updated)
|
||||
|
||||
|
@ -114,11 +118,11 @@ class AtomWindow:
|
|||
changed
|
||||
"""
|
||||
count = len(self.__class__.entries)
|
||||
if count>0:
|
||||
if count:
|
||||
self.new_entry_label.set_text(i18n.ngettext(
|
||||
'You have received new entries (and %d not displayed):',
|
||||
'You have received new entries (and %d not displayed):', count,
|
||||
count, count))
|
||||
'You have received new entries (and %d not displayed):',
|
||||
'You have received new entries (and %d not displayed):', count,
|
||||
count, count))
|
||||
self.next_button.set_sensitive(True)
|
||||
else:
|
||||
self.new_entry_label.set_text(_('You have received new entry:'))
|
||||
|
@ -131,7 +135,7 @@ class AtomWindow:
|
|||
def on_next_button_clicked(self, widget):
|
||||
self.displayNextEntry()
|
||||
|
||||
def on_entry_title_button_press_event(self, widget, event):
|
||||
def on_entry_title_eventbox_button_press_event(self, widget, event):
|
||||
#FIXME: make it using special gtk2.10 widget
|
||||
if event.button == 1: # left click
|
||||
uri = self.entry.uri
|
||||
|
@ -139,7 +143,7 @@ class AtomWindow:
|
|||
helpers.launch_browser_mailer('url', uri)
|
||||
return True
|
||||
|
||||
def on_feed_title_button_press_event(self, widget, event):
|
||||
def on_feed_title_eventbox_button_press_event(self, widget, event):
|
||||
#FIXME: make it using special gtk2.10 widget
|
||||
if event.button == 1: # left click
|
||||
uri = self.entry.feed_uri
|
||||
|
|
|
@ -46,6 +46,7 @@ from common import exceptions
|
|||
from message_control import MessageControl
|
||||
from conversation_textview import ConversationTextview
|
||||
from message_textview import MessageTextView
|
||||
from common.stanza_session import EncryptedStanzaSession, ArchivingStanzaSession
|
||||
from common.contacts import GC_Contact
|
||||
from common.logger import constants
|
||||
from common.pep import MOODS, ACTIVITIES
|
||||
|
@ -95,8 +96,14 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
"""
|
||||
|
||||
keymap = gtk.gdk.keymap_get_default()
|
||||
keycode_c = keymap.get_entries_for_keyval(gtk.keysyms.c)[0][0]
|
||||
keycode_ins = keymap.get_entries_for_keyval(gtk.keysyms.Insert)[0][0]
|
||||
try:
|
||||
keycode_c = keymap.get_entries_for_keyval(gtk.keysyms.c)[0][0]
|
||||
except TypeError:
|
||||
keycode_c = 54
|
||||
try:
|
||||
keycode_ins = keymap.get_entries_for_keyval(gtk.keysyms.Insert)[0][0]
|
||||
except TypeError:
|
||||
keycode_ins = 118
|
||||
def make_href(self, match):
|
||||
url_color = gajim.config.get('urlmsgcolor')
|
||||
url = match.group()
|
||||
|
@ -149,6 +156,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
"""
|
||||
self.draw_banner_text()
|
||||
self._update_banner_state_image()
|
||||
gajim.plugin_manager.gui_extension_point('chat_control_base_draw_banner',
|
||||
self)
|
||||
|
||||
def draw_banner_text(self):
|
||||
"""
|
||||
|
@ -232,6 +241,29 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
def status_url_clicked(self, widget, url):
|
||||
helpers.launch_browser_mailer('url', url)
|
||||
|
||||
def setup_seclabel(self, combo):
|
||||
self.seclabel_combo = combo
|
||||
self.seclabel_combo.hide()
|
||||
self.seclabel_combo.set_no_show_all(True)
|
||||
lb = gtk.ListStore(str)
|
||||
self.seclabel_combo.set_model(lb)
|
||||
cell = gtk.CellRendererText()
|
||||
cell.set_property('xpad', 5) # padding for status text
|
||||
self.seclabel_combo.pack_start(cell, True)
|
||||
# text to show is in in first column of liststore
|
||||
self.seclabel_combo.add_attribute(cell, 'text', 0)
|
||||
if gajim.connections[self.account].seclabel_supported:
|
||||
gajim.connections[self.account].seclabel_catalogue(self.contact.jid, self.on_seclabels_ready)
|
||||
|
||||
def on_seclabels_ready(self):
|
||||
lb = self.seclabel_combo.get_model()
|
||||
lb.clear()
|
||||
for label in gajim.connections[self.account].seclabel_catalogues[self.contact.jid][2]:
|
||||
lb.append([label])
|
||||
self.seclabel_combo.set_active(0)
|
||||
self.seclabel_combo.set_no_show_all(False)
|
||||
self.seclabel_combo.show_all()
|
||||
|
||||
def __init__(self, type_id, parent_win, widget_name, contact, acct,
|
||||
resource=None):
|
||||
# Undo needs this variable to know if space has been pressed.
|
||||
|
@ -380,6 +412,14 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
self.command_hits = []
|
||||
self.last_key_tabs = False
|
||||
|
||||
# PluginSystem: adding GUI extension point for ChatControlBase
|
||||
# instance object (also subclasses, eg. ChatControl or GroupchatControl)
|
||||
gajim.plugin_manager.gui_extension_point('chat_control_base', self)
|
||||
|
||||
# This is bascially a very nasty hack to surpass the inability
|
||||
# to properly use the super, because of the old code.
|
||||
CommandTools.__init__(self)
|
||||
|
||||
def set_speller(self):
|
||||
# now set the one the user selected
|
||||
per_type = 'contacts'
|
||||
|
@ -415,6 +455,12 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
i += 1
|
||||
menu.show_all()
|
||||
|
||||
def shutdown(self):
|
||||
# PluginSystem: removing GUI extension points connected with ChatControlBase
|
||||
# instance object
|
||||
gajim.plugin_manager.remove_gui_extension_point('chat_control_base', self)
|
||||
gajim.plugin_manager.remove_gui_extension_point('chat_control_base_draw_banner', self)
|
||||
|
||||
def on_msg_textview_populate_popup(self, textview, menu):
|
||||
"""
|
||||
Override the default context menu and we prepend an option to switch
|
||||
|
@ -721,6 +767,16 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
self.drag_entered_conv = True
|
||||
self.conv_textview.tv.set_editable(True)
|
||||
|
||||
def get_seclabel(self):
|
||||
label = None
|
||||
if self.seclabel_combo is not None:
|
||||
idx = self.seclabel_combo.get_active()
|
||||
if idx != -1:
|
||||
cat = gajim.connections[self.account].seclabel_catalogues[self.contact.jid]
|
||||
lname = cat[2][idx]
|
||||
label = cat[1][lname]
|
||||
return label
|
||||
|
||||
def send_message(self, message, keyID='', type_='chat', chatstate=None,
|
||||
msg_id=None, composing_xep=None, resource=None, xhtml=None,
|
||||
callback=None, callback_args=[], process_commands=True):
|
||||
|
@ -733,9 +789,11 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
if process_commands and self.process_as_command(message):
|
||||
return
|
||||
|
||||
label = self.get_seclabel()
|
||||
MessageControl.send_message(self, message, keyID, type_=type_,
|
||||
chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
|
||||
resource=resource, user_nick=self.user_nick, xhtml=xhtml,
|
||||
label=label,
|
||||
callback=callback, callback_args=callback_args)
|
||||
|
||||
# Record message history
|
||||
|
@ -769,7 +827,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
other_tags_for_name=[], other_tags_for_time=[],
|
||||
other_tags_for_text=[], count_as_new=True, subject=None,
|
||||
old_kind=None, xhtml=None, simple=False, xep0184_id=None,
|
||||
graphics=True):
|
||||
graphics=True, displaymarking=None):
|
||||
"""
|
||||
Print 'chat' type messages
|
||||
"""
|
||||
|
@ -781,7 +839,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
end = True
|
||||
textview.print_conversation_line(text, jid, kind, name, tim,
|
||||
other_tags_for_name, other_tags_for_time, other_tags_for_text,
|
||||
subject, old_kind, xhtml, simple=simple, graphics=graphics)
|
||||
subject, old_kind, xhtml, simple=simple, graphics=graphics,
|
||||
displaymarking=displaymarking)
|
||||
|
||||
if xep0184_id is not None:
|
||||
textview.show_xep0184_warning(xep0184_id)
|
||||
|
@ -1413,6 +1472,15 @@ class ChatControl(ChatControlBase):
|
|||
id_ = widget.connect('released', self.on_num_button_released)
|
||||
self.handlers[id_] = widget
|
||||
|
||||
self.dtmf_window = self.xml.get_object('dtmf_window')
|
||||
id_ = self.dtmf_window.connect('focus-out-event',
|
||||
self.on_dtmf_window_focus_out_event)
|
||||
self.handlers[id_] = self.dtmf_window
|
||||
|
||||
widget = self.xml.get_object('dtmf_button')
|
||||
id_ = widget.connect('clicked', self.on_dtmf_button_clicked)
|
||||
self.handlers[id_] = widget
|
||||
|
||||
widget = self.xml.get_object('mic_hscale')
|
||||
id_ = widget.connect('value_changed', self.on_mic_hscale_value_changed)
|
||||
self.handlers[id_] = widget
|
||||
|
@ -1429,6 +1497,7 @@ class ChatControl(ChatControlBase):
|
|||
session = gajim.connections[self.account].find_controlless_session(
|
||||
self.contact.jid, resource)
|
||||
|
||||
self.setup_seclabel(self.xml.get_object('label_selector'))
|
||||
if session:
|
||||
session.control = self
|
||||
self.session = session
|
||||
|
@ -1533,6 +1602,10 @@ class ChatControl(ChatControlBase):
|
|||
else:
|
||||
img.hide()
|
||||
|
||||
# PluginSystem: adding GUI extension point for this ChatControl
|
||||
# instance object
|
||||
gajim.plugin_manager.gui_extension_point('chat_control', self)
|
||||
|
||||
def _update_jingle(self, jingle_type):
|
||||
if jingle_type not in ('audio', 'video'):
|
||||
return
|
||||
|
@ -1558,7 +1631,7 @@ class ChatControl(ChatControlBase):
|
|||
|
||||
def update_audio(self):
|
||||
self._update_jingle('audio')
|
||||
vbox = self.xml.get_object('audio_vbox')
|
||||
hbox = self.xml.get_object('audio_buttons_hbox')
|
||||
if self.audio_state == self.JINGLE_STATE_CONNECTED:
|
||||
# Set volume from config
|
||||
input_vol = gajim.config.get('audio_input_volume')
|
||||
|
@ -1568,11 +1641,11 @@ class ChatControl(ChatControlBase):
|
|||
self.xml.get_object('mic_hscale').set_value(input_vol)
|
||||
self.xml.get_object('sound_hscale').set_value(output_vol)
|
||||
# Show vbox
|
||||
vbox.set_no_show_all(False)
|
||||
vbox.show_all()
|
||||
hbox.set_no_show_all(False)
|
||||
hbox.show_all()
|
||||
elif not self.audio_sid:
|
||||
vbox.set_no_show_all(True)
|
||||
vbox.hide()
|
||||
hbox.set_no_show_all(True)
|
||||
hbox.hide()
|
||||
|
||||
def update_video(self):
|
||||
self._update_jingle('video')
|
||||
|
@ -1646,19 +1719,21 @@ class ChatControl(ChatControlBase):
|
|||
def on_num_button_released(self, released):
|
||||
self._get_audio_content()._stop_dtmf()
|
||||
|
||||
def on_mic_hscale_value_changed(self, widget):
|
||||
value = widget.get_value()
|
||||
def on_dtmf_button_clicked(self, widget):
|
||||
self.dtmf_window.show_all()
|
||||
|
||||
def on_dtmf_window_focus_out_event(self, widget, event):
|
||||
self.dtmf_window.hide()
|
||||
|
||||
def on_mic_hscale_value_changed(self, widget, value):
|
||||
self._get_audio_content().set_mic_volume(value / 100)
|
||||
# Save volume to config
|
||||
# FIXME: Putting it here is maybe not the right thing to do?
|
||||
gajim.config.set('audio_input_volume', value)
|
||||
|
||||
|
||||
def on_sound_hscale_value_changed(self, widget):
|
||||
value = widget.get_value()
|
||||
def on_sound_hscale_value_changed(self, widget, value):
|
||||
self._get_audio_content().set_out_volume(value / 100)
|
||||
# Save volume to config
|
||||
# FIXME: Putting it here is maybe not the right thing to do?
|
||||
gajim.config.set('audio_output_volume', value)
|
||||
|
||||
def on_avatar_eventbox_enter_notify_event(self, widget, event):
|
||||
|
@ -2048,20 +2123,23 @@ class ChatControl(ChatControlBase):
|
|||
gobject.source_remove(self.possible_inactive_timeout_id)
|
||||
self._schedule_activity_timers()
|
||||
|
||||
def _on_sent(id_, contact, message, encrypted, xhtml):
|
||||
def _on_sent(id_, contact, message, encrypted, xhtml, label):
|
||||
if contact.supports(NS_RECEIPTS) and gajim.config.get_per('accounts',
|
||||
self.account, 'request_receipt'):
|
||||
xep0184_id = id_
|
||||
else:
|
||||
xep0184_id = None
|
||||
|
||||
if label:
|
||||
displaymarking = label.getTag('displaymarking')
|
||||
else:
|
||||
displaymarking = None
|
||||
self.print_conversation(message, self.contact.jid, encrypted=encrypted,
|
||||
xep0184_id=xep0184_id, xhtml=xhtml)
|
||||
xep0184_id=xep0184_id, xhtml=xhtml, displaymarking=displaymarking)
|
||||
|
||||
ChatControlBase.send_message(self, message, keyID, type_='chat',
|
||||
chatstate=chatstate_to_send, composing_xep=composing_xep,
|
||||
xhtml=xhtml, callback=_on_sent,
|
||||
callback_args=[contact, message, encrypted, xhtml],
|
||||
callback_args=[contact, message, encrypted, xhtml, self.get_seclabel()],
|
||||
process_commands=process_commands)
|
||||
|
||||
def check_for_possible_paused_chatstate(self, arg):
|
||||
|
@ -2125,6 +2203,18 @@ class ChatControl(ChatControlBase):
|
|||
msg = _('Session negotiation cancelled')
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
|
||||
|
||||
def print_archiving_session_details(self):
|
||||
"""
|
||||
Print esession settings to textview
|
||||
"""
|
||||
archiving = bool(self.session) and isinstance(self.session,
|
||||
ArchivingStanzaSession) and self.session.archiving
|
||||
if archiving:
|
||||
msg = _('This session WILL be archived on server')
|
||||
else:
|
||||
msg = _('This session WILL NOT be archived on server')
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
|
||||
|
||||
def print_esession_details(self):
|
||||
"""
|
||||
Print esession settings to textview
|
||||
|
@ -2149,8 +2239,15 @@ class ChatControl(ChatControlBase):
|
|||
self._show_lock_image(e2e_is_active, 'E2E', e2e_is_active, self.session and \
|
||||
self.session.is_loggable(), self.session and self.session.verified_identity)
|
||||
|
||||
def print_session_details(self):
|
||||
if isinstance(self.session, EncryptedStanzaSession):
|
||||
self.print_esession_details()
|
||||
elif isinstance(self.session, ArchivingStanzaSession):
|
||||
self.print_archiving_session_details()
|
||||
|
||||
def print_conversation(self, text, frm='', tim=None, encrypted=False,
|
||||
subject=None, xhtml=None, simple=False, xep0184_id=None):
|
||||
subject=None, xhtml=None, simple=False, xep0184_id=None,
|
||||
displaymarking=None):
|
||||
"""
|
||||
Print a line in the conversation
|
||||
|
||||
|
@ -2213,7 +2310,7 @@ class ChatControl(ChatControlBase):
|
|||
xhtml = '<body xmlns="%s">%s</body>' % (NS_XHTML, xhtml)
|
||||
ChatControlBase.print_conversation_line(self, text, kind, name, tim,
|
||||
subject=subject, old_kind=self.old_msg_kind, xhtml=xhtml,
|
||||
simple=simple, xep0184_id=xep0184_id)
|
||||
simple=simple, xep0184_id=xep0184_id, displaymarking=displaymarking)
|
||||
if text.startswith('/me ') or text.startswith('/me\n'):
|
||||
self.old_msg_kind = None
|
||||
else:
|
||||
|
@ -2397,7 +2494,13 @@ class ChatControl(ChatControlBase):
|
|||
self.reset_kbd_mouse_timeout_vars()
|
||||
|
||||
def shutdown(self):
|
||||
# Send 'gone' chatstate
|
||||
# PluginSystem: calling shutdown of super class (ChatControlBase) to let it remove
|
||||
# it's GUI extension points
|
||||
super(ChatControl, self).shutdown()
|
||||
# PluginSystem: removing GUI extension points connected with ChatControl
|
||||
# instance object
|
||||
gajim.plugin_manager.remove_gui_extension_point('chat_control', self) # Send 'gone' chatstate
|
||||
|
||||
self.send_chatstate('gone', self.contact)
|
||||
self.contact.chatstate = None
|
||||
self.contact.our_chatstate = None
|
||||
|
@ -2574,6 +2677,9 @@ class ChatControl(ChatControlBase):
|
|||
if want_e2e and not self.no_autonegotiation \
|
||||
and gajim.HAVE_PYCRYPTO and self.contact.supports(NS_ESESSION):
|
||||
self.begin_e2e_negotiation()
|
||||
elif (not self.session or not self.session.status) and \
|
||||
gajim.connections[self.account].archiving_supported:
|
||||
self.begin_archiving_negotiation()
|
||||
else:
|
||||
self.send_chatstate('active', self.contact)
|
||||
|
||||
|
@ -2660,8 +2766,12 @@ class ChatControl(ChatControlBase):
|
|||
kind = 'info'
|
||||
else:
|
||||
kind = 'print_queue'
|
||||
dm = None
|
||||
if len(data) > 10:
|
||||
dm = data[10]
|
||||
self.print_conversation(data[0], kind, tim = data[3],
|
||||
encrypted = data[4], subject = data[1], xhtml = data[7])
|
||||
encrypted = data[4], subject = data[1], xhtml = data[7],
|
||||
displaymarking=dm)
|
||||
if len(data) > 6 and isinstance(data[6], int):
|
||||
message_ids.append(data[6])
|
||||
|
||||
|
@ -2813,7 +2923,7 @@ class ChatControl(ChatControlBase):
|
|||
else:
|
||||
self.begin_e2e_negotiation()
|
||||
|
||||
def begin_e2e_negotiation(self):
|
||||
def begin_negotiation(self):
|
||||
self.no_autonegotiation = True
|
||||
|
||||
if not self.session:
|
||||
|
@ -2821,8 +2931,14 @@ class ChatControl(ChatControlBase):
|
|||
new_sess = gajim.connections[self.account].make_new_session(fjid, type_=self.type_id)
|
||||
self.set_session(new_sess)
|
||||
|
||||
def begin_e2e_negotiation(self):
|
||||
self.begin_negotiation()
|
||||
self.session.negotiate_e2e(False)
|
||||
|
||||
def begin_archiving_negotiation(self):
|
||||
self.begin_negotiation()
|
||||
self.session.negotiate_archiving()
|
||||
|
||||
def got_connected(self):
|
||||
ChatControlBase.got_connected(self)
|
||||
# Refreshing contact
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
# Copyright (c) 2010, Alexander Cherniuk (ts33kr@gmail.com)
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Backbone of the command system. Provides smart and controllable
|
||||
dispatching mechanism with an auto-discovery functionality. In addition
|
||||
to automatic discovery and dispatching, also features manual control
|
||||
over the process.
|
||||
"""
|
||||
|
||||
from types import NoneType
|
||||
from tools import remove
|
||||
|
||||
COMMANDS = {}
|
||||
CONTAINERS = {}
|
||||
|
||||
def add_host(host):
|
||||
CONTAINERS[host] = []
|
||||
|
||||
def remove_host(host):
|
||||
remove(CONTAINERS, host)
|
||||
|
||||
def add_container(container):
|
||||
for host in container.HOSTS:
|
||||
CONTAINERS[host].append(container)
|
||||
|
||||
def remove_container(container):
|
||||
for host in container.HOSTS:
|
||||
remove(CONTAINERS[host], container)
|
||||
|
||||
def add_commands(container):
|
||||
commands = COMMANDS.setdefault(container, {})
|
||||
for command in traverse_commands(container):
|
||||
for name in command.names:
|
||||
commands[name] = command
|
||||
|
||||
def remove_commands(container):
|
||||
remove(COMMANDS, container)
|
||||
|
||||
def traverse_commands(container):
|
||||
for name in dir(container):
|
||||
attribute = getattr(container, name)
|
||||
if is_command(attribute):
|
||||
yield attribute
|
||||
|
||||
def is_command(attribute):
|
||||
from framework import Command
|
||||
return isinstance(attribute, Command)
|
||||
|
||||
def is_root(namespace):
|
||||
metaclass = namespace.get("__metaclass__", NoneType)
|
||||
return issubclass(metaclass, Dispatchable)
|
||||
|
||||
def get_command(host, name):
|
||||
for container in CONTAINERS[host]:
|
||||
command = COMMANDS[container].get(name)
|
||||
if command:
|
||||
return command
|
||||
|
||||
def list_commands(host):
|
||||
for container in CONTAINERS[host]:
|
||||
commands = COMMANDS[container]
|
||||
for name, command in commands.iteritems():
|
||||
yield name, command
|
||||
|
||||
class Dispatchable(type):
|
||||
|
||||
def __init__(self, name, bases, namespace):
|
||||
parents = super(Dispatchable, self)
|
||||
parents.__init__(name, bases, namespace)
|
||||
if not is_root(namespace):
|
||||
self.dispatch()
|
||||
|
||||
def dispatch(self):
|
||||
if self.AUTOMATIC:
|
||||
self.enable()
|
||||
|
||||
class Host(Dispatchable):
|
||||
|
||||
def enable(self):
|
||||
add_host(self)
|
||||
|
||||
def disable(self):
|
||||
remove_host(self)
|
||||
|
||||
class Container(Dispatchable):
|
||||
|
||||
def enable(self):
|
||||
add_container(self)
|
||||
add_commands(self)
|
||||
|
||||
def disable(self):
|
||||
remove_commands(self)
|
||||
remove_container(self)
|
|
@ -1,90 +0,0 @@
|
|||
# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
The backbone of the command system. Provides automatic dispatching which
|
||||
does not require explicit registering commands or containers and remains
|
||||
active even after everything is done, so new commands can be added
|
||||
during the runtime.
|
||||
"""
|
||||
|
||||
from types import NoneType
|
||||
|
||||
class Dispatcher(type):
|
||||
|
||||
containers = {}
|
||||
commands = {}
|
||||
|
||||
@classmethod
|
||||
def register_host(klass, host):
|
||||
klass.containers[host] = []
|
||||
|
||||
@classmethod
|
||||
def register_container(klass, container):
|
||||
for host in container.HOSTS:
|
||||
klass.containers[host].append(container)
|
||||
|
||||
@classmethod
|
||||
def register_commands(klass, container):
|
||||
klass.commands[container] = {}
|
||||
for command in klass.traverse_commands(container):
|
||||
for name in command.names:
|
||||
klass.commands[container][name] = command
|
||||
|
||||
@classmethod
|
||||
def get_command(klass, host, name):
|
||||
for container in klass.containers[host]:
|
||||
command = klass.commands[container].get(name)
|
||||
if command:
|
||||
return command
|
||||
|
||||
@classmethod
|
||||
def list_commands(klass, host):
|
||||
for container in klass.containers[host]:
|
||||
commands = klass.commands[container]
|
||||
for name, command in commands.iteritems():
|
||||
yield name, command
|
||||
|
||||
@classmethod
|
||||
def traverse_commands(klass, container):
|
||||
for name in dir(container):
|
||||
attribute = getattr(container, name)
|
||||
if klass.is_command(attribute):
|
||||
yield attribute
|
||||
|
||||
@staticmethod
|
||||
def is_root(ns):
|
||||
meta = ns.get('__metaclass__', NoneType)
|
||||
return issubclass(meta, Dispatcher)
|
||||
|
||||
@staticmethod
|
||||
def is_command(attribute):
|
||||
name = attribute.__class__.__name__
|
||||
return name == 'Command'
|
||||
|
||||
class HostDispatcher(Dispatcher):
|
||||
|
||||
def __init__(klass, name, bases, ns):
|
||||
if not Dispatcher.is_root(ns):
|
||||
HostDispatcher.register_host(klass)
|
||||
super(HostDispatcher, klass).__init__(name, bases, ns)
|
||||
|
||||
class ContainerDispatcher(Dispatcher):
|
||||
|
||||
def __init__(klass, name, bases, ns):
|
||||
if not Dispatcher.is_root(ns):
|
||||
ContainerDispatcher.register_container(klass)
|
||||
ContainerDispatcher.register_commands(klass)
|
||||
super(ContainerDispatcher, klass).__init__(name, bases, ns)
|
|
@ -21,9 +21,10 @@ declarative way.
|
|||
|
||||
import re
|
||||
from types import FunctionType
|
||||
from inspect import getargspec
|
||||
from inspect import getargspec, getdoc
|
||||
|
||||
from dispatching import Dispatcher, HostDispatcher, ContainerDispatcher
|
||||
from dispatcher import Host, Container
|
||||
from dispatcher import get_command, list_commands
|
||||
from mapping import parse_arguments, adapt_arguments
|
||||
from errors import DefinitionError, CommandError, NoCommandError
|
||||
|
||||
|
@ -32,8 +33,12 @@ class CommandHost(object):
|
|||
Command host is a hub between numerous command processors and
|
||||
command containers. Aimed to participate in a dispatching process in
|
||||
order to provide clean and transparent architecture.
|
||||
|
||||
The AUTOMATIC class variable, which must be defined by a command
|
||||
host, specifies whether the command host should be automatically
|
||||
dispatched and enabled by the dispatcher or not.
|
||||
"""
|
||||
__metaclass__ = HostDispatcher
|
||||
__metaclass__ = Host
|
||||
|
||||
class CommandContainer(object):
|
||||
"""
|
||||
|
@ -41,11 +46,15 @@ class CommandContainer(object):
|
|||
allowing them to be dispatched and proccessed correctly. Each
|
||||
command container may be bound to a one or more command hosts.
|
||||
|
||||
Bounding is controlled by the HOSTS variable, which must be defined
|
||||
in the body of the command container. This variable should contain a
|
||||
list of hosts to bound to, as a tuple or list.
|
||||
The AUTOMATIC class variable, which must be defined by a command
|
||||
processor, specifies whether the command processor should be
|
||||
automatically dispatched and enabled by the dispatcher or not.
|
||||
|
||||
Bounding is controlled by the HOSTS class variable, which must be
|
||||
defined by the command container. This variable should contain a
|
||||
sequence of hosts to bound to, as a tuple or list.
|
||||
"""
|
||||
__metaclass__ = ContainerDispatcher
|
||||
__metaclass__ = Container
|
||||
|
||||
class CommandProcessor(object):
|
||||
"""
|
||||
|
@ -126,24 +135,18 @@ class CommandProcessor(object):
|
|||
pass
|
||||
|
||||
def get_command(self, name):
|
||||
command = Dispatcher.get_command(self.COMMAND_HOST, name)
|
||||
command = get_command(self.COMMAND_HOST, name)
|
||||
if not command:
|
||||
raise NoCommandError("Command does not exist", name=name)
|
||||
return command
|
||||
|
||||
def list_commands(self):
|
||||
commands = Dispatcher.list_commands(self.COMMAND_HOST)
|
||||
commands = list_commands(self.COMMAND_HOST)
|
||||
commands = dict(commands)
|
||||
return sorted(set(commands.itervalues()))
|
||||
|
||||
class Command(object):
|
||||
|
||||
# These two regular expression patterns control how command
|
||||
# documentation will be formatted to be transformed to a normal,
|
||||
# readable state.
|
||||
DOC_STRIP_PATTERN = re.compile(r'(?:^[ \t]+|\A\n)', re.MULTILINE)
|
||||
DOC_FORMAT_PATTERN = re.compile(r'(?<!\n)\n(?!\n)', re.MULTILINE)
|
||||
|
||||
def __init__(self, handler, *names, **properties):
|
||||
self.handler = handler
|
||||
self.names = names
|
||||
|
@ -192,19 +195,8 @@ class Command(object):
|
|||
"""
|
||||
Extract handler's documentation which is a doc-string and
|
||||
transform it to a usable format.
|
||||
|
||||
Transformation is done based on the DOC_STRIP_PATTERN and
|
||||
DOC_FORMAT_PATTERN regular expression patterns.
|
||||
"""
|
||||
documentation = self.handler.__doc__ or None
|
||||
|
||||
if not documentation:
|
||||
return
|
||||
|
||||
documentation = re.sub(self.DOC_STRIP_PATTERN, str(), documentation)
|
||||
documentation = re.sub(self.DOC_FORMAT_PATTERN, ' ', documentation)
|
||||
|
||||
return documentation
|
||||
return getdoc(self.handler)
|
||||
|
||||
def extract_description(self):
|
||||
"""
|
||||
|
@ -243,9 +235,9 @@ def command(*names, **properties):
|
|||
can be reached. If no names are given - the the native name (the one
|
||||
extracted from the command handler) will be used.
|
||||
|
||||
If include_native=True is given (default) and names is non-empty -
|
||||
then the native name of the command will be prepended in addition to
|
||||
the given names.
|
||||
If native=True is given (default) and names is non-empty - then the
|
||||
native name of the command will be prepended in addition to the
|
||||
given names.
|
||||
|
||||
If usage=True is given (default) - then command help will be
|
||||
appended with autogenerated usage info, based of the command handler
|
||||
|
@ -273,14 +265,14 @@ def command(*names, **properties):
|
|||
If overlap=True is given - then all the extra arguments will be
|
||||
mapped as if they were values for the keyword arguments.
|
||||
|
||||
If expand_short=True is given (default) - then short, one-letter
|
||||
options will be expanded to a verbose ones, based of the comparison
|
||||
of the first letter. If more then one option with the same first
|
||||
letter is given - then only first one will be used in the expansion.
|
||||
If expand=True is given (default) - then short, one-letter options
|
||||
will be expanded to a verbose ones, based of the comparison of the
|
||||
first letter. If more then one option with the same first letter is
|
||||
given - then only first one will be used in the expansion.
|
||||
"""
|
||||
names = list(names)
|
||||
|
||||
include_native = properties.get('include_native', True)
|
||||
native = properties.get('native', True)
|
||||
|
||||
usage = properties.get('usage', True)
|
||||
source = properties.get('source', False)
|
||||
|
@ -288,7 +280,7 @@ def command(*names, **properties):
|
|||
empty = properties.get('empty', False)
|
||||
extra = properties.get('extra', False)
|
||||
overlap = properties.get('overlap', False)
|
||||
expand_short = properties.get('expand_short', True)
|
||||
expand = properties.get('expand', True)
|
||||
|
||||
if empty and not raw:
|
||||
raise DefinitionError("Empty option can be used only with raw commands")
|
||||
|
@ -303,7 +295,7 @@ def command(*names, **properties):
|
|||
'extra': extra,
|
||||
'overlap': overlap,
|
||||
'empty': empty,
|
||||
'expand_short': expand_short
|
||||
'expand': expand
|
||||
}
|
||||
|
||||
def decorator(handler):
|
||||
|
@ -314,9 +306,9 @@ def command(*names, **properties):
|
|||
command = Command(handler, *names, **properties)
|
||||
|
||||
# Extract and inject a native name if either no other names are
|
||||
# specified or include_native property is enabled, while making
|
||||
# specified or native property is enabled, while making
|
||||
# sure it is going to be the first one in the list.
|
||||
if not names or include_native:
|
||||
if not names or native:
|
||||
names.insert(0, command.native_name)
|
||||
command.names = tuple(names)
|
||||
|
||||
|
|
|
@ -1,38 +1,56 @@
|
|||
# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
|
||||
# Copyright (c) 2009-2010, Alexander Cherniuk (ts33kr@gmail.com)
|
||||
# All rights reserved.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 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.
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
The module contains examples of how to create your own commands, by
|
||||
creating a new command container and definding a set of commands.
|
||||
This module contains examples of how to create your own commands, by
|
||||
creating a new command container, bounded to a specific command host,
|
||||
and definding a set of commands inside of it.
|
||||
|
||||
Keep in mind that this module is not being loaded, so the code will not
|
||||
be executed and commands defined here will not be detected.
|
||||
Keep in mind that this module is not being loaded from anywhere, so the
|
||||
code in here will not be executed and commands defined here will not be
|
||||
detected.
|
||||
"""
|
||||
|
||||
from ..framework import CommandContainer, command, doc
|
||||
from hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
|
||||
from hosts import *
|
||||
|
||||
class CustomCommonCommands(CommandContainer):
|
||||
"""
|
||||
The AUTOMATIC class variable, set to a positive value, instructs the
|
||||
command system to automatically discover the command container and
|
||||
enable it.
|
||||
|
||||
This command container bounds to all three available in the default
|
||||
implementation command hosts. This means that commands defined in
|
||||
this container will be available to all - chat, private chat and a
|
||||
this container will be available to all: chat, private chat and a
|
||||
group chat.
|
||||
"""
|
||||
|
||||
HOSTS = (ChatCommands, PrivateChatCommands, GroupChatCommands)
|
||||
AUTOMATIC = True
|
||||
HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands
|
||||
|
||||
@command
|
||||
def dance(self):
|
||||
|
@ -46,45 +64,58 @@ class CustomCommonCommands(CommandContainer):
|
|||
|
||||
After all the documentation - there will be autogenerated (based
|
||||
on the method signature) usage information appended. You can
|
||||
turn it off though, if you want.
|
||||
turn it off, if you want.
|
||||
"""
|
||||
return "I can't dance, you stupid fuck, I'm just a command system! A cool one, though..."
|
||||
return "I don't dance."
|
||||
|
||||
class CustomChatCommands(CommandContainer):
|
||||
"""
|
||||
This command container bounds only to the ChatCommands command host.
|
||||
Therefore command defined here will be available only to a chat.
|
||||
Therefore commands defined inside of the container will be available
|
||||
only to a chat.
|
||||
"""
|
||||
|
||||
HOSTS = (ChatCommands,)
|
||||
AUTOMATIC = True
|
||||
HOSTS = ChatCommands,
|
||||
|
||||
@doc(_("The same as using a doc-string, except it supports translation"))
|
||||
@command
|
||||
@command("squal", "bawl")
|
||||
def sing(self):
|
||||
return "Are you phreaking kidding me? Buy yourself a damn stereo..."
|
||||
"""
|
||||
This command has an additional aliases. It means the command will
|
||||
be available under three names: sing (the native name), squal
|
||||
(the first alias), bawl (the second alias).
|
||||
|
||||
You can turn off the usage of the native name, if you want, and
|
||||
specify a name or a set of names, as aliases, under which a
|
||||
command will be available.
|
||||
"""
|
||||
return "Buy yourself a stereo."
|
||||
|
||||
class CustomPrivateChatCommands(CommandContainer):
|
||||
"""
|
||||
This command container bounds only to the PrivateChatCommands
|
||||
command host. Therefore command defined here will be available only
|
||||
to a private chat.
|
||||
command host. Therefore commands defined inside of the container
|
||||
will be available only to a private chat.
|
||||
"""
|
||||
|
||||
HOSTS = (PrivateChatCommands,)
|
||||
AUTOMATIC = True
|
||||
HOSTS = PrivateChatCommands,
|
||||
|
||||
@command
|
||||
@doc(_("The same as using a doc-string, except it supports translation"))
|
||||
def make_coffee(self):
|
||||
return "What do I look like, you ass? A coffee machine!?"
|
||||
return "I'm not a coffee machine!"
|
||||
|
||||
class CustomGroupChatCommands(CommandContainer):
|
||||
"""
|
||||
This command container bounds only to the GroupChatCommands command
|
||||
host. Therefore command defined here will be available only to a
|
||||
group chat.
|
||||
host. Therefore commands defined inside of the container will be
|
||||
available only to a group chat.
|
||||
"""
|
||||
|
||||
HOSTS = (GroupChatCommands,)
|
||||
AUTOMATIC = True
|
||||
HOSTS = GroupChatCommands,
|
||||
|
||||
@command
|
||||
def fetch(self):
|
||||
return "You should really buy yourself a dog and start torturing it instead of me..."
|
||||
return "Buy yourself a dog."
|
|
@ -0,0 +1,120 @@
|
|||
# Copyright (c) 2010, Alexander Cherniuk (ts33kr@gmail.com)
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Provides facilities to safely execute expressions inside a shell process
|
||||
and capture the resulting output, in an asynchronous fashion, avoiding
|
||||
deadlocks. If the process execution time reaches the threshold - it is
|
||||
forced to terminate. Consists of a tiny framework and a couple of
|
||||
commands as a frontend.
|
||||
"""
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
from os.path import expanduser
|
||||
|
||||
from glib import timeout_add
|
||||
|
||||
from ..framework import CommandContainer, command, doc
|
||||
from hosts import *
|
||||
|
||||
class Execute(CommandContainer):
|
||||
AUTOMATIC = True
|
||||
HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands
|
||||
|
||||
DIRECTORY = "~"
|
||||
|
||||
POLL_INTERVAL = 100
|
||||
POLL_COUNT = 5
|
||||
|
||||
@command("exec", raw=True)
|
||||
@doc(_("Execute expression inside a shell, show output"))
|
||||
def execute(self, expression):
|
||||
Execute.spawn(self, expression)
|
||||
|
||||
@classmethod
|
||||
def spawn(cls, processor, expression):
|
||||
pipes = dict(stdout=PIPE, stderr=PIPE)
|
||||
directory = expanduser(cls.DIRECTORY)
|
||||
popen = Popen(expression, shell=True, cwd=directory, **pipes)
|
||||
cls.monitor(processor, popen)
|
||||
|
||||
@classmethod
|
||||
def monitor(cls, processor, popen):
|
||||
poller = cls.poller(processor, popen)
|
||||
timeout_add(cls.POLL_INTERVAL, poller.next)
|
||||
|
||||
@classmethod
|
||||
def poller(cls, processor, popen):
|
||||
for x in xrange(cls.POLL_COUNT):
|
||||
yield cls.brush(processor, popen)
|
||||
cls.overdue(processor, popen)
|
||||
yield False
|
||||
|
||||
@classmethod
|
||||
def brush(cls, processor, popen):
|
||||
if popen.poll() is not None:
|
||||
cls.terminated(processor, popen)
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def terminated(cls, processor, popen):
|
||||
stdout, stderr = cls.fetch(popen)
|
||||
success = popen.returncode == 0
|
||||
if success and stdout:
|
||||
processor.echo(stdout)
|
||||
elif not success and stderr:
|
||||
processor.echo_error(stderr)
|
||||
|
||||
@classmethod
|
||||
def overdue(cls, processor, popen):
|
||||
popen.terminate()
|
||||
|
||||
@classmethod
|
||||
def fetch(cls, popen):
|
||||
data = popen.communicate()
|
||||
return map(cls.clean, data)
|
||||
|
||||
@staticmethod
|
||||
def clean(text):
|
||||
strip = chr(10) + chr(32)
|
||||
return text.strip(strip)
|
||||
|
||||
class Show(Execute):
|
||||
|
||||
@command("sh", raw=True)
|
||||
@doc(_("Execute expression inside a shell, send output"))
|
||||
def show(self, expression):
|
||||
Show.spawn(self, expression)
|
||||
|
||||
@classmethod
|
||||
def terminated(cls, processor, popen):
|
||||
stdout, stderr = cls.fetch(popen)
|
||||
success = popen.returncode == 0
|
||||
if success and stdout:
|
||||
processor.send(stdout)
|
||||
elif not success and stderr:
|
||||
processor.echo_error(stderr)
|
|
@ -25,18 +25,18 @@ class ChatCommands(CommandHost):
|
|||
This command host is bound to the command processor which processes
|
||||
commands from a chat.
|
||||
"""
|
||||
pass
|
||||
AUTOMATIC = True
|
||||
|
||||
class PrivateChatCommands(CommandHost):
|
||||
"""
|
||||
This command host is bound to the command processor which processes
|
||||
commands from a private chat.
|
||||
"""
|
||||
pass
|
||||
AUTOMATIC = True
|
||||
|
||||
class GroupChatCommands(CommandHost):
|
||||
"""
|
||||
This command host is bound to the command processor which processes
|
||||
commands from a group chat.
|
||||
"""
|
||||
pass
|
||||
AUTOMATIC = True
|
||||
|
|
|
@ -1,42 +1,58 @@
|
|||
# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
|
||||
# Copyright (c) 2009-2010, Alexander Cherniuk (ts33kr@gmail.com)
|
||||
# All rights reserved.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 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.
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Provides a glue to tie command system framework and the actual code
|
||||
where it would be dropped in. Defines a little bit of scaffolding to
|
||||
support interaction between the two and a few utility methods so you
|
||||
don't need to dig up the code itself code to write basic commands.
|
||||
don't need to dig up the code itself to write basic commands.
|
||||
"""
|
||||
|
||||
from types import StringTypes
|
||||
from traceback import print_exc
|
||||
|
||||
from pango import FontDescription
|
||||
from common import gajim
|
||||
|
||||
from ..framework import CommandProcessor
|
||||
from ..errors import CommandError, NoCommandError
|
||||
from ..tools import gconf
|
||||
|
||||
class ChatCommandProcessor(CommandProcessor):
|
||||
"""
|
||||
A basic scaffolding to provide convenient interaction between the
|
||||
command system and chat controls.
|
||||
command system and chat controls. It will be merged directly into
|
||||
the controls, by ChatCommandProcessor being among superclasses of
|
||||
the controls.
|
||||
"""
|
||||
|
||||
def process_as_command(self, text):
|
||||
self.command_succeeded = False
|
||||
flag = super(ChatCommandProcessor, self).process_as_command(text)
|
||||
parents = super(ChatCommandProcessor, self)
|
||||
flag = parents.process_as_command(text)
|
||||
if flag and self.command_succeeded:
|
||||
self.add_history(text)
|
||||
self.clear_input()
|
||||
|
@ -44,17 +60,18 @@ class ChatCommandProcessor(CommandProcessor):
|
|||
|
||||
def execute_command(self, name, arguments):
|
||||
try:
|
||||
super(ChatCommandProcessor, self).execute_command(name, arguments)
|
||||
parents = super(ChatCommandProcessor, self)
|
||||
parents.execute_command(name, arguments)
|
||||
except NoCommandError, error:
|
||||
details = dict(name=error.name, message=error.message)
|
||||
message = "%(name)s: %(message)s\n" % details
|
||||
message += "Try using the //%(name)s or /say /%(name)s " % details
|
||||
message += "construct if you intended to send it as a text."
|
||||
self.echo(message, 'error')
|
||||
self.echo_error(message)
|
||||
except CommandError, error:
|
||||
self.echo("%s: %s" % (error.name, error.message), 'error')
|
||||
self.echo_error("%s: %s" % (error.name, error.message))
|
||||
except Exception:
|
||||
self.echo("An error occured while trying to execute the command", 'error')
|
||||
self.echo_error("Error during command execution!")
|
||||
print_exc()
|
||||
else:
|
||||
self.command_succeeded = True
|
||||
|
@ -87,14 +104,52 @@ class ChatCommandProcessor(CommandProcessor):
|
|||
class CommandTools:
|
||||
"""
|
||||
Contains a set of basic tools and shortcuts you can use in your
|
||||
commands to performe some simple operations.
|
||||
commands to perform some simple operations. These will be merged
|
||||
directly into the controls, by CommandTools being among superclasses
|
||||
of the controls.
|
||||
"""
|
||||
|
||||
def echo(self, text, kind='info'):
|
||||
def __init__(self):
|
||||
self.install_tags()
|
||||
|
||||
def install_tags(self):
|
||||
buffer = self.conv_textview.tv.get_buffer()
|
||||
|
||||
name = gconf("/desktop/gnome/interface/monospace_font_name")
|
||||
name = name if name else "Monospace"
|
||||
font = FontDescription(name)
|
||||
|
||||
command_ok_tag = buffer.create_tag("command_ok")
|
||||
command_ok_tag.set_property("font-desc", font)
|
||||
command_ok_tag.set_property("foreground", "#3465A4")
|
||||
|
||||
command_error_tag = buffer.create_tag("command_error")
|
||||
command_error_tag.set_property("font-desc", font)
|
||||
command_error_tag.set_property("foreground", "#F57900")
|
||||
|
||||
def shift_line(self):
|
||||
buffer = self.conv_textview.tv.get_buffer()
|
||||
iter = buffer.get_end_iter()
|
||||
if iter.ends_line() and not iter.is_start():
|
||||
buffer.insert_with_tags_by_name(iter, "\n", "eol")
|
||||
|
||||
def append_with_tags(self, text, *tags):
|
||||
buffer = self.conv_textview.tv.get_buffer()
|
||||
iter = buffer.get_end_iter()
|
||||
buffer.insert_with_tags_by_name(iter, text, *tags)
|
||||
|
||||
def echo(self, text, tag="command_ok"):
|
||||
"""
|
||||
Print given text to the user.
|
||||
Print given text to the user, as a regular command output.
|
||||
"""
|
||||
self.print_conversation(str(text), kind)
|
||||
self.shift_line()
|
||||
self.append_with_tags(text, tag)
|
||||
|
||||
def echo_error(self, text):
|
||||
"""
|
||||
Print given text to the user, as an error command output.
|
||||
"""
|
||||
self.echo(text, "command_error")
|
||||
|
||||
def send(self, text):
|
||||
"""
|
||||
|
@ -128,3 +183,10 @@ class CommandTools:
|
|||
Get the current connection object.
|
||||
"""
|
||||
return gajim.connections[self.account]
|
||||
|
||||
@property
|
||||
def full_jid(self):
|
||||
"""
|
||||
Get a full JID of the contact.
|
||||
"""
|
||||
return self.contact.get_full_jid()
|
|
@ -30,7 +30,8 @@ from ..errors import CommandError
|
|||
from ..framework import CommandContainer, command, doc
|
||||
from ..mapping import generate_usage
|
||||
|
||||
from hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
|
||||
from hosts import *
|
||||
import execute
|
||||
|
||||
# This holds constants fron the logger, which we'll be using in some of our
|
||||
# commands.
|
||||
|
@ -42,7 +43,8 @@ class StandardCommonCommands(CommandContainer):
|
|||
to all - chat, private chat, group chat.
|
||||
"""
|
||||
|
||||
HOSTS = (ChatCommands, PrivateChatCommands, GroupChatCommands)
|
||||
AUTOMATIC = True
|
||||
HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands
|
||||
|
||||
@command
|
||||
@doc(_("Clear the text window"))
|
||||
|
@ -56,7 +58,7 @@ class StandardCommonCommands(CommandContainer):
|
|||
self.chat_buttons_set_visible(new_status)
|
||||
|
||||
@command(overlap=True)
|
||||
@doc(_("Show help on a given command or a list of available commands if -(-a)ll is given"))
|
||||
@doc(_("Show help on a given command or a list of available commands if -a is given"))
|
||||
def help(self, command=None, all=False):
|
||||
if command:
|
||||
command = self.get_command(command)
|
||||
|
@ -163,7 +165,8 @@ class StandardCommonChatCommands(CommandContainer):
|
|||
to a chat and a private chat only.
|
||||
"""
|
||||
|
||||
HOSTS = (ChatCommands, PrivateChatCommands)
|
||||
AUTOMATIC = True
|
||||
HOSTS = ChatCommands, PrivateChatCommands
|
||||
|
||||
@command
|
||||
@doc(_("Toggle the GPG encryption"))
|
||||
|
@ -178,41 +181,37 @@ class StandardCommonChatCommands(CommandContainer):
|
|||
gajim.connections[self.account].sendPing(self.contact)
|
||||
|
||||
@command
|
||||
@doc(_("Send DTMF events through an open audio session"))
|
||||
def dtmf(self, events):
|
||||
@doc(_("Send DTMF sequence through an open audio session"))
|
||||
def dtmf(self, sequence):
|
||||
if not self.audio_sid:
|
||||
raise CommandError(_("There is no open audio session with this contact"))
|
||||
# Valid values for DTMF tones are *, # or a number.
|
||||
events = [e for e in events if e in ('*', '#') or e.isdigit()]
|
||||
if events:
|
||||
session = gajim.connections[self.account].get_jingle_session(
|
||||
self.contact.get_full_jid(), self.audio_sid)
|
||||
content = session.get_content('audio')
|
||||
content.batch_dtmf(events)
|
||||
else:
|
||||
raise CommandError(_("No valid DTMF event specified"))
|
||||
raise CommandError(_("No open audio sessions with the contact"))
|
||||
for tone in sequence:
|
||||
if not (tone in ("*", "#") or tone.isdigit()):
|
||||
raise CommandError(_("%s is not a valid tone") % tone)
|
||||
gjs = self.connection.get_jingle_session
|
||||
session = gjs(self.full_jid, self.audio_sid)
|
||||
content = session.get_content("audio")
|
||||
content.batch_dtmf(sequence)
|
||||
|
||||
@command
|
||||
@doc(_("Toggle audio session"))
|
||||
def audio(self):
|
||||
if not self.audio_available:
|
||||
raise CommandError(_("Audio sessions are not available"))
|
||||
else:
|
||||
# A state of an audio session is toggled by inverting a state of the
|
||||
# appropriate button.
|
||||
state = self._audio_button.get_active()
|
||||
self._audio_button.set_active(not state)
|
||||
# An audio session is toggled by inverting the state of the
|
||||
# appropriate button.
|
||||
state = self._audio_button.get_active()
|
||||
self._audio_button.set_active(not state)
|
||||
|
||||
@command
|
||||
@doc(_("Toggle video session"))
|
||||
def video(self):
|
||||
if not self.video_available:
|
||||
raise CommandError(_("Video sessions are not available"))
|
||||
else:
|
||||
# A state of a video session is toggled by inverting a state of the
|
||||
# appropriate button.
|
||||
state = self._video_button.get_active()
|
||||
self._video_button.set_active(not state)
|
||||
# A video session is toggled by inverting the state of the
|
||||
# appropriate button.
|
||||
state = self._video_button.get_active()
|
||||
self._video_button.set_active(not state)
|
||||
|
||||
class StandardChatCommands(CommandContainer):
|
||||
"""
|
||||
|
@ -220,7 +219,8 @@ class StandardChatCommands(CommandContainer):
|
|||
to a chat.
|
||||
"""
|
||||
|
||||
HOSTS = (ChatCommands,)
|
||||
AUTOMATIC = True
|
||||
HOSTS = ChatCommands,
|
||||
|
||||
class StandardPrivateChatCommands(CommandContainer):
|
||||
"""
|
||||
|
@ -228,7 +228,8 @@ class StandardPrivateChatCommands(CommandContainer):
|
|||
to a private chat.
|
||||
"""
|
||||
|
||||
HOSTS = (PrivateChatCommands,)
|
||||
AUTOMATIC = True
|
||||
HOSTS = PrivateChatCommands,
|
||||
|
||||
class StandardGroupChatCommands(CommandContainer):
|
||||
"""
|
||||
|
@ -236,7 +237,8 @@ class StandardGroupChatCommands(CommandContainer):
|
|||
to a group chat.
|
||||
"""
|
||||
|
||||
HOSTS = (GroupChatCommands,)
|
||||
AUTOMATIC = True
|
||||
HOSTS = GroupChatCommands,
|
||||
|
||||
@command(raw=True)
|
||||
@doc(_("Change your nickname in a group chat"))
|
||||
|
@ -324,23 +326,24 @@ class StandardGroupChatCommands(CommandContainer):
|
|||
@command
|
||||
@doc(_("Display names of all group chat occupants"))
|
||||
def names(self, verbose=False):
|
||||
get_contact = lambda nick: gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
||||
nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
|
||||
ggc = gajim.contacts.get_gc_contact
|
||||
gnl = gajim.contacts.get_nick_list
|
||||
|
||||
# First we do alpha-numeric sort and then role-based one.
|
||||
nicks.sort()
|
||||
nicks.sort(key=lambda nick: get_contact(nick).role)
|
||||
get_contact = lambda nick: ggc(self.account, self.room_jid, nick)
|
||||
get_role = lambda nick: get_contact(nick).role
|
||||
nicks = gnl(self.account, self.room_jid)
|
||||
|
||||
if verbose:
|
||||
for nick in nicks:
|
||||
contact = get_contact(nick)
|
||||
nicks = sorted(nicks)
|
||||
nicks = sorted(nicks, key=get_role)
|
||||
|
||||
role = helpers.get_uf_role(contact.role)
|
||||
affiliation = helpers.get_uf_affiliation(contact.affiliation)
|
||||
if not verbose:
|
||||
return ", ".join(nicks)
|
||||
|
||||
self.echo("%s - %s - %s" % (nick, role, affiliation))
|
||||
else:
|
||||
return ', '.join(nicks)
|
||||
for nick in nicks:
|
||||
contact = get_contact(nick)
|
||||
role = helpers.get_uf_role(contact.role)
|
||||
affiliation = helpers.get_uf_affiliation(contact.affiliation)
|
||||
self.echo("%s - %s - %s" % (nick, role, affiliation))
|
||||
|
||||
@command('ignore', raw=True)
|
||||
@doc(_("Forbid an occupant to send you public or private messages"))
|
||||
|
@ -350,4 +353,4 @@ class StandardGroupChatCommands(CommandContainer):
|
|||
@command('unignore', raw=True)
|
||||
@doc(_("Allow an occupant to send you public or private messages"))
|
||||
def unblock(self, who):
|
||||
self.on_unblock(None, who)
|
||||
self.on_unblock(None, who)
|
|
@ -205,7 +205,7 @@ def adapt_arguments(command, arguments, args, opts):
|
|||
# The second stage of transforming options to an associatable state.
|
||||
# Expanding short, one-letter options to a verbose ones, if
|
||||
# corresponding optin has been given.
|
||||
if command.expand_short:
|
||||
if command.expand:
|
||||
expanded = []
|
||||
for spec_key, spec_value in norm_kwargs.iteritems():
|
||||
letter = spec_key[0] if len(spec_key) > 1 else None
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright (c) 2010, Alexander Cherniuk (ts33kr@gmail.com)
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from types import *
|
||||
from glib import GError
|
||||
|
||||
def remove(sequence, target):
|
||||
if isinstance(sequence, ListType):
|
||||
if target in sequence:
|
||||
sequence.remove(target)
|
||||
elif isinstance(sequence, DictType):
|
||||
if target in sequence:
|
||||
del sequence[target]
|
||||
|
||||
def gconf(path):
|
||||
try:
|
||||
from gconf import client_get_default
|
||||
client = client_get_default()
|
||||
return client.get_string(path)
|
||||
except ImportError, GError:
|
||||
pass
|
|
@ -271,6 +271,8 @@ def check_and_possibly_create_paths():
|
|||
XTLS_CERTS = configpaths.gajimpaths['MY_PEER_CERTS']
|
||||
LOCAL_XTLS_CERTS = configpaths.gajimpaths['MY_CERT']
|
||||
|
||||
PLUGINS_CONFIG_PATH = gajim.PLUGINS_CONFIG_DIR
|
||||
|
||||
if not os.path.exists(MY_DATA):
|
||||
create_path(MY_DATA)
|
||||
elif os.path.isfile(MY_DATA):
|
||||
|
@ -347,6 +349,18 @@ def check_and_possibly_create_paths():
|
|||
jingle_xtls.make_certs(cert_name, 'gajim')
|
||||
|
||||
|
||||
if not os.path.exists(PLUGINS_CONFIG_PATH):
|
||||
create_path(PLUGINS_CONFIG_PATH)
|
||||
elif os.path.isfile(PLUGINS_CONFIG_PATH):
|
||||
print _('%s is a file but it should be a directory') % PLUGINS_CONFIG_PATH
|
||||
print _('Gajim will now exit')
|
||||
sys.exit()
|
||||
|
||||
def create_path(directory):
|
||||
head, tail = os.path.split(directory)
|
||||
if not os.path.exists(head):
|
||||
create_path(head)
|
||||
if os.path.exists(directory):
|
||||
return
|
||||
print _('creating %s directory') % directory
|
||||
os.mkdir(directory, 0700)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue