- allow multiple xml control files for one wizard (to combine wizards, that should...
[enigma2.git] / lib / python / Screens / Wizard.py
1 from Screen import Screen
2
3 import string
4
5 from Screens.HelpMenu import HelpableScreen
6 from Components.config import config, KEY_LEFT, KEY_RIGHT
7 from Components.Label import Label
8 from Components.Slider import Slider
9 from Components.ActionMap import NumberActionMap
10 from Components.MenuList import MenuList
11 from Components.ConfigList import ConfigList
12 from Components.Sources.List import List
13
14 from enigma import eTimer
15
16 from xml.sax import make_parser
17 from xml.sax.handler import ContentHandler
18
19 class WizardSummary(Screen):
20         skin = """
21         <screen position="0,0" size="132,64">
22                 <widget name="text" position="6,4" size="120,42" font="Regular;14" transparent="1" />
23                 <widget source="parent.list" render="Label" position="6,25" size="120,21" font="Regular;16">
24                         <convert type="StringListSelection" />
25                 </widget>
26         </screen>"""
27         
28         def __init__(self, session, parent):
29                 Screen.__init__(self, session, parent)
30                 
31                 #names = parent.skinName
32                 #if not isinstance(names, list):
33                         #names = [names]
34 #                       
35                 #self.skinName = [x + "_summary" for x in names ]
36                 #self.skinName.append("Wizard")
37                 #print "*************+++++++++++++++++****************++++++++++******************* WizardSummary", self.skinName
38                         #
39                 self["text"] = Label("")
40                 self.onShow.append(self.setCallback)
41                 
42         def setCallback(self):
43                 self.parent.setLCDTextCallback(self.setText)
44                 
45         def setText(self, text):
46                 self["text"].setText(text)
47
48 class Wizard(Screen, HelpableScreen):
49         def createSummary(self):
50                         print "WizardCreateSummary"
51                         return WizardSummary
52
53         class parseWizard(ContentHandler):
54                 def __init__(self, wizard):
55                         self.isPointsElement, self.isReboundsElement = 0, 0
56                         self.wizard = wizard
57                         self.currContent = ""
58                         self.lastStep = 0       
59
60                 def startElement(self, name, attrs):
61                         #print "startElement", name
62                         self.currContent = name
63                         if (name == "step"):
64                                 self.lastStep += 1
65                                 if attrs.has_key('id'):
66                                         id = str(attrs.get('id'))
67                                 else:
68                                         id = ""
69                                 #print "id:", id
70                                 if attrs.has_key('nextstep'):
71                                         nextstep = str(attrs.get('nextstep'))
72                                 else:
73                                         nextstep = None
74                                 if attrs.has_key('timeout'):
75                                         timeout = int(attrs.get('timeout'))
76                                 else:
77                                         timeout = None
78                                 if attrs.has_key('timeoutaction'):
79                                         timeoutaction = str(attrs.get('timeoutaction'))
80                                 else:
81                                         timeoutaction = 'nextpage'
82
83                                 if attrs.has_key('timeoutstep'):
84                                         timeoutstep = str(attrs.get('timeoutstep'))
85                                 else:
86                                         timeoutstep = ''
87                                 self.wizard[self.lastStep] = {"id": id, "condition": "", "text": "", "timeout": timeout, "timeoutaction": timeoutaction, "timeoutstep": timeoutstep, "list": [], "config": {"screen": None, "args": None, "type": "" }, "code": "", "codeafter": "", "nextstep": nextstep}
88                         elif (name == "text"):
89                                 self.wizard[self.lastStep]["text"] = string.replace(str(attrs.get('value')), "\\n", "\n")
90                         elif (name == "displaytext"):
91                                 self.wizard[self.lastStep]["displaytext"] = string.replace(str(attrs.get('value')), "\\n", "\n")
92                         elif (name == "list"):
93                                 if (attrs.has_key('type')):
94                                         if attrs["type"] == "dynamic":
95                                                 self.wizard[self.lastStep]["dynamiclist"] = attrs.get("source")
96                                         #self.wizard[self.lastStep]["list"].append(("Hallo", "test"))
97                                 if (attrs.has_key("evaluation")):
98                                         #print "evaluation"
99                                         self.wizard[self.lastStep]["listevaluation"] = attrs.get("evaluation")
100                                 if (attrs.has_key("onselect")):
101                                         self.wizard[self.lastStep]["onselect"] = attrs.get("onselect")                  
102                         elif (name == "listentry"):
103                                 self.wizard[self.lastStep]["list"].append((str(attrs.get('caption')), str(attrs.get('step'))))
104                         elif (name == "config"):
105                                 type = str(attrs.get('type'))
106                                 self.wizard[self.lastStep]["config"]["type"] = type
107                                 if type == "ConfigList" or type == "standalone":
108                                         exec "from Screens." + str(attrs.get('module')) + " import *"
109                                 
110                                         self.wizard[self.lastStep]["config"]["screen"] = eval(str(attrs.get('screen')))
111                                         if (attrs.has_key('args')):
112                                                 #print "has args"
113                                                 self.wizard[self.lastStep]["config"]["args"] = str(attrs.get('args'))
114                                 elif type == "dynamic":
115                                         self.wizard[self.lastStep]["config"]["source"] = str(attrs.get('source'))
116                                         if (attrs.has_key('evaluation')):
117                                                 self.wizard[self.lastStep]["config"]["evaluation"] = str(attrs.get('evaluation'))
118                         elif (name == "code"):
119                                 if attrs.has_key('pos') and str(attrs.get('pos')) == "after":
120                                         self.codeafter = True
121                                 else:
122                                         self.codeafter = False
123                         elif (name == "condition"):
124                                 pass
125                         
126                 def endElement(self, name):
127                         self.currContent = ""
128                         if name == 'code':
129                                 if self.codeafter:
130                                         self.wizard[self.lastStep]["codeafter"] = self.wizard[self.lastStep]["codeafter"].strip()
131                                 else:
132                                         self.wizard[self.lastStep]["code"] = self.wizard[self.lastStep]["code"].strip()
133                         elif name == 'condition':
134                                 self.wizard[self.lastStep]["condition"] = self.wizard[self.lastStep]["condition"].strip()
135                         elif name == 'step':
136                                 #print "Step number", self.lastStep, ":", self.wizard[self.lastStep]
137                                 pass
138                                                                 
139                 def characters(self, ch):
140                         if self.currContent == "code":
141                                 if self.codeafter:
142                                         self.wizard[self.lastStep]["codeafter"] = self.wizard[self.lastStep]["codeafter"] + ch
143                                 else:
144                                         self.wizard[self.lastStep]["code"] = self.wizard[self.lastStep]["code"] + ch
145                         elif self.currContent == "condition":
146                                  self.wizard[self.lastStep]["condition"] = self.wizard[self.lastStep]["condition"] + ch
147
148         def __init__(self, session, showSteps = True, showStepSlider = True, showList = True, showConfig = True):
149                 Screen.__init__(self, session)
150                 HelpableScreen.__init__(self)
151
152                 self.stepHistory = []
153
154                 self.wizard = {}
155                 parser = make_parser()
156                 if not isinstance(self.xmlfile, list):
157                         self.xmlfile = [self.xmlfile]
158                 print "Reading ", self.xmlfile
159                 wizardHandler = self.parseWizard(self.wizard)
160                 parser.setContentHandler(wizardHandler)
161                 for xmlfile in self.xmlfile:
162                         if xmlfile[0] != '/':
163                                 parser.parse('/usr/share/enigma2/' + xmlfile)
164                         else:
165                                 parser.parse(xmlfile)
166
167                 self.showSteps = showSteps
168                 self.showStepSlider = showStepSlider
169                 self.showList = showList
170                 self.showConfig = showConfig
171
172                 self.numSteps = len(self.wizard)
173                 self.currStep = self.getStepWithID("start") + 1
174                 
175                 self.timeoutTimer = eTimer()
176                 self.timeoutTimer.callback.append(self.timeoutCounterFired)
177
178                 self["text"] = Label()
179
180                 if showConfig:
181                         self["config"] = ConfigList([])
182
183                 if self.showSteps:
184                         self["step"] = Label()
185                 
186                 if self.showStepSlider:
187                         self["stepslider"] = Slider(1, self.numSteps)
188                 
189                 if self.showList:
190                         self.list = []
191                         self["list"] = List(self.list, enableWrapAround = True)
192                         self["list"].onSelectionChanged.append(self.selChanged)
193                         #self["list"] = MenuList(self.list, enableWrapAround = True)
194
195                 self.onShown.append(self.updateValues)
196
197                 self.configInstance = None
198                 
199                 self.lcdCallbacks = []
200                 
201                 self.disableKeys = False
202                 
203                 self["actions"] = NumberActionMap(["WizardActions", "NumberActions", "ColorActions"],
204                 {
205                         "ok": self.ok,
206                         "back": self.back,
207                         "left": self.left,
208                         "right": self.right,
209                         "up": self.up,
210                         "down": self.down,
211                         "red": self.red,
212                         "green": self.green,
213                         "yellow": self.yellow,
214                         "blue":self.blue,
215                         "1": self.keyNumberGlobal,
216                         "2": self.keyNumberGlobal,
217                         "3": self.keyNumberGlobal,
218                         "4": self.keyNumberGlobal,
219                         "5": self.keyNumberGlobal,
220                         "6": self.keyNumberGlobal,
221                         "7": self.keyNumberGlobal,
222                         "8": self.keyNumberGlobal,
223                         "9": self.keyNumberGlobal,
224                         "0": self.keyNumberGlobal
225                 }, -1)
226                 
227         def red(self):
228                 print "red"
229                 pass
230
231         def green(self):
232                 print "green"
233                 pass
234         
235         def yellow(self):
236                 print "yellow"
237                 pass
238         
239         def blue(self):
240                 print "blue"
241                 pass
242         
243         def setLCDTextCallback(self, callback):
244                 self.lcdCallbacks.append(callback)
245
246         def back(self):
247                 if self.disableKeys:
248                         return
249                 print "getting back..."
250                 print "stepHistory:", self.stepHistory
251                 if len(self.stepHistory) > 1:
252                         self.currStep = self.stepHistory[-2]
253                         self.stepHistory = self.stepHistory[:-2]
254                 if self.currStep < 1:
255                         self.currStep = 1
256                 print "currStep:", self.currStep
257                 print "new stepHistory:", self.stepHistory
258                 self.updateValues()
259                 print "after updateValues stepHistory:", self.stepHistory
260                 
261         def markDone(self):
262                 pass
263         
264         def getStepWithID(self, id):
265                 print "getStepWithID:", id
266                 count = 0
267                 for x in self.wizard.keys():
268                         if self.wizard[x]["id"] == id:
269                                 print "result:", count
270                                 return count
271                         count += 1
272                 print "result: nothing"
273                 return 0
274
275         def finished(self, gotoStep = None, *args, **kwargs):
276                 print "finished"
277                 currStep = self.currStep
278
279                 if self.updateValues not in self.onShown:
280                         self.onShown.append(self.updateValues)
281                         
282                 if self.showConfig:
283                         if self.wizard[currStep]["config"]["type"] == "dynamic":
284                                 eval("self." + self.wizard[currStep]["config"]["evaluation"])()
285                         
286                 if self.showList:
287                         if (len(self.wizard[currStep]["evaluatedlist"]) > 0):
288                                 print "current:", self["list"].current
289                                 nextStep = self["list"].current[1]
290                                 if (self.wizard[currStep].has_key("listevaluation")):
291                                         exec("self." + self.wizard[self.currStep]["listevaluation"] + "('" + nextStep + "')")
292                                 else:
293                                         self.currStep = self.getStepWithID(nextStep)
294
295                 if ((currStep == self.numSteps and self.wizard[currStep]["nextstep"] is None) or self.wizard[currStep]["id"] == "end"): # wizard finished
296                         print "wizard finished"
297                         self.markDone()
298                         self.close()
299                 else:
300                         self.runCode(self.wizard[currStep]["codeafter"])
301                         if self.wizard[currStep]["nextstep"] is not None:
302                                 self.currStep = self.getStepWithID(self.wizard[currStep]["nextstep"])
303                         if gotoStep is not None:
304                                 self.currStep = self.getStepWithID(gotoStep)
305                         self.currStep += 1
306                         self.updateValues()
307
308                 print "Now: " + str(self.currStep)
309
310
311         def ok(self):
312                 print "OK"
313                 if self.disableKeys:
314                         return
315                 currStep = self.currStep
316                 
317                 if self.showConfig:
318                         if (self.wizard[currStep]["config"]["screen"] != None):
319                                 # TODO: don't die, if no run() is available
320                                 # there was a try/except here, but i can't see a reason
321                                 # for this. If there is one, please do a more specific check
322                                 # and/or a comment in which situation there is no run()
323                                 if callable(getattr(self.configInstance, "runAsync", None)):
324                                         self.onShown.remove(self.updateValues)
325                                         self.configInstance.runAsync(self.finished)
326                                         return
327                                 else:
328                                         self.configInstance.run()
329                 self.finished()
330
331         def keyNumberGlobal(self, number):
332                 if (self.wizard[self.currStep]["config"]["screen"] != None):
333                         self.configInstance.keyNumberGlobal(number)
334                 
335         def left(self):
336                 self.resetCounter()
337                 if (self.wizard[self.currStep]["config"]["screen"] != None):
338                         self.configInstance.keyLeft()
339                 elif (self.wizard[self.currStep]["config"]["type"] == "dynamic"):
340                         self["config"].handleKey(KEY_LEFT)
341                 print "left"
342         
343         def right(self):
344                 self.resetCounter()
345                 if (self.wizard[self.currStep]["config"]["screen"] != None):
346                         self.configInstance.keyRight()
347                 elif (self.wizard[self.currStep]["config"]["type"] == "dynamic"):
348                         self["config"].handleKey(KEY_RIGHT)     
349                 print "right"
350
351         def up(self):
352                 self.resetCounter()
353                 if (self.showConfig and self.wizard[self.currStep]["config"]["screen"] != None  or self.wizard[self.currStep]["config"]["type"] == "dynamic"):
354                                 self["config"].instance.moveSelection(self["config"].instance.moveUp)
355                 elif (self.showList and len(self.wizard[self.currStep]["evaluatedlist"]) > 0):
356                         self["list"].selectPrevious()
357                         if self.wizard[self.currStep].has_key("onselect"):
358                                 print "current:", self["list"].current
359                                 self.selection = self["list"].current[-1]
360                                 #self.selection = self.wizard[self.currStep]["evaluatedlist"][self["list"].l.getCurrentSelectionIndex()][1]
361                                 exec("self." + self.wizard[self.currStep]["onselect"] + "()")
362                 print "up"
363                 
364         def down(self):
365                 self.resetCounter()
366                 if (self.showConfig and self.wizard[self.currStep]["config"]["screen"] != None  or self.wizard[self.currStep]["config"]["type"] == "dynamic"):
367                         self["config"].instance.moveSelection(self["config"].instance.moveDown)
368                 elif (self.showList and len(self.wizard[self.currStep]["evaluatedlist"]) > 0):
369                         #self["list"].instance.moveSelection(self["list"].instance.moveDown)
370                         self["list"].selectNext()
371                         if self.wizard[self.currStep].has_key("onselect"):
372                                 print "current:", self["list"].current
373                                 #self.selection = self.wizard[self.currStep]["evaluatedlist"][self["list"].l.getCurrentSelectionIndex()][1]
374                                 #exec("self." + self.wizard[self.currStep]["onselect"] + "()")
375                                 self.selection = self["list"].current[-1]
376                                 #self.selection = self.wizard[self.currStep]["evaluatedlist"][self["list"].l.getCurrentSelectionIndex()][1]
377                                 exec("self." + self.wizard[self.currStep]["onselect"] + "()")
378                 print "down"
379                 
380         def selChanged(self):
381                 self.resetCounter()
382                 
383                 if (self.showConfig and self.wizard[self.currStep]["config"]["screen"] != None):
384                                 self["config"].instance.moveSelection(self["config"].instance.moveUp)
385                 elif (self.showList and len(self.wizard[self.currStep]["evaluatedlist"]) > 0):
386                         if self.wizard[self.currStep].has_key("onselect"):
387                                 self.selection = self["list"].current[-1]
388                                 print "self.selection:", self.selection
389                                 exec("self." + self.wizard[self.currStep]["onselect"] + "()")
390                 
391         def resetCounter(self):
392                 self.timeoutCounter = self.wizard[self.currStep]["timeout"]
393                 
394         def runCode(self, code):
395                 if code != "":
396                         print "code", code
397                         exec(code)
398                         
399         def updateText(self, firstset = False):
400                 text = _(self.wizard[self.currStep]["text"])
401                 if text.find("[timeout]") != -1:
402                         text = text.replace("[timeout]", str(self.timeoutCounter))
403                         self["text"].setText(text)
404                 else:
405                         if firstset:
406                                 self["text"].setText(text)
407                 
408         def updateValues(self):
409                 print "Updating values in step " + str(self.currStep)
410                 # calling a step which doesn't exist can only happen if the condition in the last step is not fulfilled
411                 # if a non-existing step is called, end the wizard 
412                 if self.currStep > len(self.wizard):
413                         self.markDone()
414                         self.close()
415                         return
416
417                 self.timeoutTimer.stop()
418                 
419                 if self.configInstance is not None:
420                         del self.configInstance["config"]
421                         self.configInstance.doClose()
422                         self.configInstance = None
423                 
424                 self.condition = True
425                 exec (self.wizard[self.currStep]["condition"])
426                 if self.condition:
427                         if len(self.stepHistory) == 0 or self.stepHistory[-1] != self.currStep:
428                                 self.stepHistory.append(self.currStep)
429                         print "wizard step:", self.wizard[self.currStep]
430                         
431                         if self.showSteps:
432                                 self["step"].setText(_("Step ") + str(self.currStep) + "/" + str(self.numSteps))
433                         if self.showStepSlider:
434                                 self["stepslider"].setValue(self.currStep)
435                 
436                         if self.wizard[self.currStep]["timeout"] is not None:
437                                 self.resetCounter() 
438                                 self.timeoutTimer.start(1000)
439                         
440                         print "wizard text", _(self.wizard[self.currStep]["text"])
441                         self.updateText(firstset = True)
442                         if self.wizard[self.currStep].has_key("displaytext"):
443                                 displaytext = self.wizard[self.currStep]["displaytext"]
444                                 print "set LCD text"
445                                 for x in self.lcdCallbacks:
446                                         x(displaytext)
447                                 
448                         self.runCode(self.wizard[self.currStep]["code"])
449                         
450                         if self.showList:
451                                 print "showing list,", self.currStep
452                                 for renderer in self.renderer:
453                                         rootrenderer = renderer
454                                         while renderer.source is not None:
455                                                 print "self.list:", self["list"]
456                                                 if renderer.source is self["list"]:
457                                                         print "setZPosition"
458                                                         rootrenderer.instance.setZPosition(1)
459                                                 renderer = renderer.source
460                                                 
461                                 #self["list"].instance.setZPosition(1)
462                                 self.list = []
463                                 if (self.wizard[self.currStep].has_key("dynamiclist")):
464                                         print "dynamic list, calling",  self.wizard[self.currStep]["dynamiclist"]
465                                         newlist = eval("self." + self.wizard[self.currStep]["dynamiclist"] + "()")
466                                         #self.wizard[self.currStep]["evaluatedlist"] = []
467                                         for entry in newlist:
468                                                 #self.wizard[self.currStep]["evaluatedlist"].append(entry)
469                                                 self.list.append(entry)
470                                         #del self.wizard[self.currStep]["dynamiclist"]
471                                 if (len(self.wizard[self.currStep]["list"]) > 0):
472                                         #self["list"].instance.setZPosition(2)
473                                         for x in self.wizard[self.currStep]["list"]:
474                                                 self.list.append((_(x[0]), x[1]))
475                                 self.wizard[self.currStep]["evaluatedlist"] = self.list
476                                 self["list"].list = self.list
477                                 self["list"].index = 0
478                         else:
479                                 self["list"].hide()
480         
481                         if self.showConfig:
482                                 print "showing config"
483                                 self["config"].instance.setZPosition(1)
484                                 if self.wizard[self.currStep]["config"]["type"] == "dynamic":
485                                                 print "config type is dynamic"
486                                                 self["config"].instance.setZPosition(2)
487                                                 self["config"].l.setList(eval("self." + self.wizard[self.currStep]["config"]["source"])())
488                                 elif (self.wizard[self.currStep]["config"]["screen"] != None):
489                                         if self.wizard[self.currStep]["config"]["type"] == "standalone":
490                                                 print "Type is standalone"
491                                                 self.session.openWithCallback(self.ok, self.wizard[self.currStep]["config"]["screen"])
492                                         else:
493                                                 self["config"].instance.setZPosition(2)
494                                                 print "wizard screen", self.wizard[self.currStep]["config"]["screen"]
495                                                 if self.wizard[self.currStep]["config"]["args"] == None:
496                                                         self.configInstance = self.session.instantiateDialog(self.wizard[self.currStep]["config"]["screen"])
497                                                 else:
498                                                         self.configInstance = self.session.instantiateDialog(self.wizard[self.currStep]["config"]["screen"], eval(self.wizard[self.currStep]["config"]["args"]))
499                                                 self["config"].l.setList(self.configInstance["config"].list)
500                                                 self.configInstance["config"].destroy()
501                                                 self.configInstance["config"] = self["config"]
502                                 else:
503                                         self["config"].l.setList([])
504                         else:
505                                 if self.has_key("config"):
506                                         self["config"].hide()
507                 else: # condition false
508                                 self.currStep += 1
509                                 self.updateValues()
510                                 
511         def timeoutCounterFired(self):
512                 self.timeoutCounter -= 1
513                 print "timeoutCounter:", self.timeoutCounter
514                 if self.timeoutCounter == 0:
515                         if self.wizard[self.currStep]["timeoutaction"] == "selectnext":
516                                 print "selection next item"
517                                 self.down()
518                         else:
519                                 if self.wizard[self.currStep]["timeoutaction"] == "changestep":
520                                         self.finished(gotoStep = self.wizard[self.currStep]["timeoutstep"])
521                 self.updateText()
522
523 class WizardManager:
524         def __init__(self):
525                 self.wizards = []
526         
527         def registerWizard(self, wizard, precondition, priority = 0):
528                 self.wizards.append((wizard, precondition, priority))
529         
530         def getWizards(self):
531                 list = []
532                 for x in self.wizards:
533                         if x[1] == 1: # precondition
534                                 list.append((x[2], x[0]))
535                 return list
536
537 wizardManager = WizardManager()