Bug Summary

File:menu_searchresults.c
Location:line 823, column 4
Description:Memory allocated by realloc() should be deallocated by free(), not 'delete'

Annotated Source Code

1/* -*- c++ -*-
2Copyright (C) 2004-2013 Christian Wieninger
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
18
19The author can be reached at cwieninger@gmx.de
20
21The project's page is at http://winni.vdr-developer.org/epgsearch
22*/
23
24#include <vector>
25#include "menu_searchresults.h"
26#include "blacklist.h"
27#include "epgsearchext.h"
28#include "menu_myedittimer.h"
29#include "menu_event.h"
30#include "menu_commands.h"
31#include "epgsearchcfg.h"
32#include "epgsearchtools.h"
33#include "changrp.h"
34#include "recdone.h"
35#include "epgsearchcats.h"
36#include <vdr/menu.h>
37#include "menu_conflictcheck.h"
38#include "uservars.h"
39#include "menu_deftimercheckmethod.h"
40#include "afuzzy.h"
41#include "timerstatus.h"
42#include "switchtimer.h"
43
44const char* ButtonBlue[3] = {NULL__null, NULL__null, NULL__null};
45extern int gl_InfoConflict;
46extern bool isUTF8;
47
48static int CompareRecording(const void *p1, const void *p2)
49{
50#if APIVERSNUM20000 < 10721
51 return (int)((*(cRecording **)p1)->start - (*(cRecording **)p2)->start);
52#else
53 return (int)((*(cRecording **)p1)->Start() - (*(cRecording **)p2)->Start());
54#endif
55}
56
57// --- cMenuSearchResultsItem -------------------------------------------------------
58cMenuSearchResultsItem::cMenuSearchResultsItem(const cEvent *EventInfo, bool EpisodeOnly,
59 bool PreviewTimer, cMenuTemplate* MenuTemplate,
60 const cSearchExt* Search)
61{
62 fileName = NULL__null;
63 event = EventInfo;
64 timerMatch = tmNone;
65 episodeOnly = EpisodeOnly;
66 previewTimer = PreviewTimer;
67 menuTemplate = MenuTemplate?MenuTemplate:cTemplFile::GetTemplateByName("MenuSearchResults");
68 search = Search;
69 inSwitchList = false;
70 Update(true);
71}
72
73bool cMenuSearchResultsItem::Update(bool Force)
74{
75 if (!menuTemplate)
76 return false;
77
78 bool result = false;
79
80 eTimerMatch OldTimerMatch = timerMatch;
81 bool OldInSwitchList = inSwitchList;
82 bool hasMatch = false;
83 cTimer* timer = NULL__null;
84 if (event) timer = Timers.GetMatch(event, &timerMatch);
85 if (event) inSwitchList = (SwitchTimers.InSwitchList(event)!=NULL__null);
86 if (timer) hasMatch = true;
87
88 if (Force || timerMatch != OldTimerMatch || inSwitchList != OldInSwitchList)
89 {
90 char t[Utf8BufSize(2)((2) * 4)]="",v[Utf8BufSize(2)((2) * 4)]="",r[Utf8BufSize(2)((2) * 4)]="";
91 char szStatus[Utf8BufSize(4)((4) * 4)] = "";
92 if (EPGSearchConfig.WarEagle)
93 {
94 if (!isUTF8)
95 {
96 t[0] = event && hasMatch ? (timerMatch == tmFull) ? ((timer && timer->Recording())?ICON_REC0x8B:ICON_CLOCK0x8C) : ICON_CLOCK_HALF0x94 : ' ';
97 t[1] = '\0';
98 v[0] = event && event->Vps() && (event->Vps() - event->StartTime()) ? ICON_VPS0x93 : ' ';
99 v[1] = '\0';
100 r[0] = event && event->IsRunning() ? ICON_RUNNING0x92 : ' ';
101 r[1] = '\0';
102
103 }
104 else
105 {
106#if defined(__GNUC__4) && __GNUC__4 < 3 && __GNUC_MINOR__2 < 96
107#else
108 sprintf(t, "%s", (event && hasMatch ? (timerMatch == tmFull) ? ((timer && timer->Recording())?ICON_REC_UTF8"\uE00B":ICON_CLOCK_UTF8"\uE00C") : ICON_CLOCK_HALF_UTF8"\uE014" : " "));
109 sprintf(v, "%s", event && event->Vps() && (event->Vps() - event->StartTime()) ? ICON_VPS_UTF8"\uE013" : " ");
110 sprintf(r, "%s", (event && event->IsRunning() ? ICON_RUNNING_UTF8"\uE012" : " "));
111#endif
112 }
113 }
114 else
115 {
116 t[0] = event && hasMatch ? (timerMatch == tmFull) ? ((timer && timer->Recording())?'R':'T') : 't' : ' ';
117 t[1] = '\0';
118 v[0] = event && event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
119 v[1] = '\0';
120 r[0] = event && event->IsRunning() ? '*' : ' ';
121 r[1] = '\0';
122 }
123 if (event && inSwitchList)
124 {
125 cSwitchTimer* s = SwitchTimers.InSwitchList(event);
126 t[0] = (s && s->mode==1)?'s':'S';
127 }
128 if (t[0] != 'T' && previewTimer)
129 t[0] = 'P';
130
131 strcpy(szStatus, t);
132 strcat(szStatus, v);
133 strcat(szStatus, r);
134
135 char* buffer = strdup(menuTemplate->MenuTemplate());
136 strreplace(buffer, '|', '\t');
137
138 if (!strcasestr(buffer, "%subtitle%") && cTemplFile::GetTemplateByName("MenuFavorites") != menuTemplate)
139 // make sure, there is a subtitle
140 buffer = strreplacei(buffer, "%title%", "%title% ~ %subtitle%");
141 if (episodeOnly)
142 buffer = strreplacei(buffer, "%title%", "");
143
144 // parse the epxression and evaluate it
145 cVarExpr varExpr(buffer);
146 char* tmp = strdup(varExpr.Evaluate(event).c_str());
147 free(buffer);
148 buffer = tmp;
149
150 buffer = strreplacei(buffer, "$status$", szStatus);
151 buffer = strreplacei(buffer, "$t_status$", t);
152 buffer = strreplacei(buffer, "$v_status$", v);
153 buffer = strreplacei(buffer, "$r_status$", r);
154
155 buffer = FixSeparators(buffer, '~');
156 buffer = FixSeparators(buffer, ':');
157 buffer = FixSeparators(buffer, '-');
158
159 SetText(buffer, false);
160
161 if (EPGSearchConfig.checkTimerConflAfterTimerProg && !Force && timer && timerMatch && timerMatch != OldTimerMatch)
162 {
163 cConflictCheck C;
164 C.Check();
165 if (C.TimerInConflict(timer))
166 gl_InfoConflict = 1;
167 }
168
169 return true;
170 }
171 return result;
172}
173
174cMenuSearchResultsItem::cMenuSearchResultsItem(cRecording *Recording)
175{
176 previewTimer = false;
177 episodeOnly = false;
178 menuTemplate = NULL__null;
179 timerMatch = tmNone;
180 inSwitchList = false;
181 event = NULL__null;
182 search = NULL__null;
183 fileName = strdup(Recording->FileName());
184 SetText(Recording->Title('\t'));
185}
186
187void cMenuSearchResultsItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
188{
189#if APIVERSNUM20000 >= 10733
190 cChannel *channel = event?Channels.GetByChannelID(event->ChannelID(), true, true):NULL__null;
191 if (!event)
192 DisplayMenu->SetItem(Text(), Index, Current, Selectable);
193 else if (!DisplayMenu->SetItemEvent(event, Index, Current, Selectable, channel, true, timerMatch))
194 DisplayMenu->SetItem(Text(), Index, Current, Selectable);
195#endif
196}
197
198// --- cMenuSearchResults -------------------------------------------------------
199
200const cEvent *cMenuSearchResults::scheduleEventInfo = NULL__null;
201
202cMenuSearchResults::cMenuSearchResults(cMenuTemplate* MenuTemplate)
203 :cOsdMenu("", MenuTemplate->Tab(0), MenuTemplate->Tab(1), MenuTemplate->Tab(2), MenuTemplate->Tab(3), MenuTemplate->Tab(4))
204{
205#if VDRVERSNUM20005 >= 10728
206 SetMenuCategory(mcSchedule);
207#endif
208
209 helpKeys = -1;
210 menuTemplate = MenuTemplate;
211 modeYellow = showTitleEpisode;
212 modeBlue = showAll;
213 m_bSort = false;
214 ignoreRunning = false;
215}
216
217int cMenuSearchResults::GetTab(int Tab)
218{
219 if (!menuTemplate)
220 menuTemplate = cTemplFile::GetTemplateByName("MenuSearchResults");
221
222 return menuTemplate->Tab(Tab-1);
223}
224
225bool cMenuSearchResults::Update(void)
226{
227 bool result = false;
228 for (cOsdItem *item = First(); item; item = Next(item)) {
229 if (((cMenuSearchResultsItem *)item)->Update())
230 result = true;
231 }
232 return result;
233}
234
235eOSState cMenuSearchResults::Record(void)
236{
237 UpdateCurrent();
238 cMenuSearchResultsItem *item = (cMenuSearchResultsItem *)Get(Current());
239 if (item) {
240 if (item->timerMatch == tmFull)
241 {
242 eTimerMatch tm = tmNone;
243 cTimer *timer = Timers.GetMatch(item->event, &tm);
244 if (timer)
245 {
246 if (EPGSearchConfig.useVDRTimerEditMenu)
247 return AddSubMenu(new cMenuEditTimer(timer));
248 else
249 return AddSubMenu(new cMenuMyEditTimer(timer, false, item->event));
250 }
251 }
252
253 cTimer *timer = new cTimer(item->event);
254 PrepareTimerFile(item->event, timer);
255 cTimer *t = Timers.GetTimer(timer);
256 if (EPGSearchConfig.onePressTimerCreation == 0 || t || !item->event || (!t && item->event && item->event->StartTime() - (Setup.MarginStart+2) * 60 < time(NULL__null)))
257 {
258 if (t)
259 {
260 delete timer;
261 timer = t;
262 }
263 if (EPGSearchConfig.useVDRTimerEditMenu)
264 return AddSubMenu(new cMenuEditTimer(timer, !t));
265 else
266 return AddSubMenu(new cMenuMyEditTimer(timer, !t, item->event));
267 }
268 else
269 {
270 string fullaux = "";
271 string aux = "";
272 if (item->event)
273 {
274 const cEvent* event = item->event;
275 int bstart = event->StartTime() - timer->StartTime();
276 int bstop = timer->StopTime() - event->EndTime();
277 int checkmode = DefTimerCheckModes.GetMode(timer->Channel());
278 aux = UpdateAuxValue(aux, "channel", NumToString(timer->Channel()->Number()) + " - " + CHANNELNAME(timer->Channel())(timer->Channel() ? timer->Channel()->ShortName(true
) : "")
);
279 aux = UpdateAuxValue(aux, "update", checkmode);
280 aux = UpdateAuxValue(aux, "eventid", event->EventID());
281 aux = UpdateAuxValue(aux, "bstart", bstart);
282 aux = UpdateAuxValue(aux, "bstop", bstop);
283 fullaux = UpdateAuxValue(fullaux, "epgsearch", aux);
284 }
285
286#ifdef USE_PINPLUGIN
287 aux = "";
288 aux = UpdateAuxValue(aux, "protected", timer->FskProtection() ? "yes" : "no");
289 fullaux = UpdateAuxValue(fullaux, "pin-plugin", aux);
290#endif
291
292 SetAux(timer, fullaux);
293 Timers.Add(timer);
294 gl_timerStatusMonitor->SetConflictCheckAdvised();
295 timer->Matches();
296 Timers.SetModified();
297 LogFile.iSysLog("timer %s added (active)", *timer->ToDescr());
298
299 if (HasSubMenu())
300 CloseSubMenu();
301 if (Update())
302 Display();
303 SetHelpKeys();
304 }
305 }
306 return osContinue;
307}
308
309
310eOSState cMenuSearchResults::Switch(void)
311{
312 UpdateCurrent();
313 cMenuSearchResultsItem *item = (cMenuSearchResultsItem *)Get(Current());
314 if (item) {
315 cChannel *channel = Channels.GetByChannelID(item->event->ChannelID(), true, true);
316 if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true))
317 return osEnd;
318 }
319 INFO(trVDR("Can't switch channel!"))Skins.Message(mtInfo, I18nTranslate("Can't switch channel!"));
320 return osContinue;
321}
322
323eOSState cMenuSearchResults::Commands(eKeys Key, cSearchExt* SearchExt)
324{
325 if (HasSubMenu() || Count() == 0)
326 return osContinue;
327 cMenuSearchResultsItem *mi = (cMenuSearchResultsItem *)Get(Current());
328 if (mi && mi->event) {
329 cMenuSearchCommands *menu;
330 eOSState state = AddSubMenu(menu = new cMenuSearchCommands(tr("EPG Commands")I18nTranslate("EPG Commands", "vdr-" "epgsearch"), mi->event, true, SearchExt));
331 if (Key != kNone)
332 state = menu->ProcessKey(Key);
333 return state;
334 }
335 return osContinue;
336}
337
338eOSState cMenuSearchResults::ShowSummary()
339{
340 if (Count())
341 {
342 const cEvent *ei = ((cMenuSearchResultsItem *)Get(Current()))->event;
343 if (ei)
344 {
345 cChannel *channel = Channels.GetByChannelID(ei->ChannelID(), true, true);
346 if (channel)
347 return AddSubMenu(new cMenuEventSearch(ei, eventObjects));
348 }
349 }
350 return osContinue;
351}
352
353eOSState cMenuSearchResults::OnRed(cSearchExt* searchExt)
354{
355 eOSState state = osUnknown;
356
357 if(HasSubMenu())
358 return Record();
359
360 if (Count())
361 {
362 if (EPGSearchConfig.redkeymode==toggleKeys)
363 state = Record();
364 else
365 {
366 cMenuSearchResultsItem* mi = (cMenuSearchResultsItem*)Get(Current());
367 if (mi) {
368 if (mi->event) {
369 state = AddSubMenu(new cMenuSearchCommands(tr("EPG Commands")I18nTranslate("EPG Commands", "vdr-" "epgsearch"),mi->event, false, searchExt));
370 }
371 }
372 }
373 }
374 return state;
375}
376
377eOSState cMenuSearchResults::OnGreen()
378{
379 eOSState state = osUnknown;
380 if(!HasSubMenu())
381 {
382 m_bSort=!m_bSort;
383 BuildList();
384 state = osContinue;
385 }
386 return state;
387}
388
389eOSState cMenuSearchResults::OnYellow()
390{
391 eOSState state = osUnknown;
392 if(!HasSubMenu())
393 {
394 modeYellow = (modeYellow==showTitleEpisode?showEpisode:showTitleEpisode);
395 BuildList();
396 state = osContinue;
397 }
398 return state;
399}
400
401void cMenuSearchResults::UpdateCurrent()
402{
403 cEventObj* cureventObj = eventObjects.GetCurrent();
404 if (cureventObj && cureventObj->Event())
405 for (cMenuSearchResultsItem* item = (cMenuSearchResultsItem*)First(); item; item = (cMenuSearchResultsItem*)Next(item))
406 if (item->Selectable() && item->event == cureventObj->Event())
407 {
408 cureventObj->Select(false);
409 SetCurrent(item);
410 Display();
411 break;
412 }
413}
414
415eOSState cMenuSearchResults::ProcessKey(eKeys Key)
416{
417 bool HadSubMenu = HasSubMenu();
418 eOSState state = cOsdMenu::ProcessKey(Key);
419
420 if (!HasSubMenu() && HadSubMenu) // navigation in summary could have changed current item, so update it
421 UpdateCurrent();
422
423 if (state == osUnknown) {
424 switch (Key) {
425 case k0:
426 if(!HasSubMenu())
427 {
428 toggleKeys = 1 - toggleKeys;
429 SetHelpKeys(true);
430 }
431 state = osContinue;
432 break;
433 case kGreen:
434 state = OnGreen();
435 break;
436 case kYellow:
437 state = OnYellow();
438 break;
439 case kOk:
440 case kInfo:
441 if(HasSubMenu())
442 {
443 state = cOsdMenu::ProcessKey(Key);
444 break;
445 }
446 if (Count())
447 state = ShowSummary();
448 else
449 state = osBack;
450 break;
451 default:
452 break;
453 }
454 }
455 if (!HasSubMenu())
456 {
457 if ((HadSubMenu || gl_TimerProgged) && Update())
458 {
459 if (gl_TimerProgged) // when using epgsearch's timer edit menu, update is delayed because of SVDRP
460 {
461 gl_TimerProgged = 0;
462 SetHelpKeys();
463 }
464 Display();
465 }
466 if (Key != kNone)
467 SetHelpKeys();
468 if (gl_InfoConflict)
469 {
470 gl_InfoConflict = 0;
471 if (Interface->Confirm(tr("Timer conflict! Show?")I18nTranslate("Timer conflict! Show?", "vdr-" "epgsearch")))
472 state = AddSubMenu(new cMenuConflictCheck());
473 }
474 }
475 return state;
476}
477
478// --- cMenuSearchResultsForSearch -------------------------------------------------------
479cMenuSearchResultsForSearch::cMenuSearchResultsForSearch(cSearchExt* SearchExt, cMenuTemplate* MenuTemplate)
480 :cMenuSearchResults(MenuTemplate)
481{
482 ButtonBlue[0] = tr("Button$all channels")I18nTranslate("Button$all channels", "vdr-" "epgsearch");
483 ButtonBlue[1] = tr("Button$only FTA")I18nTranslate("Button$only FTA", "vdr-" "epgsearch");
484 ButtonBlue[2] = tr("Button$Timer preview")I18nTranslate("Button$Timer preview", "vdr-" "epgsearch");
485
486 searchExt = SearchExt;
487 m_bSort = true;
488 if (searchExt)
489 {
490 modeBlue = searchExt->useChannel==3?showNoPayTV:(EPGSearchConfig.ignorePayTV?showNoPayTV:showAll);
491 BuildList();
492 }
493}
494
495bool cMenuSearchResultsForSearch::BuildList()
496{
497 bool hasResults = false;
498 int current = Current();
499 Clear();
500 time_t now = time(NULL__null);
501 cSearchResults* pSearchResults = searchExt->Run(modeBlue == showNoPayTV?1:0, false, 0, NULL__null, modeBlue != showTimerPreview);
502 Clear();
503 eventObjects.Clear();
504
505 if (pSearchResults)
506 {
507 pSearchResults->SortBy(m_bSort? CompareEventTime: CompareEventChannel);
508
509 for (cSearchResult* pResultObj = pSearchResults->First();
510 pResultObj;
511 pResultObj = pSearchResults->Next(pResultObj))
512 {
513 if (ignoreRunning && now > pResultObj->event->StartTime())
514 continue;
515 if (!(searchExt->useAsSearchTimer && searchExt->avoidRepeats && modeBlue == showTimerPreview))
516 pResultObj->needsTimer = false;
517 hasResults = true;
518 Add(new cMenuSearchResultsItem(pResultObj->event, modeYellow == showEpisode, pResultObj->needsTimer, menuTemplate));
519 eventObjects.Add(pResultObj->event);
520 }
521
522 delete pSearchResults;
523 }
524 if (Count())
525 SetCurrent(Get(0));
526 SetHelpKeys(true);
527
528 cString szTitle = cString::sprintf("%d %s - %s", Count(), tr("Search results")I18nTranslate("Search results", "vdr-" "epgsearch"), searchExt->search);
529 SetTitle(szTitle);
530
531 SetCurrent(Get(current));
532 Display();
533
534 return hasResults;
535}
536
537void cMenuSearchResultsForSearch::SetHelpKeys(bool Force)
538{
539 cMenuSearchResultsItem *item = (cMenuSearchResultsItem *)Get(Current());
540 int NewHelpKeys = 0;
541 if (item) {
542 if (item->Selectable() && item->timerMatch == tmFull)
543 NewHelpKeys = 2;
544 else
545 NewHelpKeys = 1;
546 }
547
548 bool hasTimer = (NewHelpKeys == 2);
549 if (NewHelpKeys != helpKeys || Force)
550 {
551 ModeBlueSR nextModeBlue = (ModeBlueSR)(((int)modeBlue+1)%3);
552 if (nextModeBlue == showTimerPreview &&
553 (searchExt->useAsSearchTimer == 0 || searchExt->avoidRepeats == 0))
554 nextModeBlue = (ModeBlueSR)(((int)nextModeBlue+1)%3);
555
556 if (toggleKeys==0)
557 SetHelp((EPGSearchConfig.redkeymode==0?(hasTimer?trVDR("Button$Timer")I18nTranslate("Button$Timer"):trVDR("Button$Record")I18nTranslate("Button$Record")):tr("Button$Commands")I18nTranslate("Button$Commands", "vdr-" "epgsearch")), m_bSort? tr("Button$by channel")I18nTranslate("Button$by channel", "vdr-" "epgsearch"):tr("Button$by time")I18nTranslate("Button$by time", "vdr-" "epgsearch"), modeYellow==showTitleEpisode?tr("Button$Episode")I18nTranslate("Button$Episode", "vdr-" "epgsearch"):tr("Button$Title")I18nTranslate("Button$Title", "vdr-" "epgsearch"), ButtonBlue[(int)nextModeBlue]);
558 else
559 SetHelp((EPGSearchConfig.redkeymode==1?(hasTimer?trVDR("Button$Timer")I18nTranslate("Button$Timer"):trVDR("Button$Record")I18nTranslate("Button$Record")):tr("Button$Commands")I18nTranslate("Button$Commands", "vdr-" "epgsearch")), m_bSort? tr("Button$by channel")I18nTranslate("Button$by channel", "vdr-" "epgsearch"):tr("Button$by time")I18nTranslate("Button$by time", "vdr-" "epgsearch"), modeYellow==showTitleEpisode?tr("Button$Episode")I18nTranslate("Button$Episode", "vdr-" "epgsearch"):tr("Button$Title")I18nTranslate("Button$Title", "vdr-" "epgsearch"), ButtonBlue[(int)nextModeBlue]);
560 helpKeys = NewHelpKeys;
561 }
562}
563
564eOSState cMenuSearchResultsForSearch::ProcessKey(eKeys Key)
565{
566 eOSState state = cMenuSearchResults::ProcessKey(Key);
567
568 if (state == osUnknown) {
569 switch (Key) {
570 case kRecord:
571 case kRed:
572 state = OnRed(searchExt);
573 break;
574 case k1...k9:
575 state = HasSubMenu()?osContinue:Commands(Key, searchExt);
576 break;
577 case kBlue:
578 if (HasSubMenu())
579 state = Switch();
580 else
581 {
582 modeBlue = (ModeBlueSR)(((int)modeBlue+1)%3);
583 if (modeBlue == showTimerPreview &&
584 (!searchExt || (searchExt && (searchExt->useAsSearchTimer == 0 || searchExt->avoidRepeats == 0))))
585 modeBlue = (ModeBlueSR)(((int)modeBlue+1)%3);
586
587 if (modeBlue == showTimerPreview)
588 m_bSort = true; // show always sorted by channel
589 BuildList();
590
591 state = osContinue;
592 }
593 break;
594 default:
595 break;
596 }
597 }
598 return state;
599}
600
601// --- cMenuSearchResultsForBlacklist -------------------------------------------------------
602cMenuSearchResultsForBlacklist::cMenuSearchResultsForBlacklist(cBlacklist* Blacklist)
603 :cMenuSearchResults(cTemplFile::GetTemplateByName("MenuSearchResults"))
604{
605 ButtonBlue[0] = tr("Button$all channels")I18nTranslate("Button$all channels", "vdr-" "epgsearch");
606 ButtonBlue[1] = tr("Button$only FTA")I18nTranslate("Button$only FTA", "vdr-" "epgsearch");
607 ButtonBlue[2] = tr("Button$Timer preview")I18nTranslate("Button$Timer preview", "vdr-" "epgsearch");
608
609 blacklist = Blacklist;
610 m_bSort = true;
611 modeBlue = blacklist->useChannel==3?showNoPayTV:(EPGSearchConfig.ignorePayTV?showNoPayTV:showAll);
612
613 BuildList();
614}
615
616bool cMenuSearchResultsForBlacklist::BuildList()
617{
618 int current = Current();
619 time_t now = time(NULL__null);
620 cSearchResults* pSearchResults = blacklist->Run();
621 Clear();
622 eventObjects.Clear();
623 if (pSearchResults)
624 {
625 pSearchResults->SortBy(m_bSort? CompareEventTime: CompareEventChannel);
626
627 for (cSearchResult* pResultObj = pSearchResults->First();
628 pResultObj;
629 pResultObj = pSearchResults->Next(pResultObj))
630 {
631 if (ignoreRunning && now > pResultObj->event->StartTime())
632 continue;
633 Add(new cMenuSearchResultsItem(pResultObj->event, modeYellow == showEpisode, false));
634 eventObjects.Add(pResultObj->event);
635 }
636
637 delete pSearchResults;
638 }
639 if (Count())
640 SetCurrent(Get(0));
641 SetHelpKeys();
642 cString szTitle = cString::sprintf("%d %s - %s", Count(), tr("Blacklist results")I18nTranslate("Blacklist results", "vdr-" "epgsearch"), blacklist->search);
643 SetTitle(szTitle);
644
645 SetCurrent(Get(current));
646 Display();
647
648 return true;
649}
650
651eOSState cMenuSearchResultsForBlacklist::ProcessKey(eKeys Key)
652{
653 eOSState state = cMenuSearchResults::ProcessKey(Key);
654
655 if (state == osUnknown) {
656 switch (Key) {
657 case k1...k9:
658 state = HasSubMenu()?osContinue:Commands(Key);
659 break;
660 case kRecord:
661 case kRed:
662 state = OnRed();
663 break;
664 case kBlue:
665 if (HasSubMenu())
666 state = Switch();
667 else
668 state = osContinue;
669 break;
670 default:
671 break;
672 }
673 }
674 return state;
675}
676
677void cMenuSearchResultsForBlacklist::SetHelpKeys(bool Force)
678{
679 cMenuSearchResultsItem *item = (cMenuSearchResultsItem *)Get(Current());
680 int NewHelpKeys = 0;
681 if (item) {
682 if (item->Selectable() && item->timerMatch == tmFull)
683 NewHelpKeys = 2;
684 else
685 NewHelpKeys = 1;
686 }
687
688 bool hasTimer = (NewHelpKeys == 2);
689 if (NewHelpKeys != helpKeys || Force)
690 {
691
692 ModeBlueSR nextModeBlue = (ModeBlueSR)(((int)modeBlue+1)%3);
693 if (nextModeBlue == showTimerPreview)
694 nextModeBlue = (ModeBlueSR)(((int)nextModeBlue+1)%3);
695
696 if (toggleKeys==0)
697 SetHelp((EPGSearchConfig.redkeymode==0?(hasTimer?trVDR("Button$Timer")I18nTranslate("Button$Timer"):trVDR("Button$Record")I18nTranslate("Button$Record")):tr("Button$Commands")I18nTranslate("Button$Commands", "vdr-" "epgsearch")), m_bSort? tr("Button$by channel")I18nTranslate("Button$by channel", "vdr-" "epgsearch"):tr("Button$by time")I18nTranslate("Button$by time", "vdr-" "epgsearch"), modeYellow==showTitleEpisode?tr("Button$Episode")I18nTranslate("Button$Episode", "vdr-" "epgsearch"):tr("Button$Title")I18nTranslate("Button$Title", "vdr-" "epgsearch"), NULL__null);
698 else
699 SetHelp((EPGSearchConfig.redkeymode==1?(hasTimer?trVDR("Button$Timer")I18nTranslate("Button$Timer"):trVDR("Button$Record")I18nTranslate("Button$Record")):tr("Button$Commands")I18nTranslate("Button$Commands", "vdr-" "epgsearch")), m_bSort? tr("Button$by channel")I18nTranslate("Button$by channel", "vdr-" "epgsearch"):tr("Button$by time")I18nTranslate("Button$by time", "vdr-" "epgsearch"), modeYellow==showTitleEpisode?tr("Button$Episode")I18nTranslate("Button$Episode", "vdr-" "epgsearch"):tr("Button$Title")I18nTranslate("Button$Title", "vdr-" "epgsearch"), NULL__null);
700 helpKeys = NewHelpKeys;
701 }
702}
703
704// --- cMenuSearchResultsForQuery -------------------------------------------------------
705cMenuSearchResultsForQuery::cMenuSearchResultsForQuery(const char *query, bool IgnoreRunning)
706 :cMenuSearchResultsForSearch(NULL__null, cTemplFile::GetTemplateByName("MenuSearchResults"))
707{
708 modeBlue = EPGSearchConfig.ignorePayTV?showNoPayTV:showAll;
709 ignoreRunning = IgnoreRunning;
710 // create a dummy search
711 if (query)
712 {
713 searchExt = new cSearchExt;
714 strcpy(searchExt->search, query);
715 searchExt->mode = 0; // substring
716 searchExt->useTitle = 1;
717 searchExt->useSubtitle = 0;
718 searchExt->useDescription = 0;
719 searchExt->blacklistMode = blacklistsNone;
720 BuildList();
721 }
722}
723
724cMenuSearchResultsForQuery::~cMenuSearchResultsForQuery()
725{
726 delete searchExt;
727}
728
729bool cMenuSearchResultsForQuery::BuildList()
730{
731 bool bRes = cMenuSearchResultsForSearch::BuildList();
732/* if (!bRes)
733 {
734 char* szMessage = NULL;
735 asprintf(&szMessage, tr("No results! Try again with tolerance %d?"), searchExt->mode == 5?searchExt->fuzzyTolerance+1:1);
736 string sMessage = szMessage;
737 free(szMessage);
738 if (Interface->Confirm(sMessage.c_str()))
739 {
740 if (searchExt->mode == 5) // fuzzy
741 searchExt->fuzzyTolerance++;
742 searchExt->mode = 5;
743 return BuildList();
744 }
745 }
746*/ return bRes;
747}
748
749// --- cMenuSearchResultsForRecs -------------------------------------------------------
750cMenuSearchResultsForRecs::cMenuSearchResultsForRecs(const char *query)
751 :cMenuSearchResultsForQuery(NULL__null)
752{
753#if VDRVERSNUM20005 >= 10728
754 SetMenuCategory(mcCommand);
755#endif
756 SetTitle(tr("found recordings")I18nTranslate("found recordings", "vdr-" "epgsearch"));
757 if (query)
758 {
759 searchExt = new cSearchExt;
760 strcpy(searchExt->search, query);
761 searchExt->mode = 0; // substring
762 searchExt->useTitle = 1;
763 searchExt->useSubtitle = 0;
764 searchExt->useDescription = 0;
765 BuildList();
766 }
767}
768
769bool cMenuSearchResultsForRecs::BuildList()
770{
771 cRecording **pArray = NULL__null;
772 int num = 0;
773
774 int current = Current();
775 Clear();
776 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
1
Loop condition is true. Entering loop body
13
Loop condition is true. Entering loop body
25
Loop condition is true. Entering loop body
36
Loop condition is false. Execution continues on line 820
777 const cRecordingInfo *recInfo = recording->Info();
778 if (!recInfo) continue;
2
Assuming 'recInfo' is non-null
3
Taking false branch
14
Assuming 'recInfo' is non-null
15
Taking false branch
26
Assuming 'recInfo' is non-null
27
Taking false branch
779 string s1 = (recInfo && recInfo->Title())?recInfo->Title():"";
4
'?' condition is false
16
'?' condition is false
28
'?' condition is false
780 string s2 = searchExt->search;
781 if (s1.empty() || s2.empty()) continue;
5
Taking false branch
17
Taking false branch
29
Taking false branch
782
783 // tolerance for fuzzy searching: 90% of the shorter text length, but at least 1
784 int tolerance = std::max(1, (int)std::min(s1.size(), s2.size()) / 10);
785
786 bool match = FindIgnoreCase(s1, s2) >= 0 ||
787 FindIgnoreCase(s2, s1) >= 0;
788
789 if (!match)
6
Taking true branch
18
Taking true branch
30
Taking false branch
790 {
791 AFUZZY af = { NULL__null, NULL__null, NULL__null, NULL__null, NULL__null, NULL__null, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 };
792 if (s1.size() > 32) s1 = s1.substr(0, 32);
7
Taking false branch
19
Taking false branch
793 afuzzy_init(s1.c_str(), tolerance, 0, &af);
794 /* Checking substring */
795 int res = afuzzy_checkSUB(s2.c_str(), &af);
796 afuzzy_free(&af);
797 match = (res > 0);
8
Assuming 'res' is <= 0
20
Assuming 'res' is <= 0
798 }
799 if (!match)
9
Taking true branch
21
Taking true branch
31
Taking false branch
800 {
801 AFUZZY af = { NULL__null, NULL__null, NULL__null, NULL__null, NULL__null, NULL__null, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 };
802 if (s2.size() > 32) s2 = s2.substr(0, 32);
10
Taking false branch
22
Taking false branch
803 afuzzy_init(s2.c_str(), tolerance, 0, &af);
804 /* Checking substring */
805 int res = afuzzy_checkSUB(s1.c_str(), &af);
806 afuzzy_free(&af);
807 match = (res > 0);
11
Assuming 'res' is <= 0
23
Assuming 'res' is <= 0
808 }
809
810 if (match) {
12
Taking false branch
24
Taking false branch
32
Taking true branch
811 cRecording **tmp = (cRecording **)realloc(pArray, (num + 1) * sizeof(cRecording *));
33
Memory is allocated
812 if (tmp)
34
Assuming 'tmp' is non-null
35
Taking true branch
813 {
814 pArray=tmp;
815 pArray[num++] = recording;
816 }
817 }
818 }
819
820 qsort(pArray, num, sizeof(cRecording *), CompareRecording);
821 for (int a = 0; a < num; a++)
37
Loop condition is true. Entering loop body
38
Loop condition is false. Execution continues on line 823
822 Add(new cMenuSearchResultsItem(pArray[a]));
823 delete pArray;
39
Memory allocated by realloc() should be deallocated by free(), not 'delete'
824
825 SetHelp(NULL__null);
826
827 SetCurrent(Get(current));
828 Display();
829 return true;
830}
831
832cRecording *cMenuSearchResultsForRecs::GetRecording(cMenuSearchResultsItem *Item)
833{
834 cRecording *recording = Recordings.GetByName(Item->FileName());
835 if (!recording)
836 ERROR(tr("Error while accessing recording!"))Skins.Message(mtError, I18nTranslate("Error while accessing recording!"
, "vdr-" "epgsearch"))
;
837 return recording;
838}
839
840eOSState cMenuSearchResultsForRecs::Play(void)
841{
842 cMenuSearchResultsItem *ri = (cMenuSearchResultsItem*)Get(Current());
843 if (ri)
844 {
845 cRecording *recording = GetRecording(ri);
846 if (recording) {
847#if APIVERSNUM20000 < 10728
848 cReplayControl::SetRecording(recording->FileName(), recording->Title());
849#else
850 cReplayControl::SetRecording(recording->FileName());
851#endif
852 return osReplay;
853 }
854 }
855 return osContinue;
856}
857
858eOSState cMenuSearchResultsForRecs::ProcessKey(eKeys Key)
859{
860 eOSState state = cOsdMenu::ProcessKey(Key);
861
862 if (state == osUnknown)
863 {
864 if (Key == kOk)
865 {
866 if (Count() > 0)
867 state = Play();
868 else
869 state = osBack;
870 }
871 else
872 state = osContinue;
873 }
874 return state;
875}
876
877// --- cMenuSearchResultsForList -------------------------------------------------------
878cMenuSearchResultsForList::cMenuSearchResultsForList(cSearchResults& SearchResults, const char* Title, bool IgnoreRunning)
879 :cMenuSearchResults(cTemplFile::GetTemplateByName("MenuSearchResults"))
880{
881 ButtonBlue[0] = tr("Button$Setup")I18nTranslate("Button$Setup", "vdr-" "epgsearch");
882 searchResults = &SearchResults;
883 m_bSort = true;
884 ignoreRunning = IgnoreRunning;
885
886 BuildList();
887
888 cString szTitle = cString::sprintf(Title, Count());
889 SetTitle(szTitle);
890}
891
892void cMenuSearchResultsForList::SetHelpKeys(bool Force)
893{
894 cMenuSearchResultsItem *item = (cMenuSearchResultsItem *)Get(Current());
895 int NewHelpKeys = 0;
896 if (item) {
897 if (item->Selectable() && item->timerMatch == tmFull)
898 NewHelpKeys = 2;
899 else
900 NewHelpKeys = 1;
901 }
902
903 bool hasTimer = (NewHelpKeys == 2);
904 if (NewHelpKeys != helpKeys || Force)
905 {
906 if (toggleKeys==0)
907 SetHelp((EPGSearchConfig.redkeymode==0?(hasTimer?trVDR("Button$Timer")I18nTranslate("Button$Timer"):trVDR("Button$Record")I18nTranslate("Button$Record")):tr("Button$Commands")I18nTranslate("Button$Commands", "vdr-" "epgsearch")), m_bSort? tr("Button$by channel")I18nTranslate("Button$by channel", "vdr-" "epgsearch"):tr("Button$by time")I18nTranslate("Button$by time", "vdr-" "epgsearch"), modeYellow==showTitleEpisode?tr("Button$Episode")I18nTranslate("Button$Episode", "vdr-" "epgsearch"):tr("Button$Title")I18nTranslate("Button$Title", "vdr-" "epgsearch"), ButtonBlue[0]);
908 else
909 SetHelp((EPGSearchConfig.redkeymode==1?(hasTimer?trVDR("Button$Timer")I18nTranslate("Button$Timer"):trVDR("Button$Record")I18nTranslate("Button$Record")):tr("Button$Commands")I18nTranslate("Button$Commands", "vdr-" "epgsearch")), m_bSort? tr("Button$by channel")I18nTranslate("Button$by channel", "vdr-" "epgsearch"):tr("Button$by time")I18nTranslate("Button$by time", "vdr-" "epgsearch"), modeYellow==showTitleEpisode?tr("Button$Episode")I18nTranslate("Button$Episode", "vdr-" "epgsearch"):tr("Button$Title")I18nTranslate("Button$Title", "vdr-" "epgsearch"), ButtonBlue[0]);
910 helpKeys = NewHelpKeys;
911 }
912}
913
914bool cMenuSearchResultsForList::BuildList()
915{
916 time_t now = time(NULL__null);
917 int current = Current();
918
919 Clear();
920 eventObjects.Clear();
921 searchResults->SortBy(m_bSort? CompareEventTime: CompareEventChannel);
922
923 for (cSearchResult* pResultObj = searchResults->First();
924 pResultObj;
925 pResultObj = searchResults->Next(pResultObj))
926 {
927 if (ignoreRunning && now > pResultObj->event->StartTime())
928 continue;
929 Add(new cMenuSearchResultsItem(pResultObj->event, modeYellow == showEpisode, false, menuTemplate, pResultObj->search));
930 eventObjects.Add(pResultObj->event);
931 }
932 if (Count())
933 SetCurrent(Get(0));
934
935 SetHelpKeys();
936
937 SetCurrent(Get(current));
938 Display();
939
940 return true;
941}
942
943eOSState cMenuSearchResultsForList::ProcessKey(eKeys Key)
944{
945 eOSState state = cMenuSearchResults::ProcessKey(Key);
946
947 if (state == osUnknown) {
948 switch (Key) {
949 case k1...k9:
950 state = HasSubMenu()?osContinue:Commands(Key);
951 break;
952 case kRecord:
953 case kRed:
954 state = OnRed();
955 break;
956 default:
957 break;
958 }
959 }
960 return state;
961}