diff --git a/whiteboard/plugin.py b/whiteboard/plugin.py index ee77762..20ceb13 100644 --- a/whiteboard/plugin.py +++ b/whiteboard/plugin.py @@ -1,22 +1,20 @@ -## 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 . -## +# Copyright (C) 2009 Jeff Ling +# Copyright (C) 2010 Yann Leboulanger +# +# This file is part of the Whiteboard Plugin. +# +# 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. @@ -27,48 +25,56 @@ Whiteboard plugin. :license: GPL ''' +from urllib.parse import quote from gi.repository import Gio from gi.repository import GLib from nbxmpp import Message from gajim import common -from gajim.common import helpers +from gajim import chat_control + from gajim.common import app +from gajim.common import ged +from gajim.common import helpers +from gajim.common.jingle_session import JingleSession +from gajim.common.jingle_content import JingleContent +from gajim.common.jingle_transport import JingleTransport +from gajim.common.jingle_transport import TransportType + +from gajim.gtk.dialogs import DialogButton +from gajim.gtk.dialogs import NewConfirmationDialog from gajim.plugins import GajimPlugin from gajim.plugins.gajimplugin import GajimPluginException -from gajim.plugins.helpers import log_calls, log +from gajim.plugins.helpers import log_calls +from gajim.plugins.helpers import log from gajim.plugins.plugins_i18n import _ -from gajim import chat_control -from gajim.common import ged -from gajim.common.jingle_session import JingleSession -from gajim.common.jingle_content import JingleContent -from gajim.common.jingle_transport import JingleTransport, TransportType -from gajim.gtk.dialogs import NonModalConfirmationDialog -from .whiteboard_widget import Whiteboard, HAS_GOOCANVAS - +from whiteboard.whiteboard_widget import Whiteboard +from whiteboard.whiteboard_widget import HAS_GOOCANVAS 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.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-connected-received': (ged.GUI1, + self._nec_jingle_connected), 'jingle-disconnected-received': (ged.GUI1, - self._nec_jingle_disconnected), + self._nec_jingle_disconnected), 'raw-message-received': (ged.GUI1, self._nec_raw_message), } self.gui_extension_points = { - 'chat_control' : (self.connect_with_chat_control, - self.disconnect_from_chat_control), + 'chat_control': (self.connect_with_chat_control, + self.disconnect_from_chat_control), 'chat_control_base_update_toolbar': (self.update_button_state, - None), + None), 'update_caps': (self._update_caps, None), } self.controls = [] @@ -119,16 +125,17 @@ class WhiteboardPlugin(GajimPlugin): 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): + if (control.contact.supports(NS_JINGLE_SXE) and + control.contact.supports(NS_SXE)): base.enable_action(True) else: base.enable_action(False) @log_calls('WhiteboardPlugin') def show_request_dialog(self, account, fjid, jid, sid, content_types): - def on_ok(): - session = app.connections[account].get_module('Jingle').get_jingle_session(fjid, sid) + def _on_accept(): + session = app.connections[account].get_module( + 'Jingle').get_jingle_session(fjid, sid) self.sid = session.sid if not session.accepted: session.approve_session() @@ -139,14 +146,15 @@ class WhiteboardPlugin(GajimPlugin): if ctrl: break if not ctrl: - # create it + # Create it app.interface.new_chat_from_jid(account, jid) ctrl = app.interface.msg_win_mgr.get_control(jid, account) session = session.contents[('initiator', 'xhtml')] ctrl.draw_whiteboard(session) - def on_cancel(): - session = app.connections[account].get_module('Jingle').get_jingle_session(fjid, sid) + def _on_decline(): + session = app.connections[account].get_module( + 'Jingle').get_jingle_session(fjid, sid) session.decline_session() contact = app.contacts.get_first_contact_from_jid(account, jid) @@ -154,12 +162,19 @@ class WhiteboardPlugin(GajimPlugin): 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 = NonModalConfirmationDialog(pritext, sectext=sectext, - on_response_ok=on_ok, on_response_cancel=on_cancel) - dialog.popup() + + NewConfirmationDialog( + _('Incoming Whiteboard'), + _('Incoming Whiteboard Request'), + _('%(name)s (%(jid)s) wants to start a whiteboard with ' + 'you.') % {'name': name, 'jid': jid}, + [DialogButton.make('Cancel', + text=_('_Decline'), + callback=_on_decline), + DialogButton.make('OK', + text=_('_Accept'), + callback=_on_accept)], + transient_for=app.app.get_active_window()).show() @log_calls('WhiteboardPlugin') def _nec_jingle_received(self, obj): @@ -168,7 +183,11 @@ class WhiteboardPlugin(GajimPlugin): content_types = obj.contents.media if content_types != 'xhtml': return - self.show_request_dialog(obj.conn.name, obj.fjid, obj.jid, obj.sid, + self.show_request_dialog( + obj.conn.name, + obj.fjid, + obj.jid, + obj.sid, content_types) @log_calls('WhiteboardPlugin') @@ -176,12 +195,12 @@ class WhiteboardPlugin(GajimPlugin): if not HAS_GOOCANVAS: return account = obj.conn.name - ctrl = (app.interface.msg_win_mgr.get_control(obj.fjid, account) - or app.interface.msg_win_mgr.get_control(obj.jid, account)) + ctrl = (app.interface.msg_win_mgr.get_control(obj.fjid, account) or + app.interface.msg_win_mgr.get_control(obj.jid, account)) if not ctrl: return - session = app.connections[obj.conn.name].get_module('Jingle').get_jingle_session(obj.fjid, - obj.sid) + session = app.connections[obj.conn.name].get_module( + 'Jingle').get_jingle_session(obj.fjid, obj.sid) if ('initiator', 'xhtml') not in session.contents: return @@ -193,7 +212,7 @@ class WhiteboardPlugin(GajimPlugin): def _nec_jingle_disconnected(self, obj): for base in self.controls: if base.sid == obj.sid: - base.stop_whiteboard(reason = obj.reason) + base.stop_whiteboard(reason=obj.reason) @log_calls('WhiteboardPlugin') def _nec_raw_message(self, obj): @@ -205,13 +224,13 @@ class WhiteboardPlugin(GajimPlugin): 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.'))) + obj.conn.dispatch('ERROR', (_('Invalid XMPP Address'), + _('A message from a non-valid XMPP address ' + 'arrived. It has been ignored.'))) jid = app.get_jid_without_resource(fjid) - ctrl = (app.interface.msg_win_mgr.get_control(fjid, account) - or app.interface.msg_win_mgr.get_control(jid, account)) + ctrl = (app.interface.msg_win_mgr.get_control(fjid, account) or + app.interface.msg_win_mgr.get_control(jid, account)) if not ctrl: return sxe = obj.stanza.getTag('sxe') @@ -220,11 +239,13 @@ class WhiteboardPlugin(GajimPlugin): sid = sxe.getAttr('session') if (jid, sid) not in obj.conn.get_module('Jingle')._sessions: pass -# newjingle = JingleSession(con=self, weinitiate=False, jid=jid, sid=sid) +# newjingle = JingleSession(con=self, weinitiate=False, jid=jid, +# sid=sid) # self.addJingle(newjingle) - # we already have such session in dispatcher... - session = obj.conn.get_module('Jingle').get_jingle_session(fjid, sid) + # We already have such session in dispatcher + session = obj.conn.get_module('Jingle').get_jingle_session(fjid, + sid) cn = session.contents[('initiator', 'xhtml')] error = obj.stanza.getTag('error') if error: @@ -234,23 +255,23 @@ class WhiteboardPlugin(GajimPlugin): 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') +# 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 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) +# 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 @@ -287,8 +308,8 @@ class Base(object): 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.plugin) + # Set minimum size self.whiteboard.hbox.set_size_request(300, 0) hbox.pack_start(self.whiteboard.hbox, False, False, 0) self.whiteboard.hbox.show_all() @@ -321,7 +342,8 @@ class Base(object): def stop_whiteboard(self, reason=None): conn = app.connections[self.chat_control.account] self.sid = None - session = conn.get_module('Jingle').get_jingle_session(self.jid, media='xhtml') + session = conn.get_module('Jingle').get_jingle_session(self.jid, + media='xhtml') if session: session.end_session() self.enable_action(False) @@ -344,6 +366,7 @@ class Base(object): menu.remove(i) break + class JingleWhiteboard(JingleContent): ''' Jingle Whiteboard sessions consist of xhtml content''' def __init__(self, session, transport=None, senders=None): @@ -351,7 +374,7 @@ class JingleWhiteboard(JingleContent): transport = JingleTransportSXE() JingleContent.__init__(self, session, transport, senders) self.media = 'xhtml' - self.negotiated = True # there is nothing to negotiate + self.negotiated = True # There is nothing to negotiate self.last_rid = 0 self.callbacks['session-accept'] += [self._sessionAcceptCB] self.callbacks['session-terminate'] += [self._stop] @@ -383,11 +406,11 @@ class JingleWhiteboard(JingleContent): @log_calls('WhiteboardPlugin') def _sessionAcceptCB(self, stanza, content, error, action): log.debug('session accepted') - self.session.connection.dispatch('WHITEBOARD_ACCEPTED', - (self.session.peerjid, self.session.sid)) + 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 + # Generates x number of rids and returns in list rids = [] for x in range(x): rids.append(str(self.last_rid)) @@ -396,29 +419,29 @@ class JingleWhiteboard(JingleContent): @log_calls('WhiteboardPlugin') def send_whiteboard_node(self, items, rids): - # takes int rid and dict items and sends it as a node + # Takes int rid and dict items and sends it as a node # sends new item jid = self.session.peerjid sid = self.session.sid message = Message(to=jid) sxe = message.addChild(name='sxe', attrs={'session': sid}, - namespace=NS_SXE) + 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']} + '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} + 'name': attr_name, + 'type': items[x]['type'], + 'chdata': chdata, + 'parent': parent} sxe.addChild(name='new', attrs=attrs) self.session.connection.connection.send(message) @@ -426,24 +449,24 @@ class JingleWhiteboard(JingleContent): def delete_whiteboard_node(self, rids): message = Message(to=self.session.peerjid) sxe = message.addChild(name='sxe', attrs={'session': self.session.sid}, - namespace=NS_SXE) + namespace=NS_SXE) for x in rids: - sxe.addChild(name='remove', attrs = {'target': x}) + sxe.addChild(name='remove', attrs={'target': x}) self.session.connection.connection.send(message) def send_items(self, items, rids): - # receives 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 + # Receives 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) + # Encodes it sendable string + return 'data:text/xml,' + quote(xml) def _fill_content(self, content): content.addChild(NS_JINGLE_XHTML + ' description') @@ -454,11 +477,14 @@ class JingleWhiteboard(JingleContent): 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, node=None): JingleTransport.__init__(self, TransportType.SOCKS5) @@ -469,4 +495,5 @@ class JingleTransportSXE(JingleTransport): transport.setTagData('host', 'TODO') return transport + common.jingle_transport.transports[NS_JINGLE_SXE] = JingleTransportSXE diff --git a/whiteboard/whiteboard_widget.py b/whiteboard/whiteboard_widget.py index 44cb861..b27f861 100644 --- a/whiteboard/whiteboard_widget.py +++ b/whiteboard/whiteboard_widget.py @@ -1,39 +1,38 @@ -## plugins/whiteboard/whiteboard_widget.py -## -## Copyright (C) 2009 Jeff Ling -## Copyright (C) 2010-2017 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 . -## +# Copyright (C) 2009 Jeff Ling +# Copyright (C) 2010-2017 Yann Leboulanger +# +# This file is part of the Whiteboard Plugin. +# +# 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 . +# from nbxmpp import Node from gi.repository import Gtk from gajim.common import app -from gajim.gtk.filechoosers import NativeFileChooserDialog, Filter +from gajim.gtk.filechoosers import NativeFileChooserDialog +from gajim.gtk.filechoosers import Filter +from gajim.plugins.helpers import get_builder from gajim.plugins.plugins_i18n import _ - try: import gi gi.require_version('GooCanvas', '2.0') from gi.repository import GooCanvas HAS_GOOCANVAS = True -except: +except ValueError: HAS_GOOCANVAS = False @@ -42,94 +41,108 @@ class SvgSaveDialog(NativeFileChooserDialog): _title = _('Save File as…') _filters = [Filter(_('All files'), '*', False), Filter(_('SVG files'), '*.svg', True)] + _action = Gtk.FileChooserAction.SAVE + ''' 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, True, True, 0) - self.hbox.reorder_child(self.canevas, 0) - 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) + path = plugin.local_file_path('whiteboard_widget.ui') + self._ui = get_builder(path) + + self.canvas = GooCanvas.Canvas() + self.hbox = self._ui.whiteboard_hbox + self._ui.whiteboard_hbox.pack_start(self.canvas, True, True, 0) + self._ui.whiteboard_hbox.reorder_child(self.canvas, 0) + self.root = self.canvas.get_root_item() + self.tool_buttons = [ + self._ui.brush_button, + self._ui.oval_button, + self._ui.line_button, + self._ui.delete_button + ] + self._ui.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) + self.canvas.connect('button-press-event', self.button_press_event) + self.canvas.connect('button-release-event', self.button_release_event) + self.canvas.connect('motion-notify-event', self.motion_notify_event) + self.canvas.connect('item-created', self.item_created) # Config self.line_width = 2 - xml.get_object('size_scale').set_value(2) - c = self.fg_color_select_button.get_rgba() - self.color = int(c.red*255*256*256*256 + c.green*255*256*256 + \ - c.blue*255*256 + 255) + self._ui.size_scale.set_value(2) + c = self._ui.fg_color_button.get_rgba() + self.color = int(c.red*255*256*256*256 + + c.green*255*256*256 + + c.blue*255*256 + 255) # SVG Storage self.image = SVGObject(self.root, session) - xml.connect_signals(self) + self._ui.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 + # Will be instance of {ID: {type:'element' + # data:[node, goocanvas]}, + # ID2: {}} self.receiving = {} - def on_tool_button_toggled(self, widget): + 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): + def _on_brush_button_toggled(self, widget): if widget.get_active(): self.image.draw_tool = 'brush' - self.on_tool_button_toggled(widget) + self._on_tool_button_toggled(widget) - def on_oval_button_toggled(self, widget): + def _on_oval_button_toggled(self, widget): if widget.get_active(): self.image.draw_tool = 'oval' - self.on_tool_button_toggled(widget) + self._on_tool_button_toggled(widget) - def on_line_button_toggled(self, widget): + def _on_line_button_toggled(self, widget): if widget.get_active(): self.image.draw_tool = 'line' - self.on_tool_button_toggled(widget) + self._on_tool_button_toggled(widget) - def on_delete_button_toggled(self, widget): + def _on_delete_button_toggled(self, widget): if widget.get_active(): self.image.draw_tool = 'delete' - self.on_tool_button_toggled(widget) + self._on_tool_button_toggled(widget) - def on_clear_button_clicked(self, widget): + def _on_clear_button_clicked(self, widget): self.image.clear_canvas() - def on_export_button_clicked(self, widget): - SvgSaveDialog(self.image.export_svg, - transient_for=app.app.get_active_window()) - - def on_fg_color_button_color_set(self, widget): - c = self.fg_color_select_button.get_rgba() - self.color = int(c.red*255*256*256*256 + c.green*255*256*256 + \ + def _on_fg_color_button_color_set(self, widget): + c = self._ui.fg_color_button.get_rgba() + self.color = int( + c.red*255*256*256*256 + + c.green*255*256*256 + c.blue*255*256 + 255) + def _on_size_scale_format_value(self, widget): + self.line_width = int(widget.get_value()) + + def _on_export_button_clicked(self, widget): + SvgSaveDialog(self.image.export_svg, + file_name=_('whiteboard.svg'), + path=app.config.get('last_save_dir'), + transient_for=app.app.get_active_window()) + def item_created(self, canvas, item, model): item.connect('button-press-event', self.item_button_press_events) @@ -137,24 +150,21 @@ class Whiteboard(object): 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.CanvasEllipse(parent=self.root, - center_x=x, - center_y=y, - radius_x=1, - radius_y=1, - stroke_color_rgba=self.color, - fill_color=self.color, - line_width=self.line_width) + self.item_temp = GooCanvas.CanvasEllipse( + parent=self.root, + center_x=x, + center_y=y, + radius_x=1, + radius_y=1, + stroke_color_rgba=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': @@ -166,47 +176,53 @@ class Whiteboard(object): 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.CanvasPath(parent=self.root, - data=self.item_data, line_width=self.line_width, - stroke_color_rgba=self.color) + self.item_temp = GooCanvas.CanvasPath( + parent=self.root, + data=self.item_data, + line_width=self.line_width, + stroke_color_rgba=self.color) elif self.image.draw_tool == 'oval': - self.item_temp = GooCanvas.CanvasEllipse(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_rgba=self.color, - line_width=self.line_width) + self.item_temp = GooCanvas.CanvasEllipse( + 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_rgba=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.CanvasPath(parent=self.root, - data=self.item_data, line_width=self.line_width, - stroke_color_rgba=self.color) + self.item_temp = GooCanvas.CanvasPath( + parent=self.root, + data=self.item_data, + line_width=self.line_width, + stroke_color_rgba=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.CanvasEllipse(parent=self.root, - center_x=x, - center_y=y, - radius_x=1, - radius_y=1, - stroke_color_rgba=self.color, - fill_color=self.color, - line_width=self.line_width) + GooCanvas.CanvasEllipse( + parent=self.root, + center_x=x, + center_y=y, + radius_x=1, + radius_y=1, + stroke_color_rgba=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': @@ -220,14 +236,15 @@ class Whiteboard(object): 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.CanvasEllipse(parent=self.root, - center_x=x, - center_y=y, - radius_x=1, - radius_y=1, - stroke_color_rgba=self.color, - fill_color_rgba=self.color, - line_width=self.line_width) + GooCanvas.CanvasEllipse( + parent=self.root, + center_x=x, + center_y=y, + radius_x=1, + radius_y=1, + stroke_color_rgba=self.color, + fill_color_rgba=self.color, + line_width=self.line_width) self.image.add_path(self.item_data, self.line_width, self.color) if self.image.draw_tool == 'delete': @@ -241,18 +258,20 @@ class Whiteboard(object): def recieve_element(self, element): node = self.image.g.addChild(name=element.getAttr('name')) self.image.g.addChild(node=node) - self.receiving[element.getAttr('rid')] = {'type':'element', - 'data':[node], - 'children':[]} + self.receiving[element.getAttr('rid')] = {'type': 'element', + 'data': [node], + 'children': []} def recieve_attr(self, element): node = self.receiving[element.getAttr('parent')]['data'][0] node.setAttr(element.getAttr('name'), element.getAttr('chdata')) - self.receiving[element.getAttr('rid')] = {'type':'attr', - 'data':element.getAttr('name'), - 'parent':node} - self.receiving[element.getAttr('parent')]['children'].append(element.getAttr('rid')) + self.receiving[element.getAttr('rid')] = {'type': 'attr', + 'data': element.getAttr( + 'name'), + 'parent': node} + self.receiving[element.getAttr('parent')]['children'].append( + element.getAttr('rid')) def apply_new(self): for x in self.receiving.keys(): @@ -264,34 +283,40 @@ class Whiteboard(object): 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 + # Will be instance of {ID: {type:'element', + # data:[node, goocanvas]}, + # ID2: {}} self.items = {} self.root = root self.draw_tool = 'brush' - # sxe session + # SXE session self.session = session - # initialize svg document + # 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 + # 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.CanvasPath(parent=self.root, data=data, - line_width=line_width, stroke_color_rgba=color) - goocanvas_obj.connect('button-press-event', self.item_button_press_events) + ''' + Adds the path to the items listing, both minidom node and goocanvas + object in a tuple + ''' + goocanvas_obj = GooCanvas.CanvasPath( + parent=self.root, + data=data, + line_width=line_width, + stroke_color_rgba=color) + goocanvas_obj.connect('button-press-event', + self.item_button_press_events) node = self.g.addChild(name='path') node.setAttr('d', data) @@ -300,16 +325,26 @@ class SVGObject(): 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.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 ''' + ''' + 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] @@ -317,35 +352,41 @@ class SVGObject(): self.items[x] = new_items[x] if node.getName() == 'path': - goocanvas_obj = GooCanvas.CanvasPath(parent=self.root, - data=node.getAttr('d'), - line_width=int(node.getAttr('stroke-width')), - stroke_color_rgba=int(node.getAttr('stroke'))) + goocanvas_obj = GooCanvas.CanvasPath( + parent=self.root, + data=node.getAttr('d'), + line_width=int(node.getAttr('stroke-width')), + stroke_color_rgba=int(node.getAttr('stroke'))) if node.getName() == 'ellipse': - goocanvas_obj = GooCanvas.CanvasEllipse(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_rgba=int(node.getAttr('stroke')), - line_width=float(node.getAttr('stroke-width'))) + goocanvas_obj = GooCanvas.CanvasEllipse( + 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_rgba=int(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) + 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.CanvasEllipse(parent=self.root, - center_x=cx, - center_y=cy, - radius_x=rx, - radius_y=ry, - stroke_color_rgba=stroke_color, - line_width=line_width) - goocanvas_obj.connect('button-press-event', self.item_button_press_events) + ''' + Adds the ellipse to the items listing, both minidom node and goocanvas + object in a tuple + ''' + goocanvas_obj = GooCanvas.CanvasEllipse( + parent=self.root, + center_x=cx, + center_y=cy, + radius_x=rx, + radius_y=ry, + stroke_color_rgba=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)) @@ -357,13 +398,27 @@ class SVGObject(): 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.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) diff --git a/whiteboard/whiteboard_widget.ui b/whiteboard/whiteboard_widget.ui index 9858771..28dbc3b 100644 --- a/whiteboard/whiteboard_widget.ui +++ b/whiteboard/whiteboard_widget.ui @@ -1,5 +1,5 @@ - + @@ -19,7 +19,7 @@ - + True False 6 @@ -30,10 +30,10 @@ True True True - Brush Tool: Draw freehand lines - + Draw freehand lines + - + True False brush_tool.png @@ -51,10 +51,10 @@ True True True - Oval Tool: Draw circles and ellipses - + Draw circles and ellipses + - + True False oval_tool.png @@ -72,10 +72,10 @@ True True True - Line Tool: Draw straight lines - + Draw straight lines + - + True False line_tool.png @@ -88,69 +88,6 @@ 2 - - - True - True - True - Delete Tool: Remove individual figures - - - - True - False - gtk-delete - - - - - False - False - 3 - - - - - True - True - True - Clear Canvas: Cleanup canvas - - - - True - False - gtk-clear - - - - - False - False - 4 - - - - - True - True - True - Export Image: Save image to svg file - - - - True - False - gtk-save-as - - - - - False - False - 5 - - 68 @@ -162,12 +99,12 @@ True 0 bottom - + False False - 6 + 3 @@ -177,7 +114,71 @@ True Foreground color rgb(0,0,0) - + + + + False + False + 4 + + + + + True + True + True + Remove individual figures + + + + True + False + edit-delete + + + + + False + False + 5 + + + + + True + True + True + Cleanup canvas + + + + True + False + edit-clear + + + + + False + False + 6 + + + + + True + True + True + Export image to SVG file + 6 + + + + True + False + document-save-as + + False