comparison cagou/core/cagou_main.py @ 491:203755bbe0fe

massive refactoring from camelCase -> snake_case. See backend commit log for more details
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:44:32 +0200
parents 38ca44d96752
children
comparison
equal deleted inserted replaced
490:962d17c4078c 491:203755bbe0fe
40 from sat.tools.common import dynamic_import 40 from sat.tools.common import dynamic_import
41 from sat.tools.common import files_utils 41 from sat.tools.common import files_utils
42 import kivy 42 import kivy
43 kivy.require('1.11.0') 43 kivy.require('1.11.0')
44 import kivy.support 44 import kivy.support
45 main_config = config.parseMainConf(log_filenames=True) 45 main_config = config.parse_main_conf(log_filenames=True)
46 bridge_name = config.getConfig(main_config, '', 'bridge', 'dbus') 46 bridge_name = config.config_get(main_config, '', 'bridge', 'dbus')
47 # FIXME: event loop is choosen according to bridge_name, a better way should be used 47 # FIXME: event loop is choosen according to bridge_name, a better way should be used
48 if 'dbus' in bridge_name: 48 if 'dbus' in bridge_name:
49 kivy.support.install_gobject_iteration() 49 kivy.support.install_gobject_iteration()
50 elif bridge_name in ('pb', 'embedded'): 50 elif bridge_name in ('pb', 'embedded'):
51 kivy.support.install_twisted_reactor() 51 kivy.support.install_twisted_reactor()
105 105
106 def on_release(self): 106 def on_release(self):
107 callback, args, kwargs = self.notifs.pop(0) 107 callback, args, kwargs = self.notifs.pop(0)
108 callback(*args, **kwargs) 108 callback(*args, **kwargs)
109 109
110 def addNotif(self, callback, *args, **kwargs): 110 def add_notif(self, callback, *args, **kwargs):
111 self.notifs.append((callback, args, kwargs)) 111 self.notifs.append((callback, args, kwargs))
112 112
113 113
114 class Note(Label): 114 class Note(Label):
115 title = properties.StringProperty() 115 title = properties.StringProperty()
157 self.add_widget(self.clear_btn) 157 self.add_widget(self.clear_btn)
158 super(NotesDrop, self).open(widget) 158 super(NotesDrop, self).open(widget)
159 159
160 def on_select(self, action_kwargs): 160 def on_select(self, action_kwargs):
161 app = App.get_running_app() 161 app = App.get_running_app()
162 app.host.doAction(**action_kwargs) 162 app.host.do_action(**action_kwargs)
163 163
164 164
165 class RootHeadWidget(BoxLayout): 165 class RootHeadWidget(BoxLayout):
166 """Notifications widget""" 166 """Notifications widget"""
167 manager = properties.ObjectProperty() 167 manager = properties.ObjectProperty()
173 super(RootHeadWidget, self).__init__() 173 super(RootHeadWidget, self).__init__()
174 self.notes_last = None 174 self.notes_last = None
175 self.notes_event = None 175 self.notes_event = None
176 self.notes_drop = NotesDrop(self.notes) 176 self.notes_drop = NotesDrop(self.notes)
177 177
178 def addNotif(self, callback, *args, **kwargs): 178 def add_notif(self, callback, *args, **kwargs):
179 """add a notification with a callback attached 179 """add a notification with a callback attached
180 180
181 when notification is pressed, callback is called 181 when notification is pressed, callback is called
182 @param *args, **kwargs: arguments of callback 182 @param *args, **kwargs: arguments of callback
183 """ 183 """
184 self.notifs_icon.addNotif(callback, *args, **kwargs) 184 self.notifs_icon.add_notif(callback, *args, **kwargs)
185 185
186 def addNote(self, title, message, level, symbol, action): 186 def add_note(self, title, message, level, symbol, action):
187 kwargs = { 187 kwargs = {
188 'title': title, 188 'title': title,
189 'message': message, 189 'message': message,
190 'level': level 190 'level': level
191 } 191 }
194 if action is not None: 194 if action is not None:
195 kwargs['action'] = action 195 kwargs['action'] = action
196 note = Note(**kwargs) 196 note = Note(**kwargs)
197 self.notes.append(note) 197 self.notes.append(note)
198 if self.notes_event is None: 198 if self.notes_event is None:
199 self.notes_event = Clock.schedule_interval(self._displayNextNote, 5) 199 self.notes_event = Clock.schedule_interval(self._display_next_note, 5)
200 self._displayNextNote() 200 self._display_next_note()
201 201
202 def addNotifUI(self, ui): 202 def add_notif_ui(self, ui):
203 self.notifs_icon.addNotif(ui.show, force=True) 203 self.notifs_icon.add_notif(ui.show, force=True)
204 204
205 def addNotifWidget(self, widget): 205 def add_notif_widget(self, widget):
206 app = App.get_running_app() 206 app = App.get_running_app()
207 self.notifs_icon.addNotif(app.host.showExtraUI, widget=widget) 207 self.notifs_icon.add_notif(app.host.show_extra_ui, widget=widget)
208 208
209 def _displayNextNote(self, __=None): 209 def _display_next_note(self, __=None):
210 screen = Screen() 210 screen = Screen()
211 try: 211 try:
212 idx = self.notes.index(self.notes_last) + 1 212 idx = self.notes.index(self.notes_last) + 1
213 except ValueError: 213 except ValueError:
214 idx = 0 214 idx = 0
246 # extra (file chooser, audio record, etc) 246 # extra (file chooser, audio record, etc)
247 extra_screen = Screen(name='extra') 247 extra_screen = Screen(name='extra')
248 self._manager.add_widget(extra_screen) 248 self._manager.add_widget(extra_screen)
249 self.root_body.add_widget(self._manager) 249 self.root_body.add_widget(self._manager)
250 250
251 def changeWidget(self, widget, screen_name="main"): 251 def change_widget(self, widget, screen_name="main"):
252 """change main widget""" 252 """change main widget"""
253 if self._manager.transition.is_active: 253 if self._manager.transition.is_active:
254 # FIXME: workaround for what seems a Kivy bug 254 # FIXME: workaround for what seems a Kivy bug
255 # TODO: report this upstream 255 # TODO: report this upstream
256 self._manager.transition.stop() 256 self._manager.transition.stop()
269 self._manager.transition = FallOutTransition() 269 self._manager.transition = FallOutTransition()
270 else: 270 else:
271 self._manager.transition = RiseInTransition() 271 self._manager.transition = RiseInTransition()
272 self._manager.current = screen 272 self._manager.current = screen
273 273
274 def newAction(self, handler, action_data, id_, security_limit, profile): 274 def new_action(self, handler, action_data, id_, security_limit, profile):
275 """Add a notification for an action""" 275 """Add a notification for an action"""
276 self.head_widget.addNotif(handler, action_data, id_, security_limit, profile) 276 self.head_widget.add_notif(handler, action_data, id_, security_limit, profile)
277 277
278 def addNote(self, title, message, level, symbol, action): 278 def add_note(self, title, message, level, symbol, action):
279 self.head_widget.addNote(title, message, level, symbol, action) 279 self.head_widget.add_note(title, message, level, symbol, action)
280 280
281 def addNotifUI(self, ui): 281 def add_notif_ui(self, ui):
282 self.head_widget.addNotifUI(ui) 282 self.head_widget.add_notif_ui(ui)
283 283
284 def addNotifWidget(self, widget): 284 def add_notif_widget(self, widget):
285 self.head_widget.addNotifWidget(widget) 285 self.head_widget.add_notif_widget(widget)
286 286
287 287
288 class CagouApp(App): 288 class CagouApp(App):
289 """Kivy App for Cagou""" 289 """Kivy App for Cagou"""
290 c_prim = properties.ListProperty(C.COLOR_PRIM) 290 c_prim = properties.ListProperty(C.COLOR_PRIM)
309 Window.bind(on_dropfile=self.on_dropfile) 309 Window.bind(on_dropfile=self.on_dropfile)
310 wid = CagouRootWidget(Label(text=_("Loading please wait"))) 310 wid = CagouRootWidget(Label(text=_("Loading please wait")))
311 local_platform.on_app_build(wid) 311 local_platform.on_app_build(wid)
312 return wid 312 return wid
313 313
314 def showProfileManager(self): 314 def show_profile_manager(self):
315 self._profile_manager = ProfileManager() 315 self._profile_manager = ProfileManager()
316 self.root.changeWidget(self._profile_manager) 316 self.root.change_widget(self._profile_manager)
317 317
318 def expand(self, path, *args, **kwargs): 318 def expand(self, path, *args, **kwargs):
319 """expand path and replace known values 319 """expand path and replace known values
320 320
321 useful in kv. Values which can be used: 321 useful in kv. Values which can be used:
324 @param *args: additional arguments used in format 324 @param *args: additional arguments used in format
325 @param **kwargs: additional keyword arguments used in format 325 @param **kwargs: additional keyword arguments used in format
326 """ 326 """
327 return os.path.expanduser(path).format(*args, media=self.host.media_dir, **kwargs) 327 return os.path.expanduser(path).format(*args, media=self.host.media_dir, **kwargs)
328 328
329 def initFrontendState(self): 329 def init_frontend_state(self):
330 """Init state to handle paused/stopped/running on mobile OSes""" 330 """Init state to handle paused/stopped/running on mobile OSes"""
331 local_platform.on_initFrontendState() 331 local_platform.on_init_frontend_state()
332 332
333 def on_pause(self): 333 def on_pause(self):
334 return local_platform.on_pause() 334 return local_platform.on_pause()
335 335
336 def on_resume(self): 336 def on_resume(self):
337 return local_platform.on_resume() 337 return local_platform.on_resume()
338 338
339 def on_stop(self): 339 def on_stop(self):
340 return local_platform.on_stop() 340 return local_platform.on_stop()
341 341
342 def showHeadWidget(self, show=None, animation=True): 342 def show_head_widget(self, show=None, animation=True):
343 """Show/Hide the head widget 343 """Show/Hide the head widget
344 344
345 @param show(bool, None): True to show, False to hide, None to switch 345 @param show(bool, None): True to show, False to hide, None to switch
346 @param animation(bool): animate the show/hide if True 346 @param animation(bool): animate the show/hide if True
347 """ 347 """
384 else: 384 else:
385 Window.fullscreen = False 385 Window.fullscreen = False
386 return True 386 return True
387 elif key == 110 and 'alt' in modifier: 387 elif key == 110 and 'alt' in modifier:
388 # M-n we hide/show notifications 388 # M-n we hide/show notifications
389 self.showHeadWidget() 389 self.show_head_widget()
390 return True 390 return True
391 else: 391 else:
392 return False 392 return False
393 393
394 def on_dropfile(self, __, file_path): 394 def on_dropfile(self, __, file_path):
416 if bridge_module is None: 416 if bridge_module is None:
417 log.error(f"Can't import {bridge_name} bridge") 417 log.error(f"Can't import {bridge_name} bridge")
418 sys.exit(3) 418 sys.exit(3)
419 else: 419 else:
420 log.debug(f"Loading {bridge_name} bridge") 420 log.debug(f"Loading {bridge_name} bridge")
421 super(Cagou, self).__init__(bridge_factory=bridge_module.Bridge, 421 super(Cagou, self).__init__(bridge_factory=bridge_module.bridge,
422 xmlui=xmlui, 422 xmlui=xmlui,
423 check_options=quick_utils.check_options, 423 check_options=quick_utils.check_options,
424 connect_bridge=False) 424 connect_bridge=False)
425 self._import_kv() 425 self._import_kv()
426 self.app = CagouApp() 426 self.app = CagouApp()
427 self.app.host = self 427 self.app.host = self
428 self.media_dir = self.app.media_dir = config.getConfig(main_config, '', 428 self.media_dir = self.app.media_dir = config.config_get(main_config, '',
429 'media_dir') 429 'media_dir')
430 self.downloads_dir = self.app.downloads_dir = config.getConfig(main_config, '', 430 self.downloads_dir = self.app.downloads_dir = config.config_get(main_config, '',
431 'downloads_dir') 431 'downloads_dir')
432 if not os.path.exists(self.downloads_dir): 432 if not os.path.exists(self.downloads_dir):
433 try: 433 try:
434 os.makedirs(self.downloads_dir) 434 os.makedirs(self.downloads_dir)
435 except OSError as e: 435 except OSError as e:
447 # visible widgets by classes 447 # visible widgets by classes
448 self._visible_widgets = {} 448 self._visible_widgets = {}
449 # used to keep track of last selected widget in "main" screen when changing 449 # used to keep track of last selected widget in "main" screen when changing
450 # root screen 450 # root screen
451 self._selected_widget_main = None 451 self._selected_widget_main = None
452 self.backend_version = sat.__version__ # will be replaced by getVersion() 452 self.backend_version = sat.__version__ # will be replaced by version_get()
453 if C.APP_VERSION.endswith('D'): 453 if C.APP_VERSION.endswith('D'):
454 self.version = "{} {}".format( 454 self.version = "{} {}".format(
455 C.APP_VERSION, 455 C.APP_VERSION,
456 sat_utils.getRepositoryData(cagou) 456 sat_utils.get_repository_data(cagou)
457 ) 457 )
458 else: 458 else:
459 self.version = C.APP_VERSION 459 self.version = C.APP_VERSION
460 460
461 self.tls_validation = not C.bool(config.getConfig(main_config, 461 self.tls_validation = not C.bool(config.config_get(main_config,
462 C.CONFIG_SECTION, 462 C.CONFIG_SECTION,
463 'no_certificate_validation', 463 'no_certificate_validation',
464 C.BOOL_FALSE)) 464 C.BOOL_FALSE))
465 if not self.tls_validation: 465 if not self.tls_validation:
466 from cagou.core import patches 466 from cagou.core import patches
482 return self.default_wid['main'] 482 return self.default_wid['main']
483 483
484 @QuickApp.sync.setter 484 @QuickApp.sync.setter
485 def sync(self, state): 485 def sync(self, state):
486 QuickApp.sync.fset(self, state) 486 QuickApp.sync.fset(self, state)
487 # widget are resynchronised in onVisible event, 487 # widget are resynchronised in on_visible event,
488 # so we must call resync for widgets which are already visible 488 # so we must call resync for widgets which are already visible
489 if state: 489 if state:
490 for w in self.visible_widgets: 490 for w in self.visible_widgets:
491 try: 491 try:
492 resync = w.resync 492 resync = w.resync
494 pass 494 pass
495 else: 495 else:
496 resync() 496 resync()
497 self.contact_lists.fill() 497 self.contact_lists.fill()
498 498
499 def getConfig(self, section, name, default=None): 499 def config_get(self, section, name, default=None):
500 return config.getConfig(main_config, section, name, default) 500 return config.config_get(main_config, section, name, default)
501 501
502 def onBridgeConnected(self): 502 def on_bridge_connected(self):
503 super(Cagou, self).onBridgeConnected() 503 super(Cagou, self).on_bridge_connected()
504 self.registerSignal("otrState", iface="plugin") 504 self.register_signal("otr_state", iface="plugin")
505 505
506 def _bridgeEb(self, failure): 506 def _bridge_eb(self, failure):
507 if bridge_name == "pb" and sys.platform == "android": 507 if bridge_name == "pb" and sys.platform == "android":
508 try: 508 try:
509 self.retried += 1 509 self.retried += 1
510 except AttributeError: 510 except AttributeError:
511 self.retried = 1 511 self.retried = 1
512 if ((isinstance(failure, exceptions.BridgeExceptionNoService) 512 if ((isinstance(failure, exceptions.BridgeExceptionNoService)
513 and self.retried < 100)): 513 and self.retried < 100)):
514 if self.retried % 20 == 0: 514 if self.retried % 20 == 0:
515 log.debug("backend not ready, retrying ({})".format(self.retried)) 515 log.debug("backend not ready, retrying ({})".format(self.retried))
516 Clock.schedule_once(lambda __: self.connectBridge(), 0.05) 516 Clock.schedule_once(lambda __: self.connect_bridge(), 0.05)
517 return 517 return
518 super(Cagou, self)._bridgeEb(failure) 518 super(Cagou, self)._bridge_eb(failure)
519 519
520 def run(self): 520 def run(self):
521 self.connectBridge() 521 self.connect_bridge()
522 self.app.bind(on_stop=self.onStop) 522 self.app.bind(on_stop=self.onStop)
523 self.app.run() 523 self.app.run()
524 524
525 def onStop(self, obj): 525 def onStop(self, obj):
526 try: 526 try:
528 except AttributeError: 528 except AttributeError:
529 pass 529 pass
530 else: 530 else:
531 sat_instance.stopService() 531 sat_instance.stopService()
532 532
533 def _getVersionCb(self, version): 533 def _get_version_cb(self, version):
534 self.backend_version = version 534 self.backend_version = version
535 535
536 def onBackendReady(self): 536 def on_backend_ready(self):
537 super().onBackendReady() 537 super().on_backend_ready()
538 self.app.showProfileManager() 538 self.app.show_profile_manager()
539 self.bridge.getVersion(callback=self._getVersionCb) 539 self.bridge.version_get(callback=self._get_version_cb)
540 self.app.initFrontendState() 540 self.app.init_frontend_state()
541 if local_platform.do_postInit(): 541 if local_platform.do_post_init():
542 self.postInit() 542 self.post_init()
543 543
544 def postInit(self, __=None): 544 def post_init(self, __=None):
545 # FIXME: resize doesn't work with SDL2 on android, so we use below_target for now 545 # FIXME: resize doesn't work with SDL2 on android, so we use below_target for now
546 self.app.root_window.softinput_mode = "below_target" 546 self.app.root_window.softinput_mode = "below_target"
547 profile_manager = self.app._profile_manager 547 profile_manager = self.app._profile_manager
548 del self.app._profile_manager 548 del self.app._profile_manager
549 super(Cagou, self).postInit(profile_manager) 549 super(Cagou, self).post_init(profile_manager)
550 550
551 def profilePlugged(self, profile): 551 def profile_plugged(self, profile):
552 super().profilePlugged(profile) 552 super().profile_plugged(profile)
553 # FIXME: this won't work with multiple profiles 553 # FIXME: this won't work with multiple profiles
554 self.app.connected = self.profiles[profile].connected 554 self.app.connected = self.profiles[profile].connected
555 555
556 def _bookmarksListCb(self, bookmarks_dict, profile): 556 def _bookmarks_list_cb(self, bookmarks_dict, profile):
557 bookmarks = set() 557 bookmarks = set()
558 for data in bookmarks_dict.values(): 558 for data in bookmarks_dict.values():
559 bookmarks.update({jid.JID(k) for k in data.keys()}) 559 bookmarks.update({jid.JID(k) for k in data.keys()})
560 self.profiles[profile]._bookmarks = sorted(bookmarks) 560 self.profiles[profile]._bookmarks = sorted(bookmarks)
561 561
562 def profileConnected(self, profile): 562 def profile_connected(self, profile):
563 self.bridge.bookmarksList( 563 self.bridge.bookmarks_list(
564 "muc", "all", profile, 564 "muc", "all", profile,
565 callback=partial(self._bookmarksListCb, profile=profile), 565 callback=partial(self._bookmarks_list_cb, profile=profile),
566 errback=partial(self.errback, title=_("Bookmark error"))) 566 errback=partial(self.errback, title=_("Bookmark error")))
567 567
568 def _defaultFactoryMain(self, plugin_info, target, profiles): 568 def _default_factory_main(self, plugin_info, target, profiles):
569 """default factory used to create main widgets instances 569 """default factory used to create main widgets instances
570 570
571 used when PLUGIN_INFO["factory"] is not set 571 used when PLUGIN_INFO["factory"] is not set
572 @param plugin_info(dict): plugin datas 572 @param plugin_info(dict): plugin datas
573 @param target: QuickWidget target 573 @param target: QuickWidget target
574 @param profiles(iterable): list of profiles 574 @param profiles(iterable): list of profiles
575 """ 575 """
576 main_cls = plugin_info['main'] 576 main_cls = plugin_info['main']
577 return self.widgets.getOrCreateWidget(main_cls, 577 return self.widgets.get_or_create_widget(main_cls,
578 target, 578 target,
579 on_new_widget=None, 579 on_new_widget=None,
580 profiles=iter(self.profiles)) 580 profiles=iter(self.profiles))
581 581
582 def _defaultFactoryTransfer(self, plugin_info, callback, cancel_cb, profiles): 582 def _default_factory_transfer(self, plugin_info, callback, cancel_cb, profiles):
583 """default factory used to create transfer widgets instances 583 """default factory used to create transfer widgets instances
584 584
585 @param plugin_info(dict): plugin datas 585 @param plugin_info(dict): plugin datas
586 @param callback(callable): method to call with path to file to transfer 586 @param callback(callable): method to call with path to file to transfer
587 @param cancel_cb(callable): call when transfer is cancelled 587 @param cancel_cb(callable): call when transfer is cancelled
635 plugin_type = suff[:suff.find('_')] 635 plugin_type = suff[:suff.find('_')]
636 636
637 # and select the variable to use according to type 637 # and select the variable to use according to type
638 if plugin_type == C.PLUG_TYPE_WID: 638 if plugin_type == C.PLUG_TYPE_WID:
639 imported_names = imported_names_main 639 imported_names = imported_names_main
640 default_factory = self._defaultFactoryMain 640 default_factory = self._default_factory_main
641 elif plugin_type == C.PLUG_TYPE_TRANSFER: 641 elif plugin_type == C.PLUG_TYPE_TRANSFER:
642 imported_names = imported_names_transfer 642 imported_names = imported_names_transfer
643 default_factory = self._defaultFactoryTransfer 643 default_factory = self._default_factory_transfer
644 else: 644 else:
645 log.error("unknown plugin type {type_} for plugin {file_}, skipping" 645 log.error("unknown plugin type {type_} for plugin {file_}, skipping"
646 .format( 646 .format(
647 type_ = plugin_type, 647 type_ = plugin_type,
648 file_ = plug 648 file_ = plug
649 )) 649 ))
650 continue 650 continue
651 plugins_set = self._getPluginsSet(plugin_type) 651 plugins_set = self._get_plugins_set(plugin_type)
652 652
653 mod = import_module(plugin_path) 653 mod = import_module(plugin_path)
654 try: 654 try:
655 plugin_info = mod.PLUGIN_INFO 655 plugin_info = mod.PLUGIN_INFO
656 except AttributeError: 656 except AttributeError:
699 # what is the main class ? 699 # what is the main class ?
700 main_cls = getattr(mod, plugin_info['main']) 700 main_cls = getattr(mod, plugin_info['main'])
701 plugin_info['main'] = main_cls 701 plugin_info['main'] = main_cls
702 702
703 # factory is used to create the instance 703 # factory is used to create the instance
704 # if not found, we use a defaut one with getOrCreateWidget 704 # if not found, we use a defaut one with get_or_create_widget
705 if 'factory' not in plugin_info: 705 if 'factory' not in plugin_info:
706 plugin_info['factory'] = default_factory 706 plugin_info['factory'] = default_factory
707 707
708 # icons 708 # icons
709 for size in ('small', 'medium'): 709 for size in ('small', 'medium'):
729 729
730 if self.default_wid is None: 730 if self.default_wid is None:
731 # we have no selector widget, we use the first widget as default 731 # we have no selector widget, we use the first widget as default
732 self.default_wid = self._plg_wids[0] 732 self.default_wid = self._plg_wids[0]
733 733
734 def _getPluginsSet(self, type_): 734 def _get_plugins_set(self, type_):
735 if type_ == C.PLUG_TYPE_WID: 735 if type_ == C.PLUG_TYPE_WID:
736 return self._plg_wids 736 return self._plg_wids
737 elif type_ == C.PLUG_TYPE_TRANSFER: 737 elif type_ == C.PLUG_TYPE_TRANSFER:
738 return self._plg_wids_transfer 738 return self._plg_wids_transfer
739 else: 739 else:
740 raise KeyError("{} plugin type is unknown".format(type_)) 740 raise KeyError("{} plugin type is unknown".format(type_))
741 741
742 def getPluggedWidgets(self, type_=C.PLUG_TYPE_WID, except_cls=None): 742 def get_plugged_widgets(self, type_=C.PLUG_TYPE_WID, except_cls=None):
743 """get available widgets plugin infos 743 """get available widgets plugin infos
744 744
745 @param type_(unicode): type of widgets to get 745 @param type_(unicode): type of widgets to get
746 one of C.PLUG_TYPE_* constant 746 one of C.PLUG_TYPE_* constant
747 @param except_cls(None, class): if not None, 747 @param except_cls(None, class): if not None,
748 widgets from this class will be excluded 748 widgets from this class will be excluded
749 @return (iter[dict]): available widgets plugin infos 749 @return (iter[dict]): available widgets plugin infos
750 """ 750 """
751 plugins_set = self._getPluginsSet(type_) 751 plugins_set = self._get_plugins_set(type_)
752 for plugin_data in plugins_set: 752 for plugin_data in plugins_set:
753 if plugin_data['main'] == except_cls: 753 if plugin_data['main'] == except_cls:
754 continue 754 continue
755 yield plugin_data 755 yield plugin_data
756 756
757 def getPluginInfo(self, type_=C.PLUG_TYPE_WID, **kwargs): 757 def get_plugin_info(self, type_=C.PLUG_TYPE_WID, **kwargs):
758 """get first plugin info corresponding to filters 758 """get first plugin info corresponding to filters
759 759
760 @param type_(unicode): type of widgets to get 760 @param type_(unicode): type of widgets to get
761 one of C.PLUG_TYPE_* constant 761 one of C.PLUG_TYPE_* constant
762 @param **kwargs: filter(s) to use, each key present here must also 762 @param **kwargs: filter(s) to use, each key present here must also
763 exist and be of the same value in requested plugin info 763 exist and be of the same value in requested plugin info
764 @return (dict, None): found plugin info or None 764 @return (dict, None): found plugin info or None
765 """ 765 """
766 plugins_set = self._getPluginsSet(type_) 766 plugins_set = self._get_plugins_set(type_)
767 for plugin_info in plugins_set: 767 for plugin_info in plugins_set:
768 for k, w in kwargs.items(): 768 for k, w in kwargs.items():
769 try: 769 try:
770 if plugin_info[k] != w: 770 if plugin_info[k] != w:
771 continue 771 continue
773 continue 773 continue
774 return plugin_info 774 return plugin_info
775 775
776 ## widgets handling 776 ## widgets handling
777 777
778 def newWidget(self, widget): 778 def new_widget(self, widget):
779 log.debug("new widget created: {}".format(widget)) 779 log.debug("new widget created: {}".format(widget))
780 if isinstance(widget, quick_chat.QuickChat) and widget.type == C.CHAT_GROUP: 780 if isinstance(widget, quick_chat.QuickChat) and widget.type == C.CHAT_GROUP:
781 self.addNote("", _("room {} has been joined").format(widget.target)) 781 self.add_note("", _("room {} has been joined").format(widget.target))
782 782
783 def switchWidget(self, old, new=None): 783 def switch_widget(self, old, new=None):
784 """Replace old widget by new one 784 """Replace old widget by new one
785 785
786 @param old(CagouWidget, None): CagouWidget instance or a child 786 @param old(CagouWidget, None): CagouWidget instance or a child
787 None to select automatically widget to switch 787 None to select automatically widget to switch
788 @param new(CagouWidget): new widget instance 788 @param new(CagouWidget): new widget instance
789 None to use default widget 789 None to use default widget
790 @return (CagouWidget): new widget 790 @return (CagouWidget): new widget
791 """ 791 """
792 if old is None: 792 if old is None:
793 old = self.getWidgetToSwitch() 793 old = self.get_widget_to_switch()
794 if new is None: 794 if new is None:
795 factory = self.default_wid['factory'] 795 factory = self.default_wid['factory']
796 try: 796 try:
797 profiles = old.profiles 797 profiles = old.profiles
798 except AttributeError: 798 except AttributeError:
809 809
810 if to_change is None: 810 if to_change is None:
811 raise exceptions.InternalError("no CagouWidget found when " 811 raise exceptions.InternalError("no CagouWidget found when "
812 "trying to switch widget") 812 "trying to switch widget")
813 813
814 # selected_widget can be modified in changeWidget, so we need to set it before 814 # selected_widget can be modified in change_widget, so we need to set it before
815 self.selected_widget = new 815 self.selected_widget = new
816 if to_change == new: 816 if to_change == new:
817 log.debug("switchWidget called with old==new, nothing to do") 817 log.debug("switch_widget called with old==new, nothing to do")
818 return new 818 return new
819 to_change.whwrapper.changeWidget(new) 819 to_change.whwrapper.change_widget(new)
820 return new 820 return new
821 821
822 def _addVisibleWidget(self, widget): 822 def _add_visible_widget(self, widget):
823 """declare a widget visible 823 """declare a widget visible
824 824
825 for internal use only! 825 for internal use only!
826 """ 826 """
827 assert isinstance(widget, CagouWidget) 827 assert isinstance(widget, CagouWidget)
828 log.debug(f"Visible widget: {widget}") 828 log.debug(f"Visible widget: {widget}")
829 self._visible_widgets.setdefault(widget.__class__, set()).add(widget) 829 self._visible_widgets.setdefault(widget.__class__, set()).add(widget)
830 log.debug(f"visible widgets list: {self.getVisibleList(None)}") 830 log.debug(f"visible widgets list: {self.get_visible_list(None)}")
831 widget.onVisible() 831 widget.on_visible()
832 832
833 def _removeVisibleWidget(self, widget, ignore_missing=False): 833 def _remove_visible_widget(self, widget, ignore_missing=False):
834 """declare a widget not visible anymore 834 """declare a widget not visible anymore
835 835
836 for internal use only! 836 for internal use only!
837 """ 837 """
838 log.debug(f"Widget not visible anymore: {widget}") 838 log.debug(f"Widget not visible anymore: {widget}")
840 self._visible_widgets[widget.__class__].remove(widget) 840 self._visible_widgets[widget.__class__].remove(widget)
841 except KeyError as e: 841 except KeyError as e:
842 if not ignore_missing: 842 if not ignore_missing:
843 log.error(f"trying to remove a not visible widget ({widget}): {e}") 843 log.error(f"trying to remove a not visible widget ({widget}): {e}")
844 return 844 return
845 log.debug(f"visible widgets list: {self.getVisibleList(None)}") 845 log.debug(f"visible widgets list: {self.get_visible_list(None)}")
846 if isinstance(widget, CagouWidget): 846 if isinstance(widget, CagouWidget):
847 widget.onNotVisible() 847 widget.on_not_visible()
848 if isinstance(widget, quick_widgets.QuickWidget): 848 if isinstance(widget, quick_widgets.QuickWidget):
849 self.widgets.deleteWidget(widget) 849 self.widgets.delete_widget(widget)
850 850
851 def getVisibleList(self, cls): 851 def get_visible_list(self, cls):
852 """get list of visible widgets for a given class 852 """get list of visible widgets for a given class
853 853
854 @param cls(type): type of widgets to get 854 @param cls(type): type of widgets to get
855 None to get all visible widgets 855 None to get all visible widgets
856 @return (set[type]): visible widgets of this class 856 @return (set[type]): visible widgets of this class
864 try: 864 try:
865 return self._visible_widgets[cls] 865 return self._visible_widgets[cls]
866 except KeyError: 866 except KeyError:
867 return set() 867 return set()
868 868
869 def deleteUnusedWidgetInstances(self, widget): 869 def delete_unused_widget_instances(self, widget):
870 """Delete instance of this widget which are not attached to a WHWrapper 870 """Delete instance of this widget which are not attached to a WHWrapper
871 871
872 @param widget(quick_widgets.QuickWidget): reference widget 872 @param widget(quick_widgets.QuickWidget): reference widget
873 other instance of this widget will be deleted if they have no parent 873 other instance of this widget will be deleted if they have no parent
874 """ 874 """
875 to_delete = [] 875 to_delete = []
876 if isinstance(widget, quick_widgets.QuickWidget): 876 if isinstance(widget, quick_widgets.QuickWidget):
877 for w in self.widgets.getWidgetInstances(widget): 877 for w in self.widgets.get_widget_instances(widget):
878 if w.whwrapper is None and w != widget: 878 if w.whwrapper is None and w != widget:
879 to_delete.append(w) 879 to_delete.append(w)
880 for w in to_delete: 880 for w in to_delete:
881 log.debug("cleaning widget: {wid}".format(wid=w)) 881 log.debug("cleaning widget: {wid}".format(wid=w))
882 self.widgets.deleteWidget(w) 882 self.widgets.delete_widget(w)
883 883
884 def getOrClone(self, widget, **kwargs): 884 def get_or_clone(self, widget, **kwargs):
885 """Get a QuickWidget if it is not in a WHWrapper, else clone it 885 """Get a QuickWidget if it is not in a WHWrapper, else clone it
886 886
887 if an other instance of this widget exist without being in a WHWrapper 887 if an other instance of this widget exist without being in a WHWrapper
888 (i.e. if it is not already in use) it will be used. 888 (i.e. if it is not already in use) it will be used.
889 """ 889 """
890 if widget.whwrapper is None: 890 if widget.whwrapper is None:
891 if widget.parent is not None: 891 if widget.parent is not None:
892 widget.parent.remove_widget(widget) 892 widget.parent.remove_widget(widget)
893 self.deleteUnusedWidgetInstances(widget) 893 self.delete_unused_widget_instances(widget)
894 return widget 894 return widget
895 for w in self.widgets.getWidgetInstances(widget): 895 for w in self.widgets.get_widget_instances(widget):
896 if w.whwrapper is None: 896 if w.whwrapper is None:
897 if w.parent is not None: 897 if w.parent is not None:
898 w.parent.remove_widget(w) 898 w.parent.remove_widget(w)
899 self.deleteUnusedWidgetInstances(w) 899 self.delete_unused_widget_instances(w)
900 return w 900 return w
901 targets = list(widget.targets) 901 targets = list(widget.targets)
902 w = self.widgets.getOrCreateWidget(widget.__class__, 902 w = self.widgets.get_or_create_widget(widget.__class__,
903 targets[0], 903 targets[0],
904 on_new_widget=None, 904 on_new_widget=None,
905 on_existing_widget=C.WIDGET_RECREATE, 905 on_existing_widget=C.WIDGET_RECREATE,
906 profiles=widget.profiles, 906 profiles=widget.profiles,
907 **kwargs) 907 **kwargs)
908 for t in targets[1:]: 908 for t in targets[1:]:
909 w.addTarget(t) 909 w.add_target(t)
910 return w 910 return w
911 911
912 def getWidgetToSwitch(self): 912 def get_widget_to_switch(self):
913 """Choose best candidate when we need to switch widget and old is not specified 913 """Choose best candidate when we need to switch widget and old is not specified
914 914
915 @return (CagouWidget): widget to switch 915 @return (CagouWidget): widget to switch
916 """ 916 """
917 if (self._selected_widget_main is not None 917 if (self._selected_widget_main is not None
929 return w 929 return w
930 930
931 # no default widget found, we return the first widget 931 # no default widget found, we return the first widget
932 return next(iter(self.visible_widgets)) 932 return next(iter(self.visible_widgets))
933 933
934 def doAction(self, action, target, profiles): 934 def do_action(self, action, target, profiles):
935 """Launch an action handler by a plugin 935 """Launch an action handler by a plugin
936 936
937 @param action(unicode): action to do, can be: 937 @param action(unicode): action to do, can be:
938 - chat: open a chat widget 938 - chat: open a chat widget
939 @param target(unicode): target of the action 939 @param target(unicode): target of the action
941 @return (CagouWidget, None): new widget 941 @return (CagouWidget, None): new widget
942 """ 942 """
943 try: 943 try:
944 # FIXME: Q&D way to get chat plugin, should be replaced by a clean method 944 # FIXME: Q&D way to get chat plugin, should be replaced by a clean method
945 # in host 945 # in host
946 plg_infos = [p for p in self.getPluggedWidgets() 946 plg_infos = [p for p in self.get_plugged_widgets()
947 if action in p['import_name']][0] 947 if action in p['import_name']][0]
948 except IndexError: 948 except IndexError:
949 log.warning("No plugin widget found to do {action}".format(action=action)) 949 log.warning("No plugin widget found to do {action}".format(action=action))
950 else: 950 else:
951 try: 951 try:
952 # does the widget already exist? 952 # does the widget already exist?
953 wid = next(self.widgets.getWidgets( 953 wid = next(self.widgets.get_widgets(
954 plg_infos['main'], 954 plg_infos['main'],
955 target=target, 955 target=target,
956 profiles=profiles)) 956 profiles=profiles))
957 except StopIteration: 957 except StopIteration:
958 # no, let's create a new one 958 # no, let's create a new one
959 factory = plg_infos['factory'] 959 factory = plg_infos['factory']
960 wid = factory(plg_infos, target=target, profiles=profiles) 960 wid = factory(plg_infos, target=target, profiles=profiles)
961 961
962 return self.switchWidget(None, wid) 962 return self.switch_widget(None, wid)
963 963
964 ## bridge handlers ## 964 ## bridge handlers ##
965 965
966 def otrStateHandler(self, state, dest_jid, profile): 966 def otr_state_handler(self, state, dest_jid, profile):
967 """OTR state has changed for on destinee""" 967 """OTR state has changed for on destinee"""
968 # XXX: this method could be in QuickApp but it's here as 968 # XXX: this method could be in QuickApp but it's here as
969 # it's only used by Cagou so far 969 # it's only used by Cagou so far
970 dest_jid = jid.JID(dest_jid) 970 dest_jid = jid.JID(dest_jid)
971 bare_jid = dest_jid.bare 971 bare_jid = dest_jid.bare
972 for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)): 972 for widget in self.widgets.get_widgets(quick_chat.QuickChat, profiles=(profile,)):
973 if widget.type == C.CHAT_ONE2ONE and widget.target == bare_jid: 973 if widget.type == C.CHAT_ONE2ONE and widget.target == bare_jid:
974 widget.onOTRState(state, dest_jid, profile) 974 widget.on_otr_state(state, dest_jid, profile)
975 975
976 def _debugHandler(self, action, parameters, profile): 976 def _debug_handler(self, action, parameters, profile):
977 if action == "visible_widgets_dump": 977 if action == "visible_widgets_dump":
978 from pprint import pformat 978 from pprint import pformat
979 log.info("Visible widgets dump:\n{data}".format( 979 log.info("Visible widgets dump:\n{data}".format(
980 data=pformat(self._visible_widgets))) 980 data=pformat(self._visible_widgets)))
981 else: 981 else:
982 return super(Cagou, self)._debugHandler(action, parameters, profile) 982 return super(Cagou, self)._debug_handler(action, parameters, profile)
983 983
984 def connectedHandler(self, jid_s, profile): 984 def connected_handler(self, jid_s, profile):
985 # FIXME: this won't work with multiple profiles 985 # FIXME: this won't work with multiple profiles
986 super().connectedHandler(jid_s, profile) 986 super().connected_handler(jid_s, profile)
987 self.app.connected = True 987 self.app.connected = True
988 988
989 def disconnectedHandler(self, profile): 989 def disconnected_handler(self, profile):
990 # FIXME: this won't work with multiple profiles 990 # FIXME: this won't work with multiple profiles
991 super().disconnectedHandler(profile) 991 super().disconnected_handler(profile)
992 self.app.connected = False 992 self.app.connected = False
993 993
994 ## misc ## 994 ## misc ##
995 995
996 def plugging_profiles(self): 996 def plugging_profiles(self):
997 self.widgets_handler = widgets_handler.WidgetsHandler() 997 self.widgets_handler = widgets_handler.WidgetsHandler()
998 self.app.root.changeWidget(self.widgets_handler) 998 self.app.root.change_widget(self.widgets_handler)
999 999
1000 def setPresenceStatus(self, show='', status=None, profile=C.PROF_KEY_NONE): 1000 def set_presence_status(self, show='', status=None, profile=C.PROF_KEY_NONE):
1001 log.info("Profile presence status set to {show}/{status}".format(show=show, 1001 log.info("Profile presence status set to {show}/{status}".format(show=show,
1002 status=status)) 1002 status=status))
1003 1003
1004 def errback(self, failure_, title=_('error'), 1004 def errback(self, failure_, title=_('error'),
1005 message=_('error while processing: {msg}')): 1005 message=_('error while processing: {msg}')):
1006 self.addNote(title, message.format(msg=failure_), level=C.XMLUI_DATA_LVL_WARNING) 1006 self.add_note(title, message.format(msg=failure_), level=C.XMLUI_DATA_LVL_WARNING)
1007 1007
1008 def addNote(self, title, message, level=C.XMLUI_DATA_LVL_INFO, symbol=None, 1008 def add_note(self, title, message, level=C.XMLUI_DATA_LVL_INFO, symbol=None,
1009 action=None): 1009 action=None):
1010 """add a note (message which disappear) to root widget's header""" 1010 """add a note (message which disappear) to root widget's header"""
1011 self.app.root.addNote(title, message, level, symbol, action) 1011 self.app.root.add_note(title, message, level, symbol, action)
1012 1012
1013 def addNotifUI(self, ui): 1013 def add_notif_ui(self, ui):
1014 """add a notification with a XMLUI attached 1014 """add a notification with a XMLUI attached
1015 1015
1016 @param ui(xmlui.XMLUIPanel): XMLUI instance to show when notification is selected 1016 @param ui(xmlui.XMLUIPanel): XMLUI instance to show when notification is selected
1017 """ 1017 """
1018 self.app.root.addNotifUI(ui) 1018 self.app.root.add_notif_ui(ui)
1019 1019
1020 def addNotifWidget(self, widget): 1020 def add_notif_widget(self, widget):
1021 """add a notification with a Kivy widget attached 1021 """add a notification with a Kivy widget attached
1022 1022
1023 @param widget(kivy.uix.Widget): widget to attach to notification 1023 @param widget(kivy.uix.Widget): widget to attach to notification
1024 """ 1024 """
1025 self.app.root.addNotifWidget(widget) 1025 self.app.root.add_notif_widget(widget)
1026 1026
1027 def showUI(self, ui): 1027 def show_ui(self, ui):
1028 """show a XMLUI""" 1028 """show a XMLUI"""
1029 self.app.root.changeWidget(ui, "xmlui") 1029 self.app.root.change_widget(ui, "xmlui")
1030 self.app.root.show("xmlui") 1030 self.app.root.show("xmlui")
1031 self._selected_widget_main = self.selected_widget 1031 self._selected_widget_main = self.selected_widget
1032 self.selected_widget = ui 1032 self.selected_widget = ui
1033 1033
1034 def showExtraUI(self, widget): 1034 def show_extra_ui(self, widget):
1035 """show any extra widget""" 1035 """show any extra widget"""
1036 self.app.root.changeWidget(widget, "extra") 1036 self.app.root.change_widget(widget, "extra")
1037 self.app.root.show("extra") 1037 self.app.root.show("extra")
1038 self._selected_widget_main = self.selected_widget 1038 self._selected_widget_main = self.selected_widget
1039 self.selected_widget = widget 1039 self.selected_widget = widget
1040 1040
1041 def closeUI(self): 1041 def close_ui(self):
1042 self.app.root.show() 1042 self.app.root.show()
1043 self.selected_widget = self._selected_widget_main 1043 self.selected_widget = self._selected_widget_main
1044 self._selected_widget_main = None 1044 self._selected_widget_main = None
1045 screen = self.app.root._manager.get_screen("extra") 1045 screen = self.app.root._manager.get_screen("extra")
1046 screen.clear_widgets() 1046 screen.clear_widgets()
1047 1047
1048 def getDefaultAvatar(self, entity=None): 1048 def get_default_avatar(self, entity=None):
1049 return self.app.default_avatar 1049 return self.app.default_avatar
1050 1050
1051 def _dialog_cb(self, cb, *args, **kwargs): 1051 def _dialog_cb(self, cb, *args, **kwargs):
1052 """generic dialog callback 1052 """generic dialog callback
1053 1053
1054 close dialog then call the callback with given arguments 1054 close dialog then call the callback with given arguments
1055 """ 1055 """
1056 def callback(): 1056 def callback():
1057 self.closeUI() 1057 self.close_ui()
1058 cb(*args, **kwargs) 1058 cb(*args, **kwargs)
1059 return callback 1059 return callback
1060 1060
1061 def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None): 1061 def show_dialog(self, message, title, type="info", answer_cb=None, answer_data=None):
1062 if type in ('info', 'warning', 'error'): 1062 if type in ('info', 'warning', 'error'):
1063 self.addNote(title, message, type) 1063 self.add_note(title, message, type)
1064 elif type == "yes/no": 1064 elif type == "yes/no":
1065 wid = dialog.ConfirmDialog(title=title, message=message, 1065 wid = dialog.ConfirmDialog(title=title, message=message,
1066 yes_cb=self._dialog_cb(answer_cb, 1066 yes_cb=self._dialog_cb(answer_cb,
1067 True, 1067 True,
1068 answer_data), 1068 answer_data),
1069 no_cb=self._dialog_cb(answer_cb, 1069 no_cb=self._dialog_cb(answer_cb,
1070 False, 1070 False,
1071 answer_data) 1071 answer_data)
1072 ) 1072 )
1073 self.addNotifWidget(wid) 1073 self.add_notif_widget(wid)
1074 else: 1074 else:
1075 log.warning(_("unknown dialog type: {dialog_type}").format(dialog_type=type)) 1075 log.warning(_("unknown dialog type: {dialog_type}").format(dialog_type=type))
1076 1076
1077 def share(self, media_type, data): 1077 def share(self, media_type, data):
1078 share_wid = ShareWidget(media_type=media_type, data=data) 1078 share_wid = ShareWidget(media_type=media_type, data=data)
1079 try: 1079 try:
1080 self.showExtraUI(share_wid) 1080 self.show_extra_ui(share_wid)
1081 except Exception as e: 1081 except Exception as e:
1082 log.error(e) 1082 log.error(e)
1083 self.closeUI() 1083 self.close_ui()
1084 1084
1085 def downloadURL( 1085 def download_url(
1086 self, url, callback, errback=None, options=None, dest=C.FILE_DEST_DOWNLOAD, 1086 self, url, callback, errback=None, options=None, dest=C.FILE_DEST_DOWNLOAD,
1087 profile=C.PROF_KEY_NONE): 1087 profile=C.PROF_KEY_NONE):
1088 """Download an URL (decrypt it if necessary) 1088 """Download an URL (decrypt it if necessary)
1089 1089
1090 @param url(str, parse.SplitResult): url to download 1090 @param url(str, parse.SplitResult): url to download
1092 @param errback(callable, None): method to call in case of error 1092 @param errback(callable, None): method to call in case of error
1093 if None, default errback will be called 1093 if None, default errback will be called
1094 @param dest(str): where the file should be downloaded: 1094 @param dest(str): where the file should be downloaded:
1095 - C.FILE_DEST_DOWNLOAD: in platform download directory 1095 - C.FILE_DEST_DOWNLOAD: in platform download directory
1096 - C.FILE_DEST_CACHE: in SàT cache 1096 - C.FILE_DEST_CACHE: in SàT cache
1097 @param options(dict, None): options to pass to bridge.fileDownloadComplete 1097 @param options(dict, None): options to pass to bridge.file_download_complete
1098 """ 1098 """
1099 if not isinstance(url, urlparse.ParseResult): 1099 if not isinstance(url, urlparse.ParseResult):
1100 url = urlparse.urlparse(url) 1100 url = urlparse.urlparse(url)
1101 if errback is None: 1101 if errback is None:
1102 errback = partial( 1102 errback = partial(
1109 dest_path = files_utils.get_unique_name(Path(self.downloads_dir)/name) 1109 dest_path = files_utils.get_unique_name(Path(self.downloads_dir)/name)
1110 elif dest == C.FILE_DEST_CACHE: 1110 elif dest == C.FILE_DEST_CACHE:
1111 dest_path = '' 1111 dest_path = ''
1112 else: 1112 else:
1113 raise exceptions.InternalError(f"Invalid dest_path: {dest_path!r}") 1113 raise exceptions.InternalError(f"Invalid dest_path: {dest_path!r}")
1114 self.bridge.fileDownloadComplete( 1114 self.bridge.file_download_complete(
1115 data_format.serialise({"uri": url.geturl()}), 1115 data_format.serialise({"uri": url.geturl()}),
1116 str(dest_path), 1116 str(dest_path),
1117 '' if not options else data_format.serialise(options), 1117 '' if not options else data_format.serialise(options),
1118 profile, 1118 profile,
1119 callback=callback, 1119 callback=callback,
1144 except Exception as e: 1144 except Exception as e:
1145 log.warning(_("Can't use notifications, disabling: {msg}").format( 1145 log.warning(_("Can't use notifications, disabling: {msg}").format(
1146 msg = e)) 1146 msg = e))
1147 notification = None 1147 notification = None
1148 1148
1149 def getParentWHWrapper(self, wid): 1149 def get_parent_wh_wrapper(self, wid):
1150 """Retrieve parent WHWrapper instance managing a widget 1150 """Retrieve parent WHWrapper instance managing a widget
1151 1151
1152 @param wid(Widget): widget to check 1152 @param wid(Widget): widget to check
1153 @return (WHWrapper, None): found instance if any, else None 1153 @return (WHWrapper, None): found instance if any, else None
1154 """ 1154 """
1155 wh = self.getAncestorWidget(wid, widgets_handler.WHWrapper) 1155 wh = self.get_ancestor_widget(wid, widgets_handler.WHWrapper)
1156 if wh is None: 1156 if wh is None:
1157 # we may have a screen 1157 # we may have a screen
1158 try: 1158 try:
1159 sm = wid.screen_manager 1159 sm = wid.screen_manager
1160 except (exceptions.InternalError, exceptions.NotFound): 1160 except (exceptions.InternalError, exceptions.NotFound):
1161 return None 1161 return None
1162 else: 1162 else:
1163 wh = self.getAncestorWidget(sm, widgets_handler.WHWrapper) 1163 wh = self.get_ancestor_widget(sm, widgets_handler.WHWrapper)
1164 return wh 1164 return wh
1165 1165
1166 def getAncestorWidget(self, wid, cls): 1166 def get_ancestor_widget(self, wid, cls):
1167 """Retrieve an ancestor of given class 1167 """Retrieve an ancestor of given class
1168 1168
1169 @param wid(Widget): current widget 1169 @param wid(Widget): current widget
1170 @param cls(type): class of the ancestor to retrieve 1170 @param cls(type): class of the ancestor to retrieve
1171 @return (Widget, None): found instance or None 1171 @return (Widget, None): found instance or None