e751509dd199cfb9de2e323345061309b5f15501
[vuplus_dvbapp-plugin] / autotimer / src / AutoTimerEditor.py
1 # -*- coding: UTF-8 -*-
2 # for localized messages
3 from . import _
4
5 # GUI (Screens)
6 from Screens.Screen import Screen
7 from Components.ConfigList import ConfigListScreen
8 from Screens.ChannelSelection import SimpleChannelSelection
9 from Screens.MessageBox import MessageBox
10 from Screens.ChoiceBox import ChoiceBox
11
12 # GUI (Summary)
13 from Screens.Setup import SetupSummary
14
15 # GUI (Components)
16 from Components.ActionMap import ActionMap
17 from Components.Button import Button
18
19 # Configuration
20 from Components.config import getConfigListEntry, ConfigEnableDisable, \
21         ConfigYesNo, ConfigText, ConfigClock, ConfigNumber, ConfigSelection, \
22         config
23
24 # Timer
25 from RecordTimer import AFTEREVENT
26
27 # Needed to convert our timestamp back and forth
28 from time import localtime, mktime
29
30 # Show ServiceName instead of ServiceReference
31 from ServiceReference import ServiceReference
32
33 # addAutotimerFromService
34 from enigma import eServiceCenter, iServiceInformation
35
36 # Default Record Directory
37 from Tools import Directories
38
39 weekdays = [
40         ("0", _("Monday")),
41         ("1", _("Tuesday")),
42         ("2", _("Wednesday")),
43         ("3", _("Thursday")),
44         ("4", _("Friday")),
45         ("5", _("Saturday")),
46         ("6", _("Sunday")),
47         ("weekend", _("Weekend")),
48         ("weekday", _("Weekday"))
49 ]
50
51 class ExtendedConfigText(ConfigText):
52         def __init__(self, default = "", fixed_size = True, visible_width = False):
53                 ConfigText.__init__(self, default = default, fixed_size = fixed_size, visible_width = visible_width)
54
55                 # Workaround some characters currently not "typeable" using NumericalTextInput
56                 mapping = self.mapping
57                 if len(mapping):
58                         if "&" not in mapping[0]:
59                                 mapping[0] += "&"
60                         if ";" not in mapping[0]:
61                                 mapping[0] += ";"
62                         if "%" not in mapping[0]:
63                                 mapping[0] += "%"
64
65 class SimpleBouquetSelection(SimpleChannelSelection):
66         def __init__(self, session, title):
67                 SimpleChannelSelection.__init__(self, session, title)
68                 self.skinName = "SimpleChannelSelection"
69
70         def channelSelected(self): # just return selected service
71                 ref = self.getCurrentSelection()
72                 if (ref.flags & 7) == 7:
73                         self.close(ref)
74                 else:
75                         # We return the currently active path here
76                         # Asking the user if this is what he wants might be better though
77                         self.close(self.servicePath[-1])
78
79 class AutoTimerEditorBase():
80         """ Base Class for all Editors """
81         def __init__(self, timer, editingDefaults = False):
82                 # Keep Timer
83                 self.timer = timer
84                 self.editingDefaults = editingDefaults
85
86                 # See if we are filtering some strings
87                 self.excludes = (
88                         timer.getExcludedTitle(),
89                         timer.getExcludedShort(),
90                         timer.getExcludedDescription(),
91                         timer.getExcludedDays()
92                 )
93                 self.includes = (
94                         timer.getIncludedTitle(),
95                         timer.getIncludedShort(),
96                         timer.getIncludedDescription(),
97                         timer.getIncludedDays()
98                 )
99                 if len(self.excludes[0]) or len(self.excludes[1]) \
100                                 or len(self.excludes[2]) or len(self.excludes[3]) \
101                                 or len(self.includes[0]) or len(self.includes[1]) \
102                                 or len(self.includes[2]) or len(self.includes[3]):
103                         self.filterSet = True
104                 else:
105                         self.filterSet = False
106
107                 # See if services are restricted
108                 self.services = timer.getServices()
109                 self.bouquets = timer.getBouquets()
110                 if len(self.services) or len(self.bouquets):
111                         self.serviceRestriction = True
112                 else:
113                         self.serviceRestriction = False
114
115                 self.createSetup(timer)
116
117         def createSetup(self, timer):
118                 # Name
119                 self.name = ExtendedConfigText(default = timer.name, fixed_size = False)
120
121                 # Match
122                 self.match = ExtendedConfigText(default = timer.match, fixed_size = False)
123                 self.match.setUseableChars('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789-_?!.:;,&%()\'"+/$@') # XXX: what exactly is useable? :-)
124
125                 # Justplay
126                 self.justplay = ConfigSelection(choices = [("zap", _("zap")), ("record", _("record"))], default = {0: "record", 1: "zap"}[int(timer.justplay)])
127
128                 # Timespan
129                 now = [x for x in localtime()]
130                 if timer.hasTimespan():
131                         default = True
132                         now[3] = timer.timespan[0][0]
133                         now[4] = timer.timespan[0][1]
134                         begin = mktime(now)
135                         now[3] = timer.timespan[1][0]
136                         now[4] = timer.timespan[1][1]
137                         end = mktime(now)
138                 else:
139                         default = False
140                         now[3] = 20
141                         now[4] = 15
142                         begin = mktime(now)
143                         now[3] = 23
144                         now[4] = 15
145                         end = mktime(now)
146                 self.timespan = ConfigEnableDisable(default = default)
147                 self.timespanbegin = ConfigClock(default = begin)
148                 self.timespanend = ConfigClock(default = end)
149
150                 # Services have their own Screen
151
152                 # Offset
153                 if timer.hasOffset():
154                         default = True
155                         begin = timer.getOffsetBegin()
156                         end = timer.getOffsetEnd()
157                 else:
158                         default = False
159                         begin = 5
160                         end = 5
161                 self.offset = ConfigEnableDisable(default = default)
162                 self.offsetbegin = ConfigNumber(default = begin)
163                 self.offsetend = ConfigNumber(default = end)
164
165                 # AfterEvent
166                 if timer.hasAfterEvent():
167                         afterevent = { None: "default", AFTEREVENT.NONE: "nothing", AFTEREVENT.DEEPSTANDBY: "deepstandby", AFTEREVENT.STANDBY: "standby"}[timer.afterevent[0][0]]
168                 else:
169                         afterevent = "default"
170                 self.afterevent = ConfigSelection(choices = [("default", _("standard")), ("nothing", _("do nothing")), ("standby", _("go to standby")), ("deepstandby", _("go to deep standby"))], default = afterevent)
171
172                 # AfterEvent (Timespan)
173                 if timer.hasAfterEvent() and timer.afterevent[0][1][0] is not None:
174                         default = True
175                         now[3] = timer.afterevent[0][1][0][0]
176                         now[4] = timer.afterevent[0][1][0][1]
177                         begin = mktime(now)
178                         now[3] = timer.afterevent[0][1][1][0]
179                         now[4] = timer.afterevent[0][1][1][1]
180                         end = mktime(now)
181                 else:
182                         default = False
183                         now[3] = 23
184                         now[4] = 15
185                         begin = mktime(now)
186                         now[3] = 7
187                         now[4] = 0
188                         end = mktime(now)
189                 self.afterevent_timespan = ConfigEnableDisable(default = default)
190                 self.afterevent_timespanbegin = ConfigClock(default = begin)
191                 self.afterevent_timespanend = ConfigClock(default = end)
192
193                 # Enabled
194                 self.enabled = ConfigYesNo(default = timer.enabled)
195
196                 # Maxduration
197                 if timer.hasDuration():
198                         default = True
199                         duration = timer.getDuration()
200                 else:
201                         default = False
202                         duration =70
203                 self.duration = ConfigEnableDisable(default = default)
204                 self.durationlength = ConfigNumber(default = duration)
205
206                 # Counter
207                 if timer.hasCounter():
208                         default = timer.matchCount
209                 else:
210                         default = 0
211                 self.counter = ConfigNumber(default = default)
212                 self.counterLeft = ConfigNumber(default = timer.matchLeft)
213                 selection = [("", _("Never")), ("%m", _("Monthly")), ("%U", _("Weekly (Sunday)")), ("%W", _("Weekly (Monday)"))]
214                 if timer.getCounterFormatString() not in ["", "%m", "%U", "%W"]:
215                         selection.append((timer.getCounterFormatString(), _("Custom (%s)") % (timer.getCounterFormatString())))
216                 self.counterFormatString = ConfigSelection(selection, default = timer.getCounterFormatString())
217
218                 # Avoid Duplicate Description
219                 self.avoidDuplicateDescription = ConfigSelection([
220                                 ("0", _("No")),
221                                 ("1", _("On same service")),
222                                 ("2", _("On any service")),
223                         ],
224                         default = str(timer.getAvoidDuplicateDescription())
225                 )
226
227                 # Custom Location
228                 if timer.hasDestination():
229                         default = True
230                 else:
231                         default = False
232
233                 self.useDestination = ConfigYesNo(default = default)
234
235                 default = timer.destination or Directories.resolveFilename(Directories.SCOPE_HDD)
236                 choices = config.movielist.videodirs.value
237
238                 if default not in choices:
239                         choices.append(default)
240                 self.destination = ConfigSelection(default = default, choices = choices)
241
242         def pathSelected(self, res):
243                 if res is not None:
244                         if res not in self.destination.choices:
245                                 self.destination.choices.append(res)
246                                 self.destination.description[res] = res
247                         self.destination.value = res
248
249         def chooseDestination(self):
250                 from Screens.LocationBox import MovieLocationBox
251
252                 self.session.openWithCallback(
253                         self.pathSelected,
254                         MovieLocationBox,
255                         _("Choose target folder"),
256                         self.destination.value,
257                         minFree = 100 # Same requirement as in Screens.TimerEntry
258                 )
259
260 class AutoTimerEditor(Screen, ConfigListScreen, AutoTimerEditorBase):
261         """Edit AutoTimer"""
262
263         skin = """<screen name="AutoTimerEdit" title="Edit AutoTimer" position="75,155" size="565,280">
264                 <widget name="config" position="5,5" size="555,225" scrollbarMode="showOnDemand" />
265                 <ePixmap position="0,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
266                 <ePixmap position="140,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
267                 <ePixmap position="280,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
268                 <ePixmap position="420,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
269                 <widget name="key_red" position="0,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
270                 <widget name="key_green" position="140,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
271                 <widget name="key_yellow" position="280,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
272                 <widget name="key_blue" position="420,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
273         </screen>"""
274
275         def __init__(self, session, timer, editingDefaults = False):
276                 Screen.__init__(self, session)
277
278                 AutoTimerEditorBase.__init__(self, timer, editingDefaults)
279
280                 # Summary
281                 self.setup_title = "AutoTimer Editor"
282                 self.onChangedEntry = []
283
284                 # We might need to change shown items, so add some notifiers
285                 self.timespan.addNotifier(self.reloadList, initial_call = False)
286                 self.offset.addNotifier(self.reloadList, initial_call = False)
287                 self.duration.addNotifier(self.reloadList, initial_call = False)
288                 self.afterevent.addNotifier(self.reloadList, initial_call = False)
289                 self.afterevent_timespan.addNotifier(self.reloadList, initial_call = False)
290                 self.counter.addNotifier(self.reloadList, initial_call = False)
291                 self.useDestination.addNotifier(self.reloadList, initial_call = False)
292
293                 self.refresh()
294
295                 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
296
297                 # Initialize Buttons
298                 self["key_red"] = Button(_("Cancel"))
299                 self["key_green"] = Button(_("OK"))
300                 self["key_yellow"] = Button()
301                 self["key_blue"] = Button()
302
303                 # Set Button texts
304                 self.renameServiceButton()
305                 self.renameFilterButton()
306
307                 # Define Actions
308                 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
309                         {
310                                 "cancel": self.cancel,
311                                 "save": self.maybeSave,
312                                 "ok": self.ok,
313                                 "yellow": self.editFilter,
314                                 "blue": self.editServices
315                         }, -2
316                 )
317
318                 # Trigger change
319                 self.changed()
320
321         def renameFilterButton(self):
322                 if self.filterSet:
323                         self["key_yellow"].setText(_("Edit Filters"))
324                 else:
325                         self["key_yellow"].setText(_("Add Filters"))
326
327         def renameServiceButton(self):
328                 if self.serviceRestriction:
329                         self["key_blue"].setText(_("Edit Services"))
330                 else:
331                         self["key_blue"].setText(_("Add Services"))
332
333         def changed(self):
334                 for x in self.onChangedEntry:
335                         try:
336                                 x()
337                         except:
338                                 pass
339
340         def getCurrentEntry(self):
341                 return self["config"].getCurrent()[0]
342
343         def getCurrentValue(self):
344                 return str(self["config"].getCurrent()[1].getText())
345
346         def createSummary(self):
347                 return SetupSummary
348
349         def refresh(self):
350                 # First three entries are only showed when not editing defaults
351                 self.list = []
352                 if not self.editingDefaults:
353                         self.list.extend([
354                                 getConfigListEntry(_("Enabled"), self.enabled),
355                                 getConfigListEntry(_("Description"), self.name),
356                                 getConfigListEntry(_("Match Title"), self.match),
357                         ])
358
359                 self.list.extend([
360                         getConfigListEntry(_("Timer Type"), self.justplay),
361                         getConfigListEntry(_("Only match during Timespan"), self.timespan)
362                 ])
363
364                 # Only allow editing timespan when it's enabled
365                 if self.timespan.value:
366                         self.list.extend([
367                                 getConfigListEntry(_("Begin of Timespan"), self.timespanbegin),
368                                 getConfigListEntry(_("End of Timespan"), self.timespanend)
369                         ])
370
371                 self.list.append(getConfigListEntry(_("Custom offset"), self.offset))
372
373                 # Only allow editing offsets when it's enabled
374                 if self.offset.value:
375                         self.list.extend([
376                                 getConfigListEntry(_("Offset before recording (in m)"), self.offsetbegin),
377                                 getConfigListEntry(_("Offset after recording (in m)"), self.offsetend)
378                         ])
379
380                 self.list.append(getConfigListEntry(_("Set maximum Duration"), self.duration))
381
382                 # Only allow editing maxduration when it's enabled
383                 if self.duration.value:
384                         self.list.append(getConfigListEntry(_("Maximum Duration (in m)"), self.durationlength))
385
386                 self.list.append(getConfigListEntry(_("After event"), self.afterevent))
387
388                 # Only allow setting afterevent timespan when afterevent is active
389                 if self.afterevent.value != "default":
390                         self.list.append(getConfigListEntry(_("Execute after Event during Timespan"), self.afterevent_timespan))
391
392                         # Only allow editing timespan when it's enabled
393                         if self.afterevent_timespan.value:
394                                 self.list.extend([
395                                         getConfigListEntry(_("Begin of after Event Timespan"), self.afterevent_timespanbegin),
396                                         getConfigListEntry(_("End of after Event Timespan"), self.afterevent_timespanend)
397                                 ])
398
399                 self.list.append(getConfigListEntry(_("Record a maximum of x times"), self.counter))
400
401                 # Only allow setting matchLeft when counting hits
402                 if self.counter.value:
403                         if not self.editingDefaults:
404                                 self.list.append(getConfigListEntry(_("Ammount of recordings left"), self.counterLeft))
405                         self.list.append(getConfigListEntry(_("Reset Count"), self.counterFormatString))
406
407                 self.list.append(getConfigListEntry(_("Require Description to be unique"), self.avoidDuplicateDescription))
408
409                 # We always add this option though its expert only in enigma2
410                 self.list.append(getConfigListEntry(_("Use a custom location"), self.useDestination))
411                 if self.useDestination.value:
412                         self.list.append(getConfigListEntry(_("Custom Location"), self.destination))
413
414         def reloadList(self, value):
415                 self.refresh()
416                 self["config"].setList(self.list)
417
418         def editFilter(self):
419                 self.session.openWithCallback(
420                         self.editFilterCallback,
421                         AutoTimerFilterEditor,
422                         self.filterSet,
423                         self.excludes,
424                         self.includes
425                 )
426
427         def editFilterCallback(self, ret):
428                 if ret:
429                         self.filterSet = ret[0]
430                         self.excludes = ret[1]
431                         self.includes = ret[2]
432                         self.renameFilterButton()
433
434         def editServices(self):
435                 self.session.openWithCallback(
436                         self.editServicesCallback,
437                         AutoTimerServiceEditor,
438                         self.serviceRestriction,
439                         self.services,
440                         self.bouquets
441                 )
442
443         def editServicesCallback(self, ret):
444                 if ret:
445                         self.serviceRestriction = ret[0]
446                         self.services = ret[1][0]
447                         self.bouquets = ret[1][1]
448                         self.renameServiceButton()
449
450         def ok(self):
451                 cur = self["config"].getCurrent()
452                 cur = cur and cur[1]
453                 if cur == self.destination:
454                         self.chooseDestination()
455                 else:
456                         ConfigListScreen.keyOK(self)
457
458         def cancel(self):
459                 if self["config"].isChanged():
460                         self.session.openWithCallback(
461                                 self.cancelConfirm,
462                                 MessageBox,
463                                 _("Really close without saving settings?")
464                         )
465                 else:
466                         self.close(None)
467
468         def cancelConfirm(self, ret):
469                 if ret:
470                         self.close(None)
471
472         def maybeSave(self):
473                 # Check if any match is set
474                 if not self.match.value.strip():
475                         self.session.open(
476                                         MessageBox,
477                                         _("The match attribute is mandatory."),
478                                         type = MessageBox.TYPE_ERROR,
479                                         timeout = 5
480                         )
481                 # Check if we have a trailing whitespace
482                 elif self.match.value[-1:] == " ":
483                         self.session.openWithCallback(
484                                 self.saveCallback,
485                                 MessageBox,
486                                 _('You entered "%s" as Text to match.\nDo you want to remove trailing whitespaces?') % (self.match.value)
487                         )
488                 # Just save else
489                 else:
490                         self.save()
491
492         def saveCallback(self, ret):
493                 if ret is not None:
494                         if ret:
495                                 self.match.value = self.match.value.rstrip()
496                         self.save()
497                 # Don't to anything if MessageBox was canceled!
498
499         def save(self):
500                 # Match
501                 self.timer.match = self.match.value
502
503                 # Name
504                 self.timer.name = self.name.value.strip() or self.timer.match
505
506                 # Enabled
507                 self.timer.enabled = self.enabled.value
508
509                 # Justplay
510                 self.timer.justplay = self.justplay.value == "zap"
511
512                 # Timespan
513                 if self.timespan.value:
514                         start = self.timespanbegin.value
515                         end = self.timespanend.value
516                         self.timer.timespan = (start, end)
517                 else:
518                         self.timer.timespan = None
519
520                 # Services
521                 if self.serviceRestriction:
522                         self.timer.services = self.services
523                         self.timer.bouquets = self.bouquets
524                 else:
525                         self.timer.services = None
526                         self.timer.bouquets = None
527
528                 # Offset
529                 if self.offset.value:
530                         self.timer.offset = (self.offsetbegin.value*60, self.offsetend.value*60)
531                 else:
532                         self.timer.offset = None
533
534                 # AfterEvent
535                 if self.afterevent.value == "default":
536                         self.timer.afterevent = []
537                 else:
538                         afterevent = {"nothing": AFTEREVENT.NONE, "deepstandby": AFTEREVENT.DEEPSTANDBY, "standby": AFTEREVENT.STANDBY}[self.afterevent.value]
539                         # AfterEvent Timespan
540                         if self.afterevent_timespan.value:
541                                 start = self.afterevent_timespanbegin.value
542                                 end = self.afterevent_timespanend.value
543                                 self.timer.afterevent = [(afterevent, (start, end))]
544                         else:
545                                 self.timer.afterevent = [(afterevent, None)]
546
547                 # Maxduration
548                 if self.duration.value:
549                         self.timer.maxduration = self.durationlength.value*60
550                 else:
551                         self.timer.maxduration = None
552
553                 # Ex-&Includes
554                 if self.filterSet:
555                         self.timer.exclude = self.excludes
556                         self.timer.include = self.includes
557                 else:
558                         self.timer.exclude = None
559                         self.timer.include = None
560
561                 # Counter
562                 if self.counter.value:
563                         self.timer.matchCount = self.counter.value
564                         if self.counterLeft.value <= self.counter.value:
565                                 self.timer.matchLeft = self.counterLeft.value
566                         else:
567                                 self.timer.matchLeft = self.counter.value
568                         if self.counterFormatString.value:
569                                 self.timer.matchFormatString = self.counterFormatString.value
570                         else:
571                                 self.timer.matchFormatString = ''
572                 else:
573                         self.timer.matchCount = 0
574                         self.timer.matchLeft = 0
575                         self.timer.matchFormatString = ''
576
577                 self.timer.avoidDuplicateDescription = int(self.avoidDuplicateDescription.value)
578
579                 if self.useDestination.value:
580                         self.timer.destination = self.destination.value
581                 else:
582                         self.timer.destination = None
583
584                 # Close
585                 self.close(self.timer)
586
587 class AutoTimerFilterEditor(Screen, ConfigListScreen):
588         """Edit AutoTimer Filter"""
589
590         skin = """<screen name="AutoFilterEditor" title="Edit AutoTimer Filters" position="75,150" size="565,245">
591                 <widget name="config" position="5,5" size="555,200" scrollbarMode="showOnDemand" />
592                 <ePixmap position="5,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
593                 <ePixmap position="145,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
594                 <ePixmap position="285,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
595                 <ePixmap position="425,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
596                 <widget name="key_red" position="5,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
597                 <widget name="key_green" position="145,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
598                 <widget name="key_yellow" position="285,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
599                 <widget name="key_blue" position="425,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
600         </screen>"""
601
602         def __init__(self, session, filterset, excludes, includes):
603                 Screen.__init__(self, session)
604
605                 # Summary
606                 self.setup_title = "AutoTimer Filters"
607                 self.onChangedEntry = []
608
609                 self.typeSelection = ConfigSelection(choices = [("title", _("in Title")), ("short", _("in Shortdescription")), ("desc", _("in Description")), ("day", _("on Weekday"))])
610                 self.typeSelection.addNotifier(self.refresh, initial_call = False)
611
612                 self.enabled = ConfigEnableDisable(default = filterset)
613
614                 self.excludes = excludes
615                 self.includes = includes
616
617                 self.reloadList()
618
619                 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
620
621                 # Initialize Buttons
622                 self["key_red"] = Button(_("Cancel"))
623                 self["key_green"] = Button(_("Save"))
624                 self["key_yellow"] = Button(_("delete"))
625                 self["key_blue"] = Button(_("New"))
626
627                 # Define Actions
628                 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
629                         {
630                                 "cancel": self.cancel,
631                                 "save": self.save,
632                                 "yellow": self.remove,
633                                 "blue": self.new
634                         }
635                 )
636
637                 # Trigger change
638                 self.changed()
639
640         def changed(self):
641                 for x in self.onChangedEntry:
642                         try:
643                                 x()
644                         except:
645                                 pass
646
647         def getCurrentEntry(self):
648                 return self["config"].getCurrent()[0]
649
650         def getCurrentValue(self):
651                 return str(self["config"].getCurrent()[1].getText())
652
653         def createSummary(self):
654                 return SetupSummary
655
656         def saveCurrent(self):
657                 del self.excludes[self.idx][:]
658                 del self.includes[self.idx][:]
659
660                 # Warning, accessing a ConfigListEntry directly might be considered evil!
661
662                 idx = -1
663                 for item in self["config"].getList()[:]:
664                         idx += 1
665                         # Skip empty entries (and those which are no filters)
666                         if item[1].value == "" or idx < 2:
667                                 continue
668                         elif idx < self.lenExcludes:
669                                 self.excludes[self.idx].append(item[1].value.encode("UTF-8"))
670                         else:
671                                 self.includes[self.idx].append(item[1].value.encode("UTF-8"))
672
673         def refresh(self, *args, **kwargs):
674                 self.saveCurrent()
675
676                 self.reloadList()
677                 self["config"].setList(self.list)
678
679         def reloadList(self):
680                 self.list = [
681                         getConfigListEntry(_("Enable Filtering"), self.enabled),
682                         getConfigListEntry(_("Filter"), self.typeSelection)
683                 ]
684
685                 if self.typeSelection.value == "day":
686                         self.idx = 3
687
688                         # Weekdays are presented as ConfigSelection
689                         self.list.extend([
690                                 getConfigListEntry(_("Exclude"), ConfigSelection(choices = weekdays, default = x))
691                                         for x in self.excludes[3]
692                         ])
693                         self.lenExcludes = len(self.list)
694                         self.list.extend([
695                                 getConfigListEntry(_("Include"), ConfigSelection(choices = weekdays, default = x))
696                                         for x in self.includes[3]
697                         ])
698                         return
699                 elif self.typeSelection.value == "title":
700                         self.idx = 0
701                 elif self.typeSelection.value == "short":
702                         self.idx = 1
703                 else: # self.typeSelection.value == "desc":
704                         self.idx = 2
705
706                 self.list.extend([
707                         getConfigListEntry(_("Exclude"), ExtendedConfigText(default = x, fixed_size = False))
708                                 for x in self.excludes[self.idx]
709                 ])
710                 self.lenExcludes = len(self.list)
711                 self.list.extend([
712                         getConfigListEntry(_("Include"), ExtendedConfigText(default = x, fixed_size = False))
713                                 for x in self.includes[self.idx]
714                 ])
715
716         def remove(self):
717                 idx = self["config"].getCurrentIndex()
718                 if idx and idx > 1:
719                         if idx < self.lenExcludes:
720                                 self.lenExcludes -= 1
721
722                         list = self["config"].getList()
723                         list.remove(self["config"].getCurrent())
724                         self["config"].setList(list)
725
726         def new(self):
727                 self.session.openWithCallback(
728                         self.typeSelected,
729                         ChoiceBox,
730                         _("Select type of Filter"),
731                         [
732                                 (_("Exclude"), 0),
733                                 (_("Include"), 1),
734                         ]
735                 )
736
737         def typeSelected(self, ret):
738                 if ret is not None:
739                         list = self["config"].getList()
740
741                         if ret[1] == 0:
742                                 pos = self.lenExcludes
743                                 self.lenExcludes += 1
744                                 text = ret[0]
745                         else:
746                                 pos = len(self.list)
747                                 text = ret[0]
748
749                         if self.typeSelection.value == "day":
750                                 entry = getConfigListEntry(text, ConfigSelection(choices = weekdays))
751                         else:
752                                 entry = getConfigListEntry(text, ExtendedConfigText(fixed_size = False))
753
754                         list.insert(pos, entry)
755                         self["config"].setList(list)
756
757         def cancel(self):
758                 if self["config"].isChanged():
759                         self.session.openWithCallback(
760                                 self.cancelConfirm,
761                                 MessageBox,
762                                 _("Really close without saving settings?")
763                         )
764                 else:
765                         self.close(None)
766
767         def cancelConfirm(self, ret):
768                 if ret:
769                         self.close(None)
770
771         def save(self):
772                 self.refresh()
773
774                 self.close((
775                         self.enabled.value,
776                         self.excludes,
777                         self.includes
778                 ))
779
780 class AutoTimerServiceEditor(Screen, ConfigListScreen):
781         """Edit allowed Services of a AutoTimer"""
782
783         skin = """<screen name="AutoTimerServiceEditor" title="Edit AutoTimer Services" position="75,150" size="565,245">
784                 <widget name="config" position="5,5" size="555,200" scrollbarMode="showOnDemand" />
785                 <ePixmap position="5,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
786                 <ePixmap position="145,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
787                 <ePixmap position="285,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
788                 <ePixmap position="425,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
789                 <widget name="key_red" position="5,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
790                 <widget name="key_green" position="145,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
791                 <widget name="key_yellow" position="285,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
792                 <widget name="key_blue" position="425,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
793         </screen>"""
794
795         def __init__(self, session, servicerestriction, servicelist, bouquetlist):
796                 Screen.__init__(self, session)
797
798                 # Summary
799                 self.setup_title = "AutoTimer Services"
800                 self.onChangedEntry = []
801
802                 self.services = (
803                         servicelist[:],
804                         bouquetlist[:]
805                 )
806
807                 self.enabled = ConfigEnableDisable(default = servicerestriction)
808                 self.typeSelection = ConfigSelection(choices = [("channels", _("Channels")), ("bouquets", _("Bouquets"))])
809                 self.typeSelection.addNotifier(self.refresh, initial_call = False)
810
811                 self.reloadList()
812
813                 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
814
815                 # Initialize Buttons
816                 self["key_red"] = Button(_("Cancel"))
817                 self["key_green"] = Button(_("OK"))
818                 self["key_yellow"] = Button(_("delete"))
819                 self["key_blue"] = Button(_("New"))
820
821                 # Define Actions
822                 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
823                         {
824                                 "cancel": self.cancel,
825                                 "save": self.save,
826                                 "yellow": self.remove,
827                                 "blue": self.new
828                         }
829                 )
830
831                 # Trigger change
832                 self.changed()
833
834         
835         def saveCurrent(self):
836                 del self.services[self.idx][:]
837                 
838                 # Warning, accessing a ConfigListEntry directly might be considered evil!
839
840                 myl = self["config"].getList()[:]
841                 myl.pop(0) # Enabled
842                 myl.pop(0) # Type
843                 for item in myl:
844                         self.services[self.idx].append(item[1].value)
845
846         def refresh(self, *args, **kwargs):
847                 self.saveCurrent()
848
849                 self.reloadList()
850                 self["config"].setList(self.list)
851
852         def reloadList(self):
853                 self.list = [
854                         getConfigListEntry(_("Enable Service Restriction"), self.enabled),
855                         getConfigListEntry(_("Editing"), self.typeSelection)
856                 ]
857                 
858                 if self.typeSelection.value == "channels":
859                         self.idx = 0
860                 else: # self.typeSelection.value == "bouquets":
861                         self.idx = 1
862
863                 self.list.extend([
864                         getConfigListEntry(_("Record on"), ConfigSelection(choices = [(str(x), ServiceReference(str(x)).getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''))]))
865                                 for x in self.services[self.idx]
866                 ])
867
868         def changed(self):
869                 for x in self.onChangedEntry:
870                         try:
871                                 x()
872                         except:
873                                 pass
874
875         def getCurrentEntry(self):
876                 return self["config"].getCurrent()[0]
877
878         def getCurrentValue(self):
879                 return str(self["config"].getCurrent()[1].getText())
880
881         def createSummary(self):
882                 return SetupSummary
883
884         def remove(self):
885                 if self["config"].getCurrentIndex() != 0:
886                         list = self["config"].getList()
887                         list.remove(self["config"].getCurrent())
888                         self["config"].setList(list)
889
890         def new(self):
891                 if self.typeSelection.value == "channels":
892                         self.session.openWithCallback(
893                                 self.finishedServiceSelection,
894                                 SimpleChannelSelection,
895                                 _("Select channel to record on")
896                         )
897                 else: # self.typeSelection.value == "bouquets":
898                         self.session.openWithCallback(
899                                 self.finishedServiceSelection,
900                                 SimpleBouquetSelection,
901                                 _("Select bouquet to record on")
902                         )
903
904         def finishedServiceSelection(self, *args):
905                 if len(args):
906                         list = self["config"].getList()
907                         sname = args[0].toString()
908
909                         if self.typeSelection.value == "channels":
910                                 # strip all after last : when adding a channel
911                                 pos = sname.rfind(':')
912                                 if pos != -1:
913                                         sname = sname[:pos+1]
914
915                         list.append(getConfigListEntry(_("Record on"), ConfigSelection(choices = [(sname, ServiceReference(args[0]).getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''))])))
916                         self["config"].setList(list)
917
918         def cancel(self):
919                 if self["config"].isChanged():
920                         self.session.openWithCallback(
921                                 self.cancelConfirm,
922                                 MessageBox,
923                                 _("Really close without saving settings?")
924                         )
925                 else:
926                         self.close(None)
927
928         def cancelConfirm(self, ret):
929                 if ret:
930                         self.close(None)
931
932         def save(self):
933                 self.refresh()
934
935                 self.close((
936                         self.enabled.value,
937                         self.services
938                 ))
939
940 def addAutotimerFromEvent(session, evt = None, service = None):
941         from AutoTimerComponent import AutoTimerComponent
942         from AutoTimerImporter import AutoTimerImporter
943         from plugin import autotimer
944                 
945         # Create instance if needed
946         if autotimer is None:
947                 from AutoTimer import AutoTimer
948                 autotimer = AutoTimer()
949                 autotimer.readXml()
950
951         match = evt and evt.getEventName() or ""
952         name = match or "New AutoTimer"
953         sref = None
954         if service is not None:
955                 service = str(service)
956                 # strip all after last :
957                 pos = service.rfind(':')
958                 if pos != -1:
959                         service = service[:pos+1]
960
961                 sref = ServiceReference(service)
962         if evt:
963                  # timespan defaults to +- 1h
964                 begin = evt.getBeginTime()-3600
965                 end = begin + evt.getDuration()+7200
966         else:
967                 begin = end = 0
968
969         # XXX: we might want to make sure that we actually collected any data because the importer does not do so :-)
970
971         session.openWithCallback(
972                 importerCallback,
973                 AutoTimerImporter,
974                 AutoTimerComponent(
975                         autotimer.getUniqueId(),                # Id
976                         name,                                                   # Name
977                         "",                                                             # Match
978                         True                                                    # Enabled
979                 ),
980                 match,                     # Proposed Match
981                 begin,                     # Proposed Begin
982                 end,                       # Proposed End
983                 None,                      # Proposed Disabled
984                 sref,                      # Proposed ServiceReference
985                 None,                  # Proposed afterEvent 
986                 None,                      # Proposed justplay 
987                 None                       # Proposed dirname, can we get anything useful here? 
988         )
989
990 def addAutotimerFromService(session, service = None):   
991         from AutoTimerComponent import AutoTimerComponent
992         from AutoTimerImporter import AutoTimerImporter
993         from plugin import autotimer
994                 
995         # Create instance if needed
996         if autotimer is None:
997                 from AutoTimer import AutoTimer
998                 autotimer = AutoTimer()
999                 autotimer.readXml()
1000
1001         serviceHandler = eServiceCenter.getInstance()
1002         info = serviceHandler.info(service)
1003
1004         match = info and info.getName(service) or ""
1005         name = match or "New AutoTimer"
1006         sref = info and info.getInfoString(service, iServiceInformation.sServiceref)
1007         if sref:
1008                 # strip all after last :
1009                 pos = sref.rfind(':')
1010                 if pos != -1:
1011                         sref = sref[:pos+1]
1012
1013                 sref = ServiceReference(sref)
1014         if info:
1015                 begin = info.getInfo(service, iServiceInformation.sTimeCreate)
1016                 end = begin + info.getLength(service)
1017         else:
1018                 begin = end = 0
1019
1020         # XXX: we might want to make sure that we actually collected any data because the importer does not do so :-)
1021
1022         session.openWithCallback(
1023                 importerCallback,
1024                 AutoTimerImporter,
1025                 AutoTimerComponent(
1026                         autotimer.getUniqueId(),                # Id
1027                         name,                                                   # Name
1028                         "",                                                             # Match
1029                         True                                                    # Enabled
1030                 ),
1031                 match,                     # Proposed Match
1032                 begin,                     # Proposed Begin
1033                 end,                       # Proposed End
1034                 None,                      # Proposed Disabled
1035                 sref,                      # Proposed ServiceReference
1036                 None,                  # Proposed afterEvent 
1037                 None,                      # Proposed justplay 
1038                 None                       # Proposed dirname, can we get anything useful here? 
1039         )
1040
1041 def importerCallback(ret):
1042         if ret:
1043                 ret, session = ret
1044                 
1045                 session.openWithCallback(
1046                         editorCallback,
1047                         AutoTimerEditor,
1048                         ret
1049                 )
1050         else:
1051                 # Remove instance if not running in background
1052                 if not config.plugins.autotimer.autopoll.value:
1053                         from plugin import autotimer
1054                         autotimer = None
1055
1056 def editorCallback(ret):
1057         if ret:
1058                 from plugin import autotimer
1059                 
1060                 # Create instance if needed (should have been created by addAutotimerFrom* above though)
1061                 if autotimer is None:
1062                         from AutoTimer import AutoTimer
1063                         autotimer = AutoTimer()
1064                         autotimer.readXml()
1065
1066                 autotimer.add(ret)
1067
1068         # Remove instance if not running in background
1069         if not config.plugins.autotimer.autopoll.value:
1070                 # Save xml (as long as we added something)
1071                 ret and autotimer and autotimer.writeXml()
1072                 autotimer = None
1073