00001
00002 #include <vector>
00003 #include <algorithm>
00004 #include <functional>
00005 using namespace std;
00006
00007
00008 #include <QCoreApplication>
00009 #include <QRegExp>
00010 #include <QLocale>
00011
00012
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
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
00717
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)
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)
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
00873
00874
00875
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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
01357
01358
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
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
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();
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 }