comparison cagou/core/menu.py @ 126:cd99f70ea592

global file reorganisation: - follow common convention by puttin cagou in "cagou" instead of "src/cagou" - added VERSION in cagou with current version - updated dates - moved main executable in /bin - moved buildozer files in root directory - temporary moved platform to assets/platform
author Goffi <goffi@goffi.org>
date Thu, 05 Apr 2018 17:11:21 +0200
parents src/cagou/core/menu.py@4d8c122b86a6
children 397f2fb67aab
comparison
equal deleted inserted replaced
125:b6e6afb0dc46 126:cd99f70ea592
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # Cagou: desktop/mobile frontend for Salut à Toi XMPP client
5 # Copyright (C) 2016-2018 Jérôme Poisson (goffi@goffi.org)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
21 from sat.core.i18n import _
22 from sat.core import log as logging
23 log = logging.getLogger(__name__)
24 from cagou.core.constants import Const as C
25 from kivy.uix.boxlayout import BoxLayout
26 from kivy.uix.label import Label
27 from kivy.uix.popup import Popup
28 from kivy import properties
29 from kivy.garden import contextmenu
30 from sat_frontends.quick_frontend import quick_menus
31 from cagou import G
32 import webbrowser
33
34 ABOUT_TITLE = _(u"About {}".format(C.APP_NAME))
35 ABOUT_CONTENT = _(u"""Cagou (Salut à Toi) v{}
36
37 Cagou is a libre communication tool based on libre standard XMPP.
38
39 Cagou is part of the "Salut à Toi" project
40 more informations at [color=5500ff][ref=website]salut-a-toi.org[/ref][/color]
41 """).format(C.APP_VERSION)
42
43
44 class AboutContent(Label):
45
46 def on_ref_press(self, value):
47 if value == "website":
48 webbrowser.open("https://salut-a-toi.org")
49
50
51 class AboutPopup(Popup):
52
53 def on_touch_down(self, touch):
54 if self.collide_point(*touch.pos):
55 self.dismiss()
56 return super(AboutPopup, self).on_touch_down(touch)
57
58
59 class MainMenu(contextmenu.AppMenu):
60 pass
61
62
63 class MenuItem(contextmenu.ContextMenuTextItem):
64 item = properties.ObjectProperty()
65
66 def on_item(self, instance, item):
67 self.text = item.name
68
69 def on_release(self):
70 super(MenuItem, self).on_release()
71 self.parent.hide()
72 selected = G.host.selected_widget
73 profile = None
74 if selected is not None:
75 try:
76 profile = selected.profile
77 except AttributeError:
78 pass
79
80 if profile is None:
81 try:
82 profile = list(selected.profiles)[0]
83 except (AttributeError, IndexError):
84 try:
85 profile = list(G.host.profiles)[0]
86 except IndexError:
87 log.warning(u"Can't find profile")
88 self.item.call(selected, profile)
89
90
91 class MenuSeparator(contextmenu.ContextMenuDivider):
92 pass
93
94
95 class RootMenuContainer(contextmenu.AppMenuTextItem):
96 pass
97
98
99 class MenuContainer(contextmenu.ContextMenuTextItem):
100 pass
101
102
103 class MenusWidget(BoxLayout):
104
105 def update(self, type_, caller=None):
106 """Method to call when menus have changed
107
108 @param type_(unicode): menu type like in sat.core.sat_main.importMenu
109 @param caller(Widget): instance linked to the menus
110 """
111 self.menus_container = G.host.menus.getMainContainer(type_)
112 self.createMenus(caller)
113
114 def _buildMenus(self, container, caller=None):
115 """Recursively build menus of the container
116
117 @param container(quick_menus.MenuContainer): menu container
118 @param caller(Widget): instance linked to the menus
119 """
120 if caller is None:
121 main_menu = MainMenu()
122 self.add_widget(main_menu)
123 caller = main_menu
124 else:
125 context_menu = contextmenu.ContextMenu()
126 caller.add_widget(context_menu)
127 # FIXME: next line is needed after parent is set to avoid a display bug in contextmenu
128 # TODO: fix this upstream
129 context_menu._on_visible(False)
130
131 caller = context_menu
132
133 for child in container.getActiveMenus():
134 if isinstance(child, quick_menus.MenuContainer):
135 if isinstance(caller, MainMenu):
136 menu_container = RootMenuContainer()
137 else:
138 menu_container = MenuContainer()
139 menu_container.text = child.name
140 caller.add_widget(menu_container)
141 self._buildMenus(child, caller=menu_container)
142 elif isinstance(child, quick_menus.MenuSeparator):
143 wid = MenuSeparator()
144 caller.add_widget(wid)
145 elif isinstance(child, quick_menus.MenuItem):
146 wid = MenuItem(item=child)
147 caller.add_widget(wid)
148 else:
149 log.error(u"Unknown child type: {}".format(child))
150
151 def createMenus(self, caller):
152 self.clear_widgets()
153 self._buildMenus(self.menus_container, caller)
154
155 def onAbout(self):
156 about = AboutPopup()
157 about.title = ABOUT_TITLE
158 about.content = AboutContent(text=ABOUT_CONTENT, markup=True)
159 about.open()
160
161
162 class TransferItem(BoxLayout):
163 plug_info = properties.DictProperty()
164
165 def on_touch_up(self, touch):
166 if not self.collide_point(*touch.pos):
167 return super(TransferItem, self).on_touch_up(touch)
168 else:
169 transfer_menu = self.parent
170 while not isinstance(transfer_menu, TransferMenu):
171 transfer_menu = transfer_menu.parent
172 transfer_menu.do_callback(self.plug_info)
173 return True
174
175
176 class TransferMenu(BoxLayout):
177 """transfer menu which handle display and callbacks"""
178 # callback will be called with path to file to transfer
179 callback = properties.ObjectProperty()
180 # cancel callback need to remove the widget for UI
181 # will be called with the widget to remove as argument
182 cancel_cb = properties.ObjectProperty()
183 # profiles if set will be sent to transfer widget, may be used to get specific files
184 profiles = properties.ObjectProperty()
185 transfer_txt = _(u"Beware! The file will be sent to your server and stay unencrypted there\nServer admin(s) can see the file, and they choose how, when and if it will be deleted")
186 send_txt = _(u"The file will be sent unencrypted directly to your contact (without transiting by the server), except in some cases")
187 items_layout = properties.ObjectProperty()
188
189 def __init__(self, **kwargs):
190 super(TransferMenu, self).__init__(**kwargs)
191 if self.cancel_cb is None:
192 self.cancel_cb = self.onTransferCancelled
193 if self.profiles is None:
194 self.profiles = iter(G.host.profiles)
195 for plug_info in G.host.getPluggedWidgets(type_=C.PLUG_TYPE_TRANSFER):
196 item = TransferItem(
197 plug_info = plug_info
198 )
199 self.items_layout.add_widget(item)
200
201 def show(self, caller_wid=None):
202 self.visible = True
203 G.host.app.root.add_widget(self)
204
205 def on_touch_down(self, touch):
206 # we remove the menu if we click outside
207 # else we want to handle the event, but not
208 # transmit it to parents
209 if not self.collide_point(*touch.pos):
210 self.parent.remove_widget(self)
211 else:
212 return super(TransferMenu, self).on_touch_down(touch)
213 return True
214
215 def _closeUI(self, wid):
216 G.host.closeUI()
217
218 def onTransferCancelled(self, wid, cleaning_cb=None):
219 self._closeUI(wid)
220 if cleaning_cb is not None:
221 cleaning_cb()
222
223 def do_callback(self, plug_info):
224 self.parent.remove_widget(self)
225 if self.callback is None:
226 log.warning(u"TransferMenu callback is not set")
227 else:
228 wid = None
229 external = plug_info.get('external', False)
230 def onTransferCb(file_path, cleaning_cb=None):
231 if not external:
232 self._closeUI(wid)
233 self.callback(
234 file_path,
235 cleaning_cb,
236 transfer_type = C.TRANSFER_UPLOAD if self.ids['upload_btn'].state == "down" else C.TRANSFER_SEND)
237 wid = plug_info['factory'](plug_info, onTransferCb, self.cancel_cb, self.profiles)
238 if not external:
239 G.host.showExtraUI(wid)