MythTV  0.26-pre
proglist.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends