search for title with Virtual KeyBoard
[enigma2-plugins.git] / epgsearch / src / EPGSearch.py
1 from enigma import eEPGCache, eServiceReference
2
3 from Screens.ChannelSelection import SimpleChannelSelection
4 from Screens.ChoiceBox import ChoiceBox
5 from Screens.EpgSelection import EPGSelection
6 from Screens.InputBox import InputBox
7 from Screens.MessageBox import MessageBox
8 from Screens.Screen import Screen
9
10 from Components.ActionMap import ActionMap
11 from Components.Button import Button
12 from Components.config import config
13 from Components.TimerList import TimerList
14
15 from Screens.VirtualKeyBoard import VirtualKeyBoard
16 from Components.Sources.ServiceEvent import ServiceEvent
17 from Components.Sources.Event import Event
18 from Components.EpgList import EPGList, EPG_TYPE_SINGLE, EPG_TYPE_MULTI
19 from ServiceReference import ServiceReference
20 from Components.GUIComponent import GUIComponent
21 from enigma import eEPGCache, eListbox, eListboxPythonMultiContent, gFont, \
22         RT_HALIGN_LEFT, RT_HALIGN_RIGHT
23 from Tools.LoadPixmap import LoadPixmap
24 from Tools.Directories import resolveFilename, SCOPE_SKIN_IMAGE
25 from time import localtime, time
26
27
28 # Partnerbox installed?
29 try:
30         from Plugins.Extensions.Partnerbox.PartnerboxEPGList import isInRemoteTimer, getRemoteClockPixmap
31         from Plugins.Extensions.Partnerbox.PartnerboxFunctions import  SetPartnerboxTimerlist, isInTimerList, sendPartnerBoxWebCommand, FillE1TimerList, FillE2TimerList
32         import Plugins.Extensions.Partnerbox.PartnerboxFunctions as partnerboxfunctions
33         from Plugins.Extensions.Partnerbox.PartnerboxSetup import PartnerboxEntriesListConfigScreen
34         PartnerBoxInstalled = True
35 except:
36         PartnerBoxInstalled = False
37
38 #AutoTimer installed?
39 try:
40         from Plugins.Extensions.AutoTimer.AutoTimerEditor import addAutotimerFromEvent
41         AutoTimerInstalled = True
42 except:
43         AutoTimerInstalled = False
44
45 baseEPGSelection__init__ = None
46 def EPGSelectionInit():
47         global baseEPGSelection__init__
48         if baseEPGSelection__init__ is None:
49                 baseEPGSelection__init__ = EPGSelection.__init__
50         EPGSelection.__init__ = EPGSelection__init__
51         EPGSelection.bluePressed = bluePressed
52
53 def EPGSelection__init__(self, session, service, zapFunc=None, eventid=None, bouquetChangeCB=None, serviceChangeCB=None):
54         baseEPGSelection__init__(self, session, service, zapFunc, eventid, bouquetChangeCB, serviceChangeCB)
55         if self.type != EPG_TYPE_MULTI:
56                 self["epgsearch_actions"] = ActionMap(["EPGSelectActions"],
57                                 {
58                                         "blue": self.bluePressed,
59                                 })
60                 self["key_blue"].setText(_("EPG Search"))
61
62 def bluePressed(self):
63         try:    # EPGList could be empty
64                 cur = self["list"].getCurrent()
65                 event = cur[0]
66                 name = event.getEventName()
67         except:
68                 name = ''
69         self.session.open(EPGSearch,name , False)
70
71 class EPGSearchList(EPGList):
72         def __init__(self, type=EPG_TYPE_SINGLE, selChangedCB=None, timer = None):
73                 self.days = (_("Mon"), _("Tue"), _("Wed"), _("Thu"), _("Fri"), _("Sat"), _("Sun"))
74                 self.timer = timer
75                 self.onSelChanged = [ ]
76                 if selChangedCB is not None:
77                         self.onSelChanged.append(selChangedCB)
78                 GUIComponent.__init__(self)
79                 self.type=type
80                 self.l = eListboxPythonMultiContent()
81                 self.l.setFont(0, gFont("Regular", 22))
82                 self.l.setFont(1, gFont("Regular", 16))
83                 self.l.setBuildFunc(self.buildEPGSearchEntry)
84                 self.epgcache = eEPGCache.getInstance()
85                 self.clock_pixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/epgclock.png'))
86                 self.clock_add_pixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/epgclock_add.png'))
87                 self.clock_pre_pixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/epgclock_pre.png'))
88                 self.clock_post_pixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/epgclock_post.png'))
89                 self.clock_prepost_pixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/epgclock_prepost.png'))
90
91                 if PartnerBoxInstalled:
92                         # Partnerbox Clock Icons
93                         self.remote_clock_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock.png')
94                         self.remote_clock_add_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_add.png')
95                         self.remote_clock_pre_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_pre.png')
96                         self.remote_clock_post_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_post.png')
97                         self.remote_clock_prepost_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_prepost.png')
98
99         def buildEPGSearchEntry(self, service, eventId, beginTime, duration, EventName):
100                 rec1=beginTime and (self.timer.isInTimer(eventId, beginTime, duration, service))
101                 # Partnerbox 
102                 if PartnerBoxInstalled:
103                         rec2=beginTime and (isInRemoteTimer(self,beginTime, duration, service))
104                 else:rec2=False
105                 r1=self.weekday_rect
106                 r2=self.datetime_rect
107                 r3=self.descr_rect
108                 t = localtime(beginTime)
109                 serviceref = ServiceReference(service) # for Servicename
110                 res = [
111                         None, # no private data needed
112                         (eListboxPythonMultiContent.TYPE_TEXT, r1.left(), r1.top(), r1.width(), r1.height(), 0, RT_HALIGN_RIGHT, self.days[t[6]]),
113                         (eListboxPythonMultiContent.TYPE_TEXT, r2.left(), r2.top(), r2.width(), r1.height(), 0, RT_HALIGN_RIGHT, "%02d.%02d, %02d:%02d"%(t[2],t[1],t[3],t[4]))
114                 ]
115                 if rec1 or rec2:
116                         if rec1:                        
117                                 clock_pic = self.getClockPixmap(service, beginTime, duration, eventId)
118                                 # maybe Partnerbox too
119                                 if rec2:
120                                         clock_pic_partnerbox = getRemoteClockPixmap(self,service, beginTime, duration, eventId)
121                         else:
122                                 clock_pic = getRemoteClockPixmap(self,service, beginTime, duration, eventId)
123                         if rec1 and rec2:
124                                 # Partnerbox and local
125                                 res.extend((
126                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r3.left(), r3.top(), 21, 21, clock_pic),
127                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r3.left() + 25, r3.top(), 21, 21, clock_pic_partnerbox),
128                                         (eListboxPythonMultiContent.TYPE_TEXT, r3.left() + 50, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, serviceref.getServiceName() + ": " + EventName)))
129                         else:
130                                 res.extend((
131                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r3.left(), r3.top(), 21, 21, clock_pic),
132                                         (eListboxPythonMultiContent.TYPE_TEXT, r3.left() + 25, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, serviceref.getServiceName() + ": " + EventName)))
133                 else:
134                         res.append((eListboxPythonMultiContent.TYPE_TEXT, r3.left(), r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, serviceref.getServiceName() + ": " + EventName))
135                 return res
136
137
138
139 class EPGSearch(EPGSelection):
140         EMPTY = 0
141         ADD_TIMER = 1
142         REMOVE_TIMER = 2
143         
144         ZAP = 1
145         def __init__(self, session, *args):
146                 Screen.__init__(self, session)
147                 self.skinName = "EPGSelection"
148                 self.bouquetChangeCB = None
149                 self.serviceChangeCB = None
150                 self.ask_time = -1 #now
151                 self["key_red"] = Button("")
152                 self.closeRecursive = False
153                 self.saved_title = None
154                 self["Service"] = ServiceEvent()
155                 self["Event"] = Event()
156                 self.type = EPG_TYPE_SINGLE
157                 # XXX: we lose sort begin/end here
158                 self["key_yellow"] = Button(_("New Search"))
159                 self["key_blue"] = Button(_("History"))
160                 self.currentService=None
161                 self.zapFunc = None
162                 self.sort_type = 0
163                 self.setSortDescription()
164                 self["key_green"] = Button(_("Add timer"))
165                 self.key_green_choice = self.ADD_TIMER
166                 self.key_red_choice = self.EMPTY
167                 self["list"] = EPGSearchList(type = self.type, selChangedCB = self.onSelectionChanged, timer = session.nav.RecordTimer)
168                 self["actions"] = ActionMap(["EPGSelectActions", "OkCancelActions"],
169                         {
170                                 "cancel": self.closeScreen,
171                                 "ok": self.eventSelected,
172                                 "timerAdd": self.timerAdd,
173                                 "yellow": self.yellowButtonPressed,
174                                 "blue": self.blueButtonPressed,
175                                 "info": self.infoKeyPressed,
176                                 "red": self.zapTo, # needed --> Partnerbox
177                                 "input_date_time": self.enterDateTime,
178                                 "nextBouquet": self.nextBouquet, # just used in multi epg yet
179                                 "prevBouquet": self.prevBouquet, # just used in multi epg yet
180                                 "nextService": self.nextService, # just used in single epg yet
181                                 "prevService": self.prevService, # just used in single epg yet
182                         })
183
184                 self["actions"].csel = self
185                 self.onLayoutFinish.append(self.onCreate)
186                 self["MenuActions"] = ActionMap(["MenuActions"],
187                 {
188                                 "menu": self.menu,
189                 })
190                 self.searchargs = args
191                 # Partnerbox
192                 if PartnerBoxInstalled:
193                         EPGSelection.PartnerboxInit(self, False)
194
195         def onCreate(self):
196                 if self.searchargs:
197                         self.searchEPG(*self.searchargs)
198                 else:
199                         l = self["list"]
200                         l.recalcEntrySize()
201                         l.list = []
202                         l.l.setList(l.list)
203                 del self.searchargs
204                 # Partnerbox
205                 if PartnerBoxInstalled:
206                         EPGSelection.GetPartnerboxTimerlist(self)
207
208         def closeScreen(self):
209                 # Save our history
210                 config.plugins.epgsearch.save()
211                 EPGSelection.closeScreen(self)
212
213         def yellowButtonPressed(self):
214                 self.session.openWithCallback(self.searchEPG, VirtualKeyBoard, title = _("Enter text to search for"))
215
216         def menu(self):
217                 options = []
218                 options.append([_("Import from Timer"), "importFromTimer"])
219                 options.append([_("Import from EPG"), "importFromEPG"])
220                 # AutoTimer
221                 if AutoTimerInstalled:
222                         options.append([_("Add AutoTimer..."), "addAutoTimer"])
223                 self.session.openWithCallback(
224                         self.menuCallback,
225                         ChoiceBox,
226                         list = options
227                 )
228
229         def menuCallback(self, ret):
230                 if ret:
231                         ret = ret[1]
232                         if ret is "importFromTimer":
233                                 self.session.openWithCallback(
234                                         self.searchEPG,
235                                         EPGSearchTimerImport
236                                 )
237                         elif ret is "importFromEPG":
238                                 self.session.openWithCallback(
239                                         self.searchEPG,
240                                         EPGSearchChannelSelection
241                                 )
242                         elif ret is "addAutoTimer":
243                                 cur = self["list"].getCurrent()
244                                 if cur is None:
245                                         return
246                                 serviceref = cur[1]
247                                 event = cur[0]
248                                 addAutotimerFromEvent(self.session, event,serviceref) 
249
250
251         def blueButtonPressed(self):
252                 options = [(x, x) for x in config.plugins.epgsearch.history.value]
253
254                 if options:
255                         self.session.openWithCallback(
256                                 self.searchEPGWrapper,
257                                 ChoiceBox,
258                                 title = _("Select text to search for"),
259                                 list = options
260                         )
261                 else:
262                         self.session.open(
263                                 MessageBox,
264                                 _("No history"),
265                                 type = MessageBox.TYPE_INFO
266                         )
267
268         def searchEPGWrapper(self, ret):
269                 if ret:
270                         self.searchEPG(ret[1])
271
272         def searchEPG(self, searchString = None, searchSave = True):
273                 if searchString:
274                         if searchSave:
275                                 # Maintain history
276                                 history = config.plugins.epgsearch.history.value
277                                 if searchString not in history:
278                                         history.insert(0, searchString)
279                                         if len(history) > 10:
280                                                 history.pop(10)
281                                 else:
282                                         history.remove(searchString)
283                                         history.insert(0, searchString)
284
285                         # Workaround to allow search for umlauts if we know the encoding (pretty bad, I know...)
286                         encoding = config.plugins.epgsearch.encoding.value
287                         if encoding != 'UTF-8':
288                                 try:
289                                         searchString = searchString.decode('UTF-8', 'replace').encode(encoding)
290                                 except UnicodeDecodeError:
291                                         pass
292
293                         # Search EPG, default to empty list
294                         epgcache = eEPGCache.getInstance() # XXX: the EPGList also keeps an instance of the cache but we better make sure that we get what we want :-)
295                         ret = epgcache.search(('RIBDT', 200, eEPGCache.PARTIAL_TITLE_SEARCH, searchString, eEPGCache.NO_CASE_CHECK)) or []
296                         ret.sort(key = lambda x: x[2]) # sort by time
297
298                         # Update List
299                         l = self["list"]
300                         l.recalcEntrySize()
301                         l.list = ret
302                         l.l.setList(ret)
303
304 class EPGSearchTimerImport(Screen):
305         def __init__(self, session):
306                 Screen.__init__(self, session)
307                 self.skinName = "TimerEditList"
308
309                 self.list = []
310                 self.fillTimerList()
311
312                 self["timerlist"] = TimerList(self.list)
313
314                 self["key_red"] = Button(_("Cancel"))
315                 self["key_green"] = Button(_("OK"))
316                 self["key_yellow"] = Button("")
317                 self["key_blue"] = Button("")
318
319                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
320                 {
321                         "ok": self.search,
322                         "cancel": self.cancel,
323                         "green": self.search,
324                         "red": self.cancel
325                 }, -1)
326                 self.onLayoutFinish.append(self.setCustomTitle)
327
328         def setCustomTitle(self):
329                 self.setTitle(_("Select a timer to search"))
330
331         def fillTimerList(self):
332                 l = self.list
333                 del l[:]
334
335                 for timer in self.session.nav.RecordTimer.timer_list:
336                         l.append((timer, False))
337
338                 for timer in self.session.nav.RecordTimer.processed_timers:
339                         l.append((timer, True))
340                 l.sort(key = lambda x: x[0].begin)
341
342         def search(self):
343                 cur = self["timerlist"].getCurrent()
344                 if cur:
345                         self.close(cur.name)
346
347         def cancel(self):
348                 self.close(None)
349
350 class EPGSearchChannelSelection(SimpleChannelSelection):
351         def __init__(self, session):
352                 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
353                 self.skinName = "SimpleChannelSelection"
354
355                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
356                 {
357                                 "showEPGList": self.channelSelected
358                 })
359
360         def channelSelected(self):
361                 ref = self.getCurrentSelection()
362                 if (ref.flags & 7) == 7:
363                         self.enterPath(ref)
364                 elif not (ref.flags & eServiceReference.isMarker):
365                         self.session.openWithCallback(
366                                 self.epgClosed,
367                                 EPGSearchEPGSelection,
368                                 ref,
369                                 False
370                         )
371
372         def epgClosed(self, ret = None):
373                 if ret:
374                         self.close(ret)
375
376 class EPGSearchEPGSelection(EPGSelection):
377         def __init__(self, session, ref, openPlugin):
378                 EPGSelection.__init__(self, session, ref)
379                 self.skinName = "EPGSelection"
380                 self["key_green"].setText(_("Search"))
381                 self.openPlugin = openPlugin
382
383         def infoKeyPressed(self):
384                 self.timerAdd()
385
386         def timerAdd(self):
387                 cur = self["list"].getCurrent()
388                 evt = cur[0]
389                 sref = cur[1]
390                 if not evt:
391                         return
392
393                 if self.openPlugin:
394                         self.session.open(
395                                 EPGSearch,
396                                 evt.getEventName()
397                         )
398                 else:
399                         self.close(evt.getEventName())
400