comparison sat_frontends/quick_frontend/quick_menus.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents be6d91572633
children 4b842c1fb686
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
25 AttributeError, 25 AttributeError,
26 ): # Error raised is not the same depending on pyjsbuild options 26 ): # Error raised is not the same depending on pyjsbuild options
27 str = str 27 str = str
28 28
29 from sat.core.log import getLogger 29 from sat.core.log import getLogger
30 from sat.core.i18n import _, languageSwitch 30 from sat.core.i18n import _, language_switch
31 31
32 log = getLogger(__name__) 32 log = getLogger(__name__)
33 from sat_frontends.quick_frontend.constants import Const as C 33 from sat_frontends.quick_frontend.constants import Const as C
34 from collections import OrderedDict 34 from collections import OrderedDict
35 35
41 ACTIVE = True 41 ACTIVE = True
42 42
43 def __init__(self, name, extra=None): 43 def __init__(self, name, extra=None):
44 """ 44 """
45 @param name(unicode): canonical name of the item 45 @param name(unicode): canonical name of the item
46 @param extra(dict[unicode, unicode], None): same as in [addMenus] 46 @param extra(dict[unicode, unicode], None): same as in [add_menus]
47 """ 47 """
48 self._name = name 48 self._name = name
49 self.setExtra(extra) 49 self.set_extra(extra)
50 50
51 @property 51 @property
52 def canonical(self): 52 def canonical(self):
53 """Return the canonical name of the container, used to identify it""" 53 """Return the canonical name of the container, used to identify it"""
54 return self._name 54 return self._name
56 @property 56 @property
57 def name(self): 57 def name(self):
58 """Return the name of the container, can be translated""" 58 """Return the name of the container, can be translated"""
59 return self._name 59 return self._name
60 60
61 def setExtra(self, extra): 61 def set_extra(self, extra):
62 if extra is None: 62 if extra is None:
63 extra = {} 63 extra = {}
64 self.icon = extra.get("icon") 64 self.icon = extra.get("icon")
65 65
66 66
71 71
72 def __init__(self, name, name_i18n, extra=None, type_=None): 72 def __init__(self, name, name_i18n, extra=None, type_=None):
73 """ 73 """
74 @param name(unicode): canonical name of the item 74 @param name(unicode): canonical name of the item
75 @param name_i18n(unicode): translated name of the item 75 @param name_i18n(unicode): translated name of the item
76 @param extra(dict[unicode, unicode], None): same as in [addMenus] 76 @param extra(dict[unicode, unicode], None): same as in [add_menus]
77 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 77 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
78 """ 78 """
79 MenuBase.__init__(self, name, extra) 79 MenuBase.__init__(self, name, extra)
80 self._name_i18n = name_i18n if name_i18n else name 80 self._name_i18n = name_i18n if name_i18n else name
81 self.type = type_ 81 self.type = type_
82 82
83 @property 83 @property
84 def name(self): 84 def name(self):
85 return self._name_i18n 85 return self._name_i18n
86 86
87 def collectData(self, caller): 87 def collect_data(self, caller):
88 """Get data according to data_collector 88 """Get data according to data_collector
89 89
90 @param caller: Menu caller 90 @param caller: Menu caller
91 """ 91 """
92 assert self.type is not None # if data collector are used, type must be set 92 assert self.type is not None # if data collector are used, type must be set
93 data_collector = QuickMenusManager.getDataCollector(self.type) 93 data_collector = QuickMenusManager.get_data_collector(self.type)
94 94
95 if data_collector is None: 95 if data_collector is None:
96 return {} 96 return {}
97 97
98 elif callable(data_collector): 98 elif callable(data_collector):
122 CALLABLE = True 122 CALLABLE = True
123 123
124 def __init__(self, host, type_, name, name_i18n, id_, extra=None): 124 def __init__(self, host, type_, name, name_i18n, id_, extra=None):
125 """ 125 """
126 @param host: %(doc_host)s 126 @param host: %(doc_host)s
127 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 127 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
128 @param name(unicode): canonical name of the item 128 @param name(unicode): canonical name of the item
129 @param name_i18n(unicode): translated name of the item 129 @param name_i18n(unicode): translated name of the item
130 @param id_(unicode): id of the distant callback 130 @param id_(unicode): id of the distant callback
131 @param extra(dict[unicode, unicode], None): same as in [addMenus] 131 @param extra(dict[unicode, unicode], None): same as in [add_menus]
132 """ 132 """
133 MenuItem.__init__(self, name, name_i18n, extra, type_) 133 MenuItem.__init__(self, name, name_i18n, extra, type_)
134 self.host = host 134 self.host = host
135 self.id = id_ 135 self.id = id_
136 136
137 def call(self, caller, profile=C.PROF_KEY_NONE): 137 def call(self, caller, profile=C.PROF_KEY_NONE):
138 data = self.collectData(caller) 138 data = self.collect_data(caller)
139 log.debug("data collected: %s" % data) 139 log.debug("data collected: %s" % data)
140 self.host.launchAction(self.id, data, profile=profile) 140 self.host.action_launch(self.id, data, profile=profile)
141 141
142 142
143 class MenuItemLocal(MenuItem): 143 class MenuItemLocal(MenuItem):
144 """A MenuItem with a local callback""" 144 """A MenuItem with a local callback"""
145 145
146 CALLABLE = True 146 CALLABLE = True
147 147
148 def __init__(self, type_, name, name_i18n, callback, extra=None): 148 def __init__(self, type_, name, name_i18n, callback, extra=None):
149 """ 149 """
150 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 150 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
151 @param name(unicode): canonical name of the item 151 @param name(unicode): canonical name of the item
152 @param name_i18n(unicode): translated name of the item 152 @param name_i18n(unicode): translated name of the item
153 @param callback(callable): local callback. 153 @param callback(callable): local callback.
154 Will be called with no argument if data_collector is None 154 Will be called with no argument if data_collector is None
155 and with caller, profile, and requested data otherwise 155 and with caller, profile, and requested data otherwise
156 @param extra(dict[unicode, unicode], None): same as in [addMenus] 156 @param extra(dict[unicode, unicode], None): same as in [add_menus]
157 """ 157 """
158 MenuItem.__init__(self, name, name_i18n, extra, type_) 158 MenuItem.__init__(self, name, name_i18n, extra, type_)
159 self.callback = callback 159 self.callback = callback
160 160
161 def call(self, caller, profile=C.PROF_KEY_NONE): 161 def call(self, caller, profile=C.PROF_KEY_NONE):
162 data_collector = QuickMenusManager.getDataCollector(self.type) 162 data_collector = QuickMenusManager.get_data_collector(self.type)
163 if data_collector is None: 163 if data_collector is None:
164 # FIXME: would not it be better if caller and profile where used as arguments? 164 # FIXME: would not it be better if caller and profile where used as arguments?
165 self.callback() 165 self.callback()
166 else: 166 else:
167 self.callback(caller, self.collectData(caller), profile) 167 self.callback(caller, self.collect_data(caller), profile)
168 168
169 169
170 class MenuHook(MenuItemLocal): 170 class MenuHook(MenuItemLocal):
171 """A MenuItem which replace an expected item from backend""" 171 """A MenuItem which replace an expected item from backend"""
172 172
214 try: 214 try:
215 return self._items[item.canonical] 215 return self._items[item.canonical]
216 except KeyError: 216 except KeyError:
217 raise KeyError(item) 217 raise KeyError(item)
218 218
219 def getOrCreate(self, item): 219 def get_or_create(self, item):
220 log.debug( 220 log.debug(
221 "MenuContainer getOrCreate: item=%s name=%s\nlist=%s" 221 "MenuContainer get_or_create: item=%s name=%s\nlist=%s"
222 % (item, item.canonical, list(self._items.keys())) 222 % (item, item.canonical, list(self._items.keys()))
223 ) 223 )
224 try: 224 try:
225 return self[item] 225 return self[item]
226 except KeyError: 226 except KeyError:
227 self.append(item) 227 self.append(item)
228 return item 228 return item
229 229
230 def getActiveMenus(self): 230 def get_active_menus(self):
231 """Return an iterator on active children""" 231 """Return an iterator on active children"""
232 for child in self._items.values(): 232 for child in self._items.values():
233 if child.ACTIVE: 233 if child.ACTIVE:
234 yield child 234 yield child
235 235
282 } # No data is associated with C.MENU_GLOBAL items 282 } # No data is associated with C.MENU_GLOBAL items
283 283
284 def __init__(self, host, menus=None, language=None): 284 def __init__(self, host, menus=None, language=None):
285 """ 285 """
286 @param host: %(doc_host)s 286 @param host: %(doc_host)s
287 @param menus(iterable): menus as in [addMenus] 287 @param menus(iterable): menus as in [add_menus]
288 @param language: same as in [i18n.languageSwitch] 288 @param language: same as in [i18n.language_switch]
289 """ 289 """
290 self.host = host 290 self.host = host
291 MenuBase.host = host 291 MenuBase.host = host
292 self.language = language 292 self.language = language
293 self.menus = {} 293 self.menus = {}
294 if menus is not None: 294 if menus is not None:
295 self.addMenus(menus) 295 self.add_menus(menus)
296 296
297 def _getPathI18n(self, path): 297 def _get_path_i_1_8_n(self, path):
298 """Return translated version of path""" 298 """Return translated version of path"""
299 languageSwitch(self.language) 299 language_switch(self.language)
300 path_i18n = [_(elt) for elt in path] 300 path_i18n = [_(elt) for elt in path]
301 languageSwitch() 301 language_switch()
302 return path_i18n 302 return path_i18n
303 303
304 def _createCategories(self, type_, path, path_i18n=None, top_extra=None): 304 def _create_categories(self, type_, path, path_i18n=None, top_extra=None):
305 """Create catogories of the path 305 """Create catogories of the path
306 306
307 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 307 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
308 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.importMenu] 308 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.import_menu]
309 @param path_i18n(list[unicode], None): translated menu path (same lenght as path) or None to get deferred translation of path 309 @param path_i18n(list[unicode], None): translated menu path (same lenght as path) or None to get deferred translation of path
310 @param top_extra: extra data to use on the first element of path only. If the first element already exists and is reused, top_extra will be ignored (you'll have to manually change it if you really want to). 310 @param top_extra: extra data to use on the first element of path only. If the first element already exists and is reused, top_extra will be ignored (you'll have to manually change it if you really want to).
311 @return (MenuContainer): last category created, or MenuType if path is empty 311 @return (MenuContainer): last category created, or MenuType if path is empty
312 """ 312 """
313 if path_i18n is None: 313 if path_i18n is None:
314 path_i18n = self._getPathI18n(path) 314 path_i18n = self._get_path_i_1_8_n(path)
315 assert len(path) == len(path_i18n) 315 assert len(path) == len(path_i18n)
316 menu_container = self.menus.setdefault(type_, MenuType(type_)) 316 menu_container = self.menus.setdefault(type_, MenuType(type_))
317 317
318 for idx, category in enumerate(path): 318 for idx, category in enumerate(path):
319 menu_category = MenuCategory(category, path_i18n[idx], extra=top_extra) 319 menu_category = MenuCategory(category, path_i18n[idx], extra=top_extra)
320 menu_container = menu_container.getOrCreate(menu_category) 320 menu_container = menu_container.get_or_create(menu_category)
321 top_extra = None 321 top_extra = None
322 322
323 return menu_container 323 return menu_container
324 324
325 @staticmethod 325 @staticmethod
326 def addDataCollector(type_, data_collector): 326 def add_data_collector(type_, data_collector):
327 """Associate a data collector to a menu type 327 """Associate a data collector to a menu type
328 328
329 A data collector is a method or a map which allow to collect context data to construct the dictionnary which will be sent to the bridge method managing the menu item. 329 A data collector is a method or a map which allow to collect context data to construct the dictionnary which will be sent to the bridge method managing the menu item.
330 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 330 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
331 @param data_collector(dict[unicode,unicode], callable, None): can be: 331 @param data_collector(dict[unicode,unicode], callable, None): can be:
332 - a dict which map data name to local name. 332 - a dict which map data name to local name.
333 The attribute named after the dict values will be getted from caller, and put in data. 333 The attribute named after the dict values will be getted from caller, and put in data.
334 e.g.: if data_collector={'room_jid':'target'}, then the "room_jid" data will be the value of the "target" attribute of the caller. 334 e.g.: if data_collector={'room_jid':'target'}, then the "room_jid" data will be the value of the "target" attribute of the caller.
335 - a callable which must return the data dictionnary. callable will have caller and item name as argument 335 - a callable which must return the data dictionnary. callable will have caller and item name as argument
336 - None: an empty dict will be used 336 - None: an empty dict will be used
337 """ 337 """
338 QuickMenusManager._data_collectors[type_] = data_collector 338 QuickMenusManager._data_collectors[type_] = data_collector
339 339
340 @staticmethod 340 @staticmethod
341 def getDataCollector(type_): 341 def get_data_collector(type_):
342 """Get data_collector associated to type_ 342 """Get data_collector associated to type_
343 343
344 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 344 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
345 @return (callable, dict, None): data_collector 345 @return (callable, dict, None): data_collector
346 """ 346 """
347 try: 347 try:
348 return QuickMenusManager._data_collectors[type_] 348 return QuickMenusManager._data_collectors[type_]
349 except KeyError: 349 except KeyError:
350 log.error("No data collector registered for {}".format(type_)) 350 log.error("No data collector registered for {}".format(type_))
351 return None 351 return None
352 352
353 def addMenuItem(self, type_, path, item, path_i18n=None, top_extra=None): 353 def add_menu_item(self, type_, path, item, path_i18n=None, top_extra=None):
354 """Add a MenuItemBase instance 354 """Add a MenuItemBase instance
355 355
356 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 356 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
357 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.importMenu], stop at the last parent category 357 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.import_menu], stop at the last parent category
358 @param item(MenuItem): a instancied item 358 @param item(MenuItem): a instancied item
359 @param path_i18n(list[unicode],None): translated menu path (same lenght as path) or None to use deferred translation of path 359 @param path_i18n(list[unicode],None): translated menu path (same lenght as path) or None to use deferred translation of path
360 @param top_extra: same as in [_createCategories] 360 @param top_extra: same as in [_create_categories]
361 """ 361 """
362 if path_i18n is None: 362 if path_i18n is None:
363 path_i18n = self._getPathI18n(path) 363 path_i18n = self._get_path_i_1_8_n(path)
364 assert path and len(path) == len(path_i18n) 364 assert path and len(path) == len(path_i18n)
365 365
366 menu_container = self._createCategories(type_, path, path_i18n, top_extra) 366 menu_container = self._create_categories(type_, path, path_i18n, top_extra)
367 367
368 if item in menu_container: 368 if item in menu_container:
369 if isinstance(item, MenuHook): 369 if isinstance(item, MenuHook):
370 menu_container.replace(item) 370 menu_container.replace(item)
371 else: 371 else:
382 else: 382 else:
383 log.error("Conflicting menus at path [{}]".format(path)) 383 log.error("Conflicting menus at path [{}]".format(path))
384 else: 384 else:
385 log.debug("Adding menu [{type_}] {path}".format(type_=type_, path=path)) 385 log.debug("Adding menu [{type_}] {path}".format(type_=type_, path=path))
386 menu_container.append(item) 386 menu_container.append(item)
387 self.host.callListeners("menu", type_, path, path_i18n, item) 387 self.host.call_listeners("menu", type_, path, path_i18n, item)
388 388
389 def addMenu( 389 def add_menu(
390 self, 390 self,
391 type_, 391 type_,
392 path, 392 path,
393 path_i18n=None, 393 path_i18n=None,
394 extra=None, 394 extra=None,
396 id_=None, 396 id_=None,
397 callback=None, 397 callback=None,
398 ): 398 ):
399 """Add a menu item 399 """Add a menu item
400 400
401 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 401 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
402 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.importMenu] 402 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.import_menu]
403 @param path_i18n(list[unicode], None): translated menu path (same lenght as path), or None to get deferred translation 403 @param path_i18n(list[unicode], None): translated menu path (same lenght as path), or None to get deferred translation
404 @param extra(dict[unicode, unicode], None): same as in [addMenus] 404 @param extra(dict[unicode, unicode], None): same as in [add_menus]
405 @param top_extra: same as in [_createCategories] 405 @param top_extra: same as in [_create_categories]
406 @param id_(unicode): callback id (mutually exclusive with callback) 406 @param id_(unicode): callback id (mutually exclusive with callback)
407 @param callback(callable): local callback (mutually exclusive with id_) 407 @param callback(callable): local callback (mutually exclusive with id_)
408 """ 408 """
409 if path_i18n is None: 409 if path_i18n is None:
410 path_i18n = self._getPathI18n(path) 410 path_i18n = self._get_path_i_1_8_n(path)
411 assert bool(id_) ^ bool(callback) # we must have id_ xor callback defined 411 assert bool(id_) ^ bool(callback) # we must have id_ xor callback defined
412 if id_: 412 if id_:
413 menu_item = MenuItemDistant( 413 menu_item = MenuItemDistant(
414 self.host, type_, path[-1], path_i18n[-1], id_=id_, extra=extra 414 self.host, type_, path[-1], path_i18n[-1], id_=id_, extra=extra
415 ) 415 )
416 else: 416 else:
417 menu_item = MenuItemLocal( 417 menu_item = MenuItemLocal(
418 type_, path[-1], path_i18n[-1], callback=callback, extra=extra 418 type_, path[-1], path_i18n[-1], callback=callback, extra=extra
419 ) 419 )
420 self.addMenuItem(type_, path[:-1], menu_item, path_i18n[:-1], top_extra) 420 self.add_menu_item(type_, path[:-1], menu_item, path_i18n[:-1], top_extra)
421 421
422 def addMenus(self, menus, top_extra=None): 422 def add_menus(self, menus, top_extra=None):
423 """Add several menus at once 423 """Add several menus at once
424 424
425 @param menus(iterable): iterable with: 425 @param menus(iterable): iterable with:
426 id_(unicode,callable): id of distant callback or local callback 426 id_(unicode,callable): id of distant callback or local callback
427 type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 427 type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
428 path(iterable[unicode]): same as in [sat.core.sat_main.SAT.importMenu] 428 path(iterable[unicode]): same as in [sat.core.sat_main.SAT.import_menu]
429 path_i18n(iterable[unicode]): translated menu path (same lenght as path) 429 path_i18n(iterable[unicode]): translated menu path (same lenght as path)
430 extra(dict[unicode,unicode]): dictionary of extra data (used on the leaf menu), can be: 430 extra(dict[unicode,unicode]): dictionary of extra data (used on the leaf menu), can be:
431 - "icon": icon name 431 - "icon": icon name
432 @param top_extra: same as in [_createCategories] 432 @param top_extra: same as in [_create_categories]
433 """ 433 """
434 # TODO: manage icons 434 # TODO: manage icons
435 for id_, type_, path, path_i18n, extra in menus: 435 for id_, type_, path, path_i18n, extra in menus:
436 if callable(id_): 436 if callable(id_):
437 self.addMenu( 437 self.add_menu(
438 type_, path, path_i18n, callback=id_, extra=extra, top_extra=top_extra 438 type_, path, path_i18n, callback=id_, extra=extra, top_extra=top_extra
439 ) 439 )
440 else: 440 else:
441 self.addMenu( 441 self.add_menu(
442 type_, path, path_i18n, id_=id_, extra=extra, top_extra=top_extra 442 type_, path, path_i18n, id_=id_, extra=extra, top_extra=top_extra
443 ) 443 )
444 444
445 def addMenuHook( 445 def add_menu_hook(
446 self, type_, path, path_i18n=None, extra=None, top_extra=None, callback=None 446 self, type_, path, path_i18n=None, extra=None, top_extra=None, callback=None
447 ): 447 ):
448 """Helper method to add a menu hook 448 """Helper method to add a menu hook
449 449
450 Menu hooks are local menus which override menu given by backend 450 Menu hooks are local menus which override menu given by backend
451 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 451 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
452 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.importMenu] 452 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.import_menu]
453 @param path_i18n(list[unicode], None): translated menu path (same lenght as path), or None to get deferred translation 453 @param path_i18n(list[unicode], None): translated menu path (same lenght as path), or None to get deferred translation
454 @param extra(dict[unicode, unicode], None): same as in [addMenus] 454 @param extra(dict[unicode, unicode], None): same as in [add_menus]
455 @param top_extra: same as in [_createCategories] 455 @param top_extra: same as in [_create_categories]
456 @param callback(callable): local callback (mutually exclusive with id_) 456 @param callback(callable): local callback (mutually exclusive with id_)
457 """ 457 """
458 if path_i18n is None: 458 if path_i18n is None:
459 path_i18n = self._getPathI18n(path) 459 path_i18n = self._get_path_i_1_8_n(path)
460 menu_item = MenuHook( 460 menu_item = MenuHook(
461 type_, path[-1], path_i18n[-1], callback=callback, extra=extra 461 type_, path[-1], path_i18n[-1], callback=callback, extra=extra
462 ) 462 )
463 self.addMenuItem(type_, path[:-1], menu_item, path_i18n[:-1], top_extra) 463 self.add_menu_item(type_, path[:-1], menu_item, path_i18n[:-1], top_extra)
464 log.info("Menu hook set on {path} ({type_})".format(path=path, type_=type_)) 464 log.info("Menu hook set on {path} ({type_})".format(path=path, type_=type_))
465 465
466 def addCategory(self, type_, path, path_i18n=None, extra=None, top_extra=None): 466 def add_category(self, type_, path, path_i18n=None, extra=None, top_extra=None):
467 """Create a category with all parents, and set extra on the last one 467 """Create a category with all parents, and set extra on the last one
468 468
469 @param type_(unicode): same as in [sat.core.sat_main.SAT.importMenu] 469 @param type_(unicode): same as in [sat.core.sat_main.SAT.import_menu]
470 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.importMenu] 470 @param path(list[unicode]): same as in [sat.core.sat_main.SAT.import_menu]
471 @param path_i18n(list[unicode], None): translated menu path (same lenght as path), or None to get deferred translation of path 471 @param path_i18n(list[unicode], None): translated menu path (same lenght as path), or None to get deferred translation of path
472 @param extra(dict[unicode, unicode], None): same as in [addMenus] (added on the leaf category only) 472 @param extra(dict[unicode, unicode], None): same as in [add_menus] (added on the leaf category only)
473 @param top_extra: same as in [_createCategories] 473 @param top_extra: same as in [_create_categories]
474 @return (MenuCategory): last category add 474 @return (MenuCategory): last category add
475 """ 475 """
476 if path_i18n is None: 476 if path_i18n is None:
477 path_i18n = self._getPathI18n(path) 477 path_i18n = self._get_path_i_1_8_n(path)
478 last_container = self._createCategories( 478 last_container = self._create_categories(
479 type_, path, path_i18n, top_extra=top_extra 479 type_, path, path_i18n, top_extra=top_extra
480 ) 480 )
481 last_container.setExtra(extra) 481 last_container.set_extra(extra)
482 return last_container 482 return last_container
483 483
484 def getMainContainer(self, type_): 484 def get_main_container(self, type_):
485 """Get a main MenuType container 485 """Get a main MenuType container
486 486
487 @param type_: a C.MENU_* constant 487 @param type_: a C.MENU_* constant
488 @return(MenuContainer): the main container 488 @return(MenuContainer): the main container
489 """ 489 """