""" This file is part of open source project located at https://github.com/polinom/bunny-xbmc. It is a framework that makes xbmc addons development easy. Feel free to contribute. """ import xbmcgui import xbmcaddon import xbmc from framework.utils import notify from threading import Thread from thread import start_new_thread from framework.settings import SCRIPT_ID __cwd__ = xbmcaddon.Addon(id=SCRIPT_ID).getAddonInfo('path') __skinsdir__ = "DefaultSkin" # broken old version in thread # def in_thread(func): # def threaded_func(*args,**kwargs): # t_id = start_new_thread(func, args, kwargs) # return threaded_func # temporary does nothing. # just returns the same function # TODO: fix problem with auth proccess. # Prevent situation when multiple treads are getting new token in the same time. # Concurency system need to be involved. def in_thread(func): return func class Settings(object): def __init__(self): self.settings = xbmcaddon.Addon(SCRIPT_ID) def GetValue(self, key): val = self.settings.getSetting(key) if val and 'none' not in val: return val return None def SetValue(self, key, value): self.settings.setSetting(key, value) def Reset(self, key): self.settings.setSetting(key, 'none') def method_factory(name): def fun(self, _self, controlID): pass return fun class XMLWindowMetaclass(type): def __new__(metacls, name, bases, dct): if len(bases): parent_callback_click_stack = getattr(bases[0], "callback_click_stack", {}) parent_callback_focus_stack = getattr(bases[0], "callback_focus_stack", {}) parent_callback_right_stack = getattr(bases[0], "callback_right_stack", {}) parent_callback_left_stack = getattr(bases[0], "callback_left_stack", {}) parent_callback_up_stack = getattr(bases[0], "callback_up_stack", {}) parent_callback_down_stack = getattr(bases[0], "callback_down_stack", {}) parent_callback_mousemove_stack = getattr(bases[0], "callback_mousemove_stack", {}) parent_action_callbacks = getattr(bases[0], "action_callbacks", {}) callback_click_stack = dct.get('callback_click_stack', {}) callback_focus_stack = dct.get('callback_focus_stack', {}) callback_right_stack = dct.get('callback_right_stack', {}) callback_left_stack = dct.get('callback_left_stack', {}) callback_up_stack = dct.get('callback_up_stack', {}) callback_down_stack = dct.get('callback_down_stack', {}) callback_mousemove_stack = dct.get('callback_mousemove_stack', {}) action_callbacks = dct.get('action_callbacks', {}) # combine dictionaries from base class callback_focus_stack = dict(callback_focus_stack, **parent_callback_focus_stack) callback_click_stack = dict(callback_click_stack, **parent_callback_click_stack) callback_right_stack = dict(callback_right_stack, **parent_callback_right_stack) callback_left_stack = dict(callback_left_stack, **parent_callback_left_stack) callback_up_stack = dict(callback_up_stack, **parent_callback_up_stack) callback_down_stack = dict(callback_down_stack, **parent_callback_down_stack) callback_mousemove_stack = dict(callback_mousemove_stack, **parent_callback_mousemove_stack) action_callbacks = dict(action_callbacks, **parent_action_callbacks) # for action_id, method_name in action_callbacks.items(): # if not dct.get(method_name, False): # dct[method_name] = method_factory(method_name) for func_name, func in dct.items(): # if on click callbacks declared if hasattr(func, "_callback_to_onclick"): for controlId in func._callback_to_onclick: callback_list = callback_click_stack.get(controlId) if callback_list is not None: callback_list.append(func) callback_click_stack[controlId] = callback_list else: callback_click_stack[controlId] = [func] # if on focus callbacks declared if hasattr(func, "_callback_to_onfocus"): for controlId in func._callback_to_onfocus: callback_list = callback_focus_stack.get(controlId) if callback_list is not None: callback_list.append(func) callback_focus_stack[controlId] = callback_list else: callback_focus_stack[controlId] = [func] # if on right callbacks declared if hasattr(func, "_callback_to_onright"): for controlId in func._callback_to_onright: callback_list = callback_right_stack.get(controlId) if callback_list is not None: callback_list.append(func) callback_right_stack[controlId] = callback_list else: callback_right_stack[controlId] = [func] # if on right callbacks declared if hasattr(func, "_callback_to_onleft"): for controlId in func._callback_to_onleft: callback_list = callback_left_stack.get(controlId) if callback_list is not None: callback_list.append(func) callback_left_stack[controlId] = callback_list else: callback_left_stack[controlId] = [func] # if on up callbacks declared if hasattr(func, "_callback_to_onup"): for controlId in func._callback_to_onup: callback_list = callback_up_stack.get(controlId) if callback_list is not None: callback_list.append(func) callback_up_stack[controlId] = callback_list else: callback_up_stack[controlId] = [func] # if on right callbacks declared if hasattr(func, "_callback_to_ondown"): for controlId in func._callback_to_ondown: callback_list = callback_down_stack.get(controlId) if callback_list is not None: callback_list.append(func) callback_down_stack[controlId] = callback_list else: callback_down_stack[controlId] = [func] # if on right callbacks declared if hasattr(func, "_callback_to_onmousemove"): for controlId in func._callback_to_onmousemove: callback_list = callback_mousemove_stack.get(controlId) if callback_list is not None: callback_list.append(func) callback_mousemove_stack[controlId] = callback_list else: callback_mousemove_stack[controlId] = [func] dct["callback_click_stack"] = callback_click_stack dct["callback_focus_stack"] = callback_focus_stack dct["callback_right_stack"] = callback_right_stack dct["callback_left_stack"] = callback_left_stack dct["callback_up_stack"] = callback_up_stack dct["callback_down_stack"] = callback_down_stack dct["callback_mousemove_stack"] = callback_mousemove_stack return type.__new__(metacls, name, bases, dct) class Manager(object): def __init__(self, window): self.window = window def show(self, *ids): for id in ids: self.window.getControl(id).setVisible(True) def hide(self, *ids): for id in ids: self.window.getControl(id).setVisible(False) def goto(self, id): self.window.setFocusId(id) class Progress(object): def __init__(self): self.progress = xbmcgui.DialogProgress() def show(self, message="Loading"): self.progress.create(message) def hide(self): self.progress.close() def with_progress(func): def with_progress_func(self, *args, **kwargs): xbmc.executebuiltin("ActivateWindow(busydialog)") try: resp = func(self, *args, **kwargs) finally: xbmc.executebuiltin("Dialog.Close(busydialog)") resp = None return resp return with_progress_func class XMLWindow(xbmcgui.WindowXML): xml = None # extra methods you can define to react on actions action_callbacks = {107: 'onMouseMove', 10: 'onExit', 0: 'onCtrl', 4: 'onDown', 3: 'onUp', 1: 'onLeft', 2: 'onRight'} config = Settings() def __new__(cls, *args, **kwargs): args = list(args) args.insert(0, cls.xml) args.insert(1, __cwd__) args.insert(2, __skinsdir__) return xbmcgui.WindowXML.__new__(cls, *args, **kwargs) def __init__(self, *args, **kwargs): super(XMLWindow, self).__init__(*args, **kwargs) self._focused_id_stack = [] self.progress = Progress() self.params = None if kwargs.get('params'): self.params = kwargs['params'] self.manager = Manager(self) __metaclass__ = XMLWindowMetaclass def onRight(self, _self, controlID): callbacks_list = self.callback_right_stack.get(controlID) if callbacks_list is not None: for callback in callbacks_list: callback(self) def onLeft(self, _self, controlID): callbacks_list = self.callback_left_stack.get(controlID) if callbacks_list is not None: for callback in callbacks_list: callback(self) def onUp(self, _self, controlID): callbacks_list = self.callback_up_stack.get(controlID) if callbacks_list is not None: for callback in callbacks_list: callback(self) def onDown(self, _self, controlID): callbacks_list = self.callback_down_stack.get(controlID) if callbacks_list is not None: for callback in callbacks_list: callback(self) def onFocus(self, controlID): self._focused_id_stack = self._focused_id_stack[-3:] self._focused_id_stack.append(self.getFocusId()) callbacks_list = self.callback_focus_stack.get(controlID) if callbacks_list is not None: for callback in callbacks_list: callback(self) self._onFocus_called = True def onClick(self, controlID): callbacks_list = self.callback_click_stack.get(controlID) if callbacks_list is not None: for callback in callbacks_list: callback(self) def onMouseMove(self, _self, controlID): callbacks_list = self.callback_mousemove_stack.get(controlID) if callbacks_list is not None: for callback in callbacks_list: callback(self) # depending on callback name (onRight, onUp, onDown, onLeft) we need to return curent ot previous focused id # all previous focused ids stored in self._focused_id_stack def get_focus_id(self, callback): special_case_callbacks = ['onUp', 'onDown', 'onLeft', 'onRight'] if callback in special_case_callbacks and self._onFocus_called: self._onFocus_called = False return self._focused_id_stack[-2] return self.getFocusId() def onAction(self, action): action_id = action.getId() callback_name = self.action_callbacks.get(action_id, None) current_focus_id = self.get_focus_id(callback_name) if callback_name is not None: callback = getattr(self, callback_name, None) if callback is not None: callback(self, current_focus_id) # define low level on action behavior return super(XMLWindow, self).onAction(action) def notify(self, message, header="Message"): xbmc.executebuiltin("XBMC.Notification(%s,%s)" % (header, message)) def onright(*controlIDs): def real_decorator(func): control_ids = getattr(func, "_callback_to_onright", None) if control_ids is not None: control_ids.extend(list(controlIDs)) setattr(func, "_callback_to_onright", control_ids) else: setattr(func, "_callback_to_onright", list(controlIDs)) return func return real_decorator def onleft(*controlIDs): def real_decorator(func): control_ids = getattr(func, "_callback_to_onleft", None) if control_ids is not None: control_ids.extend(list(controlIDs)) setattr(func, "_callback_to_onleft", control_ids) else: setattr(func, "_callback_to_onleft", list(controlIDs)) return func return real_decorator def onup(*controlIDs): def real_decorator(func): control_ids = getattr(func, "_callback_to_onup", None) if control_ids is not None: control_ids.extend(list(controlIDs)) setattr(func, "_callback_to_onup", control_ids) else: setattr(func, "_callback_to_onup", list(controlIDs)) return func return real_decorator def ondown(*controlIDs): def real_decorator(func): control_ids = getattr(func, "_callback_to_ondown", None) if control_ids is not None: control_ids.extend(list(controlIDs)) setattr(func, "_callback_to_ondown", control_ids) else: setattr(func, "_callback_to_ondown", list(controlIDs)) return func return real_decorator def onclick(*controlIDs): def real_decorator(func): control_ids = getattr(func, "_callback_to_onclick", None) if control_ids is not None: control_ids.extend(list(controlIDs)) setattr(func, "_callback_to_onclick", control_ids) else: setattr(func, "_callback_to_onclick", list(controlIDs)) return func return real_decorator def onfocus(*controlIDs): def real_decorator(func): control_ids = getattr(func, "_callback_to_onfocus", None) if control_ids is not None: control_ids.extend(list(controlIDs)) setattr(func, "_callback_to_onfocus", control_ids) else: setattr(func, "_callback_to_onfocus", list(controlIDs)) return func return real_decorator def onmousemove(*controlIDs): def real_decorator(func): control_ids = getattr(func, "_callback_to_onmousemove", None) if control_ids is not None: control_ids.extend(list(controlIDs)) setattr(func, "_callback_to_onmousemove", control_ids) else: setattr(func, "_callback_to_onmousemove", list(controlIDs)) return func return real_decorator