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