remove plugins that have been move to gajim-plugins reprository

This commit is contained in:
Yann Leboulanger 2011-11-16 21:26:36 +01:00
parent 87504e3954
commit 6ad3d01fb5
31 changed files with 1 additions and 4444 deletions

View File

@ -1,4 +1,4 @@
SUBDIRS = src data plugins po icons
SUBDIRS = src data po icons
ACLOCAL_AMFLAGS = -I m4

View File

@ -79,7 +79,6 @@ AC_CONFIG_FILES([
scripts/gajim-remote:scripts/gajim.in
scripts/gajim-history-manager:scripts/gajim.in
po/Makefile.in
plugins/Makefile
])
AC_OUTPUT
echo "

View File

@ -1,17 +0,0 @@
INCLUDES = \
$(PYTHON_INCLUDES)
gajimpluginsdir = $(gajim_pluginsdir)
installedplugins = acronyms_expander banner_tweaks length_notifier plugin_installer triggers whiteboard
installedpluginsfiles = $(wildcard ${srcdir}/${p}/*.py ${srcdir}/${p}/manifest.ini ${srcdir}/${p}/*.ui ${srcdir}/${p}/*.png)
nobase_dist_gajimplugins_PYTHON = $(foreach p, ${installedplugins}, $(installedpluginsfiles))
EXTRA_DIST = \
$(srcdir)/*/*.py \
$(srcdir)/*/manifest.ini \
$(srcdir)/*/*.ui
MAINTAINERCLEANFILES = Makefile.in

View File

@ -1,2 +0,0 @@
from plugin import BannerTweaksPlugin

View File

@ -1,75 +0,0 @@
<?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>

View File

@ -1,10 +0,0 @@
[info]
name: Banner Tweaks
short_name: banner_tweaks
version: 0.1
description: 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 = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -1,194 +0,0 @@
# -*- 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 gajim
from common import helpers
from plugins import GajimPlugin
from plugins.helpers import log, log_calls
from plugins.gui import GajimPluginConfigDialog
class BannerTweaksPlugin(GajimPlugin):
@log_calls('BannerTweaksPlugin')
def init(self):
self.description = _('Allows user to tweak chat window banner '
'appearance (eg. make it compact).\n'
'Based on patch by pb in ticket #4133:\n'
'http://trac.gajim.org/attachment/ticket/4133.')
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']:
if chat_control.TYPE_ID == message_control.TYPE_GC:
banner_status_img = chat_control.xml.get_object(
'gc_banner_status_image')
else:
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 st == 'all' or cs == 'composing':
chatstate = helpers.get_uf_chatstate(cs)
else:
chatstate = ''
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('gajim_plugins')
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()

View File

@ -1 +0,0 @@
from plugin import GoogleTranslationPlugin

View File

@ -1,8 +0,0 @@
[info]
name: Google Translation
short_name: google_translation
version: 0.2
description: Translates (currently only incoming) messages using Google Translate.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -1,284 +0,0 @@
# -*- 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 json
import urllib2
import HTMLParser
import gtk
from sys import getfilesystemencoding
import chat_control
import groupchat_control
from common import helpers
from common import gajim
from plugins import GajimPlugin
from plugins.helpers import log_calls, log
from common import ged
languages = {
_('Afrikaans'): 'af',
_('Albanian'): 'sq',
_('Armenian'): 'hy',
_('Azerbaijani'): 'az',
_('Arabic'): 'ar',
_('Basque'): 'eu',
_('Belarusian'): 'be',
_('Bulgarian'): 'bg',
_('Catalan'): 'ca',
_('Chinese (Simplified)'): 'zh-cn',
_('Chinese (Traditional)'): 'zh-tw',
_('Croatian'): 'hr',
_('Czech'): 'cs',
_('Danish'): 'da',
_('Dutch'): 'nl',
_('English'): 'en',
_('Estonian'): 'et',
_('Filipino'): 'tl',
_('Finnish'): 'fi',
_('French'): 'fr',
_('Galician'): 'gl',
_('Georgian'): 'ka',
_('German'): 'de',
_('Greek'): 'el',
_('Haitian Creole'): 'ht',
_('Hebrew'): 'iw',
_('Hindi'): 'hi',
_('Hungarian'): 'hu',
_('Icelandic'): 'is',
_('Indonesian'): 'id',
_('Italian'): 'it',
_('Irish'): 'da',
_('Japanese'): 'ja',
_('Korean'): 'ko',
_('Latvian'): 'lv',
_('Lithuanian'): 'lt',
_('Macedonian'): 'mk',
_('Malay'): 'ml',
_('Maltese'): 'mt',
_('Norwegian'): 'no',
_('Persian'): 'fa',
_('Polish'): 'pl',
_('Portuguese'): 'pt-BR',
_('Romanian'): 'ro',
_('Russian'): 'ru',
_('Serbian'): 'sr',
_('Slovak'): 'sk',
_('Slovenian'): 'sl',
_('Spanish'): 'es',
_('Swahili'): 'sw',
_('Swedish'): 'sv',
_('Thai'): 'th',
_('Turkish'): 'tr',
_('Ukrainian'): 'uk',
_('Urdu'): 'ur',
_('Vietnamese'): 'vi',
_('Welsh'): 'cy',
_('Yiddish'): 'yi',
}
class GoogleTranslationPlugin(GajimPlugin):
@log_calls('GoogleTranslationPlugin')
def init(self):
self.description = _('Translates (currently only incoming)'
'messages using Google Translate.')
self.config_dialog = None
self.config_default_values = {
'per_jid_config': ({}, ''),
}
self.events_handlers = {'decrypted-message-received': (ged.PREGUI,
self._nec_decrypted_message_received)}
self.gui_extension_points = {
'chat_control_base' : (self.connect_with_control,
self.disconnect_from_control),
}
self.controls = []
@log_calls('GoogleTranslationPlugin')
def translate_text(self, account, text, from_lang, to_lang):
# Converts text so it can be used within URL as query to Google
# Translate.
quoted_text = urllib2.quote(text.encode(getfilesystemencoding()))
# prepare url
translation_url = u'https://ajax.googleapis.com/ajax/services/' \
'language/translate?q=%(quoted_text)s&' \
'langpair=%(from_lang)s%%7C%(to_lang)s&key=notsupplied&v=1.0' % \
locals()
results = helpers.download_image(account, {'src': translation_url})[0]
if not results:
return text
result = json.loads(results)
if result.get('responseStatus', '') != 200:
return text
translated_text = result['responseData'].get('translatedText', '')
if translated_text:
try:
htmlparser = HTMLParser.HTMLParser()
translated_text = htmlparser.unescape(translated_text)
except Exception:
pass
return translated_text
return text
@log_calls('GoogleTranslationPlugin')
def _nec_decrypted_message_received(self, obj):
if not obj.msgtxt:
return
if obj.jid not in self.config['per_jid_config']:
return
if not self.config['per_jid_config'][obj.jid]['enabled']:
return
from_lang = self.config['per_jid_config'][obj.jid]['from']
to_lang = self.config['per_jid_config'][obj.jid]['to']
translated_text = self.translate_text(obj.conn.name, obj.msgtxt,
from_lang, to_lang)
if translated_text:
obj.msgtxt = translated_text + '\n/' + _('Original text:') + '/ ' +\
obj.msgtxt
@log_calls('GoogleTranslationPlugin')
def activate(self):
pass
@log_calls('GoogleTranslationPlugin')
def deactivate(self):
pass
@log_calls('GoogleTranslationPlugin')
def connect_with_control(self, control):
base = Base(self, control)
self.controls.append(base)
@log_calls('GoogleTranslationPlugin')
def disconnect_from_control(self, chat_control):
for base in self.controls:
base.disconnect_from_control()
self.controls = []
class Base(object):
def __init__(self, plugin, control):
self.plugin = plugin
self.control = control
self.contact = control.contact
self.account = control.account
self.jid = self.contact.jid
if self.jid in self.plugin.config['per_jid_config']:
self.config = self.plugin.config['per_jid_config'][self.jid]
else:
self.config = {'from': '', 'to': 'en', 'enabled': False}
self.create_buttons()
def create_buttons(self):
if isinstance(self.control, chat_control.ChatControl):
vbox = self.control.xml.get_object('vbox106')
elif isinstance(self.control, groupchat_control.GroupchatControl):
vbox = self.control.xml.get_object('gc_textviews_vbox')
else:
return
self.expander = gtk.Expander(_('Google translation'))
hbox = gtk.HBox(spacing=6)
self.expander.add(hbox)
label = gtk.Label(_('Translate from'))
hbox.pack_start(label, False, False)
liststore1 = gtk.ListStore(str, str)
liststore2 = gtk.ListStore(str, str)
cb1 = gtk.ComboBox(liststore1)
cb2 = gtk.ComboBox(liststore2)
cell = gtk.CellRendererText()
cb1.pack_start(cell, True)
cb1.add_attribute(cell, 'text', 0)
cell = gtk.CellRendererText()
cb2.pack_start(cell, True)
cb2.add_attribute(cell, 'text', 0)
#Language to translate from
liststore1.append([_('Auto'), ''])
if self.config['from'] == '':
cb1.set_active(0)
if self.config['from'] == '':
cb1.set_active(0)
i = 0
ls = languages.items()
ls.sort()
for l in ls:
liststore1.append(l)
if l[1] == self.config['from']:
cb1.set_active(i+1)
liststore2.append(l)
if l[1] == self.config['to']:
cb2.set_active(i)
i += 1
hbox.pack_start(cb1, False, False)
label = gtk.Label(_('to'))
hbox.pack_start(label, False, False)
hbox.pack_start(cb2, False, False)
cb = gtk.CheckButton(_('enable'))
if self.config['enabled']:
cb.set_active(True)
hbox.pack_start(cb, False, False)
vbox.pack_start(self.expander, False, False)
vbox.reorder_child(self.expander, 1)
cb1.connect('changed', self.on_cb_changed, 'from')
cb2.connect('changed', self.on_cb_changed, 'to')
cb.connect('toggled', self.on_cb_toggled)
self.expander.show_all()
def on_cb_changed(self, widget, option):
model = widget.get_model()
it = widget.get_active_iter()
self.config[option] = model[it][1]
self.plugin.config['per_jid_config'][self.jid] = self.config
self.plugin.config.save()
def on_cb_toggled(self, widget):
self.config['enabled'] = widget.get_active()
self.plugin.config['per_jid_config'][self.jid] = self.config
self.plugin.config.save()
def disconnect_from_control(self):
if isinstance(self.control, chat_control.ChatControl):
vbox = self.control.xml.get_object('vbox106')
elif isinstance(self.control, groupchat_control.GroupchatControl):
vbox = self.control.xml.get_object('gc_textviews_vbox')
else:
return
vbox.remove(self.expander)

View File

@ -1,2 +0,0 @@
from length_notifier import LengthNotifierPlugin

View File

@ -1,152 +0,0 @@
<?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>

View File

@ -1,157 +0,0 @@
# -*- 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 plugins import GajimPlugin
from plugins.helpers import log, log_calls
from plugins.gui import GajimPluginConfigDialog
class LengthNotifierPlugin(GajimPlugin):
@log_calls('LengthNotifierPlugin')
def init(self):
self.description = _('Highlights message entry field in chat window '
'when given length of message is exceeded.')
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('gajim_plugins')
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

View File

@ -1,9 +0,0 @@
[info]
name: Message Length Notifier
short_name: length_notifier
version: 0.1
description: Highlights message entry field in chat window when given length of message is exceeded.
authors = Mateusz Biliński <mateusz@bilinski.it>
homepage = http://blog.bilinski.it

View File

@ -1 +0,0 @@
from plugin_installer import PluginInstaller

View File

@ -1,294 +0,0 @@
<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkTextBuffer" id="textbuffer1">
<property name="text">Plug-in decription should be displayed here. This text will be erased during PluginsWindow initialization.</property>
</object>
<object class="GtkWindow" id="window1">
<child>
<object class="GtkHPaned" id="hpaned2">
<property name="width_request">600</property>
<property name="height_request">350</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">340</property>
<property name="position_set">True</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">6</property>
<property name="hscrollbar_policy">never</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<object class="GtkTreeView" id="available_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="search_column">1</property>
</object>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkProgressBar" id="progressbar">
<property name="ellipsize">end</property>
</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="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="plugin_name_label1">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label">&lt;empty&gt;</property>
<property name="selectable">True</property>
<property name="ellipsize">end</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox8">
<property name="visible">True</property>
<property name="spacing">6</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="xalign">0</property>
<property name="xpad">6</property>
<property name="label">&lt;empty&gt;</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">1</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">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>
<property name="xalign">0</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="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">Description:</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>
<property name="pixels_above_lines">6</property>
<property name="wrap_mode">word</property>
<property name="left_margin">6</property>
<property name="right_margin">6</property>
<property name="indent">1</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox15">
<property name="visible">True</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkHButtonBox" id="hbuttonbox3">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="inslall_upgrade_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_inslall_upgrade_clicked"/>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Install/Upgrade</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>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkWindow" id="window2">
<child>
<object class="GtkHBox" id="hbox111">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Ftp server:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="ftp_server">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -1,8 +0,0 @@
[info]
name: Plugin Installer
short_name: plugin_installer
version: 0.5
description: Install and upgrade plugins from ftp
authors: Denis Fomin <fominde@gmail.com>
Yann Leboulanger <asterix@lagaule.org>
homepage: http://www.gajim.org/

View File

@ -1,532 +0,0 @@
# -*- coding: utf-8 -*-
#
## plugins/plugin_installer/plugin_installer.py
##
## Copyright (C) 2010-2011 Denis Fomin <fominde AT gmail.com>
## Copyright (C) 2011 Yann Leboulanger <asterix AT lagaule.org>
##
## 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/>.
##
import gtk
import pango
import gobject
import ftplib
import io
import threading
import ConfigParser
import os
import fnmatch
import sys
from common import gajim
from plugins import GajimPlugin
from plugins.helpers import log_calls, log
from dialogs import WarningDialog, HigDialog, YesNoDialog
from plugins.gui import GajimPluginConfigDialog
def convert_version_to_list(version_str):
version_list = version_str.split('.')
l = []
while len(version_list):
l.append(int(version_list.pop(0)))
return l
class PluginInstaller(GajimPlugin):
@log_calls('PluginInstallerPlugin')
def init(self):
self.description = _('Install and upgrade plugins from ftp')
self.config_dialog = PluginInstallerPluginConfigDialog(self)
self.config_default_values = {'ftp_server': ('ftp.gajim.org', '')}
self.window = None
self.progressbar = None
self.available_plugins_model = None
self.upgrading = False # True when opened from upgrade popup dialog
@log_calls('PluginInstallerPlugin')
def activate(self):
self.pl_menuitem = gajim.interface.roster.xml.get_object(
'plugins_menuitem')
self.id_ = self.pl_menuitem.connect_after('activate', self.on_activate)
if 'plugins' in gajim.interface.instances:
self.on_activate(None)
gobject.timeout_add_seconds(30, self.check_update)
@log_calls('PluginInstallerPlugin')
def warn_update(self, plugins):
def open_update(dummy):
self.upgrading = True
self.pl_menuitem.activate()
nb = gajim.interface.instances['plugins'].plugins_notebook
gobject.idle_add(nb.set_current_page, 1)
if plugins:
plugins_str = '\n'.join(plugins)
YesNoDialog(_('Plugins updates'), _('Some updates are available for'
' your installer plugins. Do you want to update those plugins:'
'\n%s') % plugins_str, on_response_yes=open_update)
@log_calls('PluginInstallerPlugin')
def check_update(self):
def _run():
try:
to_update = []
con = ftplib.FTP_TLS(ftp.server)
con.login()
con.prot_p()
con.cwd('plugins')
plugins_dirs = con.nlst()
for dir_ in plugins_dirs:
try:
con.retrbinary('RETR %s/manifest.ini' % dir_,
ftp.handleDownload)
except Exception, error:
if str(error).startswith('550'):
continue
ftp.config.readfp(io.BytesIO(ftp.buffer_.getvalue()))
local_version = ftp.get_plugin_version(ftp.config.get(
'info', 'name'))
if local_version:
local = convert_version_to_list(local_version)
remote = convert_version_to_list(ftp.config.get('info',
'version'))
if remote > local:
to_update.append(ftp.config.get('info', 'name'))
con.quit()
gobject.idle_add(self.warn_update, to_update)
except Exception, e:
log.debug('Ftp error when check updates: %s' % str(e))
ftp = Ftp(self)
ftp.run = _run
ftp.start()
@log_calls('PluginInstallerPlugin')
def deactivate(self):
self.pl_menuitem.disconnect(self.id_)
if hasattr(self, 'page_num'):
self.notebook.remove_page(self.page_num)
self.notebook.set_current_page(0)
if hasattr(self, 'ftp'):
del self.ftp
def on_activate(self, widget):
if 'plugins' not in gajim.interface.instances:
return
self.installed_plugins_model = gajim.interface.instances[
'plugins'].installed_plugins_model
self.notebook = gajim.interface.instances['plugins'].plugins_notebook
self.id_n = self.notebook.connect('switch-page',
self.on_notebook_switch_page)
self.window = gajim.interface.instances['plugins'].window
self.window.connect('destroy', self.on_win_destroy)
self.GTK_BUILDER_FILE_PATH = self.local_file_path('config_dialog.ui')
self.xml = gtk.Builder()
self.xml.set_translation_domain('gajim_plugins')
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, ['hpaned2'])
hpaned = self.xml.get_object('hpaned2')
self.page_num = self.notebook.append_page(hpaned,
gtk.Label(_('Available')))
widgets_to_extract = ('plugin_name_label1',
'available_treeview', 'progressbar', 'inslall_upgrade_button',
'plugin_authors_label1', 'plugin_authors_label1',
'plugin_homepage_linkbutton1', 'plugin_description_textview1')
for widget_name in widgets_to_extract:
setattr(self, widget_name, self.xml.get_object(widget_name))
attr_list = pango.AttrList()
attr_list.insert(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1))
self.plugin_name_label1.set_attributes(attr_list)
self.available_plugins_model = gtk.ListStore(gobject.TYPE_PYOBJECT,
gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_BOOLEAN, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
gobject.TYPE_PYOBJECT)
self.available_treeview.set_model(self.available_plugins_model)
self.progressbar.set_property('no-show-all', True)
renderer = gtk.CellRendererText()
col = gtk.TreeViewColumn(_('Plugin'), renderer, text=1)
col.set_resizable(True)
col.set_property('expand', True)
col.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
self.available_treeview.append_column(col)
col = gtk.TreeViewColumn(_('Installed\nversion'), renderer, text=2)
self.available_treeview.append_column(col)
col = gtk.TreeViewColumn(_('Available\nversion'), renderer, text=3)
col.set_property('expand', False)
self.available_treeview.append_column(col)
renderer = gtk.CellRendererToggle()
renderer.set_property('activatable', True)
renderer.connect('toggled', self.available_plugins_toggled_cb)
col = gtk.TreeViewColumn(_('Install /\nUpgrade'), renderer, active=4)
self.available_treeview.append_column(col)
if gobject.signal_lookup('error_signal', self.window) is 0:
gobject.signal_new('error_signal', self.window,
gobject.SIGNAL_RUN_LAST, gobject.TYPE_STRING,
(gobject.TYPE_STRING,))
gobject.signal_new('plugin_downloaded', self.window,
gobject.SIGNAL_RUN_LAST, gobject.TYPE_STRING,
(gobject.TYPE_PYOBJECT,))
self.window.connect('error_signal', self.on_some_ftp_error)
self.window.connect('plugin_downloaded', self.on_plugin_downloaded)
selection = self.available_treeview.get_selection()
selection.connect('changed',
self.available_plugins_treeview_selection_changed)
selection.set_mode(gtk.SELECTION_SINGLE)
self._clear_available_plugin_info()
self.xml.connect_signals(self)
self.window.show_all()
def on_win_destroy(self, widget):
if hasattr(self, 'ftp'):
del self.ftp
def available_plugins_toggled_cb(self, cell, path):
is_active = self.available_plugins_model[path][4]
self.available_plugins_model[path][4] = not is_active
dir_list = []
for i in xrange(len(self.available_plugins_model)):
if self.available_plugins_model[i][4]:
dir_list.append(self.available_plugins_model[i][0])
if not dir_list:
self.inslall_upgrade_button.set_property('sensitive', False)
else:
self.inslall_upgrade_button.set_property('sensitive', True)
def on_notebook_switch_page(self, widget, page, page_num):
if not hasattr(self, 'ftp') and self.page_num == page_num:
self.available_plugins_model.clear()
self.progressbar.show()
self.ftp = Ftp(self)
self.ftp.remote_dirs = None
self.ftp.upgrading = True
self.ftp.start()
def on_inslall_upgrade_clicked(self, widget):
self.inslall_upgrade_button.set_property('sensitive', False)
dir_list = []
for i in xrange(len(self.available_plugins_model)):
if self.available_plugins_model[i][4]:
dir_list.append(self.available_plugins_model[i][0])
ftp = Ftp(self)
ftp.remote_dirs = dir_list
ftp.start()
def on_some_ftp_error(self, widget, error_text):
for i in xrange(len(self.available_plugins_model)):
self.available_plugins_model[i][4] = False
self.progressbar.hide()
WarningDialog(_('Ftp error'), error_text, self.window)
def on_plugin_downloaded(self, widget, plugin_dirs):
for _dir in plugin_dirs:
is_active = False
plugins = None
plugin_dir = os.path.join(gajim.PLUGINS_DIRS[1], _dir)
plugin = gajim.plugin_manager.get_plugin_by_path(plugin_dir)
if plugin:
if plugin.active and plugin.name != self.name:
is_active = True
gobject.idle_add(gajim.plugin_manager.deactivate_plugin,
plugin)
gajim.plugin_manager.plugins.remove(plugin)
model = self.installed_plugins_model
for row in xrange(len(model)):
if plugin == model[row][0]:
model.remove(model.get_iter((row, 0)))
break
plugins = self.scan_dir_for_plugin(plugin_dir)
if not plugins:
continue
gajim.plugin_manager.add_plugin(plugins[0])
plugin = gajim.plugin_manager.plugins[-1]
for row in xrange(len(self.available_plugins_model)):
if plugin.name == self.available_plugins_model[row][1]:
self.available_plugins_model[row][2] = plugin.version
self.available_plugins_model[row][4] = False
continue
if is_active and plugin.name != self.name:
gobject.idle_add(gajim.plugin_manager.activate_plugin, plugin)
if plugin.name != 'Plugin Installer':
self.installed_plugins_model.append([plugin, plugin.name,
is_active])
dialog = HigDialog(None, gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
'', _('All selected plugins downloaded'))
dialog.set_modal(False)
dialog.set_transient_for(self.window)
dialog.popup()
def available_plugins_treeview_selection_changed(self, treeview_selection):
model, iter = treeview_selection.get_selected()
if iter:
self.plugin_name_label1.set_text(model.get_value(iter, 1))
self.plugin_authors_label1.set_text(model.get_value(iter, 6))
self.plugin_homepage_linkbutton1.set_uri(model.get_value(iter, 7))
self.plugin_homepage_linkbutton1.set_label(model.get_value(iter, 7))
label = self.plugin_homepage_linkbutton1.get_children()[0]
label.set_ellipsize(pango.ELLIPSIZE_END)
self.plugin_homepage_linkbutton1.set_property('sensitive', True)
desc_textbuffer = self.plugin_description_textview1.get_buffer()
desc_textbuffer.set_text(_(model.get_value(iter, 5)))
self.plugin_description_textview1.set_property('sensitive', True)
else:
self._clear_available_plugin_info()
def _clear_available_plugin_info(self):
self.plugin_name_label1.set_text('')
self.plugin_authors_label1.set_text('')
self.plugin_homepage_linkbutton1.set_uri('')
self.plugin_homepage_linkbutton1.set_label('')
self.plugin_homepage_linkbutton1.set_property('sensitive', False)
desc_textbuffer = self.plugin_description_textview1.get_buffer()
desc_textbuffer.set_text('')
self.plugin_description_textview1.set_property('sensitive', False)
def scan_dir_for_plugin(self, path):
plugins_found = []
conf = ConfigParser.ConfigParser()
fields = ('name', 'short_name', 'version', 'description', 'authors',
'homepage')
if not os.path.isdir(path):
return plugins_found
dir_list = os.listdir(path)
dir_, mod = os.path.split(path)
sys.path.insert(0, dir_)
manifest_path = os.path.join(path, 'manifest.ini')
if not os.path.isfile(manifest_path):
return plugins_found
for elem_name in dir_list:
file_path = os.path.join(path, elem_name)
module = None
if os.path.isfile(file_path) and fnmatch.fnmatch(file_path, '*.py'):
module_name = os.path.splitext(elem_name)[0]
if module_name == '__init__':
continue
try:
module = __import__('%s.%s' % (mod, module_name))
except ValueError, value_error:
pass
except ImportError, import_error:
pass
except AttributeError, attribute_error:
pass
if module is None:
continue
for module_attr_name in [attr_name for attr_name in dir(module)
if not (attr_name.startswith('__') or attr_name.endswith('__'))]:
module_attr = getattr(module, module_attr_name)
try:
if not issubclass(module_attr, GajimPlugin) or \
module_attr is GajimPlugin:
continue
module_attr.__path__ = os.path.abspath(os.path.dirname(
file_path))
# read metadata from manifest.ini
conf.readfp(open(manifest_path, 'r'))
for option in fields:
if conf.get('info', option) is '':
raise ConfigParser.NoOptionError, 'field empty'
setattr(module_attr, option, conf.get('info', option))
conf.remove_section('info')
plugins_found.append(module_attr)
except TypeError, type_error:
pass
except ConfigParser.NoOptionError, type_error:
# all fields are required
pass
return plugins_found
class Ftp(threading.Thread):
def __init__(self, plugin):
super(Ftp, self).__init__()
self.plugin = plugin
self.window = plugin.window
self.server = plugin.config['ftp_server']
self.progressbar = plugin.progressbar
self.model = plugin.available_plugins_model
self.config = ConfigParser.ConfigParser()
self.buffer_ = io.BytesIO()
self.remote_dirs = None
self.append_to_model = True
self.upgrading = False
def model_append(self, row):
self.model.append(row)
return False
def progressbar_pulse(self):
self.progressbar.pulse()
return True
def get_plugin_version(self, plugin_name):
for plugin in gajim.plugin_manager.plugins:
if plugin.name == plugin_name:
return plugin.version
def run(self):
try:
gobject.idle_add(self.progressbar.set_text,
_('Connecting to server'))
self.ftp = ftplib.FTP_TLS(self.server)
self.ftp.login()
self.ftp.prot_p()
self.ftp.cwd('plugins')
if not self.remote_dirs:
self.plugins_dirs = self.ftp.nlst()
progress_step = 1.0 / len(self.plugins_dirs)
gobject.idle_add(self.progressbar.set_text,
_('Scan files on the server'))
for dir_ in self.plugins_dirs:
fract = self.progressbar.get_fraction() + progress_step
gobject.idle_add(self.progressbar.set_fraction, fract)
gobject.idle_add(self.progressbar.set_text,
_('Read "%s"') % dir_)
try:
self.ftp.retrbinary('RETR %s/manifest.ini' % dir_,
self.handleDownload)
except Exception, error:
if str(error).startswith('550'):
continue
self.config.readfp(io.BytesIO(self.buffer_.getvalue()))
local_version = self.get_plugin_version(
self.config.get('info', 'name'))
upgrade = False
if self.upgrading and local_version:
local = convert_version_to_list(local_version)
remote = convert_version_to_list(self.config.get('info',
'version'))
if remote > local:
upgrade = True
gobject.idle_add(
self.plugin.inslall_upgrade_button.set_property,
'sensitive', True)
gobject.idle_add(self.model_append, [dir_,
self.config.get('info', 'name'), local_version,
self.config.get('info', 'version'), upgrade,
self.config.get('info', 'description'),
self.config.get('info', 'authors'),
self.config.get('info', 'homepage'), ])
self.plugins_dirs = None
self.ftp.quit()
gobject.idle_add(self.progressbar.set_fraction, 0)
if self.remote_dirs:
self.download_plugin()
gobject.idle_add(self.progressbar.hide)
except Exception, e:
self.window.emit('error_signal', str(e))
def handleDownload(self, block):
self.buffer_.write(block)
def download_plugin(self):
gobject.idle_add(self.progressbar.show)
self.pulse = gobject.timeout_add(150, self.progressbar_pulse)
gobject.idle_add(self.progressbar.set_text, _('Create a list of files'))
for remote_dir in self.remote_dirs:
def nlstr(dir_, subdir=None):
if subdir:
dir_ = dir_ + '/' + subdir
list_ = self.ftp.nlst(dir_)
for i in list_:
name = i.split('/')[-1]
if '.' not in name:
try:
if i == self.ftp.nlst(i)[0]:
files.append(i[1:])
del dirs[i[1:]]
except Exception, e:
# empty dir or file
continue
dirs.append(i[1:])
subdirs = name
nlstr(dir_, subdirs)
else:
files.append(i[1:])
dirs, files = [], []
nlstr('/plugins/' + remote_dir)
if not os.path.isdir(gajim.PLUGINS_DIRS[1]):
os.mkdir(gajim.PLUGINS_DIRS[1])
local_dir = ld = os.path.join(gajim.PLUGINS_DIRS[1], remote_dir)
if not os.path.isdir(local_dir):
os.mkdir(local_dir)
local_dir = os.path.split(gajim.PLUGINS_DIRS[1])[0]
# creating dirs
for dir_ in dirs:
try:
os.mkdir(os.path.join(local_dir, dir_))
except OSError, e:
if str(e).startswith('[Errno 17]'):
continue
raise
# downloading files
for filename in files:
gobject.idle_add(self.progressbar.set_text,
_('Downloading "%s"') % filename)
full_filename = os.path.join(local_dir, filename)
try:
self.ftp.retrbinary('RETR /%s' % filename,
open(full_filename, 'wb').write)
#full_filename.close()
except ftplib.error_perm:
print 'ERROR: cannot read file "%s"' % filename
os.unlink(filename)
self.ftp.quit()
gobject.idle_add(self.window.emit, 'plugin_downloaded',
self.remote_dirs)
gobject.source_remove(self.pulse)
class PluginInstallerPluginConfigDialog(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('gajim_plugins')
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, ['hbox111'])
hbox = self.xml.get_object('hbox111')
self.child.pack_start(hbox)
self.xml.connect_signals(self)
self.connect('hide', self.on_hide)
def on_run(self):
widget = self.xml.get_object('ftp_server')
widget.set_text(str(self.plugin.config['ftp_server']))
def on_hide(self, widget):
widget = self.xml.get_object('ftp_server')
self.plugin.config['ftp_server'] = widget.get_text()

View File

@ -1 +0,0 @@
from triggers import Triggers

View File

@ -1,910 +0,0 @@
<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkListStore" id="liststore1">
<columns>
<!-- column-name item -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">contact(s)</col>
</row>
<row>
<col id="0" translatable="yes">group(s)</col>
</row>
<row>
<col id="0" translatable="yes">everybody</col>
</row>
</data>
</object>
<object class="GtkListStore" id="liststore2">
<columns>
<!-- column-name item -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Receive a Message</col>
</row>
<row>
<col id="0" translatable="yes">Contact Connects</col>
</row>
<row>
<col id="0" translatable="yes">Contact Disconnects</col>
</row>
<row>
<col id="0" translatable="yes">Contact Changes Status</col>
</row>
</data>
</object>
<object class="GtkWindow" id="advanced_notifications_window">
<property name="border_width">6</property>
<property name="title" translatable="yes">Advanced Notifications Control</property>
<property name="role">Advanced Notifications Control</property>
<property name="resizable">False</property>
<property name="destroy_with_parent">True</property>
<child>
<object class="GtkVBox" id="vbox">
<property name="visible">True</property>
<property name="border_width">12</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox100">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow23">
<property name="height_request">90</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="conditions_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="cursor_changed" handler="on_conditions_treeview_cursor_changed"/>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment99">
<property name="visible">True</property>
<property name="xalign">1</property>
<property name="left_padding">212</property>
<child>
<object class="GtkHBox" id="hbox3045">
<property name="visible">True</property>
<child>
<object class="GtkHButtonBox" id="hbuttonbox2">
<property name="visible">True</property>
<property name="spacing">10</property>
<child>
<object class="GtkButton" id="new_button">
<property name="label">gtk-new</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_new_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="up_button">
<property name="label">gtk-go-up</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_up_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="down_button">
<property name="label">gtk-go-down</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_down_button_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="delete_button">
<property name="label">gtk-delete</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_delete_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="fill">False</property>
<property name="position">0</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="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="config_vbox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="label391">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Conditions&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox101">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkHBox" id="hbox3042">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="label401">
<property name="visible">True</property>
<property name="label" translatable="yes">When </property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="event_combobox">
<property name="visible">True</property>
<property name="model">liststore2</property>
<signal name="changed" handler="on_event_combobox_changed"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext2"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox3048">
<property name="visible">True</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="label400">
<property name="visible">True</property>
<property name="label" translatable="yes">for </property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="recipient_type_combobox">
<property name="visible">True</property>
<property name="model">liststore1</property>
<signal name="changed" handler="on_recipient_type_combobox_changed"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="recipient_list_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">comma separated list</property>
<signal name="changed" handler="on_recipient_list_entry_changed"/>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox3049">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label402">
<property name="visible">True</property>
<property name="label" translatable="yes">when I'm in</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="status_hbox">
<property name="visible">True</property>
<property name="spacing">3</property>
<child>
<object class="GtkRadioButton" id="all_status_rb">
<property name="label" translatable="yes">All statuses</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_status_radiobutton_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="special_status_rb">
<property name="label" translatable="yes">One or more special statuses...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<property name="group">all_status_rb</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="online_cb">
<property name="label" translatable="yes">Online / Free For Chat</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_status_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="away_cb">
<property name="label" translatable="yes">Away</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_status_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="xa_cb">
<property name="label" translatable="yes">Not Available</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_status_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="dnd_cb">
<property name="label" translatable="yes">Busy </property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_status_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="invisible_cb">
<property name="label" translatable="yes">Invisible</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_status_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">6</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox3053">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label408">
<property name="visible">True</property>
<property name="label" translatable="yes">and I </property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="tab_opened_cb">
<property name="label" translatable="yes">Have </property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_tab_opened_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="not_tab_opened_cb">
<property name="label" translatable="yes">Don't have </property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_not_tab_opened_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label409">
<property name="visible">True</property>
<property name="label" translatable="yes"> a window/tab opened with that contact </property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label392">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Actions&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame35">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkHBox" id="hbox3027">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="use_popup_cb">
<property name="label" translatable="yes">_Inform me with a popup window</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_use_popup_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="disable_popup_cb">
<property name="label" translatable="yes">_Disable existing popup window</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_disable_popup_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkFrame" id="frame38">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment93">
<property name="visible">True</property>
<property name="border_width">6</property>
<property name="left_padding">12</property>
<child>
<object class="GtkVBox" id="vbox98">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="hbox3028">
<property name="visible">True</property>
<property name="spacing">6</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkCheckButton" id="use_sound_cb">
<property name="label" translatable="yes">Play a sound</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_use_sound_cb_toggled"/>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="sound_file_hbox">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkEntry" id="sound_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="changed" handler="on_sound_entry_changed"/>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button4">
<property name="label">...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_browse_for_sounds_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="play_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<signal name="clicked" handler="on_play_button_clicked"/>
<child>
<object class="GtkImage" id="image1372">
<property name="visible">True</property>
<property name="stock">gtk-media-play</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</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="GtkCheckButton" id="disable_sound_cb">
<property name="label" translatable="yes">_Disable existing sound for this event</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_disable_sound_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label394">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Sounds&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox3032">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="use_auto_open_cb">
<property name="label" translatable="yes">_Open chat window with user</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_use_auto_open_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="disable_auto_open_cb">
<property name="label" translatable="yes">_Disable auto opening chat window</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_disable_auto_open_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkExpander" id="expander1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="expanded">True</property>
<child>
<object class="GtkVBox" id="vbox99">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkHBox" id="hbox3033">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="run_command_cb">
<property name="label" translatable="yes">Launch a command</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_run_command_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="command_entry">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<signal name="changed" handler="on_command_entry_changed"/>
</object>
<packing>
<property name="position">1</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="hbox3035">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="use_systray_cb">
<property name="label" translatable="yes">_Show event in notification area</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_use_systray_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="disable_systray_cb">
<property name="label" translatable="yes">_Disable showing event in notification area</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_disable_systray_cb_toggled"/>
</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>
<child>
<object class="GtkHBox" id="hbox3052">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="use_roster_cb">
<property name="label" translatable="yes">_Show event in roster</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_use_roster_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="disable_roster_cb">
<property name="label" translatable="yes">_Disable showing event in roster</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_disable_roster_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="use_urgency_hint_cb">
<property name="label" translatable="yes">_Activate window manager's UrgencyHint to make chat window in taskbar flash</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_use_urgency_hint_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="disable_urgency_hint_cb">
<property name="label" translatable="yes">_Deactivate window manager's UrgencyHint</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="no_show_all">True</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_disable_urgency_hint_cb_toggled"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label395">
<property name="visible">True</property>
<property name="label" translatable="yes">Advanced Actions</property>
</object>
</child>
</object>
<packing>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -1,7 +0,0 @@
[info]
name: Triggers
short_name: triggers
version: 0.0.3
description: Configure Gajim's behaviour for each contact
authors: Yann Leboulanger <asterix@lagaule.org>
homepage: http://trac.gajim.org/wiki/

View File

@ -1,677 +0,0 @@
# -*- coding: utf-8 -*-
#
## plugins/triggers/triggers.py
##
## Copyright (C) 2011 Yann Leboulanger <asterix AT lagaule.org>
##
## 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/>.
##
import gtk
import sys
from common import gajim
from plugins import GajimPlugin
from plugins.helpers import log_calls
from plugins.gui import GajimPluginConfigDialog
from common import ged
from common import helpers
class Triggers(GajimPlugin):
@log_calls('TriggersPlugin')
def init(self):
self.description = _('Configure Gajim\'s behaviour for each contact')
self.config_dialog = TriggersPluginConfigDialog(self)
self.config_default_values = {}
self.events_handlers = {'notification': (ged.PREGUI, self._nec_notif),
'decrypted-message-received': (ged.PREGUI2,
self._nec_decrypted_message_received),
'presence-received': (ged.PREGUI, self._nec_presence_received)}
def _check_rule_recipients(self, obj, rule):
rule_recipients = [t.strip() for t in rule['recipients'].split(',')]
if rule['recipient_type'] == 'contact' and obj.jid not in \
rule_recipients:
return False
contact = gajim.contacts.get_first_contact_from_jid(obj.conn.name, obj.jid)
if not contact: # PM?
return False
contact_groups = contact.groups
group_found = False
for group in contact_groups:
if group in rule_recipients:
group_found = True
break
if rule['recipient_type'] == 'group' and not group_found:
return False
return True
def _check_rule_status(self, obj, rule):
rule_statuses = rule['status'].split()
our_status = gajim.SHOW_LIST[obj.conn.connected]
if rule['status'] != 'all' and our_status not in rule_statuses:
return False
return True
def _check_rule_tab_opened(self, obj, rule):
if rule['tab_opened'] == 'both':
return True
tab_opened = False
if gajim.interface.msg_win_mgr.get_control(obj.jid, obj.conn.name):
tab_opened = True
if tab_opened and rule['tab_opened'] == 'no':
return False
elif not tab_opened and rule['tab_opened'] == 'yes':
return False
return True
def check_rule_all(self, event, obj, rule):
# Check notification type
if rule['event'] != event:
return False
# notification type is ok. Now check recipient
if not self._check_rule_recipients(obj, rule):
return False
# recipient is ok. Now check our status
if not self._check_rule_status(obj, rule):
return False
# our_status is ok. Now check opened chat window
if not self._check_rule_tab_opened(obj, rule):
return False
# All is ok
return True
def check_rule_apply_notif(self, obj, rule):
# Check notification type
notif_type = ''
if obj.notif_type == 'msg':
notif_type = 'message_received'
elif obj.notif_type == 'pres':
if obj.base_event.old_show < 2 and obj.base_event.new_show > 1:
notif_type = 'contact_connected'
elif obj.base_event.old_show > 1 and obj.base_event.new_show < 2:
notif_type = 'contact_disconnected'
else:
notif_type = 'contact_status_change'
return self.check_rule_all(notif_type, obj, rule)
def check_rule_apply_decrypted_msg(self, obj, rule):
return self.check_rule_all('message_received', obj, rule)
def check_rule_apply_connected(self, obj, rule):
return self.check_rule_all('contact_connected', obj, rule)
def check_rule_apply_disconnected(self, obj, rule):
return self.check_rule_all('contact_disconnected', obj, rule)
def check_rule_apply_status_changed(self, obj, rule):
return self.check_rule_all('contact_status_change', obj, rule)
def apply_rule_notif(self, obj, rule):
if rule['sound'] == 'no':
obj.do_sound = False
elif rule['sound'] == 'yes':
obj.do_sound = True
obj.sound_event = ''
obj.sound_file = rule['sound_file']
if rule['popup'] == 'no':
obj.do_popup = False
elif rule['popup'] == 'yes':
obj.do_popup = True
if rule['run_command']:
obj.do_command = True
obj.command = rule['command']
else:
obj.do_command = False
if rule['systray'] == 'no':
obj.show_in_notification_area = False
elif rule['systray'] == 'yes':
obj.show_in_notification_area = True
if rule['roster'] == 'no':
obj.show_in_roster = False
elif rule['roster'] == 'yes':
obj.show_in_roster = True
# if rule['urgency_hint'] == 'no':
# ?? not in obj actions
# elif rule['urgency_hint'] == 'yes':
def apply_rule_decrypted_message(self, obj, rule):
if rule['auto_open'] == 'no':
obj.popup = False
elif rule['auto_open'] == 'yes':
obj.popup = True
def apply_rule_presence(self, obj, rule):
if rule['auto_open'] == 'no':
obj.popup = False
elif rule['auto_open'] == 'yes':
obj.popup = True
def _nec_all(self, obj, check_func, apply_func):
# check rules in order
rules_num = [int(i) for i in self.config.keys()]
rules_num.sort()
for num in rules_num:
rule = self.config[str(num)]
if check_func(obj, rule):
apply_func(obj, rule)
# Should we stop after first valid rule ?
# break
def _nec_notif(self, obj):
self._nec_all(obj, self.check_rule_apply_notif, self.apply_rule_notif)
def _nec_decrypted_message_received(self, obj):
self._nec_all(obj, self.check_rule_apply_decrypted_msg,
self.apply_rule_decrypted_message)
def _nec_presence_received(self, obj):
if obj.old_show < 2 and obj.new_show > 1:
check_func = self.check_rule_apply_connected
elif obj.old_show > 1 and obj.new_show < 2:
check_func = self.check_rule_apply_disconnected
else:
check_func = self.check_rule_apply_status_changed
self._nec_all(obj, check_func, self.apply_rule_presence)
class TriggersPluginConfigDialog(GajimPluginConfigDialog):
# {event: widgets_to_disable, }
events_list = {
'message_received': [],
'contact_connected': ['use_systray_cb', 'disable_systray_cb',
'use_roster_cb', 'disable_roster_cb'],
'contact_disconnected': ['use_systray_cb', 'disable_systray_cb',
'use_roster_cb', 'disable_roster_cb'],
'contact_status_change': ['use_systray_cb', 'disable_systray_cb',
'use_roster_cb', 'disable_roster_cb']
#, 'gc_msg_highlight': [], 'gc_msg': []}
}
recipient_types_list = ['contact', 'group', 'all']
config_options = ['event', 'recipient_type', 'recipients', 'status',
'tab_opened', 'sound', 'sound_file', 'popup', 'auto_open',
'run_command', 'command', 'systray', 'roster', 'urgency_hint']
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('gajim_plugins')
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH,
['vbox', 'liststore1', 'liststore2'])
vbox = self.xml.get_object('vbox')
self.child.pack_start(vbox)
self.xml.connect_signals(self)
self.connect('hide', self.on_hide)
def on_run(self):
# fill window
for w in ('conditions_treeview', 'config_vbox', 'event_combobox',
'recipient_type_combobox', 'recipient_list_entry', 'delete_button',
'status_hbox', 'use_sound_cb', 'disable_sound_cb', 'use_popup_cb',
'disable_popup_cb', 'use_auto_open_cb', 'disable_auto_open_cb',
'use_systray_cb', 'disable_systray_cb', 'use_roster_cb',
'disable_roster_cb', 'tab_opened_cb', 'not_tab_opened_cb',
'sound_entry', 'sound_file_hbox', 'up_button', 'down_button',
'run_command_cb', 'command_entry', 'use_urgency_hint_cb',
'disable_urgency_hint_cb'):
self.__dict__[w] = self.xml.get_object(w)
self.config = {}
for n in self.plugin.config:
self.config[int(n)] = self.plugin.config[n]
# Contains status checkboxes
childs = self.status_hbox.get_children()
self.all_status_rb = childs[0]
self.special_status_rb = childs[1]
self.online_cb = childs[2]
self.away_cb = childs[3]
self.xa_cb = childs[4]
self.dnd_cb = childs[5]
self.invisible_cb = childs[6]
if not self.conditions_treeview.get_column(0):
# window never opened
model = gtk.ListStore(int, str)
model.set_sort_column_id(0, gtk.SORT_ASCENDING)
self.conditions_treeview.set_model(model)
# means number
col = gtk.TreeViewColumn(_('#'))
self.conditions_treeview.append_column(col)
renderer = gtk.CellRendererText()
col.pack_start(renderer, expand=False)
col.set_attributes(renderer, text=0)
col = gtk.TreeViewColumn(_('Condition'))
self.conditions_treeview.append_column(col)
renderer = gtk.CellRendererText()
col.pack_start(renderer, expand=True)
col.set_attributes(renderer, text=1)
else:
model = self.conditions_treeview.get_model()
model.clear()
# Fill conditions_treeview
num = 0
while num in self.config:
iter_ = model.append((num, ''))
path = model.get_path(iter_)
self.conditions_treeview.set_cursor(path)
self.active_num = num
self.initiate_rule_state()
self.set_treeview_string()
num += 1
# No rule selected at init time
self.conditions_treeview.get_selection().unselect_all()
self.active_num = -1
self.config_vbox.set_sensitive(False)
self.delete_button.set_sensitive(False)
self.down_button.set_sensitive(False)
self.up_button.set_sensitive(False)
def initiate_rule_state(self):
"""
Set values for all widgets
"""
if self.active_num < 0:
return
# event
value = self.config[self.active_num]['event']
if value:
self.event_combobox.set_active(self.events_list.keys().index(value))
else:
self.event_combobox.set_active(-1)
# recipient_type
value = self.config[self.active_num]['recipient_type']
if value:
self.recipient_type_combobox.set_active(
self.recipient_types_list.index(value))
else:
self.recipient_type_combobox.set_active(-1)
# recipient
value = self.config[self.active_num]['recipients']
if not value:
value = ''
self.recipient_list_entry.set_text(value)
# status
value = self.config[self.active_num]['status']
if value == 'all':
self.all_status_rb.set_active(True)
else:
self.special_status_rb.set_active(True)
values = value.split()
for v in ('online', 'away', 'xa', 'dnd', 'invisible'):
if v in values:
self.__dict__[v + '_cb'].set_active(True)
else:
self.__dict__[v + '_cb'].set_active(False)
self.on_status_radiobutton_toggled(self.all_status_rb)
# tab_opened
value = self.config[self.active_num]['tab_opened']
self.tab_opened_cb.set_active(True)
self.not_tab_opened_cb.set_active(True)
if value == 'no':
self.tab_opened_cb.set_active(False)
elif value == 'yes':
self.not_tab_opened_cb.set_active(False)
# sound_file
value = self.config[self.active_num]['sound_file']
self.sound_entry.set_text(value)
# sound, popup, auto_open, systray, roster
for option in ('sound', 'popup', 'auto_open', 'systray', 'roster',
'urgency_hint'):
value = self.config[self.active_num][option]
if value == 'yes':
self.__dict__['use_' + option + '_cb'].set_active(True)
else:
self.__dict__['use_' + option + '_cb'].set_active(False)
if value == 'no':
self.__dict__['disable_' + option + '_cb'].set_active(True)
else:
self.__dict__['disable_' + option + '_cb'].set_active(False)
# run_command
value = self.config[self.active_num]['run_command']
self.run_command_cb.set_active(value)
# command
value = self.config[self.active_num]['command']
self.command_entry.set_text(value)
def set_treeview_string(self):
(model, iter_) = self.conditions_treeview.get_selection().get_selected()
if not iter_:
return
event = self.event_combobox.get_active_text()
recipient_type = self.recipient_type_combobox.get_active_text()
recipient = ''
if recipient_type != 'everybody':
recipient = self.recipient_list_entry.get_text()
if self.all_status_rb.get_active():
status = ''
else:
status = _('when I am ')
for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
if self.__dict__[st + '_cb'].get_active():
status += helpers.get_uf_show(st) + ' '
model[iter_][1] = "When %s for %s %s %s" % (event, recipient_type,
recipient, status)
def on_conditions_treeview_cursor_changed(self, widget):
(model, iter_) = widget.get_selection().get_selected()
if not iter_:
self.active_num = ''
return
self.active_num = model[iter_][0]
if self.active_num == '0':
self.up_button.set_sensitive(False)
else:
self.up_button.set_sensitive(True)
max = self.conditions_treeview.get_model().iter_n_children(None)
if self.active_num == max - 1:
self.down_button.set_sensitive(False)
else:
self.down_button.set_sensitive(True)
self.initiate_rule_state()
self.config_vbox.set_sensitive(True)
self.delete_button.set_sensitive(True)
def on_new_button_clicked(self, widget):
model = self.conditions_treeview.get_model()
num = self.conditions_treeview.get_model().iter_n_children(None)
self.config[num] = {'event': '', 'recipient_type': 'all',
'recipients': '', 'status': 'all', 'tab_opened': 'both',
'sound': '', 'sound_file': '', 'popup': '', 'auto_open': '',
'run_command': False, 'command': '', 'systray': '', 'roster': '',
'urgency_hint': False}
iter_ = model.append((num, ''))
path = model.get_path(iter_)
self.conditions_treeview.set_cursor(path)
self.active_num = num
self.set_treeview_string()
self.config_vbox.set_sensitive(True)
def on_delete_button_clicked(self, widget):
(model, iter_) = self.conditions_treeview.get_selection().get_selected()
if not iter_:
return
# up all others
iter2 = model.iter_next(iter_)
num = self.active_num
while iter2:
num = model[iter2][0]
model[iter2][0] = num - 1
self.config[num - 1] = self.config[num].copy()
iter2 = model.iter_next(iter2)
model.remove(iter_)
del self.config[num]
self.active_num = ''
self.config_vbox.set_sensitive(False)
self.delete_button.set_sensitive(False)
self.up_button.set_sensitive(False)
self.down_button.set_sensitive(False)
def on_up_button_clicked(self, widget):
(model, iter_) = self.conditions_treeview.get_selection().get_selected()
if not iter_:
return
conf = self.config[self.active_num].copy()
self.config[self.active_num] = self.config[self.active_num - 1]
self.config[self.active_num - 1] = conf
model[iter_][0] = self.active_num - 1
# get previous iter
path = model.get_path(iter_)
iter_ = model.get_iter((path[0] - 1,))
model[iter_][0] = self.active_num
self.on_conditions_treeview_cursor_changed(self.conditions_treeview)
def on_down_button_clicked(self, widget):
(model, iter_) = self.conditions_treeview.get_selection().get_selected()
if not iter_:
return
conf = self.config[self.active_num].copy()
self.config[self.active_num] = self.config[self.active_num + 1]
self.config[self.active_num + 1] = conf
model[iter_][0] = self.active_num + 1
iter_ = model.iter_next(iter_)
model[iter_][0] = self.active_num
self.on_conditions_treeview_cursor_changed(self.conditions_treeview)
def on_event_combobox_changed(self, widget):
if self.active_num < 0:
return
active = self.event_combobox.get_active()
if active == -1:
event = ''
else:
event = self.events_list.keys()[active]
self.config[self.active_num]['event'] = event
for w in ('use_systray_cb', 'disable_systray_cb', 'use_roster_cb',
'disable_roster_cb'):
self.__dict__[w].set_sensitive(True)
for w in self.events_list[event]:
self.__dict__[w].set_sensitive(False)
self.__dict__[w].set_state(False)
self.set_treeview_string()
def on_recipient_type_combobox_changed(self, widget):
if self.active_num < 0:
return
recipient_type = self.recipient_types_list[
self.recipient_type_combobox.get_active()]
self.config[self.active_num]['recipient_type'] = recipient_type
if recipient_type == 'all':
self.recipient_list_entry.hide()
else:
self.recipient_list_entry.show()
self.set_treeview_string()
def on_recipient_list_entry_changed(self, widget):
if self.active_num < 0:
return
recipients = widget.get_text().decode('utf-8')
#TODO: do some check
self.config[self.active_num]['recipients'] = recipients
self.set_treeview_string()
def set_status_config(self):
if self.active_num < 0:
return
status = ''
for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
if self.__dict__[st + '_cb'].get_active():
status += st + ' '
if status:
status = status[:-1]
self.config[self.active_num]['status'] = status
self.set_treeview_string()
def on_status_radiobutton_toggled(self, widget):
if self.active_num < 0:
return
if self.all_status_rb.get_active():
self.config[self.active_num]['status'] = 'all'
# 'All status' clicked
for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
self.__dict__[st + '_cb'].hide()
self.special_status_rb.show()
else:
self.set_status_config()
# 'special status' clicked
for st in ('online', 'away', 'xa', 'dnd', 'invisible'):
self.__dict__[st + '_cb'].show()
self.special_status_rb.hide()
self.set_treeview_string()
def on_status_cb_toggled(self, widget):
if self.active_num < 0:
return
self.set_status_config()
# tab_opened OR (not xor) not_tab_opened must be active
def on_tab_opened_cb_toggled(self, widget):
if self.active_num < 0:
return
if self.tab_opened_cb.get_active():
if self.not_tab_opened_cb.get_active():
self.config[self.active_num]['tab_opened'] = 'both'
else:
self.config[self.active_num]['tab_opened'] = 'yes'
else:
self.not_tab_opened_cb.set_active(True)
self.config[self.active_num]['tab_opened'] = 'no'
def on_not_tab_opened_cb_toggled(self, widget):
if self.active_num < 0:
return
if self.not_tab_opened_cb.get_active():
if self.tab_opened_cb.get_active():
self.config[self.active_num]['tab_opened'] = 'both'
else:
self.config[self.active_num]['tab_opened'] = 'no'
else:
self.tab_opened_cb.set_active(True)
self.config[self.active_num]['tab_opened'] = 'yes'
def on_use_it_toggled(self, widget, oposite_widget, option):
if widget.get_active():
if oposite_widget.get_active():
oposite_widget.set_active(False)
self.config[self.active_num][option] = 'yes'
elif oposite_widget.get_active():
self.config[self.active_num][option] = 'no'
else:
self.config[self.active_num][option] = ''
def on_disable_it_toggled(self, widget, oposite_widget, option):
if widget.get_active():
if oposite_widget.get_active():
oposite_widget.set_active(False)
self.config[self.active_num][option] = 'no'
elif oposite_widget.get_active():
self.config[self.active_num][option] = 'yes'
else:
self.config[self.active_num][option] = ''
def on_use_sound_cb_toggled(self, widget):
self.on_use_it_toggled(widget, self.disable_sound_cb, 'sound')
if widget.get_active():
self.sound_file_hbox.set_sensitive(True)
else:
self.sound_file_hbox.set_sensitive(False)
def on_browse_for_sounds_button_clicked(self, widget, data=None):
if self.active_num < 0:
return
def on_ok(widget, path_to_snd_file):
dialog.destroy()
if not path_to_snd_file:
path_to_snd_file = ''
self.config[self.active_num]['sound_file'] = path_to_snd_file
self.sound_entry.set_text(path_to_snd_file)
path_to_snd_file = self.sound_entry.get_text().decode('utf-8')
path_to_snd_file = os.path.join(os.getcwd(), path_to_snd_file)
dialog = SoundChooserDialog(path_to_snd_file, on_ok)
def on_play_button_clicked(self, widget):
helpers.play_sound_file(self.sound_entry.get_text().decode('utf-8'))
def on_disable_sound_cb_toggled(self, widget):
self.on_disable_it_toggled(widget, self.use_sound_cb, 'sound')
def on_sound_entry_changed(self, widget):
self.config[self.active_num]['sound_file'] = widget.get_text().\
decode('utf-8')
def on_use_popup_cb_toggled(self, widget):
self.on_use_it_toggled(widget, self.disable_popup_cb, 'popup')
def on_disable_popup_cb_toggled(self, widget):
self.on_disable_it_toggled(widget, self.use_popup_cb, 'popup')
def on_use_auto_open_cb_toggled(self, widget):
self.on_use_it_toggled(widget, self.disable_auto_open_cb, 'auto_open')
def on_disable_auto_open_cb_toggled(self, widget):
self.on_disable_it_toggled(widget, self.use_auto_open_cb, 'auto_open')
def on_run_command_cb_toggled(self, widget):
self.config[self.active_num]['run_command'] = widget.get_active()
if widget.get_active():
self.command_entry.set_sensitive(True)
else:
self.command_entry.set_sensitive(False)
def on_command_entry_changed(self, widget):
self.config[self.active_num]['command'] = widget.get_text().\
decode('utf-8')
def on_use_systray_cb_toggled(self, widget):
self.on_use_it_toggled(widget, self.disable_systray_cb, 'systray')
def on_disable_systray_cb_toggled(self, widget):
self.on_disable_it_toggled(widget, self.use_systray_cb, 'systray')
def on_use_roster_cb_toggled(self, widget):
self.on_use_it_toggled(widget, self.disable_roster_cb, 'roster')
def on_disable_roster_cb_toggled(self, widget):
self.on_disable_it_toggled(widget, self.use_roster_cb, 'roster')
def on_use_urgency_hint_cb_toggled(self, widget):
self.on_use_it_toggled(widget, self.disable_urgency_hint_cb,
'uregency_hint')
def on_disable_urgency_hint_cb_toggled(self, widget):
self.on_disable_it_toggled(widget, self.use_urgency_hint_cb,
'uregency_hint')
def on_hide(self, widget):
# save config
for n in self.plugin.config:
del self.plugin.config[n]
for n in self.config:
self.plugin.config[str(n)] = self.config[n]

View File

@ -1 +0,0 @@
from plugin import WhiteboardPlugin

Binary file not shown.

Before

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,7 +0,0 @@
[info]
name: Whiteboard
short_name: whiteboard
version: 0.1
description: Shows a whiteboard in chat. python-pygoocanvas is required.
authors = Yann Leboulanger <asterix@lagaule.org>
homepage = www.gajim.org

Binary file not shown.

Before

Width:  |  Height:  |  Size: 989 B

View File

@ -1,483 +0,0 @@
## plugins/whiteboard/plugin.py
##
## Copyright (C) 2009 Jeff Ling <jeff.ummu AT gmail.com>
## Copyright (C) 2010 Yann Leboulanger <asterix AT lagaule.org>
##
## 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/>.
##
'''
Whiteboard plugin.
:author: Yann Leboulanger <asterix@lagaule.org>
:since: 1st November 2010
:copyright: Copyright (2010) Yann Leboulanger <asterix@lagaule.org>
:license: GPL
'''
from common import helpers
from common import gajim
from plugins import GajimPlugin
from plugins.plugin import GajimPluginException
from plugins.helpers import log_calls, log
import common.xmpp
import gtk
import chat_control
from common import ged
from common.jingle_session import JingleSession
from common.jingle_content import JingleContent
from common.jingle_transport import JingleTransport, TransportType
import dialogs
from whiteboard_widget import Whiteboard, HAS_GOOCANVAS
from common import xmpp
from common import caps_cache
NS_JINGLE_XHTML = 'urn:xmpp:tmp:jingle:apps:xhtml'
NS_JINGLE_SXE = 'urn:xmpp:tmp:jingle:transports:sxe'
NS_SXE = 'urn:xmpp:sxe:0'
class WhiteboardPlugin(GajimPlugin):
@log_calls('WhiteboardPlugin')
def init(self):
self.description = _('Shows a whiteboard in chat.'
' python-pygoocanvas is required.')
self.config_dialog = None
self.events_handlers = {
'jingle-request-received': (ged.GUI1, self._nec_jingle_received),
'jingle-connected-received': (ged.GUI1, self._nec_jingle_connected),
'jingle-disconnected-received': (ged.GUI1,
self._nec_jingle_disconnected),
'raw-message-received': (ged.GUI1, self._nec_raw_message),
}
self.gui_extension_points = {
'chat_control_base' : (self.connect_with_chat_control,
self.disconnect_from_chat_control),
'chat_control_base_update_toolbar': (self.update_button_state,
None),
}
self.controls = []
self.sid = None
@log_calls('WhiteboardPlugin')
def _compute_caps_hash(self):
for a in gajim.connections:
gajim.caps_hash[a] = caps_cache.compute_caps_hash([
gajim.gajim_identity], gajim.gajim_common_features + \
gajim.gajim_optional_features[a])
# re-send presence with new hash
connected = gajim.connections[a].connected
if connected > 1 and gajim.SHOW_LIST[connected] != 'invisible':
gajim.connections[a].change_status(gajim.SHOW_LIST[connected],
gajim.connections[a].status)
@log_calls('WhiteboardPlugin')
def activate(self):
if not HAS_GOOCANVAS:
raise GajimPluginException('python-pygoocanvas is missing!')
if NS_JINGLE_SXE not in gajim.gajim_common_features:
gajim.gajim_common_features.append(NS_JINGLE_SXE)
if NS_SXE not in gajim.gajim_common_features:
gajim.gajim_common_features.append(NS_SXE)
self._compute_caps_hash()
@log_calls('WhiteboardPlugin')
def deactivate(self):
if NS_JINGLE_SXE in gajim.gajim_common_features:
gajim.gajim_common_features.remove(NS_JINGLE_SXE)
if NS_SXE in gajim.gajim_common_features:
gajim.gajim_common_features.remove(NS_SXE)
self._compute_caps_hash()
@log_calls('WhiteboardPlugin')
def connect_with_chat_control(self, control):
if isinstance(control, chat_control.ChatControl):
base = Base(self, control)
self.controls.append(base)
@log_calls('WhiteboardPlugin')
def disconnect_from_chat_control(self, chat_control):
for base in self.controls:
base.disconnect_from_chat_control()
self.controls = []
@log_calls('WhiteboardPlugin')
def update_button_state(self, control):
for base in self.controls:
if base.chat_control == control:
if control.contact.supports(NS_JINGLE_SXE) and \
control.contact.supports(NS_SXE):
base.button.set_sensitive(True)
tooltip_text = _('Show whiteboard')
else:
base.button.set_sensitive(False)
tooltip_text = _('Client on the other side '
'does not support the whiteboard')
base.button.set_tooltip_text(tooltip_text)
@log_calls('WhiteboardPlugin')
def show_request_dialog(self, account, fjid, jid, sid, content_types):
def on_ok():
session = gajim.connections[account].get_jingle_session(fjid, sid)
self.sid = session.sid
if not session.accepted:
session.approve_session()
for content in content_types:
session.approve_content(content)
for _jid in (fjid, jid):
ctrl = gajim.interface.msg_win_mgr.get_control(_jid, account)
if ctrl:
break
if not ctrl:
# create it
gajim.interface.new_chat_from_jid(account, jid)
ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
session = session.contents[('initiator', 'xhtml')]
ctrl.draw_whiteboard(session)
def on_cancel():
session = gajim.connections[account].get_jingle_session(fjid, sid)
session.decline_session()
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
if contact:
name = contact.get_shown_name()
else:
name = jid
pritext = _('Incoming Whiteboard')
sectext = _('%(name)s (%(jid)s) wants to start a whiteboard with '
'you. Do you want to accept?') % {'name': name, 'jid': jid}
dialog = dialogs.NonModalConfirmationDialog(pritext, sectext=sectext,
on_response_ok=on_ok, on_response_cancel=on_cancel)
dialog.popup()
@log_calls('WhiteboardPlugin')
def _nec_jingle_received(self, obj):
if not HAS_GOOCANVAS:
return
content_types = set(c[0] for c in obj.contents)
if 'xhtml' not in content_types:
return
self.show_request_dialog(obj.conn.name, obj.fjid, obj.jid, obj.sid,
content_types)
@log_calls('WhiteboardPlugin')
def _nec_jingle_connected(self, obj):
if not HAS_GOOCANVAS:
return
account = obj.conn.name
ctrl = (gajim.interface.msg_win_mgr.get_control(obj.fjid, account)
or gajim.interface.msg_win_mgr.get_control(obj.jid, account))
if not ctrl:
return
session = gajim.connections[obj.conn.name].get_jingle_session(obj.fjid,
obj.sid)
if ('initiator', 'xhtml') not in session.contents:
return
session = session.contents[('initiator', 'xhtml')]
ctrl.draw_whiteboard(session)
@log_calls('WhiteboardPlugin')
def _nec_jingle_disconnected(self, obj):
for base in self.controls:
if base.sid == obj.sid:
base.stop_whiteboard(reason = obj.reason)
@log_calls('WhiteboardPlugin')
def _nec_raw_message(self, obj):
if not HAS_GOOCANVAS:
return
if obj.stanza.getTag('sxe', namespace=NS_SXE):
account = obj.conn.name
try:
fjid = helpers.get_full_jid_from_iq(obj.stanza)
except helpers.InvalidFormat:
obj.conn.dispatch('ERROR', (_('Invalid Jabber ID'),
_('A message from a non-valid JID arrived, it has been '
'ignored.')))
jid = gajim.get_jid_without_resource(fjid)
ctrl = (gajim.interface.msg_win_mgr.get_control(fjid, account)
or gajim.interface.msg_win_mgr.get_control(jid, account))
if not ctrl:
return
sxe = obj.stanza.getTag('sxe')
if not sxe:
return
sid = sxe.getAttr('session')
if (jid, sid) not in obj.conn._sessions:
pass
# newjingle = JingleSession(con=self, weinitiate=False, jid=jid, sid=sid)
# self.addJingle(newjingle)
# we already have such session in dispatcher...
session = obj.conn.get_jingle_session(fjid, sid)
cn = session.contents[('initiator', 'xhtml')]
error = obj.stanza.getTag('error')
if error:
action = 'iq-error'
else:
action = 'edit'
cn.on_stanza(obj.stanza, sxe, error, action)
# def __editCB(self, stanza, content, error, action):
#new_tags = sxe.getTags('new')
#remove_tags = sxe.getTags('remove')
#if new_tags is not None:
## Process new elements
#for tag in new_tags:
#if tag.getAttr('type') == 'element':
#ctrl.whiteboard.recieve_element(tag)
#elif tag.getAttr('type') == 'attr':
#ctrl.whiteboard.recieve_attr(tag)
#ctrl.whiteboard.apply_new()
#if remove_tags is not None:
## Delete rids
#for tag in remove_tags:
#target = tag.getAttr('target')
#ctrl.whiteboard.image.del_rid(target)
# Stop propagating this event, it's handled
return True
class Base(object):
def __init__(self, plugin, chat_control):
self.plugin = plugin
self.chat_control = chat_control
self.chat_control.draw_whiteboard = self.draw_whiteboard
self.contact = self.chat_control.contact
self.account = self.chat_control.account
self.jid = self.contact.get_full_jid()
self.create_buttons()
self.whiteboard = None
self.sid = None
def create_buttons(self):
# create whiteboard button
actions_hbox = self.chat_control.xml.get_object('actions_hbox')
self.button = gtk.ToggleButton(label=None, use_underline=True)
self.button.set_property('relief', gtk.RELIEF_NONE)
self.button.set_property('can-focus', False)
img = gtk.Image()
img_path = self.plugin.local_file_path('whiteboard.png')
pixbuf = gtk.gdk.pixbuf_new_from_file(img_path)
iconset = gtk.IconSet(pixbuf=pixbuf)
factory = gtk.IconFactory()
factory.add('whiteboard', iconset)
img_path = self.plugin.local_file_path('brush_tool.png')
pixbuf = gtk.gdk.pixbuf_new_from_file(img_path)
iconset = gtk.IconSet(pixbuf=pixbuf)
factory.add('brush_tool', iconset)
img_path = self.plugin.local_file_path('line_tool.png')
pixbuf = gtk.gdk.pixbuf_new_from_file(img_path)
iconset = gtk.IconSet(pixbuf=pixbuf)
factory.add('line_tool', iconset)
img_path = self.plugin.local_file_path('oval_tool.png')
pixbuf = gtk.gdk.pixbuf_new_from_file(img_path)
iconset = gtk.IconSet(pixbuf=pixbuf)
factory.add('oval_tool', iconset)
factory.add_default()
img.set_from_stock('whiteboard', gtk.ICON_SIZE_MENU)
self.button.set_image(img)
send_button = self.chat_control.xml.get_object('send_button')
send_button_pos = actions_hbox.child_get_property(send_button,
'position')
actions_hbox.add_with_properties(self.button, 'position',
send_button_pos - 1, 'expand', False)
id_ = self.button.connect('toggled', self.on_whiteboard_button_toggled)
self.chat_control.handlers[id_] = self.button
self.button.show()
def draw_whiteboard(self, content):
hbox = self.chat_control.xml.get_object('chat_control_hbox')
if len(hbox.get_children()) == 1:
self.whiteboard = Whiteboard(self.account, self.contact, content,
self.plugin)
# set minimum size
self.whiteboard.hbox.set_size_request(300, 0)
hbox.pack_start(self.whiteboard.hbox, expand=False, fill=False)
self.whiteboard.hbox.show_all()
self.button.set_active(True)
content.control = self
self.sid = content.session.sid
def on_whiteboard_button_toggled(self, widget):
"""
Popup whiteboard
"""
if widget.get_active():
if not self.whiteboard:
self.start_whiteboard()
else:
self.stop_whiteboard()
def start_whiteboard(self):
conn = gajim.connections[self.chat_control.account]
jingle = JingleSession(conn, weinitiate=True, jid=self.jid)
self.sid = jingle.sid
conn._sessions[jingle.sid] = jingle
content = JingleWhiteboard(jingle)
content.control = self
jingle.add_content('xhtml', content)
jingle.start_session()
def stop_whiteboard(self, reason=None):
conn = gajim.connections[self.chat_control.account]
self.sid = None
session = conn.get_jingle_session(self.jid, media='xhtml')
if session:
session.end_session()
self.button.set_active(False)
if reason:
txt = _('Whiteboard stopped: %(reason)s') % {'reason': reason}
self.chat_control.print_conversation(txt, 'info')
if not self.whiteboard:
return
hbox = self.chat_control.xml.get_object('chat_control_hbox')
if self.whiteboard.hbox in hbox.get_children():
if hasattr(self.whiteboard, 'hbox'):
hbox.remove(self.whiteboard.hbox)
self.whiteboard = None
def disconnect_from_chat_control(self):
actions_hbox = self.chat_control.xml.get_object('actions_hbox')
actions_hbox.remove(self.button)
class JingleWhiteboard(JingleContent):
''' Jingle Whiteboard sessions consist of xhtml content'''
def __init__(self, session, transport=None):
if not transport:
transport = JingleTransportSXE()
JingleContent.__init__(self, session, transport)
self.media = 'xhtml'
self.negotiated = True # there is nothing to negotiate
self.last_rid = 0
self.callbacks['session-accept'] += [self._sessionAcceptCB]
self.callbacks['session-terminate'] += [self._stop]
self.callbacks['session-terminate-sent'] += [self._stop]
self.callbacks['edit'] = [self._EditCB]
def _EditCB(self, stanza, content, error, action):
new_tags = content.getTags('new')
remove_tags = content.getTags('remove')
if new_tags is not None:
# Process new elements
for tag in new_tags:
if tag.getAttr('type') == 'element':
self.control.whiteboard.recieve_element(tag)
elif tag.getAttr('type') == 'attr':
self.control.whiteboard.recieve_attr(tag)
self.control.whiteboard.apply_new()
if remove_tags is not None:
# Delete rids
for tag in remove_tags:
target = tag.getAttr('target')
self.control.whiteboard.image.del_rid(target)
def _sessionAcceptCB(self, stanza, content, error, action):
log.debug('session accepted')
self.session.connection.dispatch('WHITEBOARD_ACCEPTED',
(self.session.peerjid, self.session.sid))
def generate_rids(self, x):
# generates x number of rids and returns in list
rids = []
for x in range(x):
rids.append(str(self.last_rid))
self.last_rid += 1
return rids
def send_whiteboard_node(self, items, rids):
# takes int rid and dict items and sends it as a node
# sends new item
jid = self.session.peerjid
sid = self.session.sid
message = xmpp.Message(to=jid)
sxe = message.addChild(name='sxe', attrs={'session': sid},
namespace=NS_SXE)
for x in rids:
if items[x]['type'] == 'element':
parent = x
attrs = {'rid': x,
'name': items[x]['data'][0].getName(),
'type': items[x]['type']}
sxe.addChild(name='new', attrs=attrs)
if items[x]['type'] == 'attr':
attr_name = items[x]['data']
chdata = items[parent]['data'][0].getAttr(attr_name)
attrs = {'rid': x,
'name': attr_name,
'type': items[x]['type'],
'chdata': chdata,
'parent': parent}
sxe.addChild(name='new', attrs=attrs)
self.session.connection.connection.send(message)
def delete_whiteboard_node(self, rids):
message = xmpp.Message(to=self.session.peerjid)
sxe = message.addChild(name='sxe', attrs={'session': self.session.sid},
namespace=NS_SXE)
for x in rids:
sxe.addChild(name='remove', attrs = {'target': x})
self.session.connection.connection.send(message)
def send_items(self, items, rids):
# recieves dict items and a list of rids of items to send
# TODO: is there a less clumsy way that doesn't involve passing
# whole list
self.send_whiteboard_node(items, rids)
def del_item(self, rids):
self.delete_whiteboard_node(rids)
def encode(self, xml):
# encodes it sendable string
return 'data:text/xml,' + urllib.quote(xml)
def _fill_content(self, content):
content.addChild(NS_JINGLE_XHTML + ' description')
def _stop(self, *things):
pass
def __del__(self):
pass
def get_content(desc):
return JingleWhiteboard
common.jingle_content.contents[NS_JINGLE_XHTML] = get_content
class JingleTransportSXE(JingleTransport):
def __init__(self):
JingleTransport.__init__(self, TransportType.streaming)
def make_transport(self, candidates=None):
transport = JingleTransport.make_transport(self, candidates)
transport.setNamespace(NS_JINGLE_SXE)
transport.setTagData('host', 'TODO')
return transport
common.jingle_transport.transports[NS_JINGLE_SXE] = JingleTransportSXE

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,418 +0,0 @@
## plugins/whiteboard/whiteboard_widget.py
##
## Copyright (C) 2009 Jeff Ling <jeff.ummu AT gmail.com>
## Copyright (C) 2010 Yann Leboulanger <asterix AT lagaule.org>
##
## 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/>.
##
import gtk
import gtkgui_helpers
try:
import goocanvas
HAS_GOOCANVAS = True
except:
HAS_GOOCANVAS = False
from common.xmpp import Node
from common import gajim
from dialogs import FileChooserDialog
'''
A whiteboard widget made for Gajim.
- Ummu
'''
class Whiteboard(object):
def __init__(self, account, contact, session, plugin):
self.plugin = plugin
file_path = plugin.local_file_path('whiteboard_widget.ui')
xml = gtk.Builder()
xml.set_translation_domain('gajim_plugins')
xml.add_from_file(file_path)
self.hbox = xml.get_object('whiteboard_hbox')
self.canevas = goocanvas.Canvas()
self.hbox.pack_start(self.canevas)
self.hbox.reorder_child(self.canevas, 0)
self.canevas.set_flags(gtk.CAN_FOCUS)
self.fg_color_select_button = xml.get_object('fg_color_button')
self.root = self.canevas.get_root_item()
self.tool_buttons = []
for tool in ('brush', 'oval', 'line', 'delete'):
self.tool_buttons.append(xml.get_object(tool + '_button'))
xml.get_object('brush_button').set_active(True)
# Events
self.canevas.connect('button-press-event', self.button_press_event)
self.canevas.connect('button-release-event', self.button_release_event)
self.canevas.connect('motion-notify-event', self.motion_notify_event)
self.canevas.connect('item-created', self.item_created)
# Config
self.line_width = 2
xml.get_object('size_scale').set_value(2)
self.color = str(self.fg_color_select_button.get_color())
# SVG Storage
self.image = SVGObject(self.root, session)
xml.connect_signals(self)
# Temporary Variables for items
self.item_temp = None
self.item_temp_coords = (0, 0)
self.item_data = None
# Will be {ID: {type:'element', data:[node, goocanvas]}, ID2: {}} instance
self.recieving = {}
def on_tool_button_toggled(self, widget):
for btn in self.tool_buttons:
if btn == widget:
continue
btn.set_active(False)
def on_brush_button_toggled(self, widget):
if widget.get_active():
self.image.draw_tool = 'brush'
self.on_tool_button_toggled(widget)
def on_oval_button_toggled(self, widget):
if widget.get_active():
self.image.draw_tool = 'oval'
self.on_tool_button_toggled(widget)
def on_line_button_toggled(self, widget):
if widget.get_active():
self.image.draw_tool = 'line'
self.on_tool_button_toggled(widget)
def on_delete_button_toggled(self, widget):
if widget.get_active():
self.image.draw_tool = 'delete'
self.on_tool_button_toggled(widget)
def on_clear_button_clicked(self, widget):
self.image.clear_canvas()
def on_export_button_clicked(self, widget):
SvgChooserDialog(self.image.export_svg)
def on_fg_color_button_color_set(self, widget):
self.color = str(self.fg_color_select_button.get_color())
def item_created(self, canvas, item, model):
print 'item created'
item.connect('button-press-event', self.item_button_press_events)
def item_button_press_events(self, item, target_item, event):
if self.image.draw_tool == 'delete':
self.image.del_item(item)
def on_size_scale_format_value(self, widget):
self.line_width = int(widget.get_value())
def button_press_event(self, widget, event):
x = event.x
y = event.y
state = event.state
self.item_temp_coords = (x, y)
if self.image.draw_tool == 'brush':
self.item_temp = goocanvas.Ellipse(parent=self.root,
center_x=x,
center_y=y,
radius_x=1,
radius_y=1,
stroke_color=self.color,
fill_color=self.color,
line_width=self.line_width)
self.item_data = 'M %s,%s L ' % (x, y)
elif self.image.draw_tool == 'oval':
self.item_data = True
if self.image.draw_tool == 'line':
self.item_data = 'M %s,%s L' % (x, y)
def motion_notify_event(self, widget, event):
x = event.x
y = event.y
state = event.state
if self.item_temp is not None:
self.item_temp.remove()
if self.item_data is not None:
if self.image.draw_tool == 'brush':
self.item_data = self.item_data + '%s,%s ' % (x, y)
self.item_temp = goocanvas.Path(parent=self.root,
data=self.item_data, line_width=self.line_width,
stroke_color=self.color)
elif self.image.draw_tool == 'oval':
self.item_temp = goocanvas.Ellipse(parent=self.root,
center_x=self.item_temp_coords[0] + (x - self.item_temp_coords[0]) / 2,
center_y=self.item_temp_coords[1] + (y - self.item_temp_coords[1]) / 2,
radius_x=abs(x - self.item_temp_coords[0]) / 2,
radius_y=abs(y - self.item_temp_coords[1]) / 2,
stroke_color=self.color,
line_width=self.line_width)
elif self.image.draw_tool == 'line':
self.item_data = 'M %s,%s L' % self.item_temp_coords
self.item_data = self.item_data + ' %s,%s' % (x, y)
self.item_temp = goocanvas.Path(parent=self.root,
data=self.item_data, line_width=self.line_width,
stroke_color=self.color)
def button_release_event(self, widget, event):
x = event.x
y = event.y
state = event.state
if self.image.draw_tool == 'brush':
self.item_data = self.item_data + '%s,%s' % (x, y)
if x == self.item_temp_coords[0] and y == self.item_temp_coords[1]:
goocanvas.Ellipse(parent=self.root,
center_x=x,
center_y=y,
radius_x=1,
radius_y=1,
stroke_color=self.color,
fill_color=self.color,
line_width=self.line_width)
self.image.add_path(self.item_data, self.line_width, self.color)
if self.image.draw_tool == 'oval':
cx = self.item_temp_coords[0] + (x - self.item_temp_coords[0]) / 2
cy = self.item_temp_coords[1] + (y - self.item_temp_coords[1]) / 2
rx = abs(x - self.item_temp_coords[0]) / 2
ry = abs(y - self.item_temp_coords[1]) / 2
self.image.add_ellipse(cx, cy, rx, ry, self.line_width, self.color)
if self.image.draw_tool == 'line':
self.item_data = 'M %s,%s L' % self.item_temp_coords
self.item_data = self.item_data + ' %s,%s' % (x, y)
if x == self.item_temp_coords[0] and y == self.item_temp_coords[1]:
goocanvas.Ellipse(parent=self.root,
center_x=x,
center_y=y,
radius_x=1,
radius_y=1,
stroke_color='black',
fill_color='black',
line_width=self.line_width)
self.image.add_path(self.item_data, self.line_width, self.color)
if self.image.draw_tool == 'delete':
pass
self.item_data = None
if self.item_temp is not None:
self.item_temp.remove()
self.item_temp = None
def recieve_element(self, element):
node = self.image.g.addChild(name=element.getAttr('name'))
self.image.g.addChild(node=node)
self.recieving[element.getAttr('rid')] = {'type':'element',
'data':[node],
'children':[]}
def recieve_attr(self, element):
node = self.recieving[element.getAttr('parent')]['data'][0]
node.setAttr(element.getAttr('name'), element.getAttr('chdata'))
self.recieving[element.getAttr('rid')] = {'type':'attr',
'data':element.getAttr('name'),
'parent':node}
self.recieving[element.getAttr('parent')]['children'].append(element.getAttr('rid'))
def apply_new(self):
for x in self.recieving.keys():
if self.recieving[x]['type'] == 'element':
self.image.add_recieved(x, self.recieving)
self.recieving = {}
class SvgChooserDialog(FileChooserDialog):
def __init__(self, on_response_ok=None, on_response_cancel=None):
'''
Choose in which SVG file to store the image
'''
def on_ok(widget, callback):
'''
check if file exists and call callback
'''
path_to_file = self.get_filename()
path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
(path_to_file,))[0]
widget.destroy()
callback(path_to_file)
FileChooserDialog.__init__(self,
title_text=_('Save Image as...'),
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
gtk.RESPONSE_OK),
current_folder='',
default_response=gtk.RESPONSE_OK,
on_response_ok=(on_ok, on_response_ok),
on_response_cancel=on_response_cancel)
filter_ = gtk.FileFilter()
filter_.set_name(_('All files'))
filter_.add_pattern('*')
self.add_filter(filter_)
filter_ = gtk.FileFilter()
filter_.set_name(_('SVG Files'))
filter_.add_pattern('*.svg')
self.add_filter(filter_)
self.set_filter(filter_)
class SVGObject():
''' A class to store the svg document and make changes to it.'''
def __init__(self, root, session, height=300, width=300):
# Will be {ID: {type:'element', data:[node, goocanvas]}, ID2: {}} instance
self.items = {}
self.root = root
self.draw_tool = 'brush'
# sxe session
self.session = session
# initialize svg document
self.svg = Node(node='<svg/>')
self.svg.setAttr('version', '1.1')
self.svg.setAttr('height', str(height))
self.svg.setAttr('width', str(width))
self.svg.setAttr('xmlns', 'http://www.w3.org/2000/svg')
# TODO: make this settable
self.g = self.svg.addChild(name='g')
self.g.setAttr('fill', 'none')
self.g.setAttr('stroke-linecap', 'round')
def add_path(self, data, line_width, color):
''' adds the path to the items listing, both minidom node and goocanvas
object in a tuple '''
goocanvas_obj = goocanvas.Path(parent=self.root, data=data,
line_width=line_width, stroke_color=color)
goocanvas_obj.connect('button-press-event', self.item_button_press_events)
node = self.g.addChild(name='path')
node.setAttr('d', data)
node.setAttr('stroke-width', str(line_width))
node.setAttr('stroke', color)
self.g.addChild(node=node)
rids = self.session.generate_rids(4)
self.items[rids[0]] = {'type':'element', 'data':[node, goocanvas_obj], 'children':rids[1:]}
self.items[rids[1]] = {'type':'attr', 'data':'d', 'parent':node}
self.items[rids[2]] = {'type':'attr', 'data':'stroke-width', 'parent':node}
self.items[rids[3]] = {'type':'attr', 'data':'stroke', 'parent':node}
self.session.send_items(self.items, rids)
def add_recieved(self, parent_rid, new_items):
''' adds the path to the items listing, both minidom node and goocanvas
object in a tuple '''
node = new_items[parent_rid]['data'][0]
self.items[parent_rid] = new_items[parent_rid]
for x in new_items[parent_rid]['children']:
self.items[x] = new_items[x]
if node.getName() == 'path':
goocanvas_obj = goocanvas.Path(parent=self.root,
data=node.getAttr('d'),
line_width=int(node.getAttr('stroke-width')),
stroke_color=node.getAttr('stroke'))
if node.getName() == 'ellipse':
goocanvas_obj = goocanvas.Ellipse(parent=self.root,
center_x=float(node.getAttr('cx')),
center_y=float(node.getAttr('cy')),
radius_x=float(node.getAttr('rx')),
radius_y=float(node.getAttr('ry')),
stroke_color=node.getAttr('stroke'),
line_width=float(node.getAttr('stroke-width')))
self.items[parent_rid]['data'].append(goocanvas_obj)
goocanvas_obj.connect('button-press-event', self.item_button_press_events)
def add_ellipse(self, cx, cy, rx, ry, line_width, stroke_color):
''' adds the ellipse to the items listing, both minidom node and goocanvas
object in a tuple '''
goocanvas_obj = goocanvas.Ellipse(parent=self.root,
center_x=cx,
center_y=cy,
radius_x=rx,
radius_y=ry,
stroke_color=stroke_color,
line_width=line_width)
goocanvas_obj.connect('button-press-event', self.item_button_press_events)
node = self.g.addChild(name='ellipse')
node.setAttr('cx', str(cx))
node.setAttr('cy', str(cy))
node.setAttr('rx', str(rx))
node.setAttr('ry', str(ry))
node.setAttr('stroke-width', str(line_width))
node.setAttr('stroke', stroke_color)
self.g.addChild(node=node)
rids = self.session.generate_rids(7)
self.items[rids[0]] = {'type':'element', 'data':[node, goocanvas_obj], 'children':rids[1:]}
self.items[rids[1]] = {'type':'attr', 'data':'cx', 'parent':node}
self.items[rids[2]] = {'type':'attr', 'data':'cy', 'parent':node}
self.items[rids[3]] = {'type':'attr', 'data':'rx', 'parent':node}
self.items[rids[4]] = {'type':'attr', 'data':'ry', 'parent':node}
self.items[rids[5]] = {'type':'attr', 'data':'stroke-width', 'parent':node}
self.items[rids[6]] = {'type':'attr', 'data':'stroke', 'parent':node}
self.session.send_items(self.items, rids)
def del_item(self, item):
rids = []
for x in self.items.keys():
if self.items[x]['type'] == 'element':
if self.items[x]['data'][1] == item:
for y in self.items[x]['children']:
rids.append(y)
self.del_rid(y)
rids.append(x)
self.del_rid(x)
break
self.session.del_item(rids)
def clear_canvas(self):
for x in self.items.keys():
if self.items[x]['type'] == 'element':
self.del_rid(x)
def del_rid(self, rid):
if self.items[rid]['type'] == 'element':
self.items[rid]['data'][1].remove()
del self.items[rid]
def export_svg(self, filename):
f = open(filename, 'w')
f.writelines(str(self.svg))
f.close()
def item_button_press_events(self, item, target_item, event):
self.del_item(item)

View File

@ -1,192 +0,0 @@
<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkHBox" id="whiteboard_hbox">
<property name="visible">True</property>
<property name="border_width">3</property>
<property name="spacing">6</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkVBox" id="vbuttonbox1">
<property name="visible">True</property>
<property name="border_width">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkToggleButton" id="brush_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Brush Tool: Draw freehand lines</property>
<signal name="toggled" handler="on_brush_button_toggled"/>
<child>
<object class="GtkImage" id="image5">
<property name="visible">True</property>
<property name="stock">brush_tool</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="oval_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Oval Tool: Draw circles and ellipses</property>
<signal name="toggled" handler="on_oval_button_toggled"/>
<child>
<object class="GtkImage" id="image6">
<property name="visible">True</property>
<property name="stock">oval_tool</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="line_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Line Tool: Draw straight lines</property>
<signal name="toggled" handler="on_line_button_toggled"/>
<child>
<object class="GtkImage" id="image7">
<property name="visible">True</property>
<property name="stock">line_tool</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="delete_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Delete Tool: Remove individual figures</property>
<signal name="toggled" handler="on_delete_button_toggled"/>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="clear_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Clear Canvas: Cleanup canvas</property>
<signal name="clicked" handler="on_clear_button_clicked"/>
<child>
<object class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="stock">gtk-clear</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="export_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Export Image: Save image to svg file</property>
<signal name="clicked" handler="on_export_button_clicked"/>
<child>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="stock">gtk-save-as</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkVScale" id="size_scale">
<property name="height_request">68</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Line width</property>
<property name="orientation">vertical</property>
<property name="adjustment">adjustment1</property>
<property name="inverted">True</property>
<property name="digits">0</property>
<property name="value_pos">bottom</property>
<signal name="value_changed" handler="on_size_scale_format_value"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkColorButton" id="fg_color_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Foreground color</property>
<property name="color">#000000000000</property>
<signal name="color_set" handler="on_fg_color_button_color_set"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">7</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
</object>
<object class="GtkAdjustment" id="adjustment1">
<property name="value">2</property>
<property name="lower">1</property>
<property name="upper">110</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
<property name="page_size">10</property>
</object>
</interface>