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