From 6ad3d01fb5684426e9bd243c78849bc7e8219b94 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Wed, 16 Nov 2011 21:26:36 +0100 Subject: [PATCH] remove plugins that have been move to gajim-plugins reprository --- Makefile.am | 2 +- configure.ac | 1 - plugins/Makefile.am | 17 - plugins/banner_tweaks/__init__.py | 2 - plugins/banner_tweaks/config_dialog.ui | 75 -- plugins/banner_tweaks/manifest.ini | 10 - plugins/banner_tweaks/plugin.py | 194 ---- plugins/google_translation/__init__.py | 1 - plugins/google_translation/manifest.ini | 8 - plugins/google_translation/plugin.py | 284 ------ plugins/length_notifier/__init__.py | 2 - plugins/length_notifier/config_dialog.ui | 152 ---- plugins/length_notifier/length_notifier.py | 157 ---- plugins/length_notifier/manifest.ini | 9 - plugins/plugin_installer/__init__.py | 1 - plugins/plugin_installer/config_dialog.ui | 294 ------ plugins/plugin_installer/manifest.ini | 8 - plugins/plugin_installer/plugin_installer.py | 532 ----------- plugins/triggers/__init__.py | 1 - plugins/triggers/config_dialog.ui | 910 ------------------- plugins/triggers/manifest.ini | 7 - plugins/triggers/triggers.py | 677 -------------- plugins/whiteboard/__init__.py | 1 - plugins/whiteboard/brush_tool.png | Bin 806 -> 0 bytes plugins/whiteboard/line_tool.png | Bin 1054 -> 0 bytes plugins/whiteboard/manifest.ini | 7 - plugins/whiteboard/oval_tool.png | Bin 989 -> 0 bytes plugins/whiteboard/plugin.py | 483 ---------- plugins/whiteboard/whiteboard.png | Bin 1550 -> 0 bytes plugins/whiteboard/whiteboard_widget.py | 418 --------- plugins/whiteboard/whiteboard_widget.ui | 192 ---- 31 files changed, 1 insertion(+), 4444 deletions(-) delete mode 100644 plugins/Makefile.am delete mode 100644 plugins/banner_tweaks/__init__.py delete mode 100644 plugins/banner_tweaks/config_dialog.ui delete mode 100644 plugins/banner_tweaks/manifest.ini delete mode 100644 plugins/banner_tweaks/plugin.py delete mode 100644 plugins/google_translation/__init__.py delete mode 100644 plugins/google_translation/manifest.ini delete mode 100644 plugins/google_translation/plugin.py delete mode 100644 plugins/length_notifier/__init__.py delete mode 100644 plugins/length_notifier/config_dialog.ui delete mode 100644 plugins/length_notifier/length_notifier.py delete mode 100644 plugins/length_notifier/manifest.ini delete mode 100644 plugins/plugin_installer/__init__.py delete mode 100644 plugins/plugin_installer/config_dialog.ui delete mode 100644 plugins/plugin_installer/manifest.ini delete mode 100644 plugins/plugin_installer/plugin_installer.py delete mode 100644 plugins/triggers/__init__.py delete mode 100644 plugins/triggers/config_dialog.ui delete mode 100644 plugins/triggers/manifest.ini delete mode 100644 plugins/triggers/triggers.py delete mode 100644 plugins/whiteboard/__init__.py delete mode 100644 plugins/whiteboard/brush_tool.png delete mode 100644 plugins/whiteboard/line_tool.png delete mode 100644 plugins/whiteboard/manifest.ini delete mode 100644 plugins/whiteboard/oval_tool.png delete mode 100644 plugins/whiteboard/plugin.py delete mode 100644 plugins/whiteboard/whiteboard.png delete mode 100644 plugins/whiteboard/whiteboard_widget.py delete mode 100644 plugins/whiteboard/whiteboard_widget.ui diff --git a/Makefile.am b/Makefile.am index 176f219b4..1bb39b2d2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = src data plugins po icons +SUBDIRS = src data po icons ACLOCAL_AMFLAGS = -I m4 diff --git a/configure.ac b/configure.ac index d9ce3bf06..1895fe2d2 100644 --- a/configure.ac +++ b/configure.ac @@ -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 " diff --git a/plugins/Makefile.am b/plugins/Makefile.am deleted file mode 100644 index 5ecf73578..000000000 --- a/plugins/Makefile.am +++ /dev/null @@ -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 diff --git a/plugins/banner_tweaks/__init__.py b/plugins/banner_tweaks/__init__.py deleted file mode 100644 index a328f68ee..000000000 --- a/plugins/banner_tweaks/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ - -from plugin import BannerTweaksPlugin diff --git a/plugins/banner_tweaks/config_dialog.ui b/plugins/banner_tweaks/config_dialog.ui deleted file mode 100644 index 1994c1c9d..000000000 --- a/plugins/banner_tweaks/config_dialog.ui +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - diff --git a/plugins/banner_tweaks/manifest.ini b/plugins/banner_tweaks/manifest.ini deleted file mode 100644 index 4cc3652f7..000000000 --- a/plugins/banner_tweaks/manifest.ini +++ /dev/null @@ -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 -homepage = http://blog.bilinski.it - diff --git a/plugins/banner_tweaks/plugin.py b/plugins/banner_tweaks/plugin.py deleted file mode 100644 index 6439dd51b..000000000 --- a/plugins/banner_tweaks/plugin.py +++ /dev/null @@ -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 . -## - -''' -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 -:since: 30 July 2008 -:copyright: Copyright (2008) Mateusz Biliński -: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 = '%s%s %s' % \ - (font_attrs, name, font_attrs_small, acct_info, chatstate) - else: - # weight="heavy" size="x-large" - label_text = '%s%s' % \ - (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() diff --git a/plugins/google_translation/__init__.py b/plugins/google_translation/__init__.py deleted file mode 100644 index dc2c3bc36..000000000 --- a/plugins/google_translation/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from plugin import GoogleTranslationPlugin diff --git a/plugins/google_translation/manifest.ini b/plugins/google_translation/manifest.ini deleted file mode 100644 index 2ad728b7d..000000000 --- a/plugins/google_translation/manifest.ini +++ /dev/null @@ -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 -homepage = http://blog.bilinski.it - diff --git a/plugins/google_translation/plugin.py b/plugins/google_translation/plugin.py deleted file mode 100644 index 1aa5ed111..000000000 --- a/plugins/google_translation/plugin.py +++ /dev/null @@ -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 . -## -''' -Google Translation plugin. - -Translates (currently only incoming) messages using Google Translate. - -:note: consider this as proof-of-concept -:author: Mateusz Biliński -:since: 25th August 2008 -:copyright: Copyright (2008) Mateusz Biliński -: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) diff --git a/plugins/length_notifier/__init__.py b/plugins/length_notifier/__init__.py deleted file mode 100644 index 67c8c614a..000000000 --- a/plugins/length_notifier/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ - -from length_notifier import LengthNotifierPlugin diff --git a/plugins/length_notifier/config_dialog.ui b/plugins/length_notifier/config_dialog.ui deleted file mode 100644 index f06bfe115..000000000 --- a/plugins/length_notifier/config_dialog.ui +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - True - 9 - 3 - 2 - 7 - 5 - - - True - Message length at which notification is invoked. - 0 - Message length: - - - GTK_FILL - GTK_FILL - - - - - True - Background color of text entry field in chat window when notification is invoked. - 0 - Notification color: - - - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - 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. - 0 - JabberIDs to include: - - - 2 - 3 - GTK_FILL - GTK_FILL - - - - - True - True - 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. - - - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - True - - - True - True - True - Background color of text entry field in chat window when notification is invoked. - 0 - Pick a color for notification - #000000000000 - - - - False - False - 0 - - - - - True - - - - - - 1 - - - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - - True - - - True - True - Message length at which notification is invoked. - 6 - True - True - - - - False - False - 0 - - - - - True - - - - - - 1 - - - - - 1 - 2 - GTK_FILL - GTK_FILL - - - - - - diff --git a/plugins/length_notifier/length_notifier.py b/plugins/length_notifier/length_notifier.py deleted file mode 100644 index 3c9e9bccc..000000000 --- a/plugins/length_notifier/length_notifier.py +++ /dev/null @@ -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 . -## - -''' -Message length notifier plugin. - -:author: Mateusz Biliński -:since: 1st June 2008 -:copyright: Copyright (2008) Mateusz Biliński -: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 diff --git a/plugins/length_notifier/manifest.ini b/plugins/length_notifier/manifest.ini deleted file mode 100644 index d50feaa8d..000000000 --- a/plugins/length_notifier/manifest.ini +++ /dev/null @@ -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 -homepage = http://blog.bilinski.it - - diff --git a/plugins/plugin_installer/__init__.py b/plugins/plugin_installer/__init__.py deleted file mode 100644 index a272f29ca..000000000 --- a/plugins/plugin_installer/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from plugin_installer import PluginInstaller diff --git a/plugins/plugin_installer/config_dialog.ui b/plugins/plugin_installer/config_dialog.ui deleted file mode 100644 index e6376335c..000000000 --- a/plugins/plugin_installer/config_dialog.ui +++ /dev/null @@ -1,294 +0,0 @@ - - - - - - Plug-in decription should be displayed here. This text will be erased during PluginsWindow initialization. - - - - - 600 - 350 - True - True - 340 - True - - - True - vertical - - - True - True - 6 - never - automatic - - - True - True - 1 - - - - - 0 - - - - - end - - - False - 1 - - - - - False - False - - - - - True - 5 - vertical - 6 - - - True - 0 - <empty> - True - end - - - False - 0 - - - - - True - 6 - - - True - Authors: - - - False - 0 - - - - - True - 0 - 6 - <empty> - True - end - - - 1 - - - - - False - 1 - - - - - True - - - True - Homepage: - - - False - 0 - - - - - button - True - True - True - none - False - 0 - - - 1 - - - - - False - 2 - - - - - True - vertical - - - True - - - True - Description: - - - False - 0 - - - - - True - - - - - - 1 - - - - - False - 0 - - - - - True - True - 6 - word - 6 - 6 - 1 - - - 1 - - - - - 3 - - - - - True - - - - - - True - end - - - True - False - False - True - - - - True - - - True - gtk-refresh - - - 0 - - - - - True - 0 - Install/Upgrade - - - 1 - - - - - - - False - False - 0 - - - - - False - False - end - 1 - - - - - False - False - 4 - - - - - True - True - - - - - - - - - True - - - True - 0 - Ftp server: - - - False - False - 0 - - - - - True - True - - - - 1 - - - - - - diff --git a/plugins/plugin_installer/manifest.ini b/plugins/plugin_installer/manifest.ini deleted file mode 100644 index 7a1508150..000000000 --- a/plugins/plugin_installer/manifest.ini +++ /dev/null @@ -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 - Yann Leboulanger -homepage: http://www.gajim.org/ diff --git a/plugins/plugin_installer/plugin_installer.py b/plugins/plugin_installer/plugin_installer.py deleted file mode 100644 index 1fe131477..000000000 --- a/plugins/plugin_installer/plugin_installer.py +++ /dev/null @@ -1,532 +0,0 @@ -# -*- coding: utf-8 -*- -# -## plugins/plugin_installer/plugin_installer.py -## -## Copyright (C) 2010-2011 Denis Fomin -## Copyright (C) 2011 Yann Leboulanger -## -## 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 . -## -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() diff --git a/plugins/triggers/__init__.py b/plugins/triggers/__init__.py deleted file mode 100644 index 4f8d9f87b..000000000 --- a/plugins/triggers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from triggers import Triggers diff --git a/plugins/triggers/config_dialog.ui b/plugins/triggers/config_dialog.ui deleted file mode 100644 index ddc10c0a2..000000000 --- a/plugins/triggers/config_dialog.ui +++ /dev/null @@ -1,910 +0,0 @@ - - - - - - - - - - - - contact(s) - - - group(s) - - - everybody - - - - - - - - - - - Receive a Message - - - Contact Connects - - - Contact Disconnects - - - Contact Changes Status - - - - - 6 - Advanced Notifications Control - Advanced Notifications Control - False - True - - - True - 12 - vertical - 12 - - - True - vertical - 5 - - - 90 - True - True - never - automatic - in - - - True - True - - - - - - False - False - 0 - - - - - True - 1 - 212 - - - True - - - True - 10 - - - gtk-new - True - True - True - False - True - - - - False - False - 0 - - - - - gtk-go-up - True - True - True - False - True - - - - False - False - 1 - - - - - gtk-go-down - True - True - True - False - True - - - - False - False - 2 - - - - - gtk-delete - True - True - True - False - True - - - - False - False - 3 - - - - - False - 0 - - - - - - - False - False - 1 - - - - - 0 - - - - - True - vertical - 5 - - - True - <b>Conditions</b> - True - - - False - False - 0 - - - - - True - vertical - 5 - - - True - 5 - - - True - When - True - - - False - False - 0 - - - - - True - liststore2 - - - - - 0 - - - - - False - False - 1 - - - - - 0 - - - - - True - 5 - - - True - for - True - - - False - False - 0 - - - - - True - liststore1 - - - - - 0 - - - - - False - 1 - - - - - True - True - True - comma separated list - - - - 2 - - - - - 1 - - - - - True - - - True - when I'm in - True - - - False - False - 0 - - - - - True - 3 - - - All statuses - True - True - False - True - True - - - - False - False - 0 - - - - - One or more special statuses... - True - True - False - True - True - all_status_rb - - - False - False - 1 - - - - - Online / Free For Chat - True - True - False - True - True - True - - - - False - False - 2 - - - - - Away - True - True - False - True - True - True - - - - False - False - 3 - - - - - Not Available - True - True - False - True - True - True - - - - False - False - 4 - - - - - Busy - True - True - False - True - True - True - - - - False - False - 5 - - - - - Invisible - True - True - False - True - True - True - - - - False - False - 6 - - - - - 1 - - - - - 2 - - - - - True - - - True - and I - True - - - False - False - 0 - - - - - Have - True - True - False - True - True - - - - False - False - 1 - - - - - Don't have - True - True - False - True - True - - - - False - False - 2 - - - - - True - a window/tab opened with that contact - True - - - False - False - 3 - - - - - 3 - - - - - 1 - - - - - True - <b>Actions</b> - True - - - False - False - 2 - - - - - True - 0 - none - - - True - 6 - - - _Inform me with a popup window - True - True - False - True - True - - - - False - False - 0 - - - - - _Disable existing popup window - True - True - False - True - True - - - - False - False - 1 - - - - - - - True - 0 - none - - - True - 6 - 12 - - - True - vertical - 6 - - - True - 6 - True - - - Play a sound - True - True - False - True - True - - - - 0 - - - - - True - False - 6 - - - True - True - - - - 0 - - - - - ... - True - True - False - True - - - - False - False - 1 - - - - - True - True - False - - - - True - gtk-media-play - - - - - False - False - 2 - - - - - 1 - - - - - False - False - 0 - - - - - _Disable existing sound for this event - True - True - False - True - True - - - - False - False - 1 - - - - - - - - - True - <b>Sounds</b> - True - - - - - - - 3 - - - - - True - 6 - - - _Open chat window with user - True - True - False - True - True - - - - False - False - 0 - - - - - _Disable auto opening chat window - True - True - False - True - True - - - - False - False - 1 - - - - - 4 - - - - - True - True - True - - - True - vertical - 5 - - - True - 6 - - - Launch a command - True - True - False - True - True - - - - False - 0 - - - - - True - False - True - - - - 1 - - - - - False - False - 0 - - - - - True - 6 - - - _Show event in notification area - True - True - False - True - True - - - - False - False - 0 - - - - - _Disable showing event in notification area - True - True - False - True - True - - - - False - False - 1 - - - - - 1 - - - - - True - 6 - - - _Show event in roster - True - True - False - True - True - - - - False - False - 0 - - - - - _Disable showing event in roster - True - True - False - True - True - - - - False - False - 1 - - - - - 2 - - - - - _Activate window manager's UrgencyHint to make chat window in taskbar flash - True - False - True - True - True - - - - False - False - 3 - - - - - _Deactivate window manager's UrgencyHint - True - False - True - True - True - - - - False - False - 4 - - - - - - - True - Advanced Actions - - - - - 5 - - - - - 1 - - - - - - diff --git a/plugins/triggers/manifest.ini b/plugins/triggers/manifest.ini deleted file mode 100644 index 173bd102d..000000000 --- a/plugins/triggers/manifest.ini +++ /dev/null @@ -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 -homepage: http://trac.gajim.org/wiki/ diff --git a/plugins/triggers/triggers.py b/plugins/triggers/triggers.py deleted file mode 100644 index 1cfdd9769..000000000 --- a/plugins/triggers/triggers.py +++ /dev/null @@ -1,677 +0,0 @@ -# -*- coding: utf-8 -*- -# -## plugins/triggers/triggers.py -## -## Copyright (C) 2011 Yann Leboulanger -## -## 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 . -## - -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] diff --git a/plugins/whiteboard/__init__.py b/plugins/whiteboard/__init__.py deleted file mode 100644 index 802d00cdf..000000000 --- a/plugins/whiteboard/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from plugin import WhiteboardPlugin diff --git a/plugins/whiteboard/brush_tool.png b/plugins/whiteboard/brush_tool.png deleted file mode 100644 index 266c321718d6e7a72245367223e7892de1fc9faa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 806 zcmV+>1KIqEP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L00^)E00^)FDVDUh00007bV*G`2igk( z4Hhd1;T%~200O2-L_t(Y$F-KfZxUe`$G^{U?_I4xfS5o?3LYfYeM(V;qVt}L#&=ktAgS*#3K(BWPhm|*Za>+gS z+~<9s=Y76;iF1x+tgSmMa9)uhIe@oc+tc^rvS6}E*j!^++ivQ7!ZKizgOIo-8(_9G zBqCcd&N)PZ@Zg4w==(EMKh5f)U69?)SY~JivfA9djRhaRw(g->Fy|bzDzW~22d_ST zGxgov`~XQ^H3wdV%>x0z%eyO(F+lp+ZBzgE&oAule)4{nZLQ0AbAQ!#4>{-NJ)F_2 zS?SWnc(e8p=UnIbN7(eUMxy~mQKrB^AfO)`n{#Ma|9HROr*5|kMNy_;%M34>9RT3t zmmgHFHsIg`IK?5ZU%QGY@z`P+F0y_$6jVU90N@Jlub79BF$2dGZL@t*@KA+bGr&1~FQ);39H2$&aYBrlxEEdsfwEzGc8yh;m z$K%l-nt38U&YCV-CX=B~r$hC69RT2TIuVb@b+Ls)0dBWje`vztFrv{Y_V)H{YbyXS z#-M2$dc7WEv6#WXySocTQQ&g9KtvFTXj(!u*rdc(CP@-pE*Bz^h#_u&e;=YKVl*1T z>-8d$Nc>iKhrHq)$07*qoM6N<$g0%!=)&Kwi diff --git a/plugins/whiteboard/line_tool.png b/plugins/whiteboard/line_tool.png deleted file mode 100644 index 151f584849108687627deacb1ecff87e29008c92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1054 zcmV+(1mXLMP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L00^)E00^)FDVDUh00007bV*G`2igk( z4H7GCjoDoQ00W*$L_t(Y$F!13>!|Fb)r?5?YcyO;zbh=&#;IoK7gx8lKL z4nlLyzf;v1BuEmW7(xx`!9$ONLNp+F2o(x7np1-(3u-`7YghAc8?#Gxva>VuJj9Jz zXY;3R^58MU@OYnjzu)h@86lJ&)4j)e%Jb&Rl z9qp}5y!KoK%f31#PBo+n8=lBC$Ie~8E{q^{dN~|^!NR1v2k43OgKL1j@l==@aNZ*0C`^1+IkZ3kp6U96&*tVH4IIi_ByEMO zH{A$UEEdJ;>M8(22wuK;k>%UBA08aaTR5g*=2jH*eP67ttx>5|@H`LC^Ds@5OeXVi z;85Pe)&;YRyAkZCKeoh1smhHz+x%K6qRJ|cR^zEm9NkVlc+p&)dV^%=IX?I{ip?$k zE`I!ThkM0c?iM`?TUCCzSLW88b>_bL8COj*I5>E6aDILsCzWMp?z;%~=IEFRft_8S zt(_YG-v*_fDzSKia=Co+kQV;B%j;L(ieQ5mOc4qd{ssTmJi@SXSj`U*s*a*4Cj)0c zoMH6J+Y#*Du>mnLbDgpA2|6FMsI_V&ZI!Op6t$|!b~(vbX@_45MNC7*w(aA=>FH_4 z$HybswQEyibaWB`OM{-S1fJ*N`#!t7I<9MwwBX5Bm8aV*Y}@AhMdNs|88$Wb9|oZ7 zdVRAK1Odfjk&TTFVzC&OW$lkKjAK9{L{n^Xad0ZWAx~?OoWOH*90LO8VQWAzCVHo1NE|z7{($dlhc6WD^ z%jF&jtAq849mgREf`%T3A)e=vNF)$Kpa>xlVsE~sr6t)sx_e`qb;B^IR;$G0abmF; zwryis7N%)p7zUcA;rl+RREoa7J_LZOssuq0!E(8rC=?2Z7S+he2pt_A#N%=5zbaD7 z$bCJ;g+gI3q|LBwHhUxnkWQ!R@9*Ew*KvJ$c^Lu7CV3IHTJ6{qXkcJ~&dyE%!Z2)D zUH$WP@VdUf-V_ukJ -homepage = www.gajim.org diff --git a/plugins/whiteboard/oval_tool.png b/plugins/whiteboard/oval_tool.png deleted file mode 100644 index efd6f0ca18d8fca110d7290a2f71b0bd17ff40a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 989 zcmV<310wv1P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L00^)E00^)FDVDUh00007bV*G`2igk( z4HY&($|FMn00Uh~L_t(Y$F-JEXd`75z<+NtlSy|IHZ4MHP%8A~VJ+QTS6X_gprFSd zgcWbydaK%9uqe$Iu`a6+4<0J7mJOCEhVhh`oDMxyOL^3v(dCo+R4m(9=0i+ z%uJgo{opb1-Z$^}-uu4yW<)8)S*)qcG2&fOq7hj5a_2~|pB2p1g)2h_L+?NE*tD~N zMI*w*Q>G8rc7`I-R^63SqHDr)PnwK;_)m}>Iq3}B1qHnt1PjeD45XBjHutOJ;M5E0 z@G}g8DWxO~1~B~c2QvHdcR_Y_@ybU;opZs!R@iAEfLET5VRFDbFI{S8IIlio|Cl#-U=?SY16Y#0VgDYmz_0f@)rjE#+XHm|L%$@22D7dS95 zz{tpm5u~;Ou zRH;;o&CSh@z=c9VT-PN`^7(u)&@c=djmD{f$;nCj`}+ZKU63~mcKX}jjJ(Z%RsjA5a^oXOoDVmr00000 LNkvXXu0mjf&koYC diff --git a/plugins/whiteboard/plugin.py b/plugins/whiteboard/plugin.py deleted file mode 100644 index 9132e9a88..000000000 --- a/plugins/whiteboard/plugin.py +++ /dev/null @@ -1,483 +0,0 @@ -## plugins/whiteboard/plugin.py -## -## Copyright (C) 2009 Jeff Ling -## Copyright (C) 2010 Yann Leboulanger -## -## 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 . -## - -''' -Whiteboard plugin. - -:author: Yann Leboulanger -:since: 1st November 2010 -:copyright: Copyright (2010) Yann Leboulanger -: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 diff --git a/plugins/whiteboard/whiteboard.png b/plugins/whiteboard/whiteboard.png deleted file mode 100644 index 13318e3a7e87e7e97fab65a2d009900f86c02d66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1550 zcmV+p2J!icP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L00^)E00^)FDVDUh00007bV*G`2igiB z6)QX7Ex%U)00oXoL_t(Y$F-JgY!p=#$Ny($?mYIr3d;vXjHo0& z(26FaCX!H)7y&Wr2Wyl-NJK@Eh<+g^3I-oh6Q6--1cVwZk0`dj*veMicDL;=J3BkG z_g+7+OQ=ueG08tOx%b@ryXTzAxrd~bI2%uA!ZZSw{;sYfzUq_Q<<)WJEWtEGqYv-8 zlwBiuIog>peP<2WnJ`IL>79;A#`G_KF6DEMSoP8_$3AO7O`+GWzu1^HWJ~?~borYN zJDpcfoToF9!}{4z`(t7KIg4j+44O(z1y^!~QYkOPA92AJ^uD)z=!^m<&U?Ol>ai!LEMd+a7gAS|+N-Ml8ZK)ckJ{!a zn8q-e9YONf0r}>^OtQr;51lq}QNo-R3n(w%G1;trD|ZCT9^C+4*Z*ur7qz_I{Kl3o zH2|`w4D3vpQ^J<=>U~#O^&cJbVa|%DplKRB&qF?+hwHk>XhgVb#*BvUJ$qgUaNQ{Z zI}_%FfI)BHpYV;_-S6RBy8&~eF?gN_P1E2w4s6?o>$-4V7lvUV7K=5XQb;=!X0yfU zgC)~_=k}yL>`8iHje+8jtyuP85h1^h4-bsumsAF-EFqwI7$5V4S;5f%{!3RrvFzS( z^Tgc?uC$uJ`<-KVzXuTVuwp;-4RRbDDBy6$L4U?YYPf{&4(GA`KpI;<{}Dws99p<= zq3@)DTeogi1Ceo^Z8t26e;HpOyMI&Qmf;E^w<(eH`UHT2Q^s(?gT&ZEk}H(p^9NZz zpKm&8VCuL2br)WE@#ML;-l3j5X(reuw+JC+kL}6Z1*uU~fFlJLLW~7^We!rArYK6& zN#$_ux;3-Mk87DX^~z~RPfrgbo;ZxDwU4ja zm<9k-gK}w05U%T@TrR_L7>Y$5VH0Qwskordgk@RS|Cv7FzdWQs(0Z$5?!5Dp$t3pe z+v^-HqjS2tx{lv(j4>Smq?F*CBa_KsaBvVlpAV*KR&Ai`dh5Re*R5N-@}`?_ zojN=`j2%0_7%jQQ_lGmNd%C*1#EEvMX|gJ?x3?F$Tn+$GEEb`vDk6~xIOhn5!;!xL z+uPe&eSQ5Se!su1uDWa*QCcQD47E~UiK(6MqE2m~Odgb)Hk z2o#G&n5K!EnwsDkU~6k@sIjr})261TDQ#_S*tTsO)~|mdHECkoiU%KDzWJXb!5CxJ zjRS!IIOk&|gn;Y12nK_GA|i!|7!d^lyc3Vdr=-(qY~Q{eg+c-Q_Z|3p>C%TjC!z)- zswbj25k-lphKPJb#B^O}rBVrgzaKuI50+)YG))+W0bSRjX&TDqG9rh-|Ddk_jDiqtWx`&!4YW^YtI1 z?(Xhd4ZvOix7oHWY}@9BVF)3_VbAl9RyVGAC;+IHyej~-bUOX}rcIj|01`q#N?GlZ zWaWC20A%HjbUN*fX`+aTPAKa1Cty_y00V##jYj=-b#-yhxyCuyq?FY)Qc)D4C`wt= zG> -## Copyright (C) 2010 Yann Leboulanger -## -## 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 . -## - -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='') - 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) diff --git a/plugins/whiteboard/whiteboard_widget.ui b/plugins/whiteboard/whiteboard_widget.ui deleted file mode 100644 index fddd2cb75..000000000 --- a/plugins/whiteboard/whiteboard_widget.ui +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - True - 3 - 6 - - - - - - True - 6 - vertical - 6 - - - True - True - True - Brush Tool: Draw freehand lines - - - - True - brush_tool - - - - - False - False - 0 - - - - - True - True - True - Oval Tool: Draw circles and ellipses - - - - True - oval_tool - - - - - False - False - 1 - - - - - True - True - True - Line Tool: Draw straight lines - - - - True - line_tool - - - - - False - False - 2 - - - - - True - True - True - Delete Tool: Remove individual figures - - - - True - gtk-delete - - - - - False - False - 3 - - - - - True - True - True - Clear Canvas: Cleanup canvas - - - - True - gtk-clear - - - - - False - False - 4 - - - - - True - True - True - Export Image: Save image to svg file - - - - True - gtk-save-as - - - - - False - False - 5 - - - - - 68 - True - True - Line width - vertical - adjustment1 - True - 0 - bottom - - - - False - False - 6 - - - - - True - True - True - Foreground color - #000000000000 - - - - False - False - 7 - - - - - False - False - 1 - - - - - True - gtk-delete - - - 2 - 1 - 110 - 1 - 10 - 10 - -