Files
addon/platformcode/xbmc_config_menu.py
2021-11-26 18:55:51 +01:00

547 lines
21 KiB
Python

# -*- 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, jsontools, servertools, filetools, support
from platformcode import config, logger, platformtools
from core.support import match
if sys.version_info[0] >= 3:
from concurrent import futures
else:
from concurrent_py2 import futures
HEADER = 1
LIST = 100
SCROLLBAR = 101
BUTTONS = 200
MUP = 300
MDN = 301
DEL = 302
CONTROLS = [MUP, MDN, DEL]
LEFT = 1
RIGHT =2
UP = 3
DOWN = 4
SCROLLDOWN = 104
SCROLLUP = 105
EXIT = 10
BACKSPACE = 92
PREVIOUS = 10
GESTUREBEGIN = 501
GESTUREPAN = 504
def title(label):
matches = support.match(label, patron=r'@(\d+)').matches
if matches:
for m in matches:
label = label.replace('@' + m, config.getLocalizedString(int(m)))
return label
def getControlList(c, v=None):
labels = []
values = []
currents = {}
if v == None: v = c.get('default', 0)
if c.get('lvalues'):
labels = [title(v) for v in c['lvalues']]
values = range(0, len(labels))
elif c.get('dvalues'):
for l, v in c['dvalues'].items():
labels.append(l)
values.append(v)
if v in values: v = values.index(v)
elif c.get('values'):
values = c['values']
labels = [str(v) for v in values]
if v in values: v = values.index(v)
elif c.get('rvalues'):
values = range(c['rvalues'][0], c['rvalues'][1] + 1, c['rvalues'][2])
labels = [str(v) for v in values]
if labels and values:
currents = {'label':labels[v], 'value':values[v]}
return labels, values, currents
class SettingsWindow(xbmcgui.WindowXMLDialog):
def start(self, *args, **kwargs):
logger.debug()
# Params
# logger.dbg()
self.controls = kwargs.get('list_controls', [])
self.values = kwargs.get('dict_values', {})
self.caption = kwargs.get('caption', '')
self.callback = kwargs.get('callback', None)
self.item = kwargs.get('item', None)
self.customButtons = kwargs.get('custom_buttons', [kwargs.get('custom_button', {})])
self.listPosition = 0
self.buttonPosition = 0
channelpath = kwargs.get('channelpath', inspect.currentframe().f_back.f_back.f_code.co_filename)
if channelpath:
self.channel = filetools.basename(channelpath).replace('.py', '')
self.ch_type = filetools.basename(filetools.dirname(channelpath))
if not self.controls:
self.controls, default_values = channeltools.get_channel_controls_settings(self.channel)
if not self.caption:
if self.ch_type in ['channels', 'specials']:
self.caption = config.getLocalizedString(30100) + ' - ' + channeltools.get_channel_json(self.channel)['name']
elif self.ch_type in ['servers']:
self.caption = config.getLocalizedString(30100) + ' - ' + servertools.get_server_parameters(self.channel)['name']
self.ret = None
self.doModal()
return self.ret
def onInit(self):
xbmc.sleep(150)
self.getControl(HEADER).setLabel(self.caption)
self.LIST = self.getControl(LIST)
self.BUTTONS = self.getControl(BUTTONS)
self.makeControls()
self.makeButtons()
def makeControls(self, index = 0, index2 = None):
itemlist = []
hidden = 0
controllist = []
def makeControl(n, control):
label2 = ''
currentval = control.get('control', config.getSetting(control['id'], self.channel))
control['control'] = currentval
label = title(control.get('label', ''))
if control.get('submenu', False):
label = "•• " + label
if control['type'] == 'list':
if type(currentval) == list: currentval = control['default']
labels, values, currents = getControlList(control, currentval)
label2 = currents.get('label', '')
elif control['type'] == 'multi':
if type(currentval) == list:
currents = [c for l, v, c in [getControlList(control, v) for v in currentval]]
else:
l, v, c = getControlList(control, currentval)
currents = [c]
label2 = ', '.join(c.get('label', '') for c in currents)
elif control['type'] == 'text':
if currentval and control.get('hidden', False) or control.get('mode', '') == 'pass' : label2 = '' * 10
else: label2 = str(currentval if currentval else '')
else: label2 = str(currentval)
item = xbmcgui.ListItem(label, label2)
item.setProperty('type', control['type'])
item.setProperties({'id': str(n), 'type':control['type']})
if control['type'] == 'bool': item.setProperty('bool', 'on.png' if currentval else 'off.png')
return n, item
# for n, control in enumerate(self.controls):
# controllist.append(makeControl(n, control))
with futures.ThreadPoolExecutor() as executor:
itlist = [executor.submit(makeControl, n, control) for n, control in enumerate(self.controls)]
for res in futures.as_completed(itlist):
if res.result():
controllist.append(res.result())
controllist.sort(key=lambda x: x[0])
for n, item in controllist:
if self.evalute('visible', n):
item.setProperty('enabled', str(self.evalute('enabled', n)))
itemlist.append(item)
elif index >= n:
hidden += 1
if hidden:
index -= 1
self.LIST.reset()
self.LIST.addItems(itemlist)
self.setFocusId(LIST)
self.LIST.selectItem(index)
if index2:
self.setFocusId(index2)
def makeButtons(self):
for button in ['ok', 'close', 'reset'] + [b.get('icon', 'btn') for b in self.customButtons]:
item = xbmcgui.ListItem(button)
item.setArt({'button': button + '.png'})
self.BUTTONS.addItem(item)
def evalute(self, name, index):
ok = False
control_value = None
condition = self.controls[index].get(name, True)
if type(condition) == bool:
return condition
conditions = support.match(condition, patron=r'''(!?eq|!?gt|!?lt)?\s*\(\s*([^, ]+)\s*,\s*["']?([^"'\)]+)["']?\)([+|])?''').matches
for operator, _id, value, next in conditions:
value = title(value)
logger.debug('CONDITIONS', operator, _id, value, next)
try:
# The control to evaluate on has to be within range, otherwise it returns False
if index + int(_id) < 0 or index + int(_id) >= len(self.controls):
return False
else:
# Obtain the value of the control on which it is compared
control_value = self.controls[index + int(_id)]["control"]
if type(value) == str:
c = self.controls[index + int(_id)]
if c.get('lvalues'): control_value = title(c['lvalues'][control_value])
elif c.get('dvalues'): control_value = title(c['lvalues'][control_value])
else: return False
except:
for control in self.controls:
if control['id'] == int(_id):
control_value = control["control"]
if not control_value:
continue
# 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 onClick(self, control):
logger.debug('CONTROL', control)
if control in [LIST] and self.getControl(control).getSelectedItem().getProperty('enabled') == 'False':
pass
elif control in [LIST]:
item = self.LIST.getSelectedItem()
_type = item.getProperty('type')
_id = int(item.getProperty('id'))
value = self.controls[_id]['control']
if _type == 'bool':
self.controls[_id]['control'] = False if value else True
elif _type == 'list':
labels, values, c = getControlList(self.controls[_id])
default = labels.index(item.getLabel2())
selection = platformtools.dialogSelect(item.getLabel(), labels, default)
if selection > -1:
self.controls[_id]['control'] = values[selection]
elif _type == 'multi':
labels, values, c = getControlList(self.controls[_id])
default = [labels.index(val.strip()) for val in item.getLabel2().split(',')]
selection = platformtools.dialogMultiselect(item.getLabel(), labels, preselect=default)
self.controls[_id]['control'] = [values[v] for v in selection]
elif _type == 'text':
mode = {'':None, 'number':0, 'date':1, 'time':2, 'ip':3, 'pass':4}[self.controls[_id].get('mode', '')]
if mode != None:
self.controls[_id]['control'] = platformtools.dialogNumeric(4, item.getLabel(), self.controls[_id]['control'])
else:
self.controls[_id]['control'] = platformtools.dialogInput(item.getLabel2(), item.getLabel(), hidden=self.controls[_id].get('hidden', False))
elif _type == 'insert':
self.insertControls(_id)
self.makeControls(_id)
elif control in [MUP]:
pos = self.LIST.getSelectedPosition()
if self.controls[pos-1]['type'] == 'insert' and self.controls[pos-1].get('label'):
self.controls[pos], self.controls[pos-1] = self.controls[pos-1], self.controls[pos]
self.makeControls(pos-1, MUP)
elif control in [MDN]:
pos = self.LIST.getSelectedPosition()
if self.controls[pos+1]['type'] == 'insert' and self.controls[pos+1].get('label'):
self.controls[pos], self.controls[pos+1] = self.controls[pos+1], self.controls[pos]
self.makeControls(pos+1, MDN)
elif control in [DEL]:
pos = self.LIST.getSelectedPosition()
if self.controls[pos]['type'] == 'insert' and self.controls[pos].get('label'):
self.controls.pop(pos)
self.makeControls(pos, DEL if self.controls[pos].get('label') else None)
elif control in [BUTTONS]:
pos = self.BUTTONS.getSelectedPosition()
if pos == 0:
self.saveControls()
self.close()
elif pos == 1:
self.close()
elif pos == 2:
self.resetControls()
else:
self.customAction(pos)
def onAction(self, action):
action = action.getId()
control = self.getFocusId()
self.listPosition = self.LIST.getSelectedPosition()
self.buttonPosition = self.BUTTONS.getSelectedPosition()
item = self.LIST.getSelectedItem()
# logger.dbg()
if item:
if item.getProperty('type') == 'label':
if action in [UP]: self.LIST.selectItem(self.listPosition - 1)
elif action in [DOWN]: self.LIST.selectItem(self.listPosition + 1)
if action in [LEFT]:
if control not in CONTROLS:
self.setFocusId(LIST)
self.LIST.selectItem(self.listPosition)
if action in [RIGHT]:
if control in [LIST]:
if item.getProperty('insert') and item.getLabel():
self.setFocusId(MUP)
elif len(self.controls) > 10:
self.setFocusId(SCROLLBAR)
else:
self.setFocusId(BUTTONS)
self.BUTTONS.selectItem(self.buttonPosition)
if control in [SCROLLBAR]:
self.setFocusId(BUTTONS)
self.BUTTONS.selectItem(self.buttonPosition)
if action in [BACKSPACE, EXIT]:
self.close()
def resetControls(self):
# logger.dbg()
controls = []
for control in self.controls:
if control['type'] == 'insert' and control.get('label'):
continue
control['control'] = control['default']
controls.append(control)
self.controls = controls
self.makeControls()
def customAction(self, position):
_id = position - 3
if '.' in self.customButtons[_id]['function']: package, function = self.customButtons[_id]['function'].rsplit('.', 1)
else:
package = '{}.{}'.format(self.ch_type, self.channel)
function = self.customButtons[_id]['function']
try: cb_channel = __import__(package, None, None, [package])
except ImportError: logger.error('Unable to import ' + package)
values = {}
for control in self.controls:
values[control['id']] = control['control']
results = getattr(cb_channel, function)(self.item, values, self.customButtons[_id])
btn = results.get('button',{})
value = results.get('result',{})
if btn.get('icon'): self.BUTTONS.getListItem(position).setArt({'button':btn['icon'] + '.png'})
# if btn.get('label'): self.BUTTONS.getListItem(position).setLabel(btn['label'])
_reload = False
if value and type(values) == dict:
for c in (self.controls):
if c['type'] in value:
if c['control'] != value[c['type']]:
c['control'] = value[c['type']]
_reload = True
if c['id'] in value:
if c['control'] != value[c['id']]:
c['control'] = value[c['id']]
_reload = True
if _reload: self.makeControls(self.listPosition)
def saveControls(self):
if self.callback:
if '.' in self.callback: package, self.callback = self.callback.rsplit('.', 1)
else: package = '{}.{}'.format(self.ch_type, self.channel)
try: cb_channel = __import__(package, None, None, [package])
except ImportError: logger.error('Unable to import ' + package)
values = {}
for control in self.controls:
if control['type'] == 'insert' and not control.get('label'): continue
if control['type'] == 'list':
if control.get('dvalues'):
control['control'] = list(control['dvalues'].values())[control['control']]
elif control.get('values'):
control['control'] = control['values'][control['control']]
values[control['id']] = control['control']
self.return_value = getattr(cb_channel, self.callback)(self.item, values)
for control in self.controls:
if control['type'] == 'insert': continue
if control['type'] == 'list':
if control.get('dvalues'):
control['control'] = list(control['dvalues'].values())[control['control']]
elif control.get('values'):
control['control'] = control['values'][control['control']]
if self.ch_type in ['channels', 'specials']:
config.setSetting(control['id'], control['control'], self.channel)
elif self.ch_type in ['servers']:
config.setSetting(control['id'], control['control'], server=self.channel)
def insertControls(self, _id):
if '.' in self.controls[_id]['function']: package, function = self.controls[_id]['function'].rsplit('.', 1)
else:
package = '{}.{}'.format(self.ch_type, self.channel)
function = self.controls[_id]['function']
try: cb_channel = __import__(package, None, None, [package])
except ImportError: logger.error('Unable to import ' + package)
values = {}
for control in self.controls:
values[control['id']] = control['control']
results = getattr(cb_channel, function)(self.item, values)
for n, result in enumerate(results):
self.controls.insert(_id, result)
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