# -*- coding: utf-8 -*- # ------------------------------------------------------------ # XBMC Config Menu # ------------------------------------------------------------ from __future__ import division import sys, os, inspect, xbmcgui, xbmc PY3 = False if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int from builtins import range from past.utils import old_div from core import channeltools, servertools, scrapertools from platformcode import config, logger, platformtools from core.support import log, dbg, match class SettingsWindow(xbmcgui.WindowXMLDialog): """ Clase derivada que permite utilizar cuadros de configuracion personalizados. Esta clase deriva de xbmcgui.WindowXMLDialog y permite crear un cuadro de dialogo con controles del tipo: Radio Button (bool), Cuadro de texto (text), Lista (list) y Etiquetas informativas (label). Tambien podemos personalizar el cuadro añadiendole un titulo (title). Metodo constructor: SettingWindow(listado_controles, dict_values, title, callback, item) Parametros: listado_controles: (list) Lista de controles a incluir en la ventana, segun el siguiente esquema: (opcional)list_controls= [ {'id': "nameControl1", 'type': "bool", # bool, text, list, label 'label': "Control 1: tipo RadioButton", 'color': '0xFFee66CC', # color del texto en formato ARGB hexadecimal 'default': True, 'enabled': True, 'visible': True }, {'id': "nameControl2", 'type': "text", # bool, text, list, label 'label': "Control 2: tipo Cuadro de texto", 'color': '0xFFee66CC', 'default': "Valor por defecto", 'hidden': False, # only for type = text Indica si hay que ocultar el texto (para passwords) 'enabled': True, 'visible': True }, {'id': "nameControl3", 'type': "list", # bool, text, list, label 'label': "Control 3: tipo Lista", 'color': '0xFFee66CC', 'default': 0, # Indice del valor por defecto en lvalues 'enabled': True, 'visible': True, 'lvalues':["item1", "item2", "item3", "item4"], # only for type = list }, {'id': "nameControl4", 'type': "label", # bool, text, list, label 'label': "Control 4: tipo Etiqueta", 'color': '0xFFee66CC', 'enabled': True, 'visible': True }] Si no se incluye el listado_controles, se intenta obtener del json del canal desde donde se hace la llamada. El formato de los controles en el json es: { ... ... "settings": [ { "id": "name_control_1", "type": "bool", "label": "Control 1: tipo RadioButton", "default": false, "enabled": true, "visible": true, "color": "0xFFee66CC" }, { "id": "name_control_2", "type": "text", "label": "Control 2: tipo Cuadro de texto", "default": "Valor por defecto", "hidden": true, "enabled": true, "visible": true, "color": "0xFFee66CC" }, { "id": "name_control_3", "type": "list", "label": "Control 3: tipo Lista", "default": 0, "enabled": true, "visible": true, "color": "0xFFee66CC", "lvalues": [ "item1", "item2", "item3", "item4" ] }, { "id": "name_control_4", "type": "label", "label": "Control 4: tipo Etiqueta", "enabled": true, "visible": true, "color": "0xFFee66CC" }, ] } Los campos 'label', 'default', 'enabled' y 'lvalues' pueden ser un numero precedido de '@'. En cuyo caso se buscara el literal en el archivo string.xml del idioma seleccionado. Los campos 'enabled' y 'visible' admiten los comparadores eq(), gt() e it() y su funcionamiento se describe en: http://kodi.wiki/view/Add-on_settings#Different_types (opcional)dict_values: (dict) Diccionario que representa el par (id: valor) de los controles de la lista. Si algun control de la lista no esta incluido en este diccionario se le asignara el valor por defecto. dict_values={"nameControl1": False, "nameControl2": "Esto es un ejemplo"} (opcional) caption: (str) Titulo de la ventana de configuracion. Se puede localizar mediante un numero precedido de '@' (opcional) callback (str) Nombre de la funcion, del canal desde el que se realiza la llamada, que sera invocada al pulsar el boton aceptar de la ventana. A esta funcion se le pasara como parametros el objeto 'item' y el dicionario 'dict_values'. Si este parametro no existe, se busca en el canal una funcion llamada 'cb_validate_config' y si existe se utiliza como callback. Retorno: Si se especifica 'callback' o el canal incluye 'cb_validate_config' se devolvera lo que devuelva esa funcion. Si no devolvera None Ejemplos de uso: platformtools.show_channel_settings(): Así tal cual, sin pasar ningún argumento, la ventana detecta de que canal se ha hecho la llamada, y lee los ajustes del json y carga los controles, cuando le das a Aceptar los vuelve a guardar. return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, callback='cb', item=item): Así abre la ventana con los controles pasados y los valores de dict_values, si no se pasa dict_values, carga los valores por defecto de los controles, cuando le das a aceptar, llama a la función 'callback' del canal desde donde se ha llamado, pasando como parámetros, el item y el dict_values """ def start(self, list_controls=None, dict_values=None, caption="", callback=None, item=None, custom_button=None, channelpath=None): log() # Media Path self.mediapath = os.path.join(config.get_runtime_path(), 'resources', 'skins', 'Default', 'media') # Params self.list_controls = list_controls self.values = dict_values self.caption = caption self.callback = callback self.item = item if isinstance(custom_button, dict): self.custom_button = {} self.custom_button["label"] = custom_button.get("label", "") self.custom_button["function"] = custom_button.get("function", "") self.custom_button["visible"] = bool(custom_button.get("visible", True)) self.custom_button["close"] = bool(custom_button.get("close", False)) else: self.custom_button = None # Load Channel Settings if not channelpath: channelpath = inspect.currentframe().f_back.f_back.f_code.co_filename self.channel = os.path.basename(channelpath).replace(".py", "") self.ch_type = os.path.basename(os.path.dirname(channelpath)) # If list_controls does not exist, it is removed from the channel json if not self.list_controls: # If the channel path is in the "channels" folder, we get the controls and values using chaneltools if os.path.join(config.get_runtime_path(), "channels") or os.path.join(config.get_runtime_path(), "specials") in channelpath: # The call is made from a channel self.list_controls, default_values = channeltools.get_channel_controls_settings(self.channel) self.kwargs = {"channel": self.channel} self.channelName = channeltools.get_channel_json(self.channel)['name'] # If the channel path is in the "servers" folder, we get the controls and values through servertools elif os.path.join(config.get_runtime_path(), "servers") in channelpath: # The call is made from a channel self.list_controls, default_values = servertools.get_server_controls_settings(self.channel) self.kwargs = {"server": self.channel} self.channelName = servertools.get_server_json(self.channel)['name'] # Else Exit else: return None # If dict_values are not passed, create a blank dict if self.values is None: self.values = {} # Make title if self.caption == "": self.caption = str(config.get_localized_string(30100)) + ' - ' + self.channelName matches = match(self.caption, patron=r'@(\d+)').matches if matches: for m in matches: self.caption = self.caption.replace('@' + match, config.get_localized_string(int(m))) # Show Window self.return_value = None self.doModal() return self.return_value @staticmethod def set_enabled(c, val): if c["type"] == "list": c["control"].setEnabled(val) c["label"].setEnabled(val) else: c["control"].setEnabled(val) @staticmethod def set_visible(c, val): if c["type"] == "list": c["control"].setVisible(val) c["label"].setVisible(val) else: c["control"].setVisible(val) def evaluate_conditions(self): for c in self.list_controls: c["active"] = self.evaluate(self.list_controls.index(c), c["enabled"]) self.set_enabled(c, c["active"]) c["show"] = self.evaluate(self.list_controls.index(c), c["visible"]) if not c["show"]: self.set_visible(c, c["show"]) self.visible_controls = [c for c in self.list_controls if c["show"]] def evaluate(self, index, cond): import re ok = False # If the condition is True or False, there is nothing else to evaluate, that is the value if isinstance(cond, bool): return cond # Get the conditions conditions = re.compile(r'''(!?eq|!?gt|!?lt)?\s*\(\s*([^, ]+)\s*,\s*["']?([^"'\)]+)["']?\)([+|])?''').findall(cond) for operator, id, value, next in conditions: # The id must be a number, otherwise it is not valid and returns False try: id = int(id) except: return False # The control to evaluate on has to be within range, otherwise it returns False if index + id < 0 or index + id >= len(self.list_controls): return False else: # Obtain the value of the control on which it is compared c = self.list_controls[index + id] if c["type"] == "bool": control_value = bool(c["control"].isSelected()) if c["type"] == "text": control_value = c["control"].getText() if c["type"] == "list": control_value = c["label"].getLabel() if c["type"] == "label": control_value = c["control"].getLabel() matches = match(self.caption, patron=r'@(\d+)').matches if matches: for m in matches: self.caption = self.caption.replace('@' + match, config.get_localized_string(int(m))) # Operations lt "less than" and gt "greater than" require comparisons to be numbers, otherwise it returns # False if operator in ["lt", "!lt", "gt", "!gt"]: try: value = int(value) except ValueError: return False # Operation eq "equal to" if operator in ["eq", "!eq"]: # int try: value = int(value) except ValueError: pass # bool if not isinstance(value, int) and value.lower() == "true": value = True elif not isinstance(value, int) and value.lower() == "false": value = False # Operation eq "equal to" if operator == "eq": if control_value == value: ok = True else: ok = False # Operation !eq "not equal to" if operator == "!eq": if not control_value == value: ok = True else: ok = False # operation "gt" "greater than" if operator == "gt": if control_value > value: ok = True else: ok = False # operation "!gt" "not greater than" if operator == "!gt": if not control_value > value: ok = True else: ok = False # operation "lt" "less than" if operator == "lt": if control_value < value: ok = True else: ok = False # operation "!lt" "not less than" if operator == "!lt": if not control_value < value: ok = True else: ok = False # Next operation, if it is "|" (or) and the result is True, there is no sense to follow, it is True if next == "|" and ok is True: break # Next operation, if it is "+" (and) and the result is False, there is no sense to follow, it is False if next == "+" and ok is False: break return ok def add_control_label(self, c): control = xbmcgui.ControlLabel(0, -100, self.controls_width + 20, 40, "", alignment=4, font=self.font, textColor=c["color"]) self.addControl(control) control.setVisible(False) control.setLabel(c["label"]) c["control"] = control def add_control_list(self, c): control = xbmcgui.ControlButton(0, -100, self.controls_width + 10, self.height_control, c["label"], os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'), os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'), 10, textColor=c["color"], font=self.font) label = xbmcgui.ControlLabel(0, -100, self.controls_width, self.height_control, "", font=self.font, textColor=c["color"], alignment= 1 | 4) self.addControl(control) self.addControl(label) control.setVisible(False) label.setVisible(False) label.setLabel(c["lvalues"][self.values[c["id"]]]) c["control"] = control c["label"] = label def add_control_text(self, c): if xbmcgui.ControlEdit == ControlEdit: control = xbmcgui.ControlEdit(0, -100, self.controls_width, self.height_control, c["label"], os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'), os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'), 0, textColor=c["color"], font=self.font, isPassword=c["hidden"], window=self) else: control = xbmcgui.ControlEdit(0, -100, self.controls_width - 5, self.height_control, c["label"], self.font, c["color"], '', 4, isPassword=c["hidden"], focusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'), noFocusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png')) self.addControl(control) control.setVisible(False) control.setLabel(c["label"]) # frodo fix s = self.values[c["id"]] if s is None: s = '' control.setText(s) # control.setText(self.values[c["id"]]) control.setWidth(self.controls_width - 5) control.setHeight(self.height_control) c["control"] = control def add_control_bool(self, c): # Old versions do not support some textures if xbmcgui.__version__ in ["1.2", "2.0"]: control = xbmcgui.ControlRadioButton(0, -100, self.controls_width + 20, self.height_control, label=c["label"], font=self.font, textColor=c["color"], focusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'), noFocusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png')) else: control = xbmcgui.ControlRadioButton(0, -100, self.controls_width + 20, self.height_control, label=c["label"], font=self.font, textColor=c["color"], focusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'), noFocusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'), focusOnTexture=os.path.join(self.mediapath, 'Controls', 'radiobutton-focus.png'), noFocusOnTexture=os.path.join(self.mediapath, 'Controls', 'radiobutton-focus.png'), focusOffTexture=os.path.join(self.mediapath, 'Controls', 'radiobutton-nofocus.png'), noFocusOffTexture=os.path.join(self.mediapath, 'Controls', 'radiobutton-nofocus.png')) self.addControl(control) control.setVisible(False) control.setRadioDimension(x=self.controls_width - (self.height_control - 5), y=0, width=self.height_control - 5, height=self.height_control - 5) control.setSelected(self.values[c["id"]]) c["control"] = control def onInit(self): self.getControl(10004).setEnabled(False) self.getControl(10005).setEnabled(False) self.getControl(10006).setEnabled(False) self.ok_enabled = False self.default_enabled = False # Kodi 18 compatibility if config.get_platform(True)['num_version'] < 18: if xbmcgui.__version__ == "1.2": self.setCoordinateResolution(1) else: self.setCoordinateResolution(5) # Title self.getControl(10002).setLabel(self.caption) if self.custom_button is not None: if self.custom_button['visible']: self.getControl(10006).setLabel(self.custom_button['label']) else: self.getControl(10006).setVisible(False) self.getControl(10004).setPosition(self.getControl(10004).getPosition()[0] + 80, self.getControl(10004).getPosition()[1]) self.getControl(10005).setPosition(self.getControl(10005).getPosition()[0] + 80, self.getControl(10005).getPosition()[1]) # Control Area Dimensions self.controls_width = self.getControl(10007).getWidth() - 30 self.controls_height = self.getControl(10007).getHeight() -100 self.controls_pos_x = self.getControl(10007).getPosition()[0] + self.getControl(10001).getPosition()[0] + 10 self.controls_pos_y = self.getControl(10007).getPosition()[1] + self.getControl(10001).getPosition()[1] self.height_control = 60 self.font = "font16" # In old versions: we create 5 controls, from the contrary when clicking the second control, # automatically change third party label to "Short By: Name" I don't know why ... if xbmcgui.ControlEdit == ControlEdit: for x in range(5): control = xbmcgui.ControlRadioButton(-500, 0, 0, 0, "") self.addControl(control) for c in self.list_controls: # Skip controls that do not have the appropriate values if "type" not in c: continue if "label" not in c: continue if c["type"] != "label" and "id" not in c: continue if c["type"] == "list" and "lvalues" not in c: continue if c["type"] == "list" and not isinstance(c["lvalues"], list): continue if c["type"] == "list" and not len(c["lvalues"]) > 0: continue if c["type"] != "label" and len([control.get("id") for control in self.list_controls if c["id"] == control.get("id")]) > 1: continue # Translation label and lvalues if c['label'].startswith('@') and unicode(c['label'][1:]).isnumeric(): c['label'] = config.get_localized_string(int(c['label'][1:])) if c['type'] == 'list': lvalues = [] for li in c['lvalues']: if li.startswith('@') and unicode(li[1:]).isnumeric(): lvalues.append(config.get_localized_string(int(li[1:]))) else: lvalues.append(li) c['lvalues'] = lvalues # Default values in case the control does not have them if c["type"] == "bool": default = False elif c["type"] == "list": default = 0 else: default = "" # label or text c["default"] = c.get("default", default) c["color"] = c.get("color", "0xFFFFFFFF") c["visible"] = c.get("visible", True) c["enabled"] = c.get("enabled", True) if c["type"] == "label" and "id" not in c: c["id"] = None if c["type"] == "text": c["hidden"] = c.get("hidden", False) # Decide whether to use the default value or the saved value if c["type"] in ["bool", "text", "list"]: if c["id"] not in self.values: if not self.callback: self.values[c["id"]] = config.get_setting(c["id"], **self.kwargs) else: self.values[c["id"]] = c["default"] if c["type"] == "bool": self.add_control_bool(c) elif c["type"] == 'text': self.add_control_text(c) elif c["type"] == 'list': self.add_control_list(c) elif c["type"] == 'label': self.add_control_label(c) self.list_controls = [c for c in self.list_controls if "control" in c] self.evaluate_conditions() self.index = -1 self.dispose_controls(0) self.getControl(100010).setVisible(False) self.getControl(10004).setEnabled(True) self.getControl(10005).setEnabled(True) self.getControl(10006).setEnabled(True) self.ok_enabled = True self.default_enabled = True self.check_default() self.check_ok(self.values) def dispose_controls(self, index, focus=False, force=False): show_controls = old_div(self.controls_height, self.height_control) - 1 visible_count = 0 if focus: if not index >= self.index or not index <= self.index + show_controls: if index < self.index: new_index = index else: new_index = index - show_controls else:new_index = self.index else: if index + show_controls >= len(self.visible_controls): index = len(self.visible_controls) - show_controls - 1 if index < 0: index = 0 new_index = index if self.index != new_index or force: for x, c in enumerate(self.visible_controls): if x < new_index or visible_count > show_controls or not c["show"]: self.set_visible(c, False) else: c["y"] = self.controls_pos_y + visible_count * self.height_control visible_count += 1 if c["type"] != "list": if c["type"] == "bool": c["control"].setPosition(self.controls_pos_x, c["y"]) else: c["control"].setPosition(self.controls_pos_x, c["y"]) else: c["control"].setPosition(self.controls_pos_x, c["y"]) if xbmcgui.__version__ == "1.2": c["label"].setPosition(self.controls_pos_x + self.controls_width - 30, c["y"]) else: c["label"].setPosition(self.controls_pos_x, c["y"]) self.set_visible(c, True) # Calculate the position and size of the ScrollBar hidden_controls = len(self.visible_controls) - show_controls - 1 if hidden_controls < 0: hidden_controls = 0 scrollbar_height = self.getControl(10008).getHeight() - (hidden_controls * 3) scrollbar_y = self.getControl(10008).getPosition()[1] + (new_index * 3) self.getControl(10009).setPosition(self.getControl(10008).getPosition()[0], scrollbar_y) self.getControl(10009).setHeight(scrollbar_height) self.index = new_index if focus: self.setFocus(self.visible_controls[index]["control"]) def check_ok(self, dict_values=None): if not self.callback: if dict_values: self.init_values = dict_values.copy() self.getControl(10004).setEnabled(False) self.ok_enabled = False else: if self.init_values == self.values: self.getControl(10004).setEnabled(False) self.ok_enabled = False else: self.getControl(10004).setEnabled(True) self.ok_enabled = True def check_default(self): if self.custom_button is None: def_values = dict([[c["id"], c.get("default")] for c in self.list_controls if not c["type"] == "label"]) if def_values == self.values: self.getControl(10006).setEnabled(False) self.default_enabled = False else: self.getControl(10006).setEnabled(True) self.default_enabled = True def onClick(self, id): # Default values if id == 10006: if self.custom_button is not None: if self.custom_button["close"]: self.close() if '.' in self.callback: package, self.callback = self.callback.rsplit('.', 1) else: package = '%s.%s' % (self.ch_type, self.channel) try: cb_channel = __import__(package, None, None, [package]) except ImportError: logger.error('Imposible importar %s' % package) else: self.return_value = getattr(cb_channel, self.custom_button['function'])(self.item, self.values) if not self.custom_button["close"]: if isinstance(self.return_value, dict) and "label" in self.return_value: self.getControl(10006).setLabel(self.return_value['label']) for c in self.list_controls: if c["type"] == "text": c["control"].setText(self.values[c["id"]]) if c["type"] == "bool": c["control"].setSelected(self.values[c["id"]]) if c["type"] == "list": c["label"].setLabel(c["lvalues"][self.values[c["id"]]]) self.evaluate_conditions() self.dispose_controls(self.index, force=True) else: for c in self.list_controls: if c["type"] == "text": c["control"].setText(c["default"]) self.values[c["id"]] = c["default"] if c["type"] == "bool": c["control"].setSelected(c["default"]) self.values[c["id"]] = c["default"] if c["type"] == "list": c["label"].setLabel(c["lvalues"][c["default"]]) self.values[c["id"]] = c["default"] self.evaluate_conditions() self.dispose_controls(self.index, force=True) self.check_default() self.check_ok() # Cancel button [X] if id == 10003 or id == 10005: self.close() # OK button if id == 10004: self.close() if self.callback and '.' in self.callback: package, self.callback = self.callback.rsplit('.', 1) else: package = '%s.%s' % (self.ch_type, self.channel) cb_channel = None try: cb_channel = __import__(package, None, None, [package]) except ImportError:logger.error('Impossible to import %s' % package) if self.callback: # If there is a callback function we invoke it... self.return_value = getattr(cb_channel, self.callback)(self.item, self.values) else: # if not, we test if there is a 'cb_validate_config' function in the channel... try: self.return_value = getattr(cb_channel, 'cb_validate_config')(self.item, self.values) except AttributeError: # if 'cb_validate_config' doesn't exist either ... for v in self.values: config.set_setting(v, self.values[v], **self.kwargs) # Adjustment controls, if the value of an adjustment is changed, we change the value saved in the value dictionary # Get the control that has been clicked # control = self.getControl(id) # We look it up in the list of controls for cont in self.list_controls: if cont['type'] == "list" and cont["control"].getId() == id: select = platformtools.dialog_select(config.get_localized_string(30041), cont["lvalues"], self.values[cont["id"]]) if select >= 0: cont["label"].setLabel(cont["lvalues"][select]) self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel()) # If the control is a "bool", we save the new value True / False if cont["type"] == "bool" and cont["control"].getId() == id: self.values[cont["id"]] = bool(cont["control"].isSelected()) # If the control is a "text", we save the new value if cont["type"] == "text" and cont["control"].getId() == id: # Older versions require opening the keyboard manually if xbmcgui.ControlEdit == ControlEdit: keyboard = xbmc.Keyboard(cont["control"].getText(), cont["control"].getLabel(), cont["control"].isPassword) keyboard.setHiddenInput(cont["control"].isPassword) keyboard.doModal() if keyboard.isConfirmed(): cont["control"].setText(keyboard.getText()) self.values[cont["id"]] = cont["control"].getText() self.evaluate_conditions() self.dispose_controls(self.index, force=True) self.check_default() self.check_ok() # Older versions require this feature def onFocus(self, a): pass def onAction(self, raw_action): # Get Focus focus = self.getFocusId() action = raw_action.getId() # On Left if action == 1: # if Focus is on close button if focus == 10003: self.dispose_controls(0, True) self.setFocusId(3001) # if focus is on List else: if self.ok_enabled: self.setFocusId(10004) else: self.setFocusId(10005) # On Right elif action == 2: # if Focus is on button if focus in [10004, 10005, 10006]: self.dispose_controls(0, True) self.setFocusId(3001) # if focus is on List else: self.setFocusId(10003) # On Down elif action == 4: # if focus is on List if focus not in [10004, 10005, 10006]: try: focus_control = [self.visible_controls.index(c) for c in self.visible_controls if c["control"].getId() == self.getFocus().getId()][0] focus_control += 1 except: focus_control = 0 while not focus_control == len(self.visible_controls) and (self.visible_controls[focus_control]["type"] == "label" or not self.visible_controls[focus_control]["active"]): focus_control += 1 if focus_control >= len(self.visible_controls): focus_control = 0 self.setFocusId(3001) self.dispose_controls(focus_control, True) # Else navigate on main buttons elif focus in [10004]: self.setFocusId(10005) elif focus in [10005]: if self.default_enabled: self.setFocusId(10006) elif self.ok_enabled: self.setFocusId(10004) elif focus in [10006]: if self.ok_enabled: self.setFocusId(10004) else: self.setFocusId(10005) # On Up elif action == 3: # if focus is on List if focus not in [10003, 10004, 10005, 10006]: try: focus_control = \ [self.visible_controls.index(c) for c in self.visible_controls if c["control"].getId() == self.getFocus().getId()][0] focus_control -= 1 while not focus_control == -1 and (self.visible_controls[focus_control]["type"] == "label" or not self.visible_controls[focus_control]["active"]): focus_control -= 1 if focus_control < 0: focus_control = len(self.visible_controls) - 1 except: focus_control = 0 self.dispose_controls(focus_control, True) # Else navigate on main buttons elif focus in [10004]: if self.default_enabled: self.setFocusId(10006) else: self.setFocusId(10005) elif focus in [10005]: if self.ok_enabled: self.setFocusId(10004) elif self.default_enabled: self.setFocusId(10006) elif focus in [10006]: self.setFocusId(10005) # Accion 104: Scroll Down elif action == 104: self.dispose_controls(self.index - 1) # Accion 105: Scroll Up elif action == 105: self.dispose_controls(self.index + 1) # ACTION_PREVIOUS_MENU 10 # ACTION_NAV_BACK 92 elif action in [10, 92]: self.close() elif action == 501: self.xx = int(raw_action.getAmount2()) elif action == 504: if self.xx > raw_action.getAmount2(): if old_div((self.xx - int(raw_action.getAmount2())), self.height_control): self.xx -= self.height_control self.dispose_controls(self.index + 1) else: if old_div((int(raw_action.getAmount2()) - self.xx), self.height_control): self.xx += self.height_control self.dispose_controls(self.index - 1) return class ControlEdit(xbmcgui.ControlButton): def __new__(cls, *args, **kwargs): del kwargs["isPassword"] del kwargs["window"] args = list(args) return xbmcgui.ControlButton.__new__(cls, *args, **kwargs) def __init__(self, *args, **kwargs): self.isPassword = kwargs["isPassword"] self.window = kwargs["window"] self.label = "" self.text = "" self.textControl = xbmcgui.ControlLabel(self.getX(), self.getY(), self.getWidth(), self.getHeight(), self.text, font=kwargs["font"], textColor=kwargs["textColor"], alignment= 4 | 1) self.window.addControl(self.textControl) def setLabel(self, val): self.label = val xbmcgui.ControlButton.setLabel(self, val) def getX(self): return xbmcgui.ControlButton.getPosition(self)[0] def getY(self): return xbmcgui.ControlButton.getPosition(self)[1] def setEnabled(self, e): xbmcgui.ControlButton.setEnabled(self, e) self.textControl.setEnabled(e) def setWidth(self, w): xbmcgui.ControlButton.setWidth(self, w) self.textControl.setWidth(old_div(w, 2)) def setHeight(self, w): xbmcgui.ControlButton.setHeight(self, w) self.textControl.setHeight(w) def setPosition(self, x, y): xbmcgui.ControlButton.setPosition(self, x, y) if xbmcgui.__version__ == "1.2": self.textControl.setPosition(x + self.getWidth(), y) else: self.textControl.setPosition(x + old_div(self.getWidth(), 2), y) def setText(self, text): self.text = text if self.isPassword: self.textControl.setLabel("*" * len(self.text)) else: self.textControl.setLabel(self.text) def getText(self): return self.text if not hasattr(xbmcgui, "ControlEdit"): xbmcgui.ControlEdit = ControlEdit