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