| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | |
| 21 | |
| 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 | |
| 44 | const char* ButtonBlue[3] = {NULL__null, NULL__null, NULL__null}; |
| 45 | extern int gl_InfoConflict; |
| 46 | extern bool isUTF8; |
| 47 | |
| 48 | static 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 | |
| 58 | cMenuSearchResultsItem::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 | |
| 73 | bool 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 | |
| 140 | buffer = strreplacei(buffer, "%title%", "%title% ~ %subtitle%"); |
| 141 | if (episodeOnly) |
| 142 | buffer = strreplacei(buffer, "%title%", ""); |
| 143 | |
| 144 | |
| 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 | |
| 174 | cMenuSearchResultsItem::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 | |
| 187 | void 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 | |
| 199 | |
| 200 | const cEvent *cMenuSearchResults::scheduleEventInfo = NULL__null; |
| 201 | |
| 202 | cMenuSearchResults::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 | |
| 217 | int cMenuSearchResults::GetTab(int Tab) |
| 218 | { |
| 219 | if (!menuTemplate) |
| 220 | menuTemplate = cTemplFile::GetTemplateByName("MenuSearchResults"); |
| 221 | |
| 222 | return menuTemplate->Tab(Tab-1); |
| 223 | } |
| 224 | |
| 225 | bool 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 | |
| 235 | eOSState 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 | |
| 310 | eOSState 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 | |
| 323 | eOSState 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 | |
| 338 | eOSState 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 | |
| 353 | eOSState 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 | |
| 377 | eOSState 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 | |
| 389 | eOSState 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 | |
| 401 | void 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 | |
| 415 | eOSState cMenuSearchResults::ProcessKey(eKeys Key) |
| 416 | { |
| 417 | bool HadSubMenu = HasSubMenu(); |
| 418 | eOSState state = cOsdMenu::ProcessKey(Key); |
| 419 | |
| 420 | if (!HasSubMenu() && HadSubMenu) |
| 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) |
| 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 | |
| 479 | cMenuSearchResultsForSearch::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 | |
| 495 | bool 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 | |
| 537 | void 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 | |
| 564 | eOSState 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; |
| 589 | BuildList(); |
| 590 | |
| 591 | state = osContinue; |
| 592 | } |
| 593 | break; |
| 594 | default: |
| 595 | break; |
| 596 | } |
| 597 | } |
| 598 | return state; |
| 599 | } |
| 600 | |
| 601 | |
| 602 | cMenuSearchResultsForBlacklist::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 | |
| 616 | bool 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 | |
| 651 | eOSState 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 | |
| 677 | void 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 | |
| 705 | cMenuSearchResultsForQuery::cMenuSearchResultsForQuery(const char *query, bool IgnoreRunning) |
| 706 | :cMenuSearchResultsForSearch(NULL__null, cTemplFile::GetTemplateByName("MenuSearchResults")) |
| 707 | { |
| 708 | modeBlue = EPGSearchConfig.ignorePayTV?showNoPayTV:showAll; |
| 709 | ignoreRunning = IgnoreRunning; |
| 710 | |
| 711 | if (query) |
| 712 | { |
| 713 | searchExt = new cSearchExt; |
| 714 | strcpy(searchExt->search, query); |
| 715 | searchExt->mode = 0; |
| 716 | searchExt->useTitle = 1; |
| 717 | searchExt->useSubtitle = 0; |
| 718 | searchExt->useDescription = 0; |
| 719 | searchExt->blacklistMode = blacklistsNone; |
| 720 | BuildList(); |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | cMenuSearchResultsForQuery::~cMenuSearchResultsForQuery() |
| 725 | { |
| 726 | delete searchExt; |
| 727 | } |
| 728 | |
| 729 | bool cMenuSearchResultsForQuery::BuildList() |
| 730 | { |
| 731 | bool bRes = cMenuSearchResultsForSearch::BuildList(); |
| 732 | |
| 733 | |
| 734 | |
| 735 | |
| 736 | |
| 737 | |
| 738 | |
| 739 | |
| 740 | |
| 741 | |
| 742 | |
| 743 | |
| 744 | |
| 745 | |
| 746 | return bRes; |
| 747 | } |
| 748 | |
| 749 | |
| 750 | cMenuSearchResultsForRecs::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; |
| 762 | searchExt->useTitle = 1; |
| 763 | searchExt->useSubtitle = 0; |
| 764 | searchExt->useDescription = 0; |
| 765 | BuildList(); |
| 766 | } |
| 767 | } |
| 768 | |
| 769 | bool 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 | |
|
| |
| 14 | | Assuming 'recInfo' is non-null | |
|
| |
| 26 | | Assuming 'recInfo' is non-null | |
|
| |
| 779 | string s1 = (recInfo && recInfo->Title())?recInfo->Title():""; |
| |
| |
| |
| 780 | string s2 = searchExt->search; |
| 781 | if (s1.empty() || s2.empty()) continue; |
| |
| |
| |
| 782 | |
| 783 | |
| 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) |
| |
| |
| |
| 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); |
| |
| |
| 793 | afuzzy_init(s1.c_str(), tolerance, 0, &af); |
| 794 | |
| 795 | int res = afuzzy_checkSUB(s2.c_str(), &af); |
| 796 | afuzzy_free(&af); |
| 797 | match = (res > 0); |
| |
| |
| 798 | } |
| 799 | if (!match) |
| |
| |
| |
| 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); |
| |
| |
| 803 | afuzzy_init(s2.c_str(), tolerance, 0, &af); |
| 804 | |
| 805 | int res = afuzzy_checkSUB(s1.c_str(), &af); |
| 806 | afuzzy_free(&af); |
| 807 | match = (res > 0); |
| |
| |
| 808 | } |
| 809 | |
| 810 | if (match) { |
| |
| |
| |
| 811 | cRecording **tmp = (cRecording **)realloc(pArray, (num + 1) * sizeof(cRecording *)); |
| |
| 812 | if (tmp) |
| 34 | | Assuming 'tmp' is non-null | |
|
| |
| 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 | |
| 832 | cRecording *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 | |
| 840 | eOSState 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 | |
| 858 | eOSState 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 | |
| 878 | cMenuSearchResultsForList::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 | |
| 892 | void 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 | |
| 914 | bool 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 | |
| 943 | eOSState 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 | } |