|
MythTV
0.26-pre
|
00001 // C/C++ 00002 #include <vector> 00003 #include <algorithm> 00004 #include <functional> 00005 using namespace std; 00006 00007 // Qt 00008 #include <QCoreApplication> 00009 #include <QRegExp> 00010 #include <QLocale> 00011 00012 // MythTV 00013 #include "scheduledrecording.h" 00014 #include "mythuibuttonlist.h" 00015 #include "mythcorecontext.h" 00016 #include "mythdialogbox.h" 00017 #include "recordinginfo.h" 00018 #include "recordingrule.h" 00019 #include "channelutil.h" 00020 #include "proglist.h" 00021 #include "mythdb.h" 00022 #include "mythmiscutil.h" 00023 00024 #define LOC QString("ProgLister: ") 00025 #define LOC_WARN QString("ProgLister, Warning: ") 00026 #define LOC_ERR QString("ProgLister, Error: ") 00027 00028 ProgLister::ProgLister(MythScreenStack *parent, ProgListType pltype, 00029 const QString &view, const QString &extraArg) : 00030 ScheduleCommon(parent, "ProgLister"), 00031 m_type(pltype), 00032 m_recid(0), 00033 m_title(), 00034 m_extraArg(extraArg), 00035 m_startTime(QDateTime::currentDateTime()), 00036 m_searchTime(m_startTime), 00037 m_channelOrdering(gCoreContext->GetSetting("ChannelOrdering", "channum")), 00038 00039 m_searchType(kNoSearch), 00040 00041 m_view(view), 00042 m_curView(-1), 00043 m_viewList(), 00044 m_viewTextList(), 00045 00046 m_itemList(), 00047 m_itemListSave(), 00048 m_schedList(), 00049 00050 m_typeList(), 00051 m_genreList(), 00052 m_stationList(), 00053 00054 m_allowEvents(true), 00055 m_titleSort(false), 00056 m_reverseSort(false), 00057 m_useGenres(false), 00058 00059 m_schedText(NULL), 00060 m_curviewText(NULL), 00061 m_positionText(NULL), 00062 m_progList(NULL), 00063 m_messageText(NULL) 00064 { 00065 switch (pltype) 00066 { 00067 case plTitleSearch: m_searchType = kTitleSearch; break; 00068 case plKeywordSearch: m_searchType = kKeywordSearch; break; 00069 case plPeopleSearch: m_searchType = kPeopleSearch; break; 00070 case plPowerSearch: m_searchType = kPowerSearch; break; 00071 case plSQLSearch: m_searchType = kPowerSearch; break; 00072 case plStoredSearch: m_searchType = kPowerSearch; break; 00073 default: m_searchType = kNoSearch; break; 00074 } 00075 } 00076 00077 // previously recorded ctor 00078 ProgLister::ProgLister( 00079 MythScreenStack *parent, uint recid, const QString &title) : 00080 ScheduleCommon(parent, "PreviousList"), 00081 m_type(plPreviouslyRecorded), 00082 m_recid(recid), 00083 m_title(title), 00084 m_extraArg(), 00085 m_startTime(QDateTime::currentDateTime()), 00086 m_searchTime(m_startTime), 00087 m_channelOrdering(gCoreContext->GetSetting("ChannelOrdering", "channum")), 00088 00089 m_searchType(kNoSearch), 00090 00091 m_view("reverse time"), 00092 m_curView(-1), 00093 m_viewList(), 00094 m_viewTextList(), 00095 00096 m_itemList(), 00097 m_itemListSave(), 00098 m_schedList(), 00099 00100 m_typeList(), 00101 m_genreList(), 00102 m_stationList(), 00103 00104 m_allowEvents(true), 00105 m_titleSort(false), 00106 m_reverseSort(true), 00107 m_useGenres(false), 00108 00109 m_schedText(NULL), 00110 m_curviewText(NULL), 00111 m_positionText(NULL), 00112 m_progList(NULL), 00113 m_messageText(NULL) 00114 { 00115 } 00116 00117 ProgLister::~ProgLister() 00118 { 00119 m_itemList.clear(); 00120 m_itemListSave.clear(); 00121 gCoreContext->removeListener(this); 00122 } 00123 00124 bool ProgLister::Create() 00125 { 00126 if (!LoadWindowFromXML("schedule-ui.xml", "programlist", this)) 00127 return false; 00128 00129 bool err = false; 00130 UIUtilW::Assign(this, m_curviewText, "curview", &err); 00131 UIUtilE::Assign(this, m_progList, "proglist", &err); 00132 UIUtilW::Assign(this, m_schedText, "sched", &err); 00133 UIUtilW::Assign(this, m_messageText, "msg", &err); 00134 UIUtilW::Assign(this, m_positionText, "position", &err); 00135 00136 if (err) 00137 { 00138 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'programlist'"); 00139 return false; 00140 } 00141 00142 connect(m_progList, SIGNAL(itemSelected(MythUIButtonListItem*)), 00143 this, SLOT( HandleSelected( MythUIButtonListItem*))); 00144 00145 connect(m_progList, SIGNAL(itemVisible(MythUIButtonListItem*)), 00146 this, SLOT( HandleVisible( MythUIButtonListItem*))); 00147 00148 connect(m_progList, SIGNAL(itemLoaded(MythUIButtonListItem*)), 00149 this, SLOT( HandleVisible( MythUIButtonListItem*))); 00150 00151 connect(m_progList, SIGNAL(itemClicked(MythUIButtonListItem*)), 00152 this, SLOT( HandleClicked())); 00153 00154 m_progList->SetLCDTitles(tr("Program List"), "title|channel|shortstarttimedate"); 00155 m_progList->SetSearchFields("titlesubtitle"); 00156 00157 BuildFocusList(); 00158 00159 QString value; 00160 switch (m_type) 00161 { 00162 case plTitle: value = tr("Program Listings"); break; 00163 case plNewListings: value = tr("New Title Search"); break; 00164 case plTitleSearch: value = tr("Title Search"); break; 00165 case plKeywordSearch: value = tr("Keyword Search"); break; 00166 case plPeopleSearch: value = tr("People Search"); break; 00167 case plStoredSearch: value = tr("Stored Search"); break; 00168 case plPowerSearch: value = tr("Power Search"); break; 00169 case plSQLSearch: value = tr("Power Search"); break; 00170 case plRecordid: value = tr("Rule Search"); break; 00171 case plCategory: value = tr("Category Search"); break; 00172 case plChannel: value = tr("Channel Search"); break; 00173 case plMovies: value = tr("Movie Search"); break; 00174 case plTime: value = tr("Time Search"); break; 00175 case plPreviouslyRecorded: value = tr("Previously Recorded"); break; 00176 default: value = tr("Unknown Search"); break; 00177 } 00178 00179 if (m_schedText) 00180 m_schedText->SetText(value); 00181 00182 gCoreContext->addListener(this); 00183 00184 LoadInBackground(); 00185 00186 return true; 00187 } 00188 00189 void ProgLister::Load(void) 00190 { 00191 if (m_curView < 0) 00192 FillViewList(m_view); 00193 00194 FillItemList(false, false); 00195 00196 ScreenLoadCompletionEvent *slce = 00197 new ScreenLoadCompletionEvent(objectName()); 00198 QCoreApplication::postEvent(this, slce); 00199 } 00200 00201 bool ProgLister::keyPressEvent(QKeyEvent *e) 00202 { 00203 if (!m_allowEvents) 00204 return true; 00205 00206 if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(e)) 00207 { 00208 m_allowEvents = true; 00209 return true; 00210 } 00211 00212 m_allowEvents = false; 00213 00214 QStringList actions; 00215 bool handled = GetMythMainWindow()->TranslateKeyPress( 00216 "TV Frontend", e, actions); 00217 00218 bool needUpdate = false; 00219 for (uint i = 0; i < uint(actions.size()) && !handled; ++i) 00220 { 00221 QString action = actions[i]; 00222 handled = true; 00223 00224 if (action == "PREVVIEW") 00225 SwitchToPreviousView(); 00226 else if (action == "NEXTVIEW") 00227 SwitchToNextView(); 00228 else if (action == "CUSTOMEDIT") 00229 { 00230 if (GetCurrent()) 00231 ScheduleCommon::EditCustom(GetCurrent()); 00232 } 00233 else if (action == "EDIT") 00234 { 00235 if (GetCurrent()) 00236 ScheduleCommon::EditScheduled(GetCurrent()); 00237 } 00238 else if (action == "DELETE") 00239 ShowDeleteItemMenu(); 00240 else if (action == "UPCOMING") 00241 ShowUpcoming(); 00242 else if (action == "DETAILS" || action == "INFO") 00243 ShowDetails(); 00244 else if (action == "TOGGLERECORD") 00245 RecordSelected(); 00246 else if (action == "1") 00247 { 00248 if (m_titleSort == true) 00249 { 00250 m_titleSort = false; 00251 m_reverseSort = (m_type == plPreviouslyRecorded); 00252 } 00253 else 00254 { 00255 m_reverseSort = !m_reverseSort; 00256 } 00257 needUpdate = true; 00258 } 00259 else if (action == "2") 00260 { 00261 if (m_titleSort == false) 00262 { 00263 m_titleSort = true; 00264 m_reverseSort = false; 00265 } 00266 else 00267 { 00268 m_reverseSort = !m_reverseSort; 00269 } 00270 needUpdate = true; 00271 } 00272 else 00273 { 00274 handled = false; 00275 } 00276 } 00277 00278 if (!handled && MythScreenType::keyPressEvent(e)) 00279 handled = true; 00280 00281 if (needUpdate) 00282 LoadInBackground(); 00283 00284 m_allowEvents = true; 00285 00286 return handled; 00287 } 00288 00289 void ProgLister::ShowMenu(void) 00290 { 00291 MythMenu *sortMenu = new MythMenu(tr("Sort Options"), this, "sortmenu"); 00292 sortMenu->AddItem(tr("Reverse Sort Order")); 00293 sortMenu->AddItem(tr("Sort By Title")); 00294 sortMenu->AddItem(tr("Sort By Time")); 00295 00296 MythMenu *menu = new MythMenu(tr("Options"), this, "menu"); 00297 00298 if (m_type != plPreviouslyRecorded) 00299 { 00300 menu->AddItem(tr("Choose Search Phrase..."), SLOT(ShowChooseViewMenu())); 00301 } 00302 00303 menu->AddItem(tr("Sort"), NULL, sortMenu); 00304 00305 if (m_type != plPreviouslyRecorded) 00306 menu->AddItem(tr("Record"), SLOT(RecordSelected())); 00307 00308 menu->AddItem(tr("Edit Schedule"), SLOT(EditScheduled())); 00309 menu->AddItem(tr("Program Details"), SLOT(ShowDetails())); 00310 menu->AddItem(tr("Upcoming"), SLOT(ShowUpcoming())); 00311 menu->AddItem(tr("Custom Edit"), SLOT(EditCustom())); 00312 00313 ProgramInfo *pi = m_itemList[m_progList->GetCurrentPos()]; 00314 if (m_type != plPreviouslyRecorded) 00315 { 00316 if (pi && pi->GetRecordingRuleID()) 00317 menu->AddItem(tr("Delete Rule"), SLOT(ShowDeleteRuleMenu())); 00318 } 00319 else 00320 { 00321 menu->AddItem( 00322 tr("Delete Episode"), SLOT(ShowDeleteOldEpisodeMenu())); 00323 } 00324 00325 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 00326 MythDialogBox *menuPopup = new MythDialogBox(menu, popupStack, "menuPopup"); 00327 00328 if (!menuPopup->Create()) 00329 { 00330 delete menuPopup; 00331 return; 00332 } 00333 00334 popupStack->AddScreen(menuPopup); 00335 } 00336 00337 void ProgLister::SwitchToPreviousView(void) 00338 { 00339 if (m_type == plTime && !m_viewList.empty() && !m_viewTextList.empty()) 00340 { 00341 m_searchTime = m_searchTime.addSecs(-3600); 00342 m_curView = 0; 00343 m_viewList[m_curView] = MythDateTimeToString(m_searchTime, 00344 kDateTimeFull | kSimplify); 00345 m_viewTextList[m_curView] = m_viewList[m_curView]; 00346 LoadInBackground(); 00347 return; 00348 } 00349 00350 if (m_viewList.size() <= 1) 00351 return; 00352 00353 m_curView--; 00354 if (m_curView < 0) 00355 m_curView = m_viewList.size() - 1; 00356 00357 LoadInBackground(); 00358 } 00359 00360 void ProgLister:: SwitchToNextView(void) 00361 { 00362 if (m_type == plTime && !m_viewList.empty() && !m_viewTextList.empty()) 00363 { 00364 m_searchTime = m_searchTime.addSecs(3600); 00365 m_curView = 0; 00366 m_viewList[m_curView] = MythDateTimeToString(m_searchTime, 00367 kDateTimeFull | kSimplify); 00368 m_viewTextList[m_curView] = m_viewList[m_curView]; 00369 LoadInBackground(); 00370 00371 return; 00372 } 00373 00374 if (m_viewList.size() <= 1) 00375 return; 00376 00377 m_curView++; 00378 if (m_curView >= (int)m_viewList.size()) 00379 m_curView = 0; 00380 00381 LoadInBackground(); 00382 } 00383 00384 void ProgLister::UpdateKeywordInDB(const QString &text, const QString &oldValue) 00385 { 00386 int oldview = m_viewList.indexOf(oldValue); 00387 int newview = m_viewList.indexOf(text); 00388 00389 if (newview >= 0 && newview == oldview) 00390 return; 00391 00392 if (oldview >= 0) 00393 { 00394 QString qphrase = m_viewList[oldview]; 00395 00396 MSqlQuery query(MSqlQuery::InitCon()); 00397 query.prepare("DELETE FROM keyword " 00398 "WHERE phrase = :PHRASE AND searchtype = :TYPE;"); 00399 query.bindValue(":PHRASE", qphrase); 00400 query.bindValue(":TYPE", m_searchType); 00401 if (!query.exec()) 00402 { 00403 MythDB::DBError( 00404 "ProgLister::updateKeywordInDB -- delete", query); 00405 } 00406 m_viewList.removeAll(qphrase); 00407 m_viewTextList.removeAll(qphrase); 00408 } 00409 00410 if (newview < 0) 00411 { 00412 QString qphrase = text; 00413 00414 MSqlQuery query(MSqlQuery::InitCon()); 00415 query.prepare("REPLACE INTO keyword (phrase, searchtype)" 00416 "VALUES(:PHRASE, :TYPE );"); 00417 query.bindValue(":PHRASE", qphrase); 00418 query.bindValue(":TYPE", m_searchType); 00419 if (!query.exec()) 00420 { 00421 MythDB::DBError( 00422 "ProgLister::updateKeywordInDB -- replace", query); 00423 } 00424 m_viewList.push_back(qphrase); 00425 m_viewTextList.push_back(qphrase); 00426 } 00427 } 00428 00429 void ProgLister::ShowChooseViewMenu(void) 00430 { 00431 MythScreenStack *popupStack = 00432 GetMythMainWindow()->GetStack("popup stack"); 00433 MythScreenType *screen = NULL; 00434 bool connect_string = true; 00435 00436 switch (m_type) 00437 { 00438 case plChannel: 00439 case plCategory: 00440 case plMovies: 00441 case plNewListings: 00442 case plStoredSearch: 00443 { 00444 if (m_viewList.empty()) 00445 return; 00446 00447 QString msg; 00448 switch (m_type) 00449 { 00450 case plMovies: msg = tr("Select Rating"); break; 00451 case plChannel: msg = tr("Select Channel"); break; 00452 case plCategory: msg = tr("Select Category"); break; 00453 case plNewListings: msg = tr("Select List"); break; 00454 case plStoredSearch: msg = QString("%1\n%2") 00455 .arg(tr("Select a search stored from")) 00456 .arg(tr("Custom Record")); break; 00457 } 00458 00459 screen = new MythUISearchDialog( 00460 popupStack, msg, m_viewTextList, true, ""); 00461 00462 break; 00463 } 00464 case plTitleSearch: 00465 case plKeywordSearch: 00466 case plPeopleSearch: 00467 screen = new PhrasePopup( 00468 popupStack, this, m_searchType, m_viewTextList, 00469 (m_curView >= 0) ? m_viewList[m_curView] : QString()); 00470 break; 00471 case plPowerSearch: 00472 screen = new PowerSearchPopup( 00473 popupStack, this, m_searchType, m_viewTextList, 00474 (m_curView >= 0) ? m_viewList[m_curView] : QString()); 00475 break; 00476 case plTime: 00477 QString message = tr("Start search from date and time"); 00478 int flags = (MythTimeInputDialog::kDay | 00479 MythTimeInputDialog::kHours | 00480 MythTimeInputDialog::kFutureDates); 00481 screen = new MythTimeInputDialog(popupStack, message, flags); 00482 connect_string = false; 00483 break; 00484 } 00485 00486 if (!screen) 00487 return; 00488 00489 if (!screen->Create()) 00490 { 00491 delete screen; 00492 return; 00493 } 00494 00495 if (connect_string) 00496 { 00497 connect(screen, SIGNAL(haveResult( QString)), 00498 this, SLOT( SetViewFromList(QString))); 00499 } 00500 else 00501 { 00502 connect(screen, SIGNAL(haveResult( QDateTime)), 00503 this, SLOT( SetViewFromTime(QDateTime))); 00504 } 00505 00506 popupStack->AddScreen(screen); 00507 } 00508 00509 void ProgLister::SetViewFromTime(QDateTime searchTime) 00510 { 00511 if (m_viewList.empty() || m_viewTextList.empty()) 00512 return; 00513 00514 m_searchTime = searchTime; 00515 m_curView = 0; 00516 m_viewList[m_curView] = MythDateTimeToString(m_searchTime, 00517 kDateTimeFull | kSimplify); 00518 m_viewTextList[m_curView] = m_viewList[m_curView]; 00519 00520 LoadInBackground(); 00521 } 00522 00523 void ProgLister::SetViewFromList(QString item) 00524 { 00525 m_curView = m_viewTextList.indexOf(item); 00526 if (m_curView >= 0) 00527 LoadInBackground(); 00528 } 00529 00530 bool ProgLister::PowerStringToSQL( 00531 const QString &qphrase, QString &output, MSqlBindings &bindings) const 00532 { 00533 output.clear(); 00534 00535 QStringList field = qphrase.split(':'); 00536 if (field.size() != 6) 00537 { 00538 LOG(VB_GENERAL, LOG_ERR, LOC + "Power search should have 6 fields," + 00539 QString("\n\t\t\tnot %1 (%2)") .arg(field.size()).arg(qphrase)); 00540 return false; 00541 }; 00542 00543 static const QString bindinglist[6] = 00544 { 00545 ":POWERTITLE", 00546 ":POWERSUB", 00547 ":POWERDESC", 00548 ":POWERCATTYPE", 00549 ":POWERGENRE", 00550 ":POWERCALLSIGN", 00551 }; 00552 00553 static const QString outputlist[6] = 00554 { 00555 "program.title LIKE :POWERTITLE ", 00556 "program.subtitle LIKE :POWERSUB ", 00557 "program.description LIKE :POWERDESC ", 00558 "program.category_type = :POWERCATTYPE ", 00559 "programgenres.genre = :POWERGENRE ", 00560 "channel.callsign = :POWERCALLSIGN ", 00561 }; 00562 00563 for (uint i = 0; i < (uint) field.size(); i++) 00564 { 00565 if (field[i].isEmpty()) 00566 continue; 00567 00568 if (!output.isEmpty()) 00569 output += "\nAND "; 00570 00571 output += outputlist[i]; 00572 bindings[bindinglist[i]] = 00573 (!outputlist[i].contains("=")) ? 00574 QString('%') + field[i] + QString('%') : field[i]; 00575 } 00576 00577 return output.contains("programgenres"); 00578 } 00579 00580 const ProgramInfo *ProgLister::GetCurrent(void) const 00581 { 00582 int pos = m_progList->GetCurrentPos(); 00583 if (pos >= 0 && pos < (int) m_itemList.size()) 00584 return m_itemList[pos]; 00585 return NULL; 00586 } 00587 00588 ProgramInfo *ProgLister::GetCurrent(void) 00589 { 00590 int pos = m_progList->GetCurrentPos(); 00591 if (pos >= 0 && pos < (int) m_itemList.size()) 00592 return m_itemList[pos]; 00593 return NULL; 00594 } 00595 00596 void ProgLister::RecordSelected(void) 00597 { 00598 ProgramInfo *pi = GetCurrent(); 00599 if (pi) 00600 { 00601 RecordingInfo ri(*pi); 00602 ri.ToggleRecord(); 00603 *pi = ri; 00604 } 00605 } 00606 00607 void ProgLister::HandleClicked(void) 00608 { 00609 ProgramInfo *pi = GetCurrent(); 00610 if (pi) 00611 { 00612 if (m_type == plPreviouslyRecorded) 00613 ShowOldRecordedMenu(); 00614 else 00615 EditRecording(pi); 00616 } 00617 } 00618 00619 void ProgLister::ShowDeleteItemMenu(void) 00620 { 00621 if (m_type == plPreviouslyRecorded) 00622 ShowDeleteOldEpisodeMenu(); 00623 else 00624 ShowDeleteRuleMenu(); 00625 } 00626 00627 void ProgLister::ShowDeleteRuleMenu(void) 00628 { 00629 ProgramInfo *pi = GetCurrent(); 00630 00631 if (!pi || !pi->GetRecordingRuleID()) 00632 return; 00633 00634 RecordingRule *record = new RecordingRule(); 00635 if (!record->LoadByProgram(pi)) 00636 { 00637 delete record; 00638 return; 00639 } 00640 00641 QString message = tr("Delete '%1' %2 rule?").arg(record->m_title) 00642 .arg(toString(pi->GetRecordingRuleType())); 00643 00644 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 00645 00646 MythConfirmationDialog *okPopup = new MythConfirmationDialog( 00647 popupStack, message, true); 00648 00649 okPopup->SetReturnEvent(this, "deleterule"); 00650 okPopup->SetData(qVariantFromValue(record)); 00651 00652 if (okPopup->Create()) 00653 popupStack->AddScreen(okPopup); 00654 else 00655 delete okPopup; 00656 } 00657 00658 void ProgLister::ShowDeleteOldEpisodeMenu(void) 00659 { 00660 ProgramInfo *pi = GetCurrent(); 00661 00662 if (!pi) 00663 return; 00664 00665 QString message = tr("Delete this episode of '%1'?").arg(pi->GetTitle()); 00666 00667 ShowOkPopup(message, this, SLOT(DeleteOldEpisode(bool)), true); 00668 } 00669 00670 void ProgLister::DeleteOldEpisode(bool ok) 00671 { 00672 ProgramInfo *pi = GetCurrent(); 00673 if (!ok || !pi) 00674 return; 00675 00676 MSqlQuery query(MSqlQuery::InitCon()); 00677 query.prepare( 00678 "DELETE FROM oldrecorded " 00679 "WHERE chanid = :CHANID AND " 00680 " starttime = :STARTTIME"); 00681 query.bindValue(":CHANID", pi->GetChanID()); 00682 query.bindValue(":STARTTIME", pi->GetScheduledStartTime()); 00683 00684 if (!query.exec()) 00685 MythDB::DBError("ProgLister::DeleteOldEpisode", query); 00686 00687 ScheduledRecording::RescheduleCheck(*pi, "DeleteOldEpisode"); 00688 FillItemList(true); 00689 } 00690 00691 void ProgLister::ShowDeleteOldSeriesMenu(void) 00692 { 00693 ProgramInfo *pi = GetCurrent(); 00694 00695 if (!pi) 00696 return; 00697 00698 QString message = tr("Delete all episodes of '%1'?").arg(pi->GetTitle()); 00699 00700 ShowOkPopup(message, this, SLOT(DeleteOldSeries(bool)), true); 00701 } 00702 00703 void ProgLister::DeleteOldSeries(bool ok) 00704 { 00705 ProgramInfo *pi = GetCurrent(); 00706 if (!ok || !pi) 00707 return; 00708 00709 MSqlQuery query(MSqlQuery::InitCon()); 00710 query.prepare("DELETE FROM oldrecorded " 00711 "WHERE title = :TITLE AND future = 0"); 00712 query.bindValue(":TITLE", pi->GetTitle()); 00713 if (!query.exec()) 00714 MythDB::DBError("ProgLister::DeleteOldSeries -- delete", query); 00715 00716 // Set the programid to the special value of "**any**" which the 00717 // scheduler recognizes to mean the entire series was deleted. 00718 RecordingInfo tempri(*pi); 00719 tempri.SetProgramID("**any**"); 00720 ScheduledRecording::RescheduleCheck(tempri, "DeleteOldSeries"); 00721 FillItemList(true); 00722 } 00723 00724 void ProgLister::ShowOldRecordedMenu(void) 00725 { 00726 ProgramInfo *pi = GetCurrent(); 00727 00728 if (!pi) 00729 return; 00730 00731 QString message = pi->toString(ProgramInfo::kTitleSubtitle, " - "); 00732 00733 if (!pi->GetDescription().isEmpty()) 00734 message += "\n\n" + pi->GetDescription(); 00735 00736 message += "\n\n\n" + tr("NOTE: removing items from this list will not " 00737 "delete any recordings."); 00738 00739 QString title = tr("Previously Recorded"); 00740 00741 MythMenu *menu = new MythMenu(title, message, this, "deletemenu"); 00742 if (pi->IsDuplicate()) 00743 menu->AddItem(tr("Allow this episode to re-record")); 00744 else 00745 menu->AddItem(tr("Never record this episode")); 00746 menu->AddItem(tr("Remove this episode from the list")); 00747 menu->AddItem(tr("Remove all episodes for this title")); 00748 menu->AddItem(tr("Cancel")); 00749 00750 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack(); 00751 MythDialogBox *menuPopup = new MythDialogBox(menu, mainStack, "deletepopup", true); 00752 00753 if (menuPopup->Create()) 00754 mainStack->AddScreen(menuPopup); 00755 else 00756 delete menuPopup; 00757 } 00758 00759 void ProgLister::ShowUpcoming(void) 00760 { 00761 ProgramInfo *pi = GetCurrent(); 00762 if (pi && m_type != plTitle) 00763 ScheduleCommon::ShowUpcoming(pi); 00764 } 00765 00766 void ProgLister::FillViewList(const QString &view) 00767 { 00768 m_viewList.clear(); 00769 m_viewTextList.clear(); 00770 00771 if (m_type == plChannel) // list by channel 00772 { 00773 DBChanList channels = ChannelUtil::GetChannels( 00774 0, true, "channum, chanid"); 00775 ChannelUtil::SortChannels(channels, m_channelOrdering, true); 00776 00777 for (uint i = 0; i < channels.size(); ++i) 00778 { 00779 QString chantext = channels[i].GetFormatted(DBChannel::kChannelShort); 00780 00781 m_viewList.push_back(QString::number(channels[i].chanid)); 00782 m_viewTextList.push_back(chantext); 00783 } 00784 00785 if (!view.isEmpty()) 00786 m_curView = m_viewList.indexOf(view); 00787 } 00788 else if (m_type == plCategory) // list by category 00789 { 00790 QDateTime query_starttime = m_startTime.addSecs(50 - 00791 m_startTime.time().second()); 00792 MSqlQuery query(MSqlQuery::InitCon()); 00793 query.prepare("SELECT g1.genre, g2.genre " 00794 "FROM program " 00795 "JOIN programgenres g1 ON " 00796 " program.chanid = g1.chanid AND " 00797 " program.starttime = g1.starttime " 00798 "LEFT JOIN programgenres g2 ON " 00799 " g1.chanid = g2.chanid AND " 00800 " g1.starttime = g2.starttime " 00801 "WHERE program.endtime > :PGILSTART " 00802 "GROUP BY g1.genre, g2.genre;"); 00803 query.bindValue(":PGILSTART", query_starttime); 00804 00805 m_useGenres = false; 00806 00807 if (query.exec()) 00808 { 00809 QString lastGenre1; 00810 00811 while (query.next()) 00812 { 00813 m_useGenres = true; 00814 00815 QString genre1 = query.value(0).toString(); 00816 if (genre1.isEmpty()) 00817 continue; 00818 00819 if (genre1 != lastGenre1) 00820 { 00821 m_viewList.push_back(genre1); 00822 m_viewTextList.push_back(genre1); 00823 lastGenre1 = genre1; 00824 } 00825 00826 QString genre2 = query.value(1).toString(); 00827 if (genre2.isEmpty() || genre2 == genre1) 00828 continue; 00829 00830 m_viewList.push_back(genre1 + ":/:" + genre2); 00831 m_viewTextList.push_back(" " + genre1 + " / " + genre2); 00832 } 00833 } 00834 00835 if (!m_useGenres) 00836 { 00837 query.prepare("SELECT category " 00838 "FROM program " 00839 "WHERE program.endtime > :PGILSTART " 00840 "GROUP BY category"); 00841 query.bindValue(":PGILSTART", query_starttime); 00842 00843 if (query.exec()) 00844 { 00845 while (query.next()) 00846 { 00847 QString category = query.value(0).toString(); 00848 if (category.isEmpty()) 00849 continue; 00850 category = query.value(0).toString(); 00851 m_viewList.push_back(category); 00852 m_viewTextList.push_back(category); 00853 } 00854 } 00855 } 00856 00857 if (!view.isEmpty()) 00858 m_curView = m_viewList.indexOf(view); 00859 } 00860 else if (m_type == plTitleSearch || m_type == plKeywordSearch || 00861 m_type == plPeopleSearch || m_type == plPowerSearch) 00862 { 00863 MSqlQuery query(MSqlQuery::InitCon()); 00864 query.prepare("SELECT phrase FROM keyword " 00865 "WHERE searchtype = :SEARCHTYPE;"); 00866 query.bindValue(":SEARCHTYPE", m_searchType); 00867 00868 if (query.exec()) 00869 { 00870 while (query.next()) 00871 { 00872 /* The keyword.phrase column uses utf8_bin collation, so 00873 * Qt uses QString::fromAscii() for toString(). Explicitly 00874 * convert the value using QString::fromUtf8() to prevent 00875 * corruption. */ 00876 QString phrase = QString::fromUtf8(query.value(0) 00877 .toByteArray().constData()); 00878 if (phrase.isEmpty()) 00879 continue; 00880 m_viewList.push_back(phrase); 00881 m_viewTextList.push_back(phrase); 00882 } 00883 } 00884 00885 if (!view.isEmpty()) 00886 { 00887 m_curView = m_viewList.indexOf(view); 00888 00889 if (m_curView < 0) 00890 { 00891 QString qphrase = view; 00892 00893 MSqlQuery query(MSqlQuery::InitCon()); 00894 query.prepare("REPLACE INTO keyword (phrase, searchtype)" 00895 "VALUES(:VIEW, :SEARCHTYPE );"); 00896 query.bindValue(":VIEW", qphrase); 00897 query.bindValue(":SEARCHTYPE", m_searchType); 00898 if (!query.exec()) 00899 MythDB::DBError("ProgLister::FillViewList -- " 00900 "replace keyword", query); 00901 00902 m_viewList.push_back(qphrase); 00903 m_viewTextList.push_back(qphrase); 00904 00905 m_curView = m_viewList.size() - 1; 00906 } 00907 } 00908 else 00909 { 00910 m_curView = -1; 00911 } 00912 } 00913 else if (m_type == plTitle) 00914 { 00915 if (!view.isEmpty()) 00916 { 00917 m_viewList.push_back(view); 00918 m_viewTextList.push_back(view); 00919 m_curView = 0; 00920 } 00921 else 00922 { 00923 m_curView = -1; 00924 } 00925 } 00926 else if (m_type == plNewListings) 00927 { 00928 m_viewList.push_back("all"); 00929 m_viewTextList.push_back(tr("All")); 00930 00931 m_viewList.push_back("premieres"); 00932 m_viewTextList.push_back(tr("Premieres")); 00933 00934 m_viewList.push_back("movies"); 00935 m_viewTextList.push_back(tr("Movies")); 00936 00937 m_viewList.push_back("series"); 00938 m_viewTextList.push_back(tr("Series")); 00939 00940 m_viewList.push_back("specials"); 00941 m_viewTextList.push_back(tr("Specials")); 00942 00943 if (!view.isEmpty()) 00944 m_curView = m_viewList.indexOf(view); 00945 } 00946 else if (m_type == plMovies) 00947 { 00948 m_viewList.push_back(">= 0.0"); 00949 m_viewTextList.push_back(tr("All")); 00950 m_viewList.push_back("= 0.0"); 00951 m_viewTextList.push_back(tr("Unrated")); 00952 m_viewList.push_back(QString("= 10.0")); 00953 m_viewTextList.push_back(tr("%n star(s)", "", 10)); 00954 for (int i = 9; i > 0; i--) 00955 { 00956 float stars = i / 10.0; 00957 m_viewList.push_back(QString(">= %1").arg(stars)); 00958 m_viewTextList.push_back(tr("%n star(s) and above", "", i)); 00959 } 00960 00961 if (!view.isEmpty()) 00962 m_curView = m_viewList.indexOf(view); 00963 } 00964 else if (m_type == plTime) 00965 { 00966 m_curView = 0; 00967 m_viewList.push_back(MythDateTimeToString(m_searchTime, 00968 kDateTimeFull | kSimplify)); 00969 m_viewTextList.push_back(m_viewList[m_curView]); 00970 } 00971 else if (m_type == plSQLSearch) 00972 { 00973 m_curView = 0; 00974 m_viewList.push_back(view); 00975 m_viewTextList.push_back(tr("Power Recording Rule")); 00976 } 00977 else if (m_type == plRecordid) 00978 { 00979 m_curView = 0; 00980 00981 MSqlQuery query(MSqlQuery::InitCon()); 00982 query.prepare("SELECT title FROM record " 00983 "WHERE recordid = :RECORDID"); 00984 query.bindValue(":RECORDID", view); 00985 00986 if (query.exec() && query.next()) 00987 { 00988 QString title = query.value(0).toString(); 00989 title = query.value(0).toString(); 00990 m_viewList.push_back(view); 00991 m_viewTextList.push_back(title); 00992 } 00993 } 00994 else if (m_type == plStoredSearch) // stored searches 00995 { 00996 MSqlQuery query(MSqlQuery::InitCon()); 00997 query.prepare("SELECT rulename FROM customexample " 00998 "WHERE search > 0 ORDER BY rulename;"); 00999 01000 if (query.exec()) 01001 { 01002 while (query.next()) 01003 { 01004 QString rulename = query.value(0).toString(); 01005 if (rulename.isEmpty() || rulename.trimmed().isEmpty()) 01006 continue; 01007 rulename = query.value(0).toString(); 01008 m_viewList.push_back(rulename); 01009 m_viewTextList.push_back(rulename); 01010 } 01011 } 01012 if (!view.isEmpty()) 01013 m_curView = m_viewList.indexOf(view); 01014 } 01015 else if (m_type == plPreviouslyRecorded) // previously recorded 01016 { 01017 m_viewList.push_back("sort by time"); 01018 m_viewTextList.push_back(tr("Time")); 01019 01020 m_viewList.push_back("reverse time"); 01021 m_viewTextList.push_back(tr("Reverse Time")); 01022 01023 m_viewList.push_back("sort by title"); 01024 m_viewTextList.push_back(tr("Title")); 01025 01026 m_viewList.push_back("reverse title"); 01027 m_viewTextList.push_back(tr("Reverse Title")); 01028 01029 if (!view.isEmpty()) 01030 m_curView = m_viewList.indexOf(view); 01031 } 01032 01033 if (m_curView >= (int)m_viewList.size()) 01034 m_curView = m_viewList.size() - 1; 01035 } 01036 01037 class plCompare : binary_function<const ProgramInfo*, const ProgramInfo*, bool> 01038 { 01039 public: 01040 virtual bool operator()(const ProgramInfo *a, const ProgramInfo *b) 01041 = 0; 01042 }; 01043 01044 class plTitleSort : public plCompare 01045 { 01046 public: 01047 bool operator()(const ProgramInfo *a, const ProgramInfo *b) 01048 { 01049 if (a->sortTitle != b->sortTitle) 01050 return (a->sortTitle < b->sortTitle); 01051 01052 if (a->GetRecordingStatus() == b->GetRecordingStatus()) 01053 return a->GetScheduledStartTime() < b->GetScheduledStartTime(); 01054 01055 if (a->GetRecordingStatus() == rsRecording) 01056 return true; 01057 if (b->GetRecordingStatus() == rsRecording) 01058 return false; 01059 01060 if (a->GetRecordingStatus() == rsWillRecord) 01061 return true; 01062 if (b->GetRecordingStatus() == rsWillRecord) 01063 return false; 01064 01065 return a->GetScheduledStartTime() < b->GetScheduledStartTime(); 01066 } 01067 }; 01068 01069 class plPrevTitleSort : public plCompare 01070 { 01071 public: 01072 plPrevTitleSort(void) : plCompare() {;} 01073 01074 bool operator()(const ProgramInfo *a, const ProgramInfo *b) 01075 { 01076 if (a->sortTitle != b->sortTitle) 01077 return (a->sortTitle < b->sortTitle); 01078 01079 if (a->GetProgramID() != b->GetProgramID()) 01080 return a->GetProgramID() < b->GetProgramID(); 01081 01082 return a->GetScheduledStartTime() < b->GetScheduledStartTime(); 01083 } 01084 }; 01085 01086 class plTimeSort : public plCompare 01087 { 01088 public: 01089 plTimeSort(void) : plCompare() {;} 01090 01091 bool operator()(const ProgramInfo *a, const ProgramInfo *b) 01092 { 01093 if (a->GetScheduledStartTime() == b->GetScheduledStartTime()) 01094 return (a->GetChanID() < b->GetChanID()); 01095 01096 return (a->GetScheduledStartTime() < b->GetScheduledStartTime()); 01097 } 01098 }; 01099 01100 void ProgLister::FillItemList(bool restorePosition, bool updateDisp) 01101 { 01102 if (m_type == plPreviouslyRecorded && m_curviewText) 01103 { 01104 if (!m_titleSort) 01105 { 01106 if (!m_reverseSort) 01107 m_curView = 0; 01108 else 01109 m_curView = 1; 01110 } 01111 else 01112 { 01113 if (!m_reverseSort) 01114 m_curView = 2; 01115 else 01116 m_curView = 3; 01117 } 01118 } 01119 01120 if (m_curView < 0) 01121 return; 01122 01123 QString where; 01124 QString qphrase = m_viewList[m_curView]; 01125 01126 MSqlBindings bindings; 01127 01128 if (m_type != plPreviouslyRecorded) 01129 bindings[":PGILSTART"] = 01130 m_startTime.addSecs(50 - m_startTime.time().second()); 01131 01132 if (m_type == plTitle) // per title listings 01133 { 01134 where = "WHERE channel.visible = 1 " 01135 " AND program.endtime > :PGILSTART " 01136 " AND (program.title = :PGILPHRASE0 OR " 01137 " (program.seriesid <> '' AND " 01138 " program.seriesid = :PGILPHRASE1)) "; 01139 bindings[":PGILPHRASE0"] = qphrase; 01140 bindings[":PGILPHRASE1"] = m_extraArg; 01141 } 01142 else if (m_type == plNewListings) // what's new list 01143 { 01144 where = "LEFT JOIN oldprogram ON " 01145 " oldprogram.oldtitle = program.title " 01146 "WHERE channel.visible = 1 " 01147 " AND program.endtime > :PGILSTART " 01148 " AND oldprogram.oldtitle IS NULL " 01149 " AND program.manualid = 0 "; 01150 01151 if (qphrase == "premieres") 01152 { 01153 where += " AND ( "; 01154 where += " ( program.originalairdate=DATE(program.starttime) "; 01155 where += " AND (program.category = 'Special' "; 01156 where += " OR program.programid LIKE 'EP%0001')) "; 01157 where += " OR (program.category_type='movie' "; 01158 where += " AND program.stars > 0.5 "; 01159 where += " AND program.airdate >= YEAR(NOW()) - 2) "; 01160 where += " ) "; 01161 } 01162 else if (qphrase == "movies") 01163 { 01164 where += " AND program.category_type = 'movie' "; 01165 } 01166 else if (qphrase == "series") 01167 { 01168 where += " AND program.category_type = 'series' "; 01169 } 01170 else if (qphrase == "specials") 01171 { 01172 where += " AND program.category_type = 'tvshow' "; 01173 } 01174 else 01175 { 01176 where += " AND (program.category_type <> 'movie' "; 01177 where += " OR program.airdate >= YEAR(NOW()) - 3) "; 01178 } 01179 } 01180 else if (m_type == plTitleSearch) // keyword search 01181 { 01182 where = "WHERE channel.visible = 1 " 01183 " AND program.endtime > :PGILSTART " 01184 " AND program.title LIKE :PGILLIKEPHRASE0 "; 01185 bindings[":PGILLIKEPHRASE0"] = QString("%") + qphrase + '%'; 01186 } 01187 else if (m_type == plKeywordSearch) // keyword search 01188 { 01189 where = "WHERE channel.visible = 1 " 01190 " AND program.endtime > :PGILSTART " 01191 " AND (program.title LIKE :PGILLIKEPHRASE1 " 01192 " OR program.subtitle LIKE :PGILLIKEPHRASE2 " 01193 " OR program.description LIKE :PGILLIKEPHRASE3 ) "; 01194 bindings[":PGILLIKEPHRASE1"] = QString("%") + qphrase + '%'; 01195 bindings[":PGILLIKEPHRASE2"] = QString("%") + qphrase + '%'; 01196 bindings[":PGILLIKEPHRASE3"] = QString("%") + qphrase + '%'; 01197 } 01198 else if (m_type == plPeopleSearch) // people search 01199 { 01200 where = ", people, credits WHERE channel.visible = 1 " 01201 " AND program.endtime > :PGILSTART " 01202 " AND people.name LIKE :PGILPHRASE1 " 01203 " AND credits.person = people.person " 01204 " AND program.chanid = credits.chanid " 01205 " AND program.starttime = credits.starttime"; 01206 bindings[":PGILPHRASE1"] = qphrase; 01207 } 01208 else if (m_type == plPowerSearch) // complex search 01209 { 01210 QString powerWhere; 01211 MSqlBindings powerBindings; 01212 01213 bool genreflag = PowerStringToSQL(qphrase, powerWhere, powerBindings); 01214 01215 if (!powerWhere.isEmpty()) 01216 { 01217 if (genreflag) 01218 where = QString("LEFT JOIN programgenres ON " 01219 "program.chanid = programgenres.chanid AND " 01220 "program.starttime = programgenres.starttime "); 01221 01222 where += QString("WHERE channel.visible = 1 " 01223 " AND program.endtime > :PGILSTART " 01224 " AND ( ") + powerWhere + " ) "; 01225 MSqlAddMoreBindings(bindings, powerBindings); 01226 } 01227 } 01228 else if (m_type == plSQLSearch) // complex search 01229 { 01230 qphrase.remove(QRegExp("^\\s*AND\\s+", Qt::CaseInsensitive)); 01231 where = QString("WHERE channel.visible = 1 " 01232 " AND program.endtime > :PGILSTART " 01233 " AND ( %1 ) ").arg(qphrase); 01234 if (!m_extraArg.isEmpty()) 01235 where = m_extraArg + ' ' + where; 01236 } 01237 else if (m_type == plChannel) // list by channel 01238 { 01239 where = "WHERE channel.visible = 1 " 01240 " AND program.endtime > :PGILSTART " 01241 " AND channel.chanid = :PGILPHRASE2 "; 01242 bindings[":PGILPHRASE2"] = qphrase; 01243 } 01244 else if (m_type == plCategory) // list by category 01245 { 01246 if (!m_useGenres) 01247 { 01248 where = "WHERE channel.visible = 1 " 01249 " AND program.endtime > :PGILSTART " 01250 " AND program.category = :PGILPHRASE3 "; 01251 bindings[":PGILPHRASE3"] = qphrase; 01252 } 01253 else if (m_viewList[m_curView].indexOf(":/:") < 0) 01254 { 01255 where = "JOIN programgenres g ON " 01256 " program.chanid = g.chanid AND " 01257 " program.starttime = g.starttime AND " 01258 " genre = :PGILPHRASE4 " 01259 "WHERE channel.visible = 1 " 01260 " AND program.endtime > :PGILSTART "; 01261 bindings[":PGILPHRASE4"] = qphrase; 01262 } 01263 else 01264 { 01265 where = "JOIN programgenres g1 ON " 01266 " program.chanid = g1.chanid AND " 01267 " program.starttime = g1.starttime AND " 01268 " g1.genre = :GENRE1 " 01269 "JOIN programgenres g2 ON " 01270 " program.chanid = g2.chanid AND " 01271 " program.starttime = g2.starttime AND " 01272 " g2.genre = :GENRE2 " 01273 "WHERE channel.visible = 1 " 01274 " AND program.endtime > :PGILSTART "; 01275 bindings[":GENRE1"] = m_viewList[m_curView].section(":/:", 0, 0); 01276 bindings[":GENRE2"] = m_viewList[m_curView].section(":/:", 1, 1); 01277 } 01278 } 01279 else if (m_type == plMovies) // list movies 01280 { 01281 where = "WHERE channel.visible = 1 " 01282 " AND program.endtime > :PGILSTART " 01283 " AND program.category_type = 'movie' " 01284 " AND program.stars " + qphrase + ' '; 01285 } 01286 else if (m_type == plTime) // list by time 01287 { 01288 QDateTime searchTime(m_searchTime); 01289 searchTime.setTime(QTime(searchTime.time().hour(), 0, 0)); 01290 bindings[":PGILSEARCHTIME1"] = searchTime; 01291 where = "WHERE channel.visible = 1 " 01292 " AND program.starttime >= :PGILSEARCHTIME1 "; 01293 if (m_titleSort) 01294 { 01295 where += " AND program.starttime < DATE_ADD(:PGILSEARCHTIME2, " 01296 "INTERVAL '1' HOUR) "; 01297 bindings[":PGILSEARCHTIME2"] = bindings[":PGILSEARCHTIME1"]; 01298 } 01299 } 01300 else if (m_type == plRecordid) // list by recordid 01301 { 01302 where = "JOIN recordmatch ON " 01303 " (program.starttime = recordmatch.starttime " 01304 " AND program.chanid = recordmatch.chanid) " 01305 "WHERE channel.visible = 1 " 01306 " AND program.endtime > :PGILSTART " 01307 " AND recordmatch.recordid = :PGILPHRASE5 "; 01308 bindings[":PGILPHRASE5"] = qphrase; 01309 } 01310 else if (m_type == plStoredSearch) // stored search 01311 { 01312 MSqlQuery query(MSqlQuery::InitCon()); 01313 query.prepare("SELECT fromclause, whereclause FROM customexample " 01314 "WHERE rulename = :RULENAME;"); 01315 query.bindValue(":RULENAME", qphrase); 01316 01317 if (query.exec() && query.next()) 01318 { 01319 QString fromc = query.value(0).toString(); 01320 QString wherec = query.value(1).toString(); 01321 01322 where = QString("WHERE channel.visible = 1 " 01323 " AND program.endtime > :PGILSTART " 01324 " AND ( %1 ) ").arg(wherec); 01325 if (!fromc.isEmpty()) 01326 where = fromc + ' ' + where; 01327 } 01328 } 01329 else if (m_type == plPreviouslyRecorded) 01330 { 01331 if (m_recid && !m_title.isEmpty()) 01332 { 01333 where = QString("AND ( recordid = %1 OR title = :MTITLE )") 01334 .arg(m_recid); 01335 bindings[":MTITLE"] = m_title; 01336 } 01337 else if (!m_title.isEmpty()) 01338 { 01339 where = QString("AND title = :MTITLE "); 01340 bindings[":MTITLE"] = m_title; 01341 } 01342 else if (m_recid) 01343 { 01344 where = QString("AND recordid = %1 ").arg(m_recid); 01345 } 01346 } 01347 01348 ProgramInfo selected; 01349 const ProgramInfo *selectedP = (restorePosition) ? GetCurrent() : NULL; 01350 if (selectedP) 01351 { 01352 selected = *selectedP; 01353 selectedP = &selected; 01354 } 01355 01356 // Save a copy of m_itemList so that deletion of the ProgramInfo 01357 // objects can be deferred until background processing of old 01358 // ProgramInfo objects has completed. 01359 m_itemListSave.clear(); 01360 m_itemListSave = m_itemList; 01361 m_itemList.setAutoDelete(false); 01362 m_itemList.clear(); 01363 m_itemList.setAutoDelete(true); 01364 01365 if (m_type == plPreviouslyRecorded) 01366 { 01367 LoadFromOldRecorded(m_itemList, where, bindings); 01368 } 01369 else 01370 { 01371 LoadFromScheduler(m_schedList); 01372 LoadFromProgram(m_itemList, where, bindings, m_schedList); 01373 } 01374 01375 const QRegExp prefixes( 01376 tr("^(The |A |An )", 01377 "Regular Expression for what to ignore when sorting")); 01378 for (uint i = 0; i < m_itemList.size(); i++) 01379 { 01380 ProgramInfo *s = m_itemList[i]; 01381 s->sortTitle = (m_type == plTitle) ? s->GetSubtitle() : s->GetTitle(); 01382 s->sortTitle.remove(prefixes); 01383 } 01384 01385 if (m_type == plNewListings || m_titleSort) 01386 { 01387 SortList(kTitleSort, m_reverseSort); 01388 if (m_type != plPreviouslyRecorded) 01389 { 01390 // Prune to one per title 01391 QString curtitle; 01392 ProgramList::iterator it = m_itemList.begin(); 01393 while (it != m_itemList.end()) 01394 { 01395 if ((*it)->sortTitle != curtitle) 01396 { 01397 curtitle = (*it)->sortTitle; 01398 ++it; 01399 } 01400 else 01401 { 01402 it = m_itemList.erase(it); 01403 } 01404 } 01405 } 01406 } 01407 01408 if (!m_titleSort) 01409 SortList(GetSortBy(), m_reverseSort); 01410 01411 if (updateDisp) 01412 UpdateDisplay(selectedP); 01413 } 01414 01415 ProgLister::SortBy ProgLister::GetSortBy(void) const 01416 { 01417 if (!m_titleSort) 01418 return kTimeSort; 01419 if (m_type == plPreviouslyRecorded) 01420 return kPrevTitleSort; 01421 return kTitleSort; 01422 } 01423 01424 void ProgLister::SortList(SortBy sortby, bool reverseSort) 01425 { 01426 if (reverseSort) 01427 { 01428 if (kTimeSort == sortby) 01429 stable_sort(m_itemList.rbegin(), m_itemList.rend(), plTimeSort()); 01430 else if (kPrevTitleSort == sortby) 01431 stable_sort(m_itemList.rbegin(), m_itemList.rend(), 01432 plPrevTitleSort()); 01433 else 01434 stable_sort(m_itemList.rbegin(), m_itemList.rend(), plTitleSort()); 01435 } 01436 else 01437 { 01438 if (kTimeSort == sortby) 01439 stable_sort(m_itemList.begin(), m_itemList.end(), plTimeSort()); 01440 else if (kPrevTitleSort == sortby) 01441 stable_sort(m_itemList.begin(), m_itemList.end(), 01442 plPrevTitleSort()); 01443 else 01444 stable_sort(m_itemList.begin(), m_itemList.end(), plTitleSort()); 01445 } 01446 } 01447 01448 void ProgLister::ClearCurrentProgramInfo(void) 01449 { 01450 InfoMap infoMap; 01451 ProgramInfo pginfo; 01452 pginfo.ToMap(infoMap); 01453 ResetMap(infoMap); 01454 01455 if (m_positionText) 01456 m_positionText->Reset(); 01457 } 01458 01459 void ProgLister::UpdateDisplay(const ProgramInfo *selected) 01460 { 01461 int offset = 0; 01462 01463 if (selected) 01464 offset = m_progList->GetCurrentPos() - m_progList->GetTopItemPos(); 01465 01466 m_progList->Reset(); 01467 01468 if (m_messageText) 01469 m_messageText->SetVisible(m_itemList.empty()); 01470 01471 ClearCurrentProgramInfo(); 01472 01473 if (m_curviewText && m_curView >= 0) 01474 m_curviewText->SetText(m_viewTextList[m_curView]); 01475 01476 UpdateButtonList(); 01477 01478 if (selected) 01479 RestoreSelection(selected, offset); 01480 } 01481 01482 void ProgLister::RestoreSelection(const ProgramInfo *selected, 01483 int selectedOffset) 01484 { 01485 plCompare *comp; 01486 if (!m_titleSort) 01487 comp = new plTimeSort(); 01488 else if (m_type == plPreviouslyRecorded) 01489 comp = new plPrevTitleSort(); 01490 else 01491 comp = new plTitleSort(); 01492 01493 int i; 01494 for (i = m_itemList.size() - 2; i >= 0; i--) 01495 { 01496 bool dobreak; 01497 if (m_reverseSort) 01498 dobreak = comp->operator()(selected, m_itemList[i]); 01499 else 01500 dobreak = comp->operator()(m_itemList[i], selected); 01501 if (dobreak) 01502 break; 01503 } 01504 01505 delete comp; 01506 01507 m_progList->SetItemCurrent(i + 1, i + 1 - selectedOffset); 01508 } 01509 01510 void ProgLister::HandleVisible(MythUIButtonListItem *item) 01511 { 01512 ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData()); 01513 01514 if (item->GetText("is_item_initialized").isNull()) 01515 { 01516 InfoMap infoMap; 01517 pginfo->ToMap(infoMap); 01518 01519 QString state = toUIState(pginfo->GetRecordingStatus()); 01520 if ((state == "warning") && (plPreviouslyRecorded == m_type)) 01521 state = "disabled"; 01522 01523 item->SetTextFromMap(infoMap, state); 01524 01525 if (m_type == plTitle) 01526 { 01527 QString tempSubTitle = pginfo->GetSubtitle(); 01528 if (tempSubTitle.trimmed().isEmpty()) 01529 tempSubTitle = pginfo->GetTitle(); 01530 item->SetText(tempSubTitle, "titlesubtitle", state); 01531 } 01532 01533 item->DisplayState(QString::number(pginfo->GetStars(10)), 01534 "ratingstate"); 01535 01536 item->DisplayState(state, "status"); 01537 01538 // Mark this button list item as initialized. 01539 item->SetText("yes", "is_item_initialized"); 01540 } 01541 } 01542 01543 void ProgLister::UpdateButtonList(void) 01544 { 01545 ProgramList::const_iterator it = m_itemList.begin(); 01546 for (; it != m_itemList.end(); ++it) 01547 new MythUIButtonListItem(m_progList, "", qVariantFromValue(*it)); 01548 m_progList->LoadInBackground(); 01549 01550 if (m_positionText) 01551 { 01552 m_positionText->SetText( 01553 tr("%1 of %2", "Current position in list where %1 is the " 01554 "position, %2 is the total count") 01555 .arg(QLocale::system().toString(m_progList->GetCurrentPos() + 1)) 01556 .arg(QLocale::system().toString(m_progList->GetCount()))); 01557 } 01558 } 01559 01560 void ProgLister::HandleSelected(MythUIButtonListItem *item) 01561 { 01562 if (!item) 01563 { 01564 ClearCurrentProgramInfo(); 01565 return; 01566 } 01567 01568 ProgramInfo *pginfo = qVariantValue<ProgramInfo*> (item->GetData()); 01569 if (!pginfo) 01570 { 01571 ClearCurrentProgramInfo(); 01572 return; 01573 } 01574 01575 InfoMap infoMap; 01576 pginfo->ToMap(infoMap); 01577 SetTextFromMap(infoMap); 01578 01579 if (m_positionText) 01580 { 01581 m_positionText->SetText( 01582 tr("%1 of %2", "Current position in list where %1 is the " 01583 "position, %2 is the total count") 01584 .arg(QLocale::system().toString(m_progList->GetCurrentPos() + 1)) 01585 .arg(QLocale::system().toString(m_progList->GetCount()))); 01586 } 01587 01588 MythUIStateType *ratingState = dynamic_cast<MythUIStateType*> 01589 (GetChild("ratingstate")); 01590 01591 if (ratingState) 01592 { 01593 QString rating = QString::number(pginfo->GetStars(10)); 01594 ratingState->DisplayState(rating); 01595 } 01596 } 01597 01598 void ProgLister::customEvent(QEvent *event) 01599 { 01600 bool needUpdate = false; 01601 01602 if (event->type() == DialogCompletionEvent::kEventType) 01603 { 01604 DialogCompletionEvent *dce = (DialogCompletionEvent*)(event); 01605 01606 QString resultid = dce->GetId(); 01607 QString resulttext = dce->GetResultText(); 01608 int buttonnum = dce->GetResult(); 01609 01610 if (resultid == "sortmenu") 01611 { 01612 switch (buttonnum) 01613 { 01614 case 0: 01615 m_reverseSort = !m_reverseSort; 01616 needUpdate = true; 01617 break; 01618 case 1: 01619 m_titleSort = true; 01620 m_reverseSort = false; 01621 needUpdate = true; 01622 break; 01623 case 2: 01624 m_titleSort = false; 01625 m_reverseSort = (m_type == plPreviouslyRecorded); 01626 needUpdate = true; 01627 break; 01628 } 01629 } 01630 else if (resultid == "deletemenu") 01631 { 01632 switch (buttonnum) 01633 { 01634 case 0: 01635 { 01636 ProgramInfo *pi = GetCurrent(); 01637 if (pi) 01638 { 01639 RecordingInfo ri(*pi); 01640 if (ri.IsDuplicate()) 01641 ri.ForgetHistory(); 01642 else 01643 ri.SetDupHistory(); 01644 *pi = ri; 01645 } 01646 break; 01647 } 01648 case 1: 01649 ShowDeleteOldEpisodeMenu(); 01650 break; 01651 case 2: 01652 ShowDeleteOldSeriesMenu(); 01653 break; 01654 } 01655 } 01656 else if (resultid == "deleterule") 01657 { 01658 RecordingRule *record = 01659 qVariantValue<RecordingRule *>(dce->GetData()); 01660 if (record && buttonnum > 0 && !record->Delete()) 01661 { 01662 LOG(VB_GENERAL, LOG_ERR, LOC + 01663 "Failed to delete recording rule"); 01664 } 01665 if (record) 01666 delete record; 01667 } 01668 else 01669 { 01670 ScheduleCommon::customEvent(event); 01671 } 01672 } 01673 else if (event->type() == ScreenLoadCompletionEvent::kEventType) 01674 { 01675 ScreenLoadCompletionEvent *slce = (ScreenLoadCompletionEvent*)(event); 01676 QString id = slce->GetId(); 01677 01678 if (id == objectName()) 01679 { 01680 CloseBusyPopup(); // opened by LoadInBackground() 01681 UpdateDisplay(); 01682 01683 if (m_curView < 0 && m_type != plPreviouslyRecorded) 01684 ShowChooseViewMenu(); 01685 } 01686 } 01687 else if ((MythEvent::Type)(event->type()) == MythEvent::MythEventMessage) 01688 { 01689 MythEvent *me = (MythEvent *)event; 01690 QString message = me->Message(); 01691 01692 if (message == "CHOOSE_VIEW") 01693 ShowChooseViewMenu(); 01694 else if (message == "SCHEDULE_CHANGE") 01695 needUpdate = true; 01696 } 01697 01698 if (needUpdate) 01699 FillItemList(true); 01700 }
1.7.6.1