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