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