Commit 2c869945 authored by Daniel Brötzmann's avatar Daniel Brötzmann
Browse files

[whiteboard] Remove plugin (may be ported later)

parent 352d0ec1
from .plugin import WhiteboardPlugin
[info]
name: Whiteboard
short_name: whiteboard
version: 1.4.0
description: Shows a whiteboard in chat. python-pygoocanvas is required.
authors = Yann Leboulanger <asterix@lagaule.org>
homepage = https://dev.gajim.org/gajim/gajim-plugins/wikis/WhiteboardPlugin
min_gajim_version: 1.4.0-dev1
max_gajim_version: 1.4.90
# Copyright (C) 2009 Jeff Ling <jeff.ummu AT gmail.com>
# Copyright (C) 2010 Yann Leboulanger <asterix AT lagaule.org>
#
# 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 <http://www.gnu.org/licenses/>.
#
'''
Whiteboard plugin.
:author: Yann Leboulanger <asterix@lagaule.org>
:since: 1st November 2010
:copyright: Copyright (2010) Yann Leboulanger <asterix@lagaule.org>
:license: GPL
'''
from 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 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.gui.dialogs import DialogButton
from gajim.gui.dialogs import ConfirmationDialog
from gajim.plugins import GajimPlugin
from gajim.plugins.gajimplugin import GajimPluginException
from gajim.plugins.helpers import log_calls
from gajim.plugins.helpers import log
from gajim.plugins.plugins_i18n import _
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-disconnected-received': (ged.GUI1,
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_base_update_toolbar': (self.update_button_state,
None),
'update_caps': (self._update_caps, None),
}
self.controls = []
self.sid = None
self.announce_caps = True
@log_calls('WhiteboardPlugin')
def _update_caps(self, _account, features):
if not self.announce_caps:
return
features.append(NS_JINGLE_SXE)
features.append(NS_SXE)
@log_calls('WhiteboardPlugin')
def activate(self):
if not HAS_GOOCANVAS:
raise GajimPluginException('python-pygoocanvas is missing!')
self.announce_caps = True
for con in app.connections.values():
con.get_module('Caps').update_caps()
@log_calls('WhiteboardPlugin')
def deactivate(self):
self.announce_caps = False
for con in app.connections.values():
con.get_module('Caps').update_caps()
@log_calls('WhiteboardPlugin')
def connect_with_chat_control(self, control):
for base in self.controls:
if base.chat_control == control:
self.controls.remove(base)
if control.is_chat:
base = Base(self, control)
self.controls.append(base)
self.update_button_state(control)
@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.enable_action(True)
else:
base.enable_action(False)
@log_calls('WhiteboardPlugin')
def show_request_dialog(self, account, fjid, jid, sid, content_types):
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()
for content in content_types:
session.approve_content('xhtml')
for _jid in (fjid, jid):
ctrl = app.window.get_control(account, _jid)
if ctrl:
break
if not ctrl:
# Create it
app.interface.start_chat_from_jid(account, jid)
ctrl = app.window.get_control(account, jid)
session = session.contents[('initiator', 'xhtml')]
ctrl.draw_whiteboard(session)
def _on_decline():
session = app.connections[account].get_module(
'Jingle').get_jingle_session(fjid, sid)
session.decline_session()
client = app.get_client(account)
contact = client.get_module('Contacts').get_contact(jid)
ConfirmationDialog(
_('Incoming Whiteboard'),
_('Incoming Whiteboard Request'),
_('%(name)s (%(jid)s) wants to start a whiteboard with '
'you.') % {'name': contact.name, 'jid': jid},
[DialogButton.make('Cancel',
text=_('_Decline'),
callback=_on_decline),
DialogButton.make('OK',
text=_('_Accept'),
callback=_on_accept)],
transient_for=app.window).show()
@log_calls('WhiteboardPlugin')
def _nec_jingle_received(self, obj):
if not HAS_GOOCANVAS:
return
content_types = obj.contents.media
if content_types != 'xhtml':
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 = app.window.get_control(account, obj.jid)
if not ctrl:
return
session = app.connections[obj.conn.name].get_module(
'Jingle').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 XMPP Address'),
_('A message from a non-valid XMPP address '
'arrived. It has been ignored.')))
jid = app.get_jid_without_resource(fjid)
ctrl = app.window.get_control(account, jid)
if not ctrl:
return
sxe = obj.stanza.getTag('sxe')
if not sxe:
return
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)
# self.addJingle(newjingle)
# 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:
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.jid
self.add_action()
self.whiteboard = None
self.sid = None
def add_action(self):
action_name = 'toggle-whiteboard-' + self.chat_control.control_id
act = Gio.SimpleAction.new_stateful(
action_name, None, GLib.Variant.new_boolean(False))
act.connect('change-state', self.on_whiteboard_button_toggled)
app.window.add_action(act)
self.chat_control.control_menu.append(
'WhiteBoard', 'win.' + action_name)
def enable_action(self, state):
action_name = 'toggle-whiteboard-' + self.chat_control.control_id
app.window.lookup_action(action_name).set_enabled(state)
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, False, False, 0)
self.whiteboard.hbox.show_all()
self.enable_action(True)
content.control = self
self.sid = content.session.sid
def on_whiteboard_button_toggled(self, action, param):
"""
Popup whiteboard
"""
action.set_state(param)
state = param.get_boolean()
if state:
if not self.whiteboard:
self.start_whiteboard()
else:
self.stop_whiteboard()
def start_whiteboard(self):
conn = app.connections[self.chat_control.account]
jingle = JingleSession(conn, weinitiate=True, jid=self.jid)
self.sid = jingle.sid
conn.get_module('Jingle')._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 = app.connections[self.chat_control.account]
self.sid = None
session = conn.get_module('Jingle').get_jingle_session(self.jid,
media='xhtml')
if session:
session.end_session()
self.enable_action(False)
if reason:
txt = _('Whiteboard stopped: %(reason)s') % {'reason': reason}
self.chat_control.add_info_message(txt)
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):
menu = self.chat_control.control_menu
for i in range(menu.get_n_items()):
label = menu.get_item_attribute_value(i, 'label')
if label.get_string() == 'WhiteBoard':
menu.remove(i)
break
class JingleWhiteboard(JingleContent):
''' Jingle Whiteboard sessions consist of xhtml content'''
def __init__(self, session, transport=None, senders=None):
if not transport:
transport = JingleTransportSXE()
JingleContent.__init__(self, session, transport, senders)
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]
@log_calls('WhiteboardPlugin')
def _EditCB(self, stanza, content, error, action):
new_tags = content.getTags('new')
remove_tags = content.getTags('remove')
if not self.control.whiteboard:
return
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)
@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))
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
@log_calls('WhiteboardPlugin')
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 = 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)
@log_calls('WhiteboardPlugin')
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)
for x in rids:
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?
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,' + 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, node=None):
JingleTransport.__init__(self, TransportType.SOCKS5)
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
# Copyright (C) 2009 Jeff Ling <jeff.ummu AT gmail.com>
# Copyright (C) 2010-2017 Yann Leboulanger <asterix AT lagaule.org>
#
# 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 <http://www.gnu.org/licenses/>.
#
from nbxmpp import Node
from gi.repository import Gtk
from gajim.common import app
from gajim.gui.filechoosers import NativeFileChooserDialog
from gajim.gui.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 ValueError:
HAS_GOOCANVAS = False
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
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.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
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)
self._ui.connect_signals(self)
# Temporary Variables for items
self.item_temp = None
self.item_temp_coords = (0, 0)