# HG changeset patch # User Goffi # Date 1577545794 -3600 # Node ID b0c9017a1db72ade6facb8a4995ef2dd0491c827 # Parent 597cc207c8e72a7ec09730f582e71fee9803a64d core: added forgotten share_widget module diff -r 597cc207c8e7 -r b0c9017a1db7 cagou/core/share_widget.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cagou/core/share_widget.py Sat Dec 28 16:09:54 2019 +0100 @@ -0,0 +1,185 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Cagou: desktop/mobile frontend for Salut à Toi XMPP client +# Copyright (C) 2016-2019 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program 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 Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +from pathlib import Path +from sat.core import log as logging +from sat.core.i18n import _ +from sat.tools.common import data_format +from sat_frontends.tools import jid +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.behaviors import ButtonBehavior +from kivy.properties import StringProperty, DictProperty, ObjectProperty +from kivy.core.window import Window +from kivy.metrics import dp +from .common import ContactItem +from .constants import Const as C +from cagou import G + + +log = logging.getLogger(__name__) + + +PLUGIN_INFO = { + "name": _("share"), + "main": "Share", + "description": _("share a file"), + "icon_symbol": "share", +} + +class ShareContactItem(ButtonBehavior, ContactItem): + + def filterDataCb(self, data): + G.host.doAction('chat', jid.JID(self.jid), [self.profile]) + chat_wid = G.host.selected_widget + + if self.share_wid.type == 'text': + text = self.share_wid.data['text'] + chat_wid.message_input.text += text + else: + path = self.share_wid.data['path'] + chat_wid.transferFile(path, cleaning_cb=data.get('cleaning_cb')) + self.share_wid.close() + + def filterDataEb(self, failure_): + G.host.addNote( + _("file filter error"), + _("Can't apply filter to file: {msg}").format(msg=failure_), + level=C.XMLUI_DATA_LVL_ERROR) + + def on_press(self): + self.share_wid = G.host.getAncestorWidget(self, ShareWidget) + self.share_wid.getFilteredData(self.filterDataCb, self.filterDataEb) + + +class TextPreview(BoxLayout): + """Widget previewing shared text""" + text = StringProperty() + + +class ImagePreview(BoxLayout): + """Widget previewing shared image""" + path = StringProperty() + reduce_layout = ObjectProperty() + reduce_checkbox = ObjectProperty() + + def _checkImageCb(self, report_raw): + self.report = data_format.deserialise(report_raw) + if self.report['too_large']: + self.reduce_layout.opacity = 1 + self.reduce_layout.height = self.reduce_layout.minimum_height + dp(10) + self.reduce_layout.padding = [0, dp(5)] + + def _checkImageEb(self, failure_): + log.error(f"Can't check image: {failure_}") + + def on_path(self, wid, path): + G.host.bridge.imageCheck( + path, callback=self._checkImageCb, errback=self._checkImageEb) + + def resizeImage(self, data, callback, errback): + + def imageResizeCb(new_path): + new_path = Path(new_path) + log.debug(f"image {data['path']} resized at {new_path}") + data['path'] = new_path + data['cleaning_cb'] = lambda: new_path.unlink() + callback(data) + + path = data['path'] + width, height = self.report['recommended_size'] + G.host.bridge.imageResize( + path, width, height, + callback=imageResizeCb, + errback=errback + ) + + def getFilter(self): + if self.report['too_large'] and self.reduce_checkbox.active: + return self.resizeImage + else: + return lambda data, callback, errback: callback(data) + + +class GenericPreview(BoxLayout): + """Widget previewing shared image""" + path = StringProperty() + + +class ShareWidget(BoxLayout): + media_type = StringProperty() + data = DictProperty() + preview_box = ObjectProperty() + layout = ObjectProperty() + + def __init__(self, **kwargs): + super().__init__(**kwargs) + G.host.addListener("contactsFilled", self.onContactsFilled) + Window.bind(on_keyboard=self.key_input) + + def on_kv_post(self, wid): + self.type, self.subtype = self.media_type.split('/') + if self.type == 'text': + self.preview_box.add_widget(TextPreview(text=self.data['text'])) + elif self.type == 'image': + self.preview_box.add_widget(ImagePreview(path=self.data['path'])) + else: + self.preview_box.add_widget(GenericPreview(path=self.data['path'])) + self.addRosterContacts() + + def close(self): + G.host.closeUI() + + def on_parent(self, wid, parent): + if parent is None: + log.debug("removing contactsFilled listener") + G.host.removeListener("contactsFilled", self.onContactsFilled) + + + def addRosterContacts(self): + log.debug("starting addRosterContacts") + self.layout.clear_widgets() + for profile in G.host.profiles: + contact_list = G.host.contact_lists[profile] + for entity_jid in sorted(contact_list.roster): + item = ShareContactItem( + jid=entity_jid, + data=contact_list.getItem(entity_jid), + profile=profile, + ) + self.layout.add_widget(item) + + def onContactsFilled(self, profile): + log.debug("onContactsFilled event received") + self.addRosterContacts() + + def getFilteredData(self, callback, errback): + """Apply filter if suitable, and call callback with with modified data""" + try: + getFilter = self.preview_box.children[0].getFilter + except AttributeError: + callback(self.data) + else: + filter_ = getFilter() + filter_(self.data, callback=callback, errback=errback) + + def key_input(self, window, key, scancode, codepoint, modifier): + if key == 27: + self.close() + return True