Mercurial > libervia-desktop-kivy
comparison cagou/plugins/plugin_wid_file_sharing.py @ 222:a676cb07c1cb
core (menu): TouchMenuBehaviour:
moved code showing ModernMenu on item from file sharing plugin to a generic behaviour, so it can be re-used elsewhere.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 26 Jun 2018 20:26:21 +0200 |
parents | 286f14127f61 |
children | 059c5b39032d |
comparison
equal
deleted
inserted
replaced
221:e1a385a791cc | 222:a676cb07c1cb |
---|---|
25 from sat.tools.common import files_utils | 25 from sat.tools.common import files_utils |
26 from sat_frontends.quick_frontend import quick_widgets | 26 from sat_frontends.quick_frontend import quick_widgets |
27 from sat_frontends.tools import jid | 27 from sat_frontends.tools import jid |
28 from cagou.core.constants import Const as C | 28 from cagou.core.constants import Const as C |
29 from cagou.core import cagou_widget | 29 from cagou.core import cagou_widget |
30 from cagou.core.menu import EntitiesSelectorMenu | 30 from cagou.core.menu import (EntitiesSelectorMenu, TouchMenuBehaviour, |
31 TouchMenuItemBehaviour) | |
31 from cagou.core.utils import FilterBehavior | 32 from cagou.core.utils import FilterBehavior |
32 from cagou import G | 33 from cagou import G |
33 from kivy import properties | 34 from kivy import properties |
34 from kivy.uix.label import Label | 35 from kivy.uix.label import Label |
35 from kivy.uix.button import Button | 36 from kivy.uix.button import Button |
36 from kivy.uix.boxlayout import BoxLayout | 37 from kivy.uix.boxlayout import BoxLayout |
37 from kivy.garden import modernmenu | |
38 from kivy.clock import Clock | |
39 from kivy.metrics import dp | 38 from kivy.metrics import dp |
40 from kivy import utils as kivy_utils | 39 from kivy import utils as kivy_utils |
41 from functools import partial | 40 from functools import partial |
42 import os.path | 41 import os.path |
43 import json | 42 import json |
104 @property | 103 @property |
105 def name(self): | 104 def name(self): |
106 return self.identities.values()[0].values()[0][0] | 105 return self.identities.values()[0].values()[0][0] |
107 | 106 |
108 | 107 |
109 class ItemWidget(BoxLayout): | 108 class ItemWidget(TouchMenuItemBehaviour, BoxLayout): |
110 click_timeout = properties.NumericProperty(0.4) | 109 name = properties.StringProperty() |
111 base_width = properties.NumericProperty(dp(100)) | 110 base_width = properties.NumericProperty(dp(100)) |
112 | 111 |
113 def __init__(self, sharing_wid, name): | |
114 self.sharing_wid = sharing_wid | |
115 self.name = name | |
116 super(ItemWidget, self).__init__() | |
117 | |
118 def on_touch_down(self, touch): | |
119 if not self.collide_point(*touch.pos): | |
120 return | |
121 t = partial(self.open_menu, touch) | |
122 touch.ud['menu_timeout'] = t | |
123 Clock.schedule_once(t, self.click_timeout) | |
124 return super(ItemWidget, self).on_touch_down(touch) | |
125 | |
126 def do_item_action(self, touch): | |
127 pass | |
128 | |
129 def on_touch_up(self, touch): | |
130 if touch.ud.get('menu_timeout'): | |
131 Clock.unschedule(touch.ud['menu_timeout']) | |
132 if self.collide_point(*touch.pos) and self.sharing_wid.menu is None: | |
133 self.do_item_action(touch) | |
134 return super(ItemWidget, self).on_touch_up(touch) | |
135 | |
136 def open_menu(self, touch, dt): | |
137 self.sharing_wid.open_menu(self, touch) | |
138 del touch.ud['menu_timeout'] | |
139 | |
140 def getMenuChoices(self): | |
141 """return choice adapted to selected item | |
142 | |
143 @return (list[dict]): choices ad expected by ModernMenu | |
144 """ | |
145 return [] | |
146 | |
147 | 112 |
148 class PathWidget(ItemWidget): | 113 class PathWidget(ItemWidget): |
149 | 114 |
150 def __init__(self, sharing_wid, filepath): | 115 def __init__(self, filepath, main_wid, **kw): |
151 name = os.path.basename(filepath) | 116 name = os.path.basename(filepath) |
152 self.filepath = os.path.normpath(filepath) | 117 self.filepath = os.path.normpath(filepath) |
153 if self.filepath == u'.': | 118 if self.filepath == u'.': |
154 self.filepath = u'' | 119 self.filepath = u'' |
155 super(PathWidget, self).__init__(sharing_wid, name) | 120 super(PathWidget, self).__init__(name=name, main_wid=main_wid, **kw) |
156 | 121 |
157 @property | 122 @property |
158 def is_dir(self): | 123 def is_dir(self): |
159 raise NotImplementedError | 124 raise NotImplementedError |
160 | 125 |
161 def do_item_action(self, touch): | 126 def do_item_action(self, touch): |
162 if self.is_dir: | 127 if self.is_dir: |
163 self.sharing_wid.current_dir = self.filepath | 128 self.main_wid.current_dir = self.filepath |
164 | 129 |
165 def open_menu(self, touch, dt): | 130 def open_menu(self, touch, dt): |
166 log.debug(_(u"opening menu for {path}").format(path=self.filepath)) | 131 log.debug(_(u"opening menu for {path}").format(path=self.filepath)) |
167 super(PathWidget, self).open_menu(touch, dt) | 132 super(PathWidget, self).open_menu(touch, dt) |
168 | 133 |
176 def getMenuChoices(self): | 141 def getMenuChoices(self): |
177 choices = [] | 142 choices = [] |
178 if self.shared: | 143 if self.shared: |
179 choices.append(dict(text=_(u'unshare'), | 144 choices.append(dict(text=_(u'unshare'), |
180 index=len(choices)+1, | 145 index=len(choices)+1, |
181 callback=self.sharing_wid.unshare)) | 146 callback=self.main_wid.unshare)) |
182 else: | 147 else: |
183 choices.append(dict(text=_(u'share'), | 148 choices.append(dict(text=_(u'share'), |
184 index=len(choices)+1, | 149 index=len(choices)+1, |
185 callback=self.sharing_wid.share)) | 150 callback=self.main_wid.share)) |
186 return choices | 151 return choices |
187 | 152 |
188 | 153 |
189 class RemotePathWidget(PathWidget): | 154 class RemotePathWidget(PathWidget): |
190 | 155 |
191 def __init__(self, sharing_wid, filepath, type_): | 156 def __init__(self, main_wid, filepath, type_, **kw): |
192 self.type_ = type_ | 157 self.type_ = type_ |
193 super(RemotePathWidget, self).__init__(sharing_wid, filepath) | 158 super(RemotePathWidget, self).__init__(filepath, main_wid=main_wid, **kw) |
194 | 159 |
195 @property | 160 @property |
196 def is_dir(self): | 161 def is_dir(self): |
197 return self.type_ == C.FILE_TYPE_DIRECTORY | 162 return self.type_ == C.FILE_TYPE_DIRECTORY |
198 | 163 |
199 def do_item_action(self, touch): | 164 def do_item_action(self, touch): |
200 if self.is_dir: | 165 if self.is_dir: |
201 if self.filepath == u'..': | 166 if self.filepath == u'..': |
202 self.sharing_wid.remote_entity = u'' | 167 self.main_wid.remote_entity = u'' |
203 else: | 168 else: |
204 super(RemotePathWidget, self).do_item_action(touch) | 169 super(RemotePathWidget, self).do_item_action(touch) |
205 else: | 170 else: |
206 self.sharing_wid.request_item(self) | 171 self.main_wid.request_item(self) |
207 return True | 172 return True |
208 | 173 |
209 | 174 |
210 class DeviceWidget(ItemWidget): | 175 class DeviceWidget(ItemWidget): |
211 | 176 |
212 def __init__(self, sharing_wid, entity_jid, identities): | 177 def __init__(self, main_wid, entity_jid, identities, **kw): |
213 self.entity_jid = entity_jid | 178 self.entity_jid = entity_jid |
214 self.identities = identities | 179 self.identities = identities |
215 own_jid = next(G.host.profiles.itervalues()).whoami | 180 own_jid = next(G.host.profiles.itervalues()).whoami |
216 self.own_device = entity_jid.bare == own_jid | 181 self.own_device = entity_jid.bare == own_jid |
217 if self.own_device: | 182 if self.own_device: |
221 elif self.entity_jid.domain.endswith(own_jid.domain): | 186 elif self.entity_jid.domain.endswith(own_jid.domain): |
222 name = _(u"your server") | 187 name = _(u"your server") |
223 else: | 188 else: |
224 name = _(u"sharing component") | 189 name = _(u"sharing component") |
225 | 190 |
226 super(DeviceWidget, self).__init__(sharing_wid, name) | 191 super(DeviceWidget, self).__init__(name=name, main_wid=main_wid, **kw) |
227 | 192 |
228 def getSymbol(self): | 193 def getSymbol(self): |
229 if self.identities.type == 'desktop': | 194 if self.identities.type == 'desktop': |
230 return 'desktop' | 195 return 'desktop' |
231 elif self.identities.type == 'phone': | 196 elif self.identities.type == 'phone': |
236 return 'terminal' | 201 return 'terminal' |
237 else: | 202 else: |
238 return 'desktop' | 203 return 'desktop' |
239 | 204 |
240 def do_item_action(self, touch): | 205 def do_item_action(self, touch): |
241 self.sharing_wid.remote_entity = self.entity_jid | 206 self.main_wid.remote_entity = self.entity_jid |
242 self.sharing_wid.remote_dir = u'' | 207 self.main_wid.remote_dir = u'' |
243 | 208 |
244 | 209 |
245 class CategorySeparator(Label): | 210 class CategorySeparator(Label): |
246 pass | 211 pass |
247 | 212 |
248 | 213 |
249 class Menu(modernmenu.ModernMenu): | 214 class FileSharing(quick_widgets.QuickWidget, cagou_widget.CagouWidget, FilterBehavior, |
250 pass | 215 TouchMenuBehaviour): |
251 | |
252 | |
253 class FileSharing(quick_widgets.QuickWidget, cagou_widget.CagouWidget, FilterBehavior): | |
254 SINGLE=False | 216 SINGLE=False |
255 float_layout = properties.ObjectProperty() | |
256 layout = properties.ObjectProperty() | 217 layout = properties.ObjectProperty() |
257 mode = properties.OptionProperty(MODE_LOCAL, options=[MODE_VIEW, MODE_LOCAL]) | 218 mode = properties.OptionProperty(MODE_LOCAL, options=[MODE_VIEW, MODE_LOCAL]) |
258 local_dir = properties.StringProperty(expanduser(u'~')) | 219 local_dir = properties.StringProperty(expanduser(u'~')) |
259 remote_dir = properties.StringProperty(u'') | 220 remote_dir = properties.StringProperty(u'') |
260 remote_entity = properties.StringProperty(u'') | 221 remote_entity = properties.StringProperty(u'') |
263 | 224 |
264 def __init__(self, host, target, profiles): | 225 def __init__(self, host, target, profiles): |
265 quick_widgets.QuickWidget.__init__(self, host, target, profiles) | 226 quick_widgets.QuickWidget.__init__(self, host, target, profiles) |
266 cagou_widget.CagouWidget.__init__(self) | 227 cagou_widget.CagouWidget.__init__(self) |
267 FilterBehavior.__init__(self) | 228 FilterBehavior.__init__(self) |
229 TouchMenuBehaviour.__init__(self) | |
268 self.mode_btn = ModeBtn(self) | 230 self.mode_btn = ModeBtn(self) |
269 self.mode_btn.bind(on_release=self.change_mode) | 231 self.mode_btn.bind(on_release=self.change_mode) |
270 self.headerInputAddExtra(self.mode_btn) | 232 self.headerInputAddExtra(self.mode_btn) |
271 self.bind(local_dir=self.update_view, | 233 self.bind(local_dir=self.update_view, |
272 remote_dir=self.update_view, | 234 remote_dir=self.update_view, |
273 remote_entity=self.update_view) | 235 remote_entity=self.update_view) |
274 self.update_view() | 236 self.update_view() |
275 self.menu = None | |
276 self.menu_item = None | |
277 self.float_layout.bind(children=self.clean_fl_children) | |
278 if not FileSharing.signals_registered: | 237 if not FileSharing.signals_registered: |
279 # FIXME: we use this hack (registering the signal for the whole class) now | 238 # FIXME: we use this hack (registering the signal for the whole class) now |
280 # as there is currently no unregisterSignal available in bridges | 239 # as there is currently no unregisterSignal available in bridges |
281 G.host.registerSignal("FISSharedPathNew", | 240 G.host.registerSignal("FISSharedPathNew", |
282 handler=FileSharing.shared_path_new, | 241 handler=FileSharing.shared_path_new, |
373 | 332 |
374 def FISListCb(self, files_data): | 333 def FISListCb(self, files_data): |
375 for file_data in files_data: | 334 for file_data in files_data: |
376 filepath = os.path.join(self.current_dir, file_data[u'name']) | 335 filepath = os.path.join(self.current_dir, file_data[u'name']) |
377 item = RemotePathWidget( | 336 item = RemotePathWidget( |
378 self, | |
379 filepath=filepath, | 337 filepath=filepath, |
338 main_wid=self, | |
380 type_=file_data[u'type']) | 339 type_=file_data[u'type']) |
381 self.layout.add_widget(item) | 340 self.layout.add_widget(item) |
382 | 341 |
383 def FISListEb(self, failure_): | 342 def FISListEb(self, failure_): |
384 self.remote_dir = u'' | 343 self.remote_dir = u'' |
398 self.header_input.text = u'' | 357 self.header_input.text = u'' |
399 self.header_input.hint_text = self.current_dir | 358 self.header_input.hint_text = self.current_dir |
400 | 359 |
401 if self.mode == MODE_LOCAL: | 360 if self.mode == MODE_LOCAL: |
402 filepath = os.path.join(self.local_dir, u'..') | 361 filepath = os.path.join(self.local_dir, u'..') |
403 self.layout.add_widget(LocalPathWidget(sharing_wid=self, filepath=filepath)) | 362 self.layout.add_widget(LocalPathWidget(filepath=filepath, main_wid=self)) |
404 try: | 363 try: |
405 files = sorted(os.listdir(self.local_dir)) | 364 files = sorted(os.listdir(self.local_dir)) |
406 except OSError as e: | 365 except OSError as e: |
407 msg = _(u"can't list files in \"{local_dir}\": {msg}").format( | 366 msg = _(u"can't list files in \"{local_dir}\": {msg}").format( |
408 local_dir=self.local_dir, | 367 local_dir=self.local_dir, |
413 level=C.XMLUI_DATA_LVL_WARNING) | 372 level=C.XMLUI_DATA_LVL_WARNING) |
414 self.local_dir = expanduser(u'~') | 373 self.local_dir = expanduser(u'~') |
415 return | 374 return |
416 for f in files: | 375 for f in files: |
417 filepath = os.path.join(self.local_dir, f) | 376 filepath = os.path.join(self.local_dir, f) |
418 self.layout.add_widget(LocalPathWidget(sharing_wid=self, | 377 self.layout.add_widget(LocalPathWidget(filepath=filepath, |
419 filepath=filepath)) | 378 main_wid=self)) |
420 elif self.mode == MODE_VIEW: | 379 elif self.mode == MODE_VIEW: |
421 if not self.remote_entity: | 380 if not self.remote_entity: |
422 self.discover_devices() | 381 self.discover_devices() |
423 else: | 382 else: |
424 # we always a way to go back | 383 # we always a way to go back |
425 # so user can return to previous list even in case of error | 384 # so user can return to previous list even in case of error |
426 parent_path = os.path.join(self.remote_dir, u'..') | 385 parent_path = os.path.join(self.remote_dir, u'..') |
427 item = RemotePathWidget( | 386 item = RemotePathWidget( |
428 self, | |
429 filepath = parent_path, | 387 filepath = parent_path, |
388 main_wid=self, | |
430 type_ = C.FILE_TYPE_DIRECTORY) | 389 type_ = C.FILE_TYPE_DIRECTORY) |
431 self.layout.add_widget(item) | 390 self.layout.add_widget(item) |
432 self.host.bridge.FISList( | 391 self.host.bridge.FISList( |
433 unicode(self.remote_entity), | 392 unicode(self.remote_entity), |
434 self.remote_dir, | 393 self.remote_dir, |
435 {}, | 394 {}, |
436 self.profile, | 395 self.profile, |
437 callback=self.FISListCb, | 396 callback=self.FISListCb, |
438 errback=self.FISListEb) | 397 errback=self.FISListEb) |
439 | |
440 ## menu methods ## | |
441 | |
442 def clean_fl_children(self, layout, children): | |
443 """insure that self.menu and self.menu_item are None when menu is dimissed""" | |
444 if self.menu is not None and self.menu not in children: | |
445 self.menu = self.menu_item = None | |
446 | |
447 def clear_menu(self): | |
448 """remove menu if there is one""" | |
449 if self.menu is not None: | |
450 self.menu.dismiss() | |
451 self.menu = None | |
452 self.menu_item = None | |
453 | |
454 def open_menu(self, item, touch): | |
455 """open menu for item | |
456 | |
457 @param item(PathWidget): item when the menu has been requested | |
458 @param touch(kivy.input.MotionEvent): touch data | |
459 """ | |
460 if self.menu_item == item: | |
461 return | |
462 self.clear_menu() | |
463 pos = self.to_widget(*touch.pos) | |
464 choices = item.getMenuChoices() | |
465 if not choices: | |
466 return | |
467 self.menu = Menu(choices=choices, | |
468 center=pos, | |
469 size_hint=(None, None)) | |
470 self.float_layout.add_widget(self.menu) | |
471 self.menu.start_display(touch) | |
472 self.menu_item = item | |
473 | 398 |
474 ## Share methods ## | 399 ## Share methods ## |
475 | 400 |
476 def do_share(self, entities_jids, item): | 401 def do_share(self, entities_jids, item): |
477 if entities_jids: | 402 if entities_jids: |