remove plugins that have been move to gajim-plugins reprository
This commit is contained in:
parent
87504e3954
commit
6ad3d01fb5
|
@ -1,4 +1,4 @@
|
|||
SUBDIRS = src data plugins po icons
|
||||
SUBDIRS = src data po icons
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
from plugin import BannerTweaksPlugin
|
|
@ -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>
|
|
@ -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
|
||||
|
|
@ -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()
|
|
@ -1 +0,0 @@
|
|||
from plugin import GoogleTranslationPlugin
|
|
@ -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
|
||||
|
|
@ -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)
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
from length_notifier import LengthNotifierPlugin
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
from plugin_installer import PluginInstaller
|
|
@ -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"><empty></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"><empty></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">●</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -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/
|
|
@ -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()
|
|
@ -1 +0,0 @@
|
|||
from triggers import Triggers
|
|
@ -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"><b>Conditions</b></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"><b>Actions</b></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"><b>Sounds</b></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>
|
|
@ -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/
|
|
@ -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]
|
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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)
|
|
@ -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>
|
Loading…
Reference in New Issue