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