MythTV  0.26-pre
playbackbox.cpp
Go to the documentation of this file.
00001 
00002 #include "playbackbox.h"
00003 
00004 // QT
00005 #include <QCoreApplication>
00006 #include <QWaitCondition>
00007 #include <QDateTime>
00008 #include <QTimer>
00009 #include <QMap>
00010 
00011 // MythTV
00012 #include "previewgeneratorqueue.h"
00013 #include "mythuiprogressbar.h"
00014 #include "mythuibuttonlist.h"
00015 #include "mythcorecontext.h"
00016 #include "mythsystemevent.h"
00017 #include "mythuistatetype.h"
00018 #include "mythuicheckbox.h"
00019 #include "mythuitextedit.h"
00020 #include "mythuispinbox.h"
00021 #include "mythdialogbox.h"
00022 #include "recordinginfo.h"
00023 #include "recordingrule.h"
00024 #include "mythuihelper.h"
00025 #include "storagegroup.h"
00026 #include "mythuibutton.h"
00027 #include "mythlogging.h"
00028 #include "mythuiimage.h"
00029 #include "programinfo.h"
00030 #include "mythplayer.h"
00031 #include "mythuitext.h"
00032 #include "remoteutil.h"
00033 #include "mythdbcon.h"
00034 #include "playgroup.h"
00035 #include "netutils.h"
00036 #include "mythdirs.h"
00037 #include "mythdb.h"
00038 #include "mythmiscutil.h"
00039 #include "tv.h"
00040 
00041 //  Mythfrontend
00042 #include "playbackboxlistitem.h"
00043 #include "customedit.h"
00044 #include "proglist.h"
00045 
00046 #define LOC      QString("PlaybackBox: ")
00047 #define LOC_WARN QString("PlaybackBox Warning: ")
00048 #define LOC_ERR  QString("PlaybackBox Error: ")
00049 
00050 static int comp_programid(const ProgramInfo *a, const ProgramInfo *b)
00051 {
00052     if (a->GetProgramID() == b->GetProgramID())
00053         return (a->GetRecordingStartTime() <
00054                 b->GetRecordingStartTime() ? 1 : -1);
00055     else
00056         return (a->GetProgramID() < b->GetProgramID() ? 1 : -1);
00057 }
00058 
00059 static int comp_programid_rev(const ProgramInfo *a, const ProgramInfo *b)
00060 {
00061     if (a->GetProgramID() == b->GetProgramID())
00062         return (a->GetRecordingStartTime() >
00063                 b->GetRecordingStartTime() ? 1 : -1);
00064     else
00065         return (a->GetProgramID() > b->GetProgramID() ? 1 : -1);
00066 }
00067 
00068 static int comp_originalAirDate(const ProgramInfo *a, const ProgramInfo *b)
00069 {
00070     QDate dt1 = (a->GetOriginalAirDate().isValid()) ?
00071         a->GetOriginalAirDate() : a->GetScheduledStartTime().date();
00072     QDate dt2 = (b->GetOriginalAirDate().isValid()) ?
00073         b->GetOriginalAirDate() : b->GetScheduledStartTime().date();
00074 
00075     if (dt1 == dt2)
00076         return (a->GetRecordingStartTime() <
00077                 b->GetRecordingStartTime() ? 1 : -1);
00078     else
00079         return (dt1 < dt2 ? 1 : -1);
00080 }
00081 
00082 static int comp_originalAirDate_rev(const ProgramInfo *a, const ProgramInfo *b)
00083 {
00084     QDate dt1 = (a->GetOriginalAirDate().isValid()) ?
00085         a->GetOriginalAirDate() : a->GetScheduledStartTime().date();
00086     QDate dt2 = (b->GetOriginalAirDate().isValid()) ?
00087         b->GetOriginalAirDate() : b->GetScheduledStartTime().date();
00088 
00089     if (dt1 == dt2)
00090         return (a->GetRecordingStartTime() >
00091                 b->GetRecordingStartTime() ? 1 : -1);
00092     else
00093         return (dt1 > dt2 ? 1 : -1);
00094 }
00095 
00096 static int comp_recpriority2(const ProgramInfo *a, const ProgramInfo *b)
00097 {
00098     if (a->GetRecordingPriority2() == b->GetRecordingPriority2())
00099         return (a->GetRecordingStartTime() <
00100                 b->GetRecordingStartTime() ? 1 : -1);
00101     else
00102         return (a->GetRecordingPriority2() <
00103                 b->GetRecordingPriority2() ? 1 : -1);
00104 }
00105 
00106 static int comp_recordDate(const ProgramInfo *a, const ProgramInfo *b)
00107 {
00108     if (a->GetScheduledStartTime().date() == b->GetScheduledStartTime().date())
00109         return (a->GetRecordingStartTime() <
00110                 b->GetRecordingStartTime() ? 1 : -1);
00111     else
00112         return (a->GetScheduledStartTime().date() <
00113                 b->GetScheduledStartTime().date() ? 1 : -1);
00114 }
00115 
00116 static int comp_recordDate_rev(const ProgramInfo *a, const ProgramInfo *b)
00117 {
00118     if (a->GetScheduledStartTime().date() == b->GetScheduledStartTime().date())
00119         return (a->GetRecordingStartTime() >
00120                 b->GetRecordingStartTime() ? 1 : -1);
00121     else
00122         return (a->GetScheduledStartTime().date() >
00123                 b->GetScheduledStartTime().date() ? 1 : -1);
00124 }
00125 
00126 static int comp_season(const ProgramInfo *a, const ProgramInfo *b)
00127 {
00128     if (a->GetSeason() == b->GetSeason())
00129         return (a->GetEpisode() <
00130                 b->GetEpisode() ? 1 : -1);
00131     else
00132         return (a->GetSeason() <
00133                 b->GetSeason() ? 1 : -1);
00134 }
00135 
00136 static int comp_season_rev(const ProgramInfo *a, const ProgramInfo *b)
00137 {
00138     if (a->GetSeason() == b->GetSeason())
00139         return (a->GetEpisode() >
00140                 b->GetEpisode() ? 1 : -1);
00141     else
00142         return (a->GetSeason() >
00143                 b->GetSeason() ? 1 : -1);
00144 }
00145 
00146 static bool comp_programid_less_than(
00147     const ProgramInfo *a, const ProgramInfo *b)
00148 {
00149     return comp_programid(a, b) < 0;
00150 }
00151 
00152 static bool comp_programid_rev_less_than(
00153     const ProgramInfo *a, const ProgramInfo *b)
00154 {
00155     return comp_programid_rev(a, b) < 0;
00156 }
00157 
00158 static bool comp_originalAirDate_less_than(
00159     const ProgramInfo *a, const ProgramInfo *b)
00160 {
00161     return comp_originalAirDate(a, b) < 0;
00162 }
00163 
00164 static bool comp_originalAirDate_rev_less_than(
00165     const ProgramInfo *a, const ProgramInfo *b)
00166 {
00167     return comp_originalAirDate_rev(a, b) < 0;
00168 }
00169 
00170 static bool comp_recpriority2_less_than(
00171     const ProgramInfo *a, const ProgramInfo *b)
00172 {
00173     return comp_recpriority2(a, b) < 0;
00174 }
00175 
00176 static bool comp_recordDate_less_than(
00177     const ProgramInfo *a, const ProgramInfo *b)
00178 {
00179     return comp_recordDate(a, b) < 0;
00180 }
00181 
00182 static bool comp_recordDate_rev_less_than(
00183     const ProgramInfo *a, const ProgramInfo *b)
00184 {
00185     return comp_recordDate_rev(a, b) < 0;
00186 }
00187 
00188 static bool comp_season_less_than(
00189     const ProgramInfo *a, const ProgramInfo *b)
00190 {
00191     return comp_season(a, b) < 0;
00192 }
00193 
00194 static bool comp_season_rev_less_than(
00195     const ProgramInfo *a, const ProgramInfo *b)
00196 {
00197     return comp_season_rev(a, b) < 0;
00198 }
00199 
00200 static const uint s_artDelay[] =
00201     { kArtworkFanTimeout, kArtworkBannerTimeout, kArtworkCoverTimeout,};
00202 
00203 static PlaybackBox::ViewMask m_viewMaskToggle(PlaybackBox::ViewMask mask,
00204         PlaybackBox::ViewMask toggle)
00205 {
00206     // can only toggle a single bit at a time
00207     if ((mask & toggle))
00208         return (PlaybackBox::ViewMask)(mask & ~toggle);
00209     return (PlaybackBox::ViewMask)(mask | toggle);
00210 }
00211 
00212 static QString construct_sort_title(
00213     QString title, PlaybackBox::ViewMask viewmask,
00214     PlaybackBox::ViewTitleSort titleSort, int recpriority,
00215     const QRegExp &prefixes)
00216 {
00217     if (title.isEmpty())
00218         return title;
00219 
00220     QString sTitle = title;
00221 
00222     sTitle.remove(prefixes);
00223     if (viewmask == PlaybackBox::VIEW_TITLES &&
00224             titleSort == PlaybackBox::TitleSortRecPriority)
00225     {
00226         // Also incorporate recpriority (reverse numeric sort). In
00227         // case different episodes of a recording schedule somehow
00228         // have different recpriority values (e.g., manual fiddling
00229         // with database), the title will appear once for each
00230         // distinct recpriority value among its episodes.
00231         //
00232         // Deal with QMap sorting. Positive recpriority values have a
00233         // '+' prefix (QMap alphabetically sorts before '-'). Positive
00234         // recpriority values are "inverted" by subtracting them from
00235         // 1000, so that high recpriorities are sorted first (QMap
00236         // alphabetically). For example:
00237         //
00238         //      recpriority =>  sort key
00239         //          95          +905
00240         //          90          +910
00241         //          89          +911
00242         //           1          +999
00243         //           0          -000
00244         //          -5          -005
00245         //         -10          -010
00246         //         -99          -099
00247 
00248         QString sortprefix;
00249         if (recpriority > 0)
00250             sortprefix.sprintf("+%03u", 1000 - recpriority);
00251         else
00252             sortprefix.sprintf("-%03u", -recpriority);
00253 
00254         sTitle = sortprefix + '-' + sTitle;
00255     }
00256     return sTitle;
00257 }
00258 
00259 static QString extract_main_state(const ProgramInfo &pginfo, const TV *player)
00260 {
00261     QString state("normal");
00262     if (pginfo.GetRecordingStatus() == rsRecording)
00263         state = "running";
00264 
00265     if (((pginfo.GetRecordingStatus() != rsRecording) &&
00266          (pginfo.GetAvailableStatus() != asAvailable) &&
00267          (pginfo.GetAvailableStatus() != asNotYetAvailable)) ||
00268         (player && player->IsSameProgram(0, &pginfo)))
00269     {
00270         state = "disabled";
00271     }
00272 
00273     if (state == "normal" && (pginfo.GetVideoProperties() & VID_DAMAGED))
00274         state = "warning";
00275 
00276     return state;
00277 }
00278 
00279 static QString extract_job_state(const ProgramInfo &pginfo)
00280 {
00281     QString job = "default";
00282 
00283     if (pginfo.GetRecordingStatus() == rsRecording)
00284         job = "recording";
00285     else if (JobQueue::IsJobQueuedOrRunning(
00286                  JOB_TRANSCODE, pginfo.GetChanID(),
00287                  pginfo.GetRecordingStartTime()))
00288         job = "transcoding";
00289     else if (JobQueue::IsJobQueuedOrRunning(
00290                  JOB_COMMFLAG,  pginfo.GetChanID(),
00291                  pginfo.GetRecordingStartTime()))
00292         job = "commflagging";
00293 
00294     return job;
00295 }
00296 
00297 static QString extract_commflag_state(const ProgramInfo &pginfo)
00298 {
00299     QString job = "default";
00300 
00301     // commflagged can be yes, no or processing
00302     if (JobQueue::IsJobRunning(JOB_COMMFLAG, pginfo))
00303         return "running";
00304     if (JobQueue::IsJobQueued(JOB_COMMFLAG, pginfo.GetChanID(),
00305                               pginfo.GetRecordingStartTime()))
00306         return "queued";
00307 
00308     return (pginfo.GetProgramFlags() & FL_COMMFLAG ? "yes" : "no");
00309 }
00310 
00311 
00312 static QString extract_subtitle(
00313     const ProgramInfo &pginfo, const QString &groupname)
00314 {
00315     QString subtitle;
00316     if (groupname != pginfo.GetTitle().toLower())
00317     {
00318         subtitle = pginfo.toString(ProgramInfo::kTitleSubtitle, " - ");
00319     }
00320     else
00321     {
00322         subtitle = pginfo.GetSubtitle();
00323         if (subtitle.trimmed().isEmpty())
00324             subtitle = pginfo.GetTitle();
00325     }
00326     return subtitle;
00327 }
00328 
00329 static void push_onto_del(QStringList &list, const ProgramInfo &pginfo)
00330 {
00331     list.clear();
00332     list.push_back(QString::number(pginfo.GetChanID()));
00333     list.push_back(pginfo.GetRecordingStartTime(ISODate));
00334     list.push_back(QString() /* force Delete */);
00335     list.push_back(QString()); /* forget history */
00336 }
00337 
00338 static bool extract_one_del(
00339     QStringList &list, uint &chanid, QDateTime &recstartts)
00340 {
00341     if (list.size() < 4)
00342     {
00343         list.clear();
00344         return false;
00345     }
00346 
00347     chanid     = list[0].toUInt();
00348     recstartts = QDateTime::fromString(list[1], Qt::ISODate);
00349 
00350     list.pop_front();
00351     list.pop_front();
00352     list.pop_front();
00353     list.pop_front();
00354 
00355     if (!chanid || !recstartts.isValid())
00356         LOG(VB_GENERAL, LOG_ERR, LOC + "extract_one_del() invalid entry");
00357 
00358     return chanid && recstartts.isValid();
00359 }
00360 
00361 void * PlaybackBox::RunPlaybackBox(void * player, bool showTV)
00362 {
00363     MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00364 
00365     PlaybackBox *pbb = new PlaybackBox(
00366         mainStack,"playbackbox", PlaybackBox::kPlayBox, (TV *)player, showTV);
00367 
00368     if (pbb->Create())
00369         mainStack->AddScreen(pbb);
00370     else
00371         delete pbb;
00372 
00373     return NULL;
00374 }
00375 
00376 PlaybackBox::PlaybackBox(MythScreenStack *parent, QString name, BoxType ltype,
00377                             TV *player, bool showTV)
00378     : ScheduleCommon(parent, name),
00379       m_prefixes(QObject::tr("^(The |A |An )")),
00380       m_titleChaff(" \\(.*\\)$"),
00381       // Artwork Variables
00382       m_artHostOverride(),
00383       // Settings
00384       m_type(ltype),
00385       m_watchListAutoExpire(false),
00386       m_watchListMaxAge(60),              m_watchListBlackOut(2),
00387       m_groupnameAsAllProg(false),
00388       m_listOrder(1),
00389       // Recording Group settings
00390       m_groupDisplayName(ProgramInfo::i18n("All Programs")),
00391       m_recGroup("All Programs"),
00392       m_watchGroupName(tr("Watch List")),
00393       m_watchGroupLabel(m_watchGroupName.toLower()),
00394       m_viewMask(VIEW_TITLES),
00395 
00396       // General m_popupMenu support
00397       m_menuDialog(NULL),
00398       m_popupMenu(NULL),
00399       m_doToggleMenu(true),
00400       // Main Recording List support
00401       m_progsInDB(0),
00402       // Other state
00403       m_op_on_playlist(false),
00404       m_programInfoCache(this),           m_playingSomething(false),
00405       // Selection state variables
00406       m_needUpdate(false),
00407       // Other
00408       m_player(NULL),
00409       m_helper(this)
00410 {
00411     for (uint i = 0; i < sizeof(m_artImage) / sizeof(MythUIImage*); i++)
00412     {
00413         m_artImage[i] = NULL;
00414         m_artTimer[i] = new QTimer(this);
00415         m_artTimer[i]->setSingleShot(true);
00416     }
00417 
00418     m_recGroup           = gCoreContext->GetSetting("DisplayRecGroup",
00419                                                 "All Programs");
00420     int pbOrder        = gCoreContext->GetNumSetting("PlayBoxOrdering", 1);
00421     // Split out sort order modes, wacky order for backward compatibility
00422     m_listOrder = (pbOrder >> 1) ^ (m_allOrder = pbOrder & 1);
00423     m_watchListStart     = gCoreContext->GetNumSetting("PlaybackWLStart", 0);
00424 
00425     m_watchListAutoExpire= gCoreContext->GetNumSetting("PlaybackWLAutoExpire", 0);
00426     m_watchListMaxAge    = gCoreContext->GetNumSetting("PlaybackWLMaxAge", 60);
00427     m_watchListBlackOut  = gCoreContext->GetNumSetting("PlaybackWLBlackOut", 2);
00428     m_groupnameAsAllProg = gCoreContext->GetNumSetting("DispRecGroupAsAllProg", 0);
00429 
00430     bool displayCat  = gCoreContext->GetNumSetting("DisplayRecGroupIsCategory", 0);
00431 
00432     m_viewMask = (ViewMask)gCoreContext->GetNumSetting(
00433                                     "DisplayGroupDefaultViewMask",
00434                                     VIEW_TITLES | VIEW_WATCHED);
00435 
00436     // Translate these external settings into mask values
00437     if (gCoreContext->GetNumSetting("PlaybackWatchList", 1) &&
00438         !(m_viewMask & VIEW_WATCHLIST))
00439     {
00440         m_viewMask = (ViewMask)(m_viewMask | VIEW_WATCHLIST);
00441         gCoreContext->SaveSetting("DisplayGroupDefaultViewMask", (int)m_viewMask);
00442     }
00443     else if (! gCoreContext->GetNumSetting("PlaybackWatchList", 1) &&
00444              m_viewMask & VIEW_WATCHLIST)
00445     {
00446         m_viewMask = (ViewMask)(m_viewMask & ~VIEW_WATCHLIST);
00447         gCoreContext->SaveSetting("DisplayGroupDefaultViewMask", (int)m_viewMask);
00448     }
00449 
00450     // This setting is deprecated in favour of viewmask, this just ensures the
00451     // that it is converted over when upgrading from earlier versions
00452     if (gCoreContext->GetNumSetting("LiveTVInAllPrograms",0) &&
00453         !(m_viewMask & VIEW_LIVETVGRP))
00454     {
00455         m_viewMask = (ViewMask)(m_viewMask | VIEW_LIVETVGRP);
00456         gCoreContext->SaveSetting("DisplayGroupDefaultViewMask", (int)m_viewMask);
00457     }
00458 
00459     if (gCoreContext->GetNumSetting("MasterBackendOverride", 0))
00460         m_artHostOverride = gCoreContext->GetMasterHostName();
00461 
00462     if (player)
00463     {
00464         m_player = player;
00465         QString tmp = m_player->GetRecordingGroup(0);
00466         if (!tmp.isEmpty())
00467             m_recGroup = tmp;
00468     }
00469 
00470     // recording group stuff
00471     m_recGroupIdx = -1;
00472     m_recGroupType.clear();
00473     m_recGroupType[m_recGroup] = (displayCat) ? "category" : "recgroup";
00474     if (m_groupnameAsAllProg)
00475         m_groupDisplayName = ProgramInfo::i18n(m_recGroup);
00476 
00477     fillRecGroupPasswordCache();
00478 
00479     // misc setup
00480     gCoreContext->addListener(this);
00481 
00482     m_popupStack = GetMythMainWindow()->GetStack("popup stack");
00483 }
00484 
00485 PlaybackBox::~PlaybackBox(void)
00486 {
00487     gCoreContext->removeListener(this);
00488     PreviewGeneratorQueue::RemoveListener(this);
00489 
00490     for (uint i = 0; i < sizeof(m_artImage) / sizeof(MythUIImage*); i++)
00491     {
00492         m_artTimer[i]->disconnect(this);
00493         m_artTimer[i] = NULL;
00494         m_artImage[i] = NULL;
00495     }
00496 
00497     if (m_player)
00498     {
00499         QString message = QString("PLAYBACKBOX_EXITING");
00500         qApp->postEvent(m_player, new MythEvent(
00501                             message, m_player_selected_new_show));
00502     }
00503 }
00504 
00505 bool PlaybackBox::Create()
00506 {
00507     if (m_type == kDeleteBox &&
00508             LoadWindowFromXML("recordings-ui.xml", "deleterecordings", this))
00509         LOG(VB_GENERAL, LOG_DEBUG,
00510             "Found a customized delete recording screen");
00511     else
00512         if (!LoadWindowFromXML("recordings-ui.xml", "watchrecordings", this))
00513             return false;
00514 
00515     m_recgroupList  = dynamic_cast<MythUIButtonList *> (GetChild("recgroups"));
00516     m_groupList     = dynamic_cast<MythUIButtonList *> (GetChild("groups"));
00517     m_recordingList = dynamic_cast<MythUIButtonList *> (GetChild("recordings"));
00518 
00519     m_noRecordingsText = dynamic_cast<MythUIText *> (GetChild("norecordings"));
00520 
00521     m_previewImage = dynamic_cast<MythUIImage *>(GetChild("preview"));
00522     m_artImage[kArtworkFanart] = dynamic_cast<MythUIImage*>(GetChild("fanart"));
00523     m_artImage[kArtworkBanner] = dynamic_cast<MythUIImage*>(GetChild("banner"));
00524     m_artImage[kArtworkCoverart]= dynamic_cast<MythUIImage*>(GetChild("coverart"));
00525 
00526     if (!m_recordingList || !m_groupList)
00527     {
00528         LOG(VB_GENERAL, LOG_ERR, LOC +
00529             "Theme is missing critical theme elements.");
00530         return false;
00531     }
00532 
00533     if (m_recgroupList)
00534         m_recgroupList->SetCanTakeFocus(false);
00535 
00536     connect(m_groupList, SIGNAL(itemSelected(MythUIButtonListItem*)),
00537             SLOT(updateRecList(MythUIButtonListItem*)));
00538     connect(m_groupList, SIGNAL(itemClicked(MythUIButtonListItem*)),
00539             SLOT(SwitchList()));
00540     connect(m_recordingList, SIGNAL(itemSelected(MythUIButtonListItem*)),
00541             SLOT(ItemSelected(MythUIButtonListItem*)));
00542     connect(m_recordingList, SIGNAL(itemClicked(MythUIButtonListItem*)),
00543             SLOT(PlayFromBookmark(MythUIButtonListItem*)));
00544     connect(m_recordingList, SIGNAL(itemVisible(MythUIButtonListItem*)),
00545             SLOT(ItemVisible(MythUIButtonListItem*)));
00546     connect(m_recordingList, SIGNAL(itemLoaded(MythUIButtonListItem*)),
00547             SLOT(ItemLoaded(MythUIButtonListItem*)));
00548 
00549     // connect up timers...
00550     connect(m_artTimer[kArtworkFanart],   SIGNAL(timeout()), SLOT(fanartLoad()));
00551     connect(m_artTimer[kArtworkBanner],   SIGNAL(timeout()), SLOT(bannerLoad()));
00552     connect(m_artTimer[kArtworkCoverart], SIGNAL(timeout()), SLOT(coverartLoad()));
00553 
00554     BuildFocusList();
00555     m_programInfoCache.ScheduleLoad(false);
00556     LoadInBackground();
00557 
00558     return true;
00559 }
00560 
00561 void PlaybackBox::Load(void)
00562 {
00563     m_programInfoCache.WaitForLoadToComplete();
00564     PreviewGeneratorQueue::AddListener(this);
00565 }
00566 
00567 void PlaybackBox::Init()
00568 {
00569     m_groupList->SetLCDTitles(tr("Groups"));
00570     m_recordingList->SetLCDTitles(tr("Recordings"),
00571                                   "titlesubtitle|shortdate|starttime");
00572 
00573     m_recordingList->SetSearchFields("titlesubtitle");
00574 
00575     if (gCoreContext->GetNumSetting("QueryInitialFilter", 0) == 1)
00576         showGroupFilter();
00577     else if (!m_player)
00578         displayRecGroup(m_recGroup);
00579     else
00580     {
00581         UpdateUILists();
00582 
00583         if ((m_titleList.size() <= 1) && (m_progsInDB > 0))
00584         {
00585             m_recGroup.clear();
00586             showGroupFilter();
00587         }
00588     }
00589 
00590     if (!gCoreContext->GetNumSetting("PlaybackBoxStartInTitle", 0))
00591         SetFocusWidget(m_recordingList);
00592 }
00593 
00594 void PlaybackBox::SwitchList()
00595 {
00596     if (GetFocusWidget() == m_groupList)
00597         SetFocusWidget(m_recordingList);
00598     else if (GetFocusWidget() == m_recordingList)
00599         SetFocusWidget(m_groupList);
00600 }
00601 
00602 void PlaybackBox::displayRecGroup(const QString &newRecGroup)
00603 {
00604     QString password = m_recGroupPwCache[newRecGroup];
00605 
00606     m_newRecGroup = newRecGroup;
00607     if (m_curGroupPassword != password && !password.isEmpty())
00608     {
00609         MythScreenStack *popupStack =
00610                                 GetMythMainWindow()->GetStack("popup stack");
00611 
00612         QString label = tr("Password for group '%1':").arg(newRecGroup);
00613 
00614         MythTextInputDialog *pwd = new MythTextInputDialog(popupStack,
00615                                             label, FilterNone, true);
00616 
00617         connect(pwd, SIGNAL(haveResult(QString)),
00618                 SLOT(checkPassword(QString)));
00619 
00620         if (pwd->Create())
00621             popupStack->AddScreen(pwd, false);
00622         return;
00623     }
00624 
00625     setGroupFilter(newRecGroup);
00626 }
00627 
00628 void PlaybackBox::checkPassword(const QString &password)
00629 {
00630     QString grouppass = m_recGroupPwCache[m_newRecGroup];
00631     if (password == grouppass)
00632         setGroupFilter(m_newRecGroup);
00633 }
00634 
00635 void PlaybackBox::updateGroupInfo(const QString &groupname,
00636                                   const QString &grouplabel)
00637 {
00638     InfoMap infoMap;
00639     int countInGroup;
00640 
00641     if (groupname.isEmpty())
00642     {
00643         countInGroup = m_progLists[""].size();
00644         infoMap["title"] = m_groupDisplayName;
00645         infoMap["group"] = m_groupDisplayName;
00646         infoMap["show"]  = ProgramInfo::i18n("All Programs");
00647     }
00648     else
00649     {
00650         countInGroup = m_progLists[groupname].size();
00651         infoMap["title"] = QString("%1 - %2").arg(m_groupDisplayName)
00652                                              .arg(grouplabel);
00653         infoMap["group"] = m_groupDisplayName;
00654         infoMap["show"]  = grouplabel;
00655     }
00656 
00657     if (m_artImage[kArtworkFanart])
00658     {
00659         if (!groupname.isEmpty() && !m_progLists[groupname].empty())
00660         {
00661             ProgramInfo *pginfo = *m_progLists[groupname].begin();
00662 
00663             QString fn = m_helper.LocateArtwork(
00664                 pginfo->GetInetRef(), pginfo->GetSeason(), kArtworkFanart, NULL, groupname);
00665 
00666             if (fn.isEmpty())
00667             {
00668                 m_artTimer[kArtworkFanart]->stop();
00669                 m_artImage[kArtworkFanart]->Reset();
00670             }
00671             else if (m_artImage[kArtworkFanart]->GetFilename() != fn)
00672             {
00673                 m_artImage[kArtworkFanart]->SetFilename(fn);
00674                 m_artTimer[kArtworkFanart]->start(kArtworkFanTimeout);
00675             }
00676         }
00677         else
00678         {
00679             m_artImage[kArtworkFanart]->Reset();
00680         }
00681     }
00682 
00683     QString desc = tr("There is/are %n recording(s) in this display group",
00684                       "", countInGroup);
00685 
00686     if (m_type == kDeleteBox && countInGroup > 1)
00687     {
00688         ProgramList  group     = m_progLists[groupname];
00689         float        groupSize = 0.0;
00690 
00691         for (ProgramList::iterator it = group.begin(); it != group.end(); ++it)
00692         {
00693             ProgramInfo *info = *it;
00694             if (info)
00695             {
00696                 uint64_t filesize = info->GetFilesize();
00697                 if (filesize == 0 || info->GetRecordingStatus() == rsRecording)
00698                 {
00699                     filesize = info->QueryFilesize();
00700                     info->SetFilesize(filesize);
00701                 }
00702                 groupSize += filesize;
00703             }
00704         }
00705 
00706         desc += tr(", which consume %1");
00707         desc += tr("GB", "GigaBytes");
00708 
00709         desc = desc.arg(groupSize / 1024.0 / 1024.0 / 1024.0, 0, 'f', 2);
00710     }
00711 
00712     infoMap["description"] = desc;
00713     infoMap["rec_count"] = QString("%1").arg(countInGroup);
00714 
00715     ResetMap(m_currentMap);
00716     SetTextFromMap(infoMap);
00717     m_currentMap = infoMap;
00718 
00719     MythUIStateType *ratingState = dynamic_cast<MythUIStateType*>
00720                                                 (GetChild("ratingstate"));
00721     if (ratingState)
00722         ratingState->Reset();
00723 
00724     MythUIStateType *jobState = dynamic_cast<MythUIStateType*>
00725                                                 (GetChild("jobstate"));
00726     if (jobState)
00727         jobState->Reset();
00728 
00729     if (m_previewImage)
00730         m_previewImage->Reset();
00731 
00732     if (m_artImage[kArtworkBanner])
00733         m_artImage[kArtworkBanner]->Reset();
00734 
00735     if (m_artImage[kArtworkCoverart])
00736         m_artImage[kArtworkCoverart]->Reset();
00737 
00738     updateIcons();
00739 }
00740 
00741 void PlaybackBox::UpdateUIListItem(ProgramInfo *pginfo,
00742                                    bool force_preview_reload)
00743 {
00744     if (!pginfo)
00745         return;
00746 
00747     MythUIButtonListItem *item =
00748         m_recordingList->GetItemByData(qVariantFromValue(pginfo));
00749 
00750     if (item)
00751     {
00752         MythUIButtonListItem *sel_item =
00753             m_recordingList->GetItemCurrent();
00754         UpdateUIListItem(item, item == sel_item, force_preview_reload);
00755     }
00756     else
00757     {
00758         LOG(VB_GENERAL, LOG_DEBUG, LOC +
00759             QString("UpdateUIListItem called with a title unknown "
00760                     "to us in m_recordingList\n\t\t\t%1")
00761                 .arg(pginfo->toString(ProgramInfo::kTitleSubtitle)));
00762     }
00763 }
00764 
00765 static const char *disp_flags[] = { "playlist", "watched", "preserve",
00766                                     "cutlist", "autoexpire", "editing",
00767                                     "bookmark", "inuse", "transcoded" };
00768 
00769 void PlaybackBox::SetItemIcons(MythUIButtonListItem *item, ProgramInfo* pginfo)
00770 {
00771     bool disp_flag_stat[sizeof(disp_flags)/sizeof(char*)];
00772 
00773     disp_flag_stat[0] = !m_playList.filter(pginfo->MakeUniqueKey()).empty();
00774     disp_flag_stat[1] = pginfo->IsWatched();
00775     disp_flag_stat[2] = pginfo->IsPreserved();
00776     disp_flag_stat[3] = pginfo->HasCutlist();
00777     disp_flag_stat[4] = pginfo->IsAutoExpirable();
00778     disp_flag_stat[5] = pginfo->GetProgramFlags() & FL_EDITING;
00779     disp_flag_stat[6] = pginfo->IsBookmarkSet();
00780     disp_flag_stat[7] = pginfo->IsInUsePlaying();
00781     disp_flag_stat[8] = pginfo->GetProgramFlags() & FL_TRANSCODED;
00782 
00783     for (uint i = 0; i < sizeof(disp_flags) / sizeof(char*); ++i)
00784         item->DisplayState(disp_flag_stat[i]?"yes":"no", disp_flags[i]);
00785 }
00786 
00787 void PlaybackBox::UpdateUIListItem(MythUIButtonListItem *item,
00788                                    bool is_sel, bool force_preview_reload)
00789 {
00790     if (!item)
00791         return;
00792 
00793     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
00794 
00795     if (!pginfo)
00796         return;
00797 
00798     QString state = extract_main_state(*pginfo, m_player);
00799 
00800     // Update the text, e.g. Title or subtitle may have been changed on another
00801     // frontend
00802     if (m_groupList && m_groupList->GetItemCurrent())
00803     {
00804         InfoMap infoMap;
00805         pginfo->ToMap(infoMap);
00806         item->SetTextFromMap(infoMap);
00807 
00808         QString groupname =
00809             m_groupList->GetItemCurrent()->GetData().toString();
00810 
00811         QString tempSubTitle  = extract_subtitle(*pginfo, groupname);
00812 
00813         if (groupname == pginfo->GetTitle().toLower())
00814             item->SetText(tempSubTitle, "titlesubtitle");
00815     }
00816 
00817     // Recording and availability status
00818     item->SetFontState(state);
00819     item->DisplayState(state, "status");
00820 
00821     // Job status (recording, transcoding, flagging)
00822     QString job = extract_job_state(*pginfo);
00823     item->DisplayState(job, "jobstate");
00824 
00825     // Flagging status (queued, running, no, yes)
00826     item->DisplayState(extract_commflag_state(*pginfo), "commflagged");
00827 
00828     SetItemIcons(item, pginfo);
00829 
00830     QString rating = QString::number(pginfo->GetStars(10));
00831 
00832     item->DisplayState(rating, "ratingstate");
00833 
00834     QString oldimgfile = item->GetImage("preview");
00835     if (oldimgfile.isEmpty() || force_preview_reload)
00836         m_preview_tokens.insert(m_helper.GetPreviewImage(*pginfo));
00837 
00838     if ((GetFocusWidget() == m_recordingList) && is_sel)
00839     {
00840         InfoMap infoMap;
00841 
00842         pginfo->ToMap(infoMap);
00843         ResetMap(m_currentMap);
00844         SetTextFromMap(infoMap);
00845         m_currentMap = infoMap;
00846 
00847         MythUIStateType *ratingState = dynamic_cast<MythUIStateType*>
00848                                                     (GetChild("ratingstate"));
00849         if (ratingState)
00850             ratingState->DisplayState(rating);
00851 
00852         MythUIStateType *jobState = dynamic_cast<MythUIStateType*>
00853                                                     (GetChild("jobstate"));
00854         if (jobState)
00855             jobState->DisplayState(job);
00856 
00857         if (m_previewImage)
00858         {
00859             m_previewImage->SetFilename(oldimgfile);
00860             m_previewImage->Load(true, true);
00861         }
00862 
00863         // Handle artwork
00864         QString arthost;
00865         for (uint i = 0; i < sizeof(m_artImage) / sizeof(MythUIImage*); i++)
00866         {
00867             if (!m_artImage[i])
00868                 continue;
00869 
00870             if (arthost.isEmpty())
00871             {
00872                 arthost = (!m_artHostOverride.isEmpty()) ?
00873                     m_artHostOverride : pginfo->GetHostname();
00874             }
00875 
00876             QString fn = m_helper.LocateArtwork(
00877                 pginfo->GetInetRef(), pginfo->GetSeason(),
00878                 (VideoArtworkType)i, pginfo);
00879 
00880             if (fn.isEmpty())
00881             {
00882                 m_artTimer[i]->stop();
00883                 m_artImage[i]->Reset();
00884             }
00885             else if (m_artImage[i]->GetFilename() != fn)
00886             {
00887                 m_artImage[i]->SetFilename(fn);
00888                 m_artTimer[i]->start(s_artDelay[i]);
00889             }
00890         }
00891 
00892         updateIcons(pginfo);
00893     }
00894 }
00895 
00896 void PlaybackBox::ItemLoaded(MythUIButtonListItem *item)
00897 {
00898     ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
00899     if (item->GetText("is_item_initialized").isNull())
00900     {
00901         QMap<AudioProps, QString> audioFlags;
00902         audioFlags[AUD_DOLBY] = "dolby";
00903         audioFlags[AUD_SURROUND] = "surround";
00904         audioFlags[AUD_STEREO] = "stereo";
00905         audioFlags[AUD_MONO] = "mono";
00906 
00907         QMap<VideoProps, QString> videoFlags;
00908         videoFlags[VID_1080] = "hd1080";
00909         videoFlags[VID_720] = "hd720";
00910         videoFlags[VID_HDTV] = "hdtv";
00911         videoFlags[VID_WIDESCREEN] = "widescreen";
00912 
00913         QMap<SubtitleTypes, QString> subtitleFlags;
00914         subtitleFlags[SUB_SIGNED] = "deafsigned";
00915         subtitleFlags[SUB_ONSCREEN] = "onscreensub";
00916         subtitleFlags[SUB_NORMAL] = "subtitles";
00917         subtitleFlags[SUB_HARDHEAR] = "cc";
00918 
00919         QString groupname =
00920             m_groupList->GetItemCurrent()->GetData().toString();
00921 
00922         QString state = extract_main_state(*pginfo, m_player);
00923 
00924         item->SetFontState(state);
00925 
00926         InfoMap infoMap;
00927         pginfo->ToMap(infoMap);
00928         item->SetTextFromMap(infoMap);
00929 
00930         QString tempSubTitle  = extract_subtitle(*pginfo, groupname);
00931 
00932         if (groupname == pginfo->GetTitle().toLower())
00933             item->SetText(tempSubTitle, "titlesubtitle");
00934 
00935         item->DisplayState(state, "status");
00936 
00937         item->DisplayState(QString::number(pginfo->GetStars(10)),
00938                            "ratingstate");
00939 
00940         SetItemIcons(item, pginfo);
00941 
00942         QMap<AudioProps, QString>::iterator ait;
00943         for (ait = audioFlags.begin(); ait != audioFlags.end(); ++ait)
00944         {
00945             if (pginfo->GetAudioProperties() & ait.key())
00946                 item->DisplayState(ait.value(), "audioprops");
00947         }
00948 
00949         QMap<VideoProps, QString>::iterator vit;
00950         for (vit = videoFlags.begin(); vit != videoFlags.end(); ++vit)
00951         {
00952             if (pginfo->GetVideoProperties() & vit.key())
00953                 item->DisplayState(vit.value(), "videoprops");
00954         }
00955 
00956         QMap<SubtitleTypes, QString>::iterator sit;
00957         for (sit = subtitleFlags.begin(); sit != subtitleFlags.end(); ++sit)
00958         {
00959             if (pginfo->GetSubtitleType() & sit.key())
00960                 item->DisplayState(sit.value(), "subtitletypes");
00961         }
00962 
00963         item->DisplayState(pginfo->GetCategoryType(), "categorytype");
00964 
00965         // Mark this button list item as initialized.
00966         item->SetText("yes", "is_item_initialized");
00967     }
00968 
00969 }
00970 
00971 void PlaybackBox::ItemVisible(MythUIButtonListItem *item)
00972 {
00973     ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
00974 
00975     ItemLoaded(item);
00976     // Job status (recording, transcoding, flagging)
00977     QString job = extract_job_state(*pginfo);
00978     item->DisplayState(job, "jobstate");
00979 
00980     // Flagging status (queued, running, no, yes)
00981     item->DisplayState(extract_commflag_state(*pginfo), "commflagged");
00982 
00983     MythUIButtonListItem *sel_item = item->parent()->GetItemCurrent();
00984     if ((item != sel_item) && item->GetImage("preview").isEmpty() &&
00985         (asAvailable == pginfo->GetAvailableStatus()))
00986     {
00987         QString token = m_helper.GetPreviewImage(*pginfo, true);
00988         if (token.isEmpty())
00989             return;
00990 
00991         m_preview_tokens.insert(token);
00992         // now make sure selected item is still at the top of the queue
00993         ProgramInfo *sel_pginfo =
00994             qVariantValue<ProgramInfo*>(sel_item->GetData());
00995         if (sel_pginfo && sel_item->GetImage("preview").isEmpty() &&
00996             (asAvailable == sel_pginfo->GetAvailableStatus()))
00997         {
00998             m_preview_tokens.insert(
00999                 m_helper.GetPreviewImage(*sel_pginfo, false));
01000         }
01001     }
01002 }
01003 
01004 
01011 void PlaybackBox::HandlePreviewEvent(const QStringList &list)
01012 {
01013     if (list.size() < 5)
01014     {
01015         LOG(VB_GENERAL, LOG_ERR, "HandlePreviewEvent() -- too few args");
01016         for (uint i = 0; i < (uint) list.size(); i++)
01017         {
01018             LOG(VB_GENERAL, LOG_INFO, QString("%1: %2")
01019                     .arg(i).arg(list[i]));
01020         }
01021         return;
01022     }
01023 
01024     const QString piKey       = list[0];
01025     const QString previewFile = list[1];
01026     const QString message     = list[2];
01027 
01028     bool found = false;
01029     for (uint i = 4; i < (uint) list.size(); i++)
01030     {
01031         QString token = list[i];
01032         QSet<QString>::iterator it = m_preview_tokens.find(token);
01033         if (it != m_preview_tokens.end())
01034         {
01035             found = true;
01036             m_preview_tokens.erase(it);
01037         }
01038     }
01039 
01040     if (!found)
01041     {
01042         QString tokens("\n\t\t\ttokens: ");
01043         for (uint i = 4; i < (uint) list.size(); i++)
01044             tokens += list[i] + ", ";
01045         LOG(VB_GENERAL, LOG_DEBUG, LOC +
01046             "Ignoring PREVIEW_SUCCESS, no matcing token" + tokens);
01047         return;
01048     }
01049 
01050     if (previewFile.isEmpty())
01051     {
01052         LOG(VB_GENERAL, LOG_ERR, LOC +
01053             "Ignoring PREVIEW_SUCCESS, no preview file.");
01054         return;
01055     }
01056 
01057     ProgramInfo *info = m_programInfoCache.GetProgramInfo(piKey);
01058     MythUIButtonListItem *item = NULL;
01059 
01060     if (info)
01061         item = m_recordingList->GetItemByData(qVariantFromValue(info));
01062 
01063     if (!item)
01064     {
01065         LOG(VB_GENERAL, LOG_DEBUG, LOC +
01066             "Ignoring PREVIEW_SUCCESS, item no longer on screen.");
01067     }
01068 
01069     if (item)
01070     {
01071         LOG(VB_GUI, LOG_INFO, LOC + QString("Loading preview %1,\n\t\t\tmsg %2")
01072                 .arg(previewFile).arg(message));
01073 
01074         item->SetImage(previewFile, "preview", true);
01075 
01076         if ((GetFocusWidget() == m_recordingList) &&
01077             (m_recordingList->GetItemCurrent() == item) &&
01078             m_previewImage)
01079         {
01080             m_previewImage->SetFilename(previewFile);
01081             m_previewImage->Load(true, true);
01082         }
01083     }
01084 }
01085 
01086 void PlaybackBox::updateIcons(const ProgramInfo *pginfo)
01087 {
01088     uint32_t flags = FL_NONE;
01089 
01090     if (pginfo)
01091         flags = pginfo->GetProgramFlags();
01092 
01093     QMap <QString, int>::iterator it;
01094     QMap <QString, int> iconMap;
01095 
01096     iconMap["commflagged"] = FL_COMMFLAG;
01097     iconMap["cutlist"]     = FL_CUTLIST;
01098     iconMap["autoexpire"]  = FL_AUTOEXP;
01099     iconMap["processing"]  = FL_COMMPROCESSING;
01100     iconMap["editing"]     = FL_EDITING;
01101     iconMap["bookmark"]    = FL_BOOKMARK;
01102     iconMap["inuse"]       = (FL_INUSERECORDING |
01103                               FL_INUSEPLAYING |
01104                               FL_INUSEOTHER);
01105     iconMap["transcoded"]  = FL_TRANSCODED;
01106     iconMap["watched"]     = FL_WATCHED;
01107     iconMap["preserved"]   = FL_PRESERVED;
01108 
01109     MythUIImage *iconImage = NULL;
01110     MythUIStateType *iconState = NULL;
01111     for (it = iconMap.begin(); it != iconMap.end(); ++it)
01112     {
01113         iconImage = dynamic_cast<MythUIImage *>(GetChild(it.key()));
01114         if (iconImage)
01115             iconImage->SetVisible(flags & (*it));
01116 
01117         iconState = dynamic_cast<MythUIStateType *>(GetChild(it.key()));
01118         if (iconState)
01119         {
01120             if (flags & (*it))
01121                 iconState->DisplayState("yes");
01122             else
01123                 iconState->DisplayState("no");
01124         }
01125     }
01126 
01127     iconMap.clear();
01128     iconMap["dolby"]  = AUD_DOLBY;
01129     iconMap["surround"]  = AUD_SURROUND;
01130     iconMap["stereo"] = AUD_STEREO;
01131     iconMap["mono"] = AUD_MONO;
01132 
01133     iconState = dynamic_cast<MythUIStateType *>(GetChild("audioprops"));
01134     bool haveIcon = false;
01135     if (pginfo && iconState)
01136     {
01137         for (it = iconMap.begin(); it != iconMap.end(); ++it)
01138         {
01139             if (pginfo->GetAudioProperties() & (*it))
01140             {
01141                 if (iconState->DisplayState(it.key()))
01142                 {
01143                     haveIcon = true;
01144                     break;
01145                 }
01146             }
01147         }
01148     }
01149 
01150     if (iconState && !haveIcon)
01151         iconState->Reset();
01152 
01153     iconMap.clear();
01154     iconMap["avchd"] = VID_AVC;
01155     iconMap["hd1080"] = VID_1080;
01156     iconMap["hd720"] = VID_720;
01157     iconMap["hdtv"] = VID_HDTV;
01158     iconMap["widescreen"] = VID_WIDESCREEN;
01159 
01160     iconState = dynamic_cast<MythUIStateType *>(GetChild("videoprops"));
01161     haveIcon = false;
01162     if (pginfo && iconState)
01163     {
01164         for (it = iconMap.begin(); it != iconMap.end(); ++it)
01165         {
01166             if (pginfo->GetVideoProperties() & (*it))
01167             {
01168                 if (iconState->DisplayState(it.key()))
01169                 {
01170                     haveIcon = true;
01171                     break;
01172                 }
01173             }
01174         }
01175     }
01176 
01177     if (iconState && !haveIcon)
01178         iconState->Reset();
01179 
01180     iconMap.clear();
01181     iconMap["damaged"] = VID_DAMAGED;
01182 
01183     iconState = dynamic_cast<MythUIStateType *>(GetChild("videoquality"));
01184     haveIcon = false;
01185     if (pginfo && iconState)
01186     {
01187         for (it = iconMap.begin(); it != iconMap.end(); ++it)
01188         {
01189             if (pginfo->GetVideoProperties() & (*it))
01190             {
01191                 if (iconState->DisplayState(it.key()))
01192                 {
01193                     haveIcon = true;
01194                     break;
01195                 }
01196             }
01197         }
01198     }
01199 
01200     if (iconState && !haveIcon)
01201         iconState->Reset();
01202     iconMap.clear();
01203     iconMap["deafsigned"] = SUB_SIGNED;
01204     iconMap["onscreensub"] = SUB_ONSCREEN;
01205     iconMap["subtitles"] = SUB_NORMAL;
01206     iconMap["cc"] = SUB_HARDHEAR;
01207 
01208     iconState = dynamic_cast<MythUIStateType *>(GetChild("subtitletypes"));
01209     haveIcon = false;
01210     if (pginfo && iconState)
01211     {
01212         for (it = iconMap.begin(); it != iconMap.end(); ++it)
01213         {
01214             if (pginfo->GetSubtitleType() & (*it))
01215             {
01216                 if (iconState->DisplayState(it.key()))
01217                 {
01218                     haveIcon = true;
01219                     break;
01220                 }
01221             }
01222         }
01223     }
01224 
01225     if (iconState && !haveIcon)
01226         iconState->Reset();
01227 
01228     iconState = dynamic_cast<MythUIStateType *>(GetChild("categorytype"));
01229     if (iconState)
01230     {
01231         if (!(pginfo && iconState->DisplayState(pginfo->GetCategoryType())))
01232             iconState->Reset();
01233     }
01234 }
01235 
01236 bool PlaybackBox::IsUsageUIVisible(void) const
01237 {
01238     return GetChild("freereport") || GetChild("usedbar");
01239 }
01240 
01241 void PlaybackBox::UpdateUsageUI(void)
01242 {
01243     MythUIText        *freereportText =
01244         dynamic_cast<MythUIText*>(GetChild("freereport"));
01245     MythUIProgressBar *usedProgress   =
01246         dynamic_cast<MythUIProgressBar *>(GetChild("usedbar"));
01247 
01248     // If the theme doesn't have these widgets,
01249     // don't waste time querying the backend...
01250     if (!freereportText && !usedProgress && !GetChild("diskspacetotal") &&
01251         !GetChild("diskspaceused") && !GetChild("diskspacefree") &&
01252         !GetChild("diskspacepercentused") && !GetChild("diskspacepercentfree"))
01253         return;
01254 
01255     double freeSpaceTotal = (double) m_helper.GetFreeSpaceTotalMB();
01256     double freeSpaceUsed  = (double) m_helper.GetFreeSpaceUsedMB();
01257 
01258     InfoMap usageMap;
01259     usageMap["diskspacetotal"] = QString().sprintf("%0.2f",
01260                                                    freeSpaceTotal / 1024.0);
01261     usageMap["diskspaceused"] = QString().sprintf("%0.2f",
01262                                                   freeSpaceUsed / 1024.0);
01263     usageMap["diskspacefree"] = QString().sprintf("%0.2f",
01264                                     (freeSpaceTotal - freeSpaceUsed) / 1024.0);
01265 
01266     QString usestr;
01267 
01268     double perc = 0.0;
01269     if (freeSpaceTotal > 0.0)
01270         perc = (100.0 * freeSpaceUsed) / freeSpaceTotal;
01271 
01272     usageMap["diskspacepercentused"] = QString().number((int)perc);
01273     usageMap["diskspacepercentfree"] = QString().number(100 - (int)perc);
01274 
01275     usestr.sprintf("%d", (int)perc);
01276     usestr = usestr + tr("% used");
01277 
01278     QString size;
01279     size.sprintf("%0.2f", (freeSpaceTotal - freeSpaceUsed) / 1024.0);
01280     QString rep = tr(", %1 GB free").arg(size);
01281     usestr = usestr + rep;
01282 
01283     if (freereportText)
01284         freereportText->SetText(usestr);
01285 
01286     if (usedProgress)
01287     {
01288         usedProgress->SetTotal((int)freeSpaceTotal);
01289         usedProgress->SetUsed((int)freeSpaceUsed);
01290     }
01291 
01292     SetTextFromMap(usageMap);
01293 }
01294 
01295 /*
01296  * \fn PlaybackBox::updateUIRecGroupList(void)
01297  * \brief called when the list of recording groups may have changed
01298  */
01299 void PlaybackBox::UpdateUIRecGroupList(void)
01300 {
01301     if (m_recGroupIdx < 0 || !m_recgroupList || m_recGroups.size() < 2)
01302         return;
01303 
01304     m_recgroupList->Reset();
01305 
01306     int idx = 0;
01307     QStringList::iterator it = m_recGroups.begin();
01308     for (; it != m_recGroups.end(); (++it), (++idx))
01309     {
01310         QString key = (*it);
01311         QString tmp = (key == "All Programs") ? "All" : key;
01312         QString name = ProgramInfo::i18n(tmp);
01313 
01314         if (m_recGroups.size() == 2 && key == "Default")
01315             continue;  // All and Default will be the same, so only show All
01316 
01317         MythUIButtonListItem *item = new MythUIButtonListItem(
01318             m_recgroupList, name, qVariantFromValue(key));
01319 
01320         if (idx == m_recGroupIdx)
01321             m_recgroupList->SetItemCurrent(item);
01322         item->SetText(name);
01323     }
01324 }
01325 
01326 void PlaybackBox::UpdateUIGroupList(const QStringList &groupPreferences)
01327 {
01328     m_groupList->Reset();
01329 
01330     if (!m_titleList.isEmpty())
01331     {
01332         int best_pref = INT_MAX, sel_idx = 0;
01333         QStringList::iterator it;
01334         for (it = m_titleList.begin(); it != m_titleList.end(); ++it)
01335         {
01336             QString groupname = (*it).simplified();
01337 
01338             MythUIButtonListItem *item =
01339                 new MythUIButtonListItem(
01340                     m_groupList, "", qVariantFromValue(groupname.toLower()));
01341 
01342             int pref = groupPreferences.indexOf(groupname.toLower());
01343             if ((pref >= 0) && (pref < best_pref))
01344             {
01345                 best_pref = pref;
01346                 sel_idx = m_groupList->GetItemPos(item);
01347                 m_currentGroup = groupname.toLower();
01348             }
01349 
01350             QString displayName = groupname;
01351             if (displayName.isEmpty())
01352                 displayName = m_groupDisplayName;
01353 
01354             item->SetText(displayName, "name");
01355             item->SetText(displayName);
01356 
01357             int count = m_progLists[groupname.toLower()].size();
01358             item->SetText(QString::number(count), "reccount");
01359         }
01360 
01361         m_needUpdate = true;
01362         m_groupList->SetItemCurrent(sel_idx);
01363         // We need to explicitly call updateRecList in this case,
01364         // since 0 is selected by default, and we need updateRecList
01365         // to be called with m_needUpdate set.
01366         if (!sel_idx)
01367             updateRecList(m_groupList->GetItemCurrent());
01368     }
01369 }
01370 
01371 void PlaybackBox::updateRecList(MythUIButtonListItem *sel_item)
01372 {
01373     if (!sel_item)
01374         return;
01375 
01376     QString groupname = sel_item->GetData().toString();
01377     QString grouplabel = sel_item->GetText();
01378 
01379     updateGroupInfo(groupname, grouplabel);
01380 
01381     if (((m_currentGroup == groupname) && !m_needUpdate) ||
01382         m_playingSomething)
01383         return;
01384 
01385     m_needUpdate = false;
01386 
01387     if (!m_isFilling)
01388         m_currentGroup = groupname;
01389 
01390     m_recordingList->Reset();
01391 
01392     ProgramMap::iterator pmit = m_progLists.find(groupname);
01393     if (pmit == m_progLists.end())
01394         return;
01395 
01396     ProgramList &progList = *pmit;
01397 
01398     ProgramList::iterator it = progList.begin();
01399     for (; it != progList.end(); ++it)
01400     {
01401         if ((*it)->GetAvailableStatus() == asPendingDelete ||
01402             (*it)->GetAvailableStatus() == asDeleted)
01403             continue;
01404 
01405         new PlaybackBoxListItem(this, m_recordingList, *it);
01406     }
01407     m_recordingList->LoadInBackground();
01408 
01409     if (m_noRecordingsText)
01410     {
01411         if (!progList.empty())
01412             m_noRecordingsText->SetVisible(false);
01413         else
01414         {
01415             QString txt = m_programInfoCache.empty() ?
01416                 tr("There are no recordings available") :
01417                 tr("There are no recordings in your current view");
01418             m_noRecordingsText->SetText(txt);
01419             m_noRecordingsText->SetVisible(true);
01420         }
01421     }
01422 }
01423 
01424 static bool save_position(
01425     const MythUIButtonList *groupList, const MythUIButtonList *recordingList,
01426     QStringList &groupSelPref, QStringList &itemSelPref,
01427     QStringList &itemTopPref)
01428 {
01429     MythUIButtonListItem *prefSelGroup = groupList->GetItemCurrent();
01430     if (!prefSelGroup)
01431         return false;
01432 
01433     groupSelPref.push_back(prefSelGroup->GetData().toString());
01434     for (int i = groupList->GetCurrentPos();
01435          i < groupList->GetCount(); i++)
01436     {
01437         prefSelGroup = groupList->GetItemAt(i);
01438         if (prefSelGroup)
01439             groupSelPref.push_back(prefSelGroup->GetData().toString());
01440     }
01441 
01442     int curPos = recordingList->GetCurrentPos();
01443     for (int i = curPos; (i >= 0) && (i < recordingList->GetCount()); i++)
01444     {
01445         MythUIButtonListItem *item = recordingList->GetItemAt(i);
01446         ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
01447         itemSelPref.push_back(groupSelPref.front());
01448         itemSelPref.push_back(pginfo->MakeUniqueKey());
01449     }
01450     for (int i = curPos; (i >= 0) && (i < recordingList->GetCount()); i--)
01451     {
01452         MythUIButtonListItem *item = recordingList->GetItemAt(i);
01453         ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
01454         itemSelPref.push_back(groupSelPref.front());
01455         itemSelPref.push_back(pginfo->MakeUniqueKey());
01456     }
01457 
01458     int topPos = recordingList->GetTopItemPos();
01459     for (int i = topPos + 1; i >= topPos - 1; i--)
01460     {
01461         if (i >= 0 && i < recordingList->GetCount())
01462         {
01463             MythUIButtonListItem *item = recordingList->GetItemAt(i);
01464             ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
01465             if (i == topPos)
01466             {
01467                 itemTopPref.push_front(pginfo->MakeUniqueKey());
01468                 itemTopPref.push_front(groupSelPref.front());
01469             }
01470             else
01471             {
01472                 itemTopPref.push_back(groupSelPref.front());
01473                 itemTopPref.push_back(pginfo->MakeUniqueKey());
01474             }
01475         }
01476     }
01477 
01478     return true;
01479 }
01480 
01481 static void restore_position(
01482     MythUIButtonList *groupList, MythUIButtonList *recordingList,
01483     const QStringList &groupSelPref, const QStringList &itemSelPref,
01484     const QStringList &itemTopPref)
01485 {
01486     // If possible reselect the item selected before,
01487     // otherwise select the nearest available item.
01488     MythUIButtonListItem *prefSelGroup = groupList->GetItemCurrent();
01489     if (!prefSelGroup ||
01490         !groupSelPref.contains(prefSelGroup->GetData().toString()) ||
01491         !itemSelPref.contains(prefSelGroup->GetData().toString()))
01492     {
01493         return;
01494     }
01495 
01496     // the group is selected in UpdateUIGroupList()
01497     QString groupname = prefSelGroup->GetData().toString();
01498 
01499     // find best selection
01500     int sel = -1;
01501     for (uint i = 0; i+1 < (uint)itemSelPref.size(); i+=2)
01502     {
01503         if (itemSelPref[i] != groupname)
01504             continue;
01505 
01506         const QString key = itemSelPref[i+1];
01507         for (uint j = 0; j < (uint)recordingList->GetCount(); j++)
01508         {
01509             MythUIButtonListItem *item = recordingList->GetItemAt(j);
01510             ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
01511             if (pginfo && (pginfo->MakeUniqueKey() == key))
01512             {
01513                 sel = j;
01514                 i = itemSelPref.size();
01515                 break;
01516             }
01517         }
01518     }
01519 
01520     // find best top item
01521     int top = -1;
01522     for (uint i = 0; i+1 < (uint)itemTopPref.size(); i+=2)
01523     {
01524         if (itemTopPref[i] != groupname)
01525             continue;
01526 
01527         const QString key = itemTopPref[i+1];
01528         for (uint j = 0; j < (uint)recordingList->GetCount(); j++)
01529         {
01530             MythUIButtonListItem *item = recordingList->GetItemAt(j);
01531             ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
01532             if (pginfo && (pginfo->MakeUniqueKey() == key))
01533             {
01534                 top = j;
01535                 i = itemTopPref.size();
01536                 break;
01537             }
01538         }
01539     }
01540 
01541     if (sel >= 0)
01542     {
01543 #if 0
01544         LOG(VB_GENERAL, LOG_DEBUG, QString("Reselect success (%1,%2)")
01545                 .arg(sel).arg(top));
01546 #endif
01547         recordingList->SetItemCurrent(sel, top);
01548     }
01549     else
01550     {
01551 #if 0
01552         LOG(VB_GENERAL, LOG_DEBUG, QString("Reselect failure (%1,%2)")
01553                 .arg(sel).arg(top));
01554 #endif
01555     }
01556 }
01557 
01558 bool PlaybackBox::UpdateUILists(void)
01559 {
01560     m_isFilling = true;
01561 
01562     // Save selection, including next few items & groups
01563     QStringList groupSelPref, itemSelPref, itemTopPref;
01564     if (!save_position(m_groupList, m_recordingList,
01565                        groupSelPref, itemSelPref, itemTopPref))
01566     {
01567         // If user wants to start in watchlist and watchlist is displayed, then
01568         // make it the current group
01569         if (m_watchListStart && (m_viewMask & VIEW_WATCHLIST))
01570             groupSelPref.push_back(m_watchGroupLabel);
01571     }
01572 
01573     // Cache available status for later restoration
01574     QMap<QString, AvailableStatusType> asCache;
01575     QString asKey;
01576 
01577     if (!m_progLists.isEmpty())
01578     {
01579         ProgramList::iterator it = m_progLists[""].begin();
01580         ProgramList::iterator end = m_progLists[""].end();
01581         for (; it != end; ++it)
01582         {
01583             asKey = (*it)->MakeUniqueKey();
01584             asCache[asKey] = (*it)->GetAvailableStatus();
01585         }
01586     }
01587 
01588     m_progsInDB = 0;
01589     m_titleList.clear();
01590     m_progLists.clear();
01591     m_recordingList->Reset();
01592     m_groupList->Reset();
01593     if (m_recgroupList)
01594         m_recgroupList->Reset();
01595     // Clear autoDelete for the "all" list since it will share the
01596     // objects with the title lists.
01597     m_progLists[""] = ProgramList(false);
01598     m_progLists[""].setAutoDelete(false);
01599 
01600     ViewTitleSort titleSort = (ViewTitleSort)gCoreContext->GetNumSetting(
01601                                 "DisplayGroupTitleSort", TitleSortAlphabetical);
01602 
01603     QMap<QString, QString> sortedList;
01604     QMap<int, QString> searchRule;
01605     QMap<int, int> recidEpisodes;
01606 
01607     m_programInfoCache.Refresh();
01608 
01609     if (!m_programInfoCache.empty())
01610     {
01611         QString sTitle;
01612 
01613         if ((m_viewMask & VIEW_SEARCHES))
01614         {
01615             MSqlQuery query(MSqlQuery::InitCon());
01616             query.prepare("SELECT recordid,title FROM record "
01617                           "WHERE search > 0 AND search != :MANUAL;");
01618             query.bindValue(":MANUAL", kManualSearch);
01619 
01620             if (query.exec())
01621             {
01622                 while (query.next())
01623                 {
01624                     QString tmpTitle = query.value(1).toString();
01625                     tmpTitle.remove(m_titleChaff);
01626                     searchRule[query.value(0).toInt()] = tmpTitle;
01627                 }
01628             }
01629         }
01630 
01631         vector<ProgramInfo*> list;
01632         bool newest_first = (0==m_allOrder) || (kDeleteBox==m_type);
01633         m_programInfoCache.GetOrdered(list, newest_first);
01634         vector<ProgramInfo*>::const_iterator it = list.begin();
01635         for ( ; it != list.end(); ++it)
01636         {
01637             if ((*it)->IsDeletePending())
01638                 continue;
01639 
01640             m_progsInDB++;
01641             ProgramInfo *p = *it;
01642 
01643             if (p->GetTitle().isEmpty())
01644                 p->SetTitle(tr("_NO_TITLE_"));
01645 
01646             if ((((p->GetRecordingGroup() == m_recGroup) ||
01647                   ((m_recGroup == "All Programs") &&
01648                    (p->GetRecordingGroup() != "Deleted") &&
01649                    (p->GetRecordingGroup() != "LiveTV")) ||
01650                   (p->GetRecordingGroup() == "LiveTV" &&
01651                    (m_viewMask & VIEW_LIVETVGRP))) &&
01652                  (m_recGroupPwCache[m_recGroup] == m_curGroupPassword)) ||
01653                 ((m_recGroupType[m_recGroup] == "category") &&
01654                  ((p->GetCategory() == m_recGroup ) ||
01655                   ((p->GetCategory().isEmpty()) &&
01656                    (m_recGroup == tr("Unknown")))) &&
01657                  ( !m_recGroupPwCache.contains(p->GetRecordingGroup()))))
01658             {
01659                 if ((!(m_viewMask & VIEW_WATCHED)) && p->IsWatched())
01660                     continue;
01661 
01662                 if (m_viewMask != VIEW_NONE &&
01663                     (p->GetRecordingGroup() != "LiveTV" ||
01664                      m_recGroup == "LiveTV"))
01665                 {
01666                     m_progLists[""].push_front(p);
01667                 }
01668 
01669                 asKey = p->MakeUniqueKey();
01670                 if (asCache.contains(asKey))
01671                     p->SetAvailableStatus(asCache[asKey], "UpdateUILists");
01672                 else
01673                     p->SetAvailableStatus(asAvailable,  "UpdateUILists");
01674 
01675                 if (m_recGroup != "LiveTV" &&
01676                     (p->GetRecordingGroup() == "LiveTV") &&
01677                     (m_viewMask & VIEW_LIVETVGRP))
01678                 {
01679                     QString tmpTitle = tr("Live TV");
01680                     sortedList[tmpTitle.toLower()] = tmpTitle;
01681                     m_progLists[tmpTitle.toLower()].push_front(p);
01682                     m_progLists[tmpTitle.toLower()].setAutoDelete(false);
01683                     continue;
01684                 }
01685 
01686                 if ((m_viewMask & VIEW_TITLES) && // Show titles
01687                     ((p->GetRecordingGroup() != "LiveTV") ||
01688                      (m_recGroup == "LiveTV")))
01689                 {
01690                     sTitle = construct_sort_title(
01691                         p->GetTitle(), m_viewMask, titleSort,
01692                         p->GetRecordingPriority(), m_prefixes);
01693                     sTitle = sTitle.toLower().simplified();
01694 
01695                     if (!sortedList.contains(sTitle))
01696                         sortedList[sTitle] = p->GetTitle();
01697                     m_progLists[sortedList[sTitle].toLower()].push_front(p);
01698                     m_progLists[sortedList[sTitle].toLower()].setAutoDelete(false);
01699                 }
01700 
01701                 if ((m_viewMask & VIEW_RECGROUPS) &&
01702                     !p->GetRecordingGroup().isEmpty() &&
01703                     p->GetRecordingGroup() != "LiveTV") // Show recording groups
01704                 {
01705                     sortedList[p->GetRecordingGroup().toLower()] =
01706                         p->GetRecordingGroup();
01707                     m_progLists[p->GetRecordingGroup().toLower()]
01708                         .push_front(p);
01709                     m_progLists[p->GetRecordingGroup().toLower()]
01710                         .setAutoDelete(false);
01711                 }
01712 
01713                 if ((m_viewMask & VIEW_CATEGORIES) &&
01714                     !p->GetCategory().isEmpty()) // Show categories
01715                 {
01716                     QString catl = p->GetCategory().toLower();
01717                     sortedList[catl] = p->GetCategory();
01718                     m_progLists[catl].push_front(p);
01719                     m_progLists[catl].setAutoDelete(false);
01720                 }
01721 
01722                 if ((m_viewMask & VIEW_SEARCHES) &&
01723                     !searchRule[p->GetRecordingRuleID()].isEmpty() &&
01724                     p->GetTitle() != searchRule[p->GetRecordingRuleID()])
01725                 {   // Show search rules
01726                     QString tmpTitle = QString("(%1)")
01727                         .arg(searchRule[p->GetRecordingRuleID()]);
01728                     sortedList[tmpTitle.toLower()] = tmpTitle;
01729                     m_progLists[tmpTitle.toLower()].push_front(p);
01730                     m_progLists[tmpTitle.toLower()].setAutoDelete(false);
01731                 }
01732 
01733                 if ((m_viewMask & VIEW_WATCHLIST) &&
01734                     (p->GetRecordingGroup() != "LiveTV"))
01735                 {
01736                     if (m_watchListAutoExpire && !p->IsAutoExpirable())
01737                     {
01738                         p->SetRecordingPriority2(wlExpireOff);
01739                         LOG(VB_FILE, LOG_INFO, QString("Auto-expire off:  %1")
01740                                 .arg(p->GetTitle()));
01741                     }
01742                     else if (p->IsWatched())
01743                     {
01744                         p->SetRecordingPriority2(wlWatched);
01745                         LOG(VB_FILE, LOG_INFO,
01746                             QString("Marked as 'watched':  %1")
01747                                 .arg(p->GetTitle()));
01748                     }
01749                     else
01750                     {
01751                         if (p->GetRecordingRuleID())
01752                             recidEpisodes[p->GetRecordingRuleID()] += 1;
01753                         if (recidEpisodes[p->GetRecordingRuleID()] == 1 ||
01754                             !p->GetRecordingRuleID())
01755                         {
01756                             m_progLists[m_watchGroupLabel].push_front(p);
01757                             m_progLists[m_watchGroupLabel].setAutoDelete(false);
01758                         }
01759                         else
01760                         {
01761                             p->SetRecordingPriority2(wlEarlier);
01762                             LOG(VB_FILE, LOG_INFO,
01763                                 QString("Not the earliest:  %1")
01764                                     .arg(p->GetTitle()));
01765                         }
01766                     }
01767                 }
01768             }
01769         }
01770     }
01771 
01772     if (sortedList.empty())
01773     {
01774         LOG(VB_GENERAL, LOG_WARNING, LOC + "SortedList is Empty");
01775         m_progLists[""];
01776         m_titleList << "";
01777         m_playList.clear();
01778 
01779         m_isFilling = false;
01780         return false;
01781     }
01782 
01783     QString episodeSort = gCoreContext->GetSetting("PlayBoxEpisodeSort", "Date");
01784 
01785     if (episodeSort == "OrigAirDate")
01786     {
01787         QMap<QString, ProgramList>::Iterator Iprog;
01788         for (Iprog = m_progLists.begin(); Iprog != m_progLists.end(); ++Iprog)
01789         {
01790             if (!Iprog.key().isEmpty())
01791             {
01792                 std::stable_sort((*Iprog).begin(), (*Iprog).end(),
01793                                  (m_listOrder == 0 || m_type == kDeleteBox) ?
01794                                  comp_originalAirDate_rev_less_than :
01795                                  comp_originalAirDate_less_than);
01796             }
01797         }
01798     }
01799     else if (episodeSort == "Id")
01800     {
01801         QMap<QString, ProgramList>::Iterator Iprog;
01802         for (Iprog = m_progLists.begin(); Iprog != m_progLists.end(); ++Iprog)
01803         {
01804             if (!Iprog.key().isEmpty())
01805             {
01806                 std::stable_sort((*Iprog).begin(), (*Iprog).end(),
01807                                  (m_listOrder == 0 || m_type == kDeleteBox) ?
01808                                  comp_programid_rev_less_than :
01809                                  comp_programid_less_than);
01810             }
01811         }
01812     }
01813     else if (episodeSort == "Date")
01814     {
01815         QMap<QString, ProgramList>::iterator it;
01816         for (it = m_progLists.begin(); it != m_progLists.end(); ++it)
01817         {
01818             if (!it.key().isEmpty())
01819             {
01820                 std::stable_sort((*it).begin(), (*it).end(),
01821                                  (!m_listOrder || m_type == kDeleteBox) ?
01822                                  comp_recordDate_rev_less_than :
01823                                  comp_recordDate_less_than);
01824             }
01825         }
01826     }
01827     else if (episodeSort == "Season")
01828     {
01829         QMap<QString, ProgramList>::iterator it;
01830         for (it = m_progLists.begin(); it != m_progLists.end(); ++it)
01831         {
01832             if (!it.key().isEmpty())
01833             {
01834                 std::stable_sort((*it).begin(), (*it).end(),
01835                                  (!m_listOrder || m_type == kDeleteBox) ?
01836                                  comp_season_rev_less_than :
01837                                  comp_season_less_than);
01838             }
01839         }
01840     }
01841 
01842     if (!m_progLists[m_watchGroupLabel].empty())
01843     {
01844         QDateTime now = QDateTime::currentDateTime();
01845         int baseValue = m_watchListMaxAge * 2 / 3;
01846 
01847         QMap<int, int> recType;
01848         QMap<int, int> maxEpisodes;
01849         QMap<int, int> avgDelay;
01850         QMap<int, int> spanHours;
01851         QMap<int, int> delHours;
01852         QMap<int, int> nextHours;
01853 
01854         MSqlQuery query(MSqlQuery::InitCon());
01855         query.prepare("SELECT recordid, type, maxepisodes, avg_delay, "
01856                       "next_record, last_record, last_delete FROM record;");
01857 
01858         if (query.exec())
01859         {
01860             while (query.next())
01861             {
01862                 int recid = query.value(0).toInt();
01863                 recType[recid] = query.value(1).toInt();
01864                 maxEpisodes[recid] = query.value(2).toInt();
01865                 avgDelay[recid] = query.value(3).toInt();
01866 
01867                 QDateTime next_record = query.value(4).toDateTime();
01868                 QDateTime last_record = query.value(5).toDateTime();
01869                 QDateTime last_delete = query.value(6).toDateTime();
01870 
01871                 // Time between the last and next recordings
01872                 spanHours[recid] = 1000;
01873                 if (last_record.isValid() && next_record.isValid())
01874                     spanHours[recid] =
01875                         last_record.secsTo(next_record) / 3600 + 1;
01876 
01877                 // Time since the last episode was deleted
01878                 delHours[recid] = 1000;
01879                 if (last_delete.isValid())
01880                     delHours[recid] = last_delete.secsTo(now) / 3600 + 1;
01881 
01882                 // Time until the next recording if any
01883                 if (next_record.isValid())
01884                     nextHours[recid] = now.secsTo(next_record) / 3600 + 1;
01885             }
01886         }
01887 
01888         ProgramList::iterator pit = m_progLists[m_watchGroupLabel].begin();
01889         while (pit != m_progLists[m_watchGroupLabel].end())
01890         {
01891             int recid = (*pit)->GetRecordingRuleID();
01892             int avgd =  avgDelay[recid];
01893 
01894             if (avgd == 0)
01895                 avgd = 100;
01896 
01897             // Set the intervals beyond range if there is no record entry
01898             if (spanHours[recid] == 0)
01899             {
01900                 spanHours[recid] = 1000;
01901                 delHours[recid] = 1000;
01902             }
01903 
01904             // add point equal to baseValue for each additional episode
01905             if (!(*pit)->GetRecordingRuleID() || maxEpisodes[recid] > 0)
01906                 (*pit)->SetRecordingPriority2(0);
01907             else
01908             {
01909                 (*pit)->SetRecordingPriority2(
01910                     (recidEpisodes[(*pit)->GetRecordingRuleID()] - 1) *
01911                     baseValue);
01912             }
01913 
01914             // add points every 3hr leading up to the next recording
01915             if (nextHours[recid] > 0 && nextHours[recid] < baseValue * 3)
01916             {
01917                 (*pit)->SetRecordingPriority2(
01918                     (*pit)->GetRecordingPriority2() +
01919                     (baseValue * 3 - nextHours[recid]) / 3);
01920             }
01921 
01922             int hrs = (*pit)->GetScheduledEndTime().secsTo(now) / 3600;
01923             if (hrs < 1)
01924                 hrs = 1;
01925 
01926             // add points for a new recording that decrease each hour
01927             if (hrs < 42)
01928             {
01929                 (*pit)->SetRecordingPriority2(
01930                     (*pit)->GetRecordingPriority2() + 42 - hrs);
01931             }
01932 
01933             // add points for how close the recorded time of day is to 'now'
01934             (*pit)->SetRecordingPriority2(
01935                 (*pit)->GetRecordingPriority2() + abs((hrs % 24) - 12) * 2);
01936 
01937             // Daily
01938             if (spanHours[recid] < 50 ||
01939                 recType[recid] == kTimeslotRecord ||
01940                 recType[recid] == kFindDailyRecord)
01941             {
01942                 if (delHours[recid] < m_watchListBlackOut * 4)
01943                 {
01944                     (*pit)->SetRecordingPriority2(wlDeleted);
01945                     LOG(VB_FILE, LOG_INFO,
01946                         QString("Recently deleted daily:  %1")
01947                             .arg((*pit)->GetTitle()));
01948                     pit = m_progLists[m_watchGroupLabel].erase(pit);
01949                     continue;
01950                 }
01951                 else
01952                 {
01953                     LOG(VB_FILE, LOG_INFO, QString("Daily interval:  %1")
01954                             .arg((*pit)->GetTitle()));
01955 
01956                     if (maxEpisodes[recid] > 0)
01957                     {
01958                         (*pit)->SetRecordingPriority2(
01959                             (*pit)->GetRecordingPriority2() +
01960                             (baseValue / 2) + (hrs / 24));
01961                     }
01962                     else
01963                     {
01964                         (*pit)->SetRecordingPriority2(
01965                             (*pit)->GetRecordingPriority2() +
01966                             (baseValue / 5) + hrs);
01967                     }
01968                 }
01969             }
01970             // Weekly
01971             else if (nextHours[recid] ||
01972                      recType[recid] == kWeekslotRecord ||
01973                      recType[recid] == kFindWeeklyRecord)
01974 
01975             {
01976                 if (delHours[recid] < (m_watchListBlackOut * 24) - 4)
01977                 {
01978                     (*pit)->SetRecordingPriority2(wlDeleted);
01979                     LOG(VB_FILE, LOG_INFO,
01980                         QString("Recently deleted weekly:  %1")
01981                             .arg((*pit)->GetTitle()));
01982                     pit = m_progLists[m_watchGroupLabel].erase(pit);
01983                     continue;
01984                 }
01985                 else
01986                 {
01987                     LOG(VB_FILE, LOG_INFO, QString("Weekly interval: %1")
01988                             .arg((*pit)->GetTitle()));
01989 
01990                     if (maxEpisodes[recid] > 0)
01991                     {
01992                         (*pit)->SetRecordingPriority2(
01993                             (*pit)->GetRecordingPriority2() +
01994                             (baseValue / 2) + (hrs / 24));
01995                     }
01996                     else
01997                     {
01998                         (*pit)->SetRecordingPriority2(
01999                             (*pit)->GetRecordingPriority2() +
02000                             (baseValue / 3) + (baseValue * hrs / 24 / 4));
02001                     }
02002                 }
02003             }
02004             // Not recurring
02005             else
02006             {
02007                 if (delHours[recid] < (m_watchListBlackOut * 48) - 4)
02008                 {
02009                     (*pit)->SetRecordingPriority2(wlDeleted);
02010                     pit = m_progLists[m_watchGroupLabel].erase(pit);
02011                     continue;
02012                 }
02013                 else
02014                 {
02015                     // add points for a new Single or final episode
02016                     if (hrs < 36)
02017                     {
02018                         (*pit)->SetRecordingPriority2(
02019                             (*pit)->GetRecordingPriority2() +
02020                             baseValue * (36 - hrs) / 36);
02021                     }
02022 
02023                     if (avgd != 100)
02024                     {
02025                         if (maxEpisodes[recid] > 0)
02026                         {
02027                             (*pit)->SetRecordingPriority2(
02028                                 (*pit)->GetRecordingPriority2() +
02029                                 (baseValue / 2) + (hrs / 24));
02030                         }
02031                         else
02032                         {
02033                             (*pit)->SetRecordingPriority2(
02034                                 (*pit)->GetRecordingPriority2() +
02035                                 (baseValue / 3) + (baseValue * hrs / 24 / 4));
02036                         }
02037                     }
02038                     else if ((hrs / 24) < m_watchListMaxAge)
02039                     {
02040                         (*pit)->SetRecordingPriority2(
02041                             (*pit)->GetRecordingPriority2() +
02042                             hrs / 24);
02043                     }
02044                     else
02045                     {
02046                         (*pit)->SetRecordingPriority2(
02047                             (*pit)->GetRecordingPriority2() +
02048                             m_watchListMaxAge);
02049                     }
02050                 }
02051             }
02052 
02053             // Factor based on the average time shift delay.
02054             // Scale the avgd range of 0 thru 200 hours to 133% thru 67%
02055             int delaypct = avgd / 3 + 67;
02056 
02057             if (avgd < 100)
02058             {
02059                 (*pit)->SetRecordingPriority2(
02060                     (*pit)->GetRecordingPriority2() * (200 - delaypct) / 100);
02061             }
02062             else if (avgd > 100)
02063             {
02064                 (*pit)->SetRecordingPriority2(
02065                     (*pit)->GetRecordingPriority2() * 100 / delaypct);
02066             }
02067 
02068             LOG(VB_FILE, LOG_INFO, QString(" %1  %2  %3")
02069                     .arg(MythDateTimeToString((*pit)->GetScheduledStartTime(),
02070                                               kDateShort))
02071                     .arg((*pit)->GetRecordingPriority2())
02072                     .arg((*pit)->GetTitle()));
02073 
02074             ++pit;
02075         }
02076         std::stable_sort(m_progLists[m_watchGroupLabel].begin(),
02077                          m_progLists[m_watchGroupLabel].end(),
02078                          comp_recpriority2_less_than);
02079     }
02080 
02081     m_titleList = QStringList("");
02082     if (m_progLists[m_watchGroupLabel].size() > 0)
02083         m_titleList << m_watchGroupName;
02084     if ((m_progLists["livetv"].size() > 0) &&
02085         (!sortedList.values().contains(tr("Live TV"))))
02086         m_titleList << tr("Live TV");
02087     m_titleList << sortedList.values();
02088 
02089     // Populate list of recording groups
02090     if (!m_programInfoCache.empty())
02091     {
02092         QMutexLocker locker(&m_recGroupsLock);
02093 
02094         m_recGroups.clear();
02095         m_recGroupIdx = -1;
02096 
02097         m_recGroups.append("All Programs");
02098 
02099         MSqlQuery query(MSqlQuery::InitCon());
02100 
02101         query.prepare("SELECT distinct recgroup from recorded WHERE "
02102                       "deletepending = 0 ORDER BY recgroup");
02103         if (query.exec())
02104         {
02105             QString name;
02106             while (query.next())
02107             {
02108                 name = query.value(0).toString();
02109                 if (name != "Deleted" && name != "LiveTV")
02110                 {
02111                     m_recGroups.append(name);
02112                     m_recGroupType[name] = "recgroup";
02113                 }
02114             }
02115 
02116             m_recGroupIdx = m_recGroups.indexOf(m_recGroup);
02117             if (m_recGroupIdx < 0)
02118                 m_recGroupIdx = 0;
02119         }
02120     }
02121 
02122     UpdateUIRecGroupList();
02123     UpdateUIGroupList(groupSelPref);
02124     UpdateUsageUI();
02125 
02126     QStringList::const_iterator it = m_playList.begin();
02127     for (; it != m_playList.end(); ++it)
02128     {
02129         ProgramInfo *pginfo = FindProgramInUILists(*it);
02130         if (!pginfo)
02131             continue;
02132         MythUIButtonListItem *item =
02133             m_recordingList->GetItemByData(qVariantFromValue(pginfo));
02134         if (item)
02135             item->DisplayState("yes", "playlist");
02136     }
02137 
02138     restore_position(m_groupList, m_recordingList,
02139                      groupSelPref, itemSelPref, itemTopPref);
02140 
02141     m_isFilling = false;
02142 
02143     return true;
02144 }
02145 
02146 void PlaybackBox::playSelectedPlaylist(bool _random)
02147 {
02148     if (_random)
02149     {
02150         m_playListPlay.clear();
02151         QStringList tmp = m_playList;
02152         while (!tmp.empty())
02153         {
02154             uint i = random() % tmp.size();
02155             m_playListPlay.push_back(tmp[i]);
02156             tmp.removeAll(tmp[i]);
02157         }
02158     }
02159     else
02160     {
02161         m_playListPlay = m_playList;
02162     }
02163 
02164     QCoreApplication::postEvent(
02165         this, new MythEvent("PLAY_PLAYLIST"));
02166 }
02167 
02168 void PlaybackBox::PlayFromBookmark(MythUIButtonListItem *item)
02169 {
02170     if (!item)
02171         item = m_recordingList->GetItemCurrent();
02172 
02173     if (!item)
02174         return;
02175 
02176     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
02177 
02178     if (pginfo)
02179         PlayX(*pginfo, false, false);
02180 }
02181 
02182 void PlaybackBox::PlayFromBeginning(MythUIButtonListItem *item)
02183 {
02184     if (!item)
02185         item = m_recordingList->GetItemCurrent();
02186 
02187     if (!item)
02188         return;
02189 
02190     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
02191 
02192     if (pginfo)
02193         PlayX(*pginfo, true, false);
02194 }
02195 
02196 void PlaybackBox::PlayX(const ProgramInfo &pginfo,
02197                         bool ignoreBookmark,
02198                         bool underNetworkControl)
02199 {
02200     if (!m_player)
02201     {
02202         Play(pginfo, false, ignoreBookmark, underNetworkControl);
02203         return;
02204     }
02205 
02206     if (!m_player->IsSameProgram(0, &pginfo))
02207     {
02208         pginfo.ToStringList(m_player_selected_new_show);
02209         m_player_selected_new_show.push_back(
02210             ignoreBookmark ? "1" : "0");
02211         m_player_selected_new_show.push_back(
02212             underNetworkControl ? "1" : "0");
02213     }
02214     Close();
02215 }
02216 
02217 void PlaybackBox::StopSelected(void)
02218 {
02219     ProgramInfo *pginfo = CurrentItem();
02220     if (pginfo)
02221         m_helper.StopRecording(*pginfo);
02222 }
02223 
02224 void PlaybackBox::deleteSelected(MythUIButtonListItem *item)
02225 {
02226     if (!item)
02227         return;
02228 
02229     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
02230 
02231     if (!pginfo)
02232         return;
02233 
02234     if (pginfo->GetAvailableStatus() == asPendingDelete)
02235     {
02236         LOG(VB_GENERAL, LOG_ERR, QString("deleteSelected(%1) -- failed ")
02237                 .arg(pginfo->toString(ProgramInfo::kTitleSubtitle)) +
02238             QString("availability status: %1 ")
02239                 .arg(pginfo->GetAvailableStatus()));
02240 
02241         ShowOkPopup(tr("Cannot delete\n") +
02242                     tr("This recording is already being deleted"));
02243     }
02244     else if (!pginfo->QueryIsDeleteCandidate())
02245     {
02246         QString byWho;
02247         pginfo->QueryIsInUse(byWho);
02248 
02249         LOG(VB_GENERAL, LOG_ERR, QString("deleteSelected(%1) -- failed ")
02250                 .arg(pginfo->toString(ProgramInfo::kTitleSubtitle)) +
02251             QString("delete candidate: %1 in use by %2")
02252                 .arg(pginfo->QueryIsDeleteCandidate()).arg(byWho));
02253 
02254         if (byWho.isEmpty())
02255         {
02256             ShowOkPopup(tr("Cannot delete\n") +
02257                         tr("This recording is already being deleted"));
02258         }
02259         else
02260         {
02261             ShowOkPopup(tr("Cannot delete\n") +
02262                         tr("This recording is currently in use by:") + "\n" +
02263                         byWho);
02264         }
02265     }
02266     else
02267     {
02268         push_onto_del(m_delList, *pginfo);
02269         ShowDeletePopup(kDeleteRecording);
02270     }
02271 }
02272 
02273 void PlaybackBox::upcoming()
02274 {
02275     ProgramInfo *pginfo = CurrentItem();
02276     if (pginfo)
02277         ShowUpcoming(pginfo);
02278 }
02279 
02280 void PlaybackBox::upcomingScheduled()
02281 {
02282     ProgramInfo *pginfo = CurrentItem();
02283     if (pginfo)
02284         ShowUpcomingScheduled(pginfo);
02285 }
02286 
02287 ProgramInfo *PlaybackBox::CurrentItem(void)
02288 {
02289     ProgramInfo *pginfo = NULL;
02290 
02291     MythUIButtonListItem *item = m_recordingList->GetItemCurrent();
02292 
02293     if (!item)
02294         return NULL;
02295 
02296     pginfo = qVariantValue<ProgramInfo *>(item->GetData());
02297 
02298     if (!pginfo)
02299         return NULL;
02300 
02301     return pginfo;
02302 }
02303 
02304 void PlaybackBox::customEdit()
02305 {
02306     ProgramInfo *pginfo = CurrentItem();
02307     if (pginfo)
02308         EditCustom(pginfo);
02309 }
02310 
02311 void PlaybackBox::details()
02312 {
02313     ProgramInfo *pginfo = CurrentItem();
02314     if (pginfo)
02315         ShowDetails(pginfo);
02316 }
02317 
02318 void PlaybackBox::selected(MythUIButtonListItem *item)
02319 {
02320     if (!item)
02321         return;
02322 
02323     switch (m_type)
02324     {
02325         case kPlayBox:   PlayFromBookmark(item);   break;
02326         case kDeleteBox: deleteSelected(item); break;
02327     }
02328 }
02329 
02330 void PlaybackBox::popupClosed(QString which, int result)
02331 {
02332     m_menuDialog = NULL;
02333 
02334     if (result == -2)
02335     {
02336         if (!m_doToggleMenu)
02337         {
02338             m_doToggleMenu = true;
02339             return;
02340         }
02341 
02342         if (which == "groupmenu")
02343         {
02344             ProgramInfo *pginfo = CurrentItem();
02345             if (pginfo)
02346             {
02347                 m_helper.CheckAvailability(*pginfo, kCheckForMenuAction);
02348 
02349                 if (asPendingDelete == pginfo->GetAvailableStatus())
02350                 {
02351                     ShowAvailabilityPopup(*pginfo);
02352                 }
02353                 else
02354                 {
02355                     ShowActionPopup(*pginfo);
02356                     m_doToggleMenu = false;
02357                 }
02358             }
02359         }
02360         else if (which == "actionmenu")
02361         {
02362             ShowGroupPopup();
02363             m_doToggleMenu = false;
02364         }
02365     }
02366     else
02367         m_doToggleMenu = true;
02368 }
02369 
02370 void PlaybackBox::ShowGroupPopup()
02371 {
02372     QString label = tr("Group List Menu");
02373 
02374     ProgramInfo *pginfo = CurrentItem();
02375 
02376     m_popupMenu = new MythMenu(label, this, "groupmenu");
02377 
02378     m_popupMenu->AddItem(tr("Change Group Filter"),
02379                          SLOT(showGroupFilter()));
02380 
02381     m_popupMenu->AddItem(tr("Change Group View"),
02382                          SLOT(showViewChanger()));
02383 
02384     if (m_recGroupType[m_recGroup] == "recgroup")
02385         m_popupMenu->AddItem(tr("Change Group Password"),
02386                              SLOT(showRecGroupPasswordChanger()));
02387 
02388     if (m_playList.size())
02389     {
02390         m_popupMenu->AddItem(tr("Playlist options"), NULL, createPlaylistMenu());
02391     }
02392     else if (!m_player)
02393     {
02394         if (GetFocusWidget() == m_groupList)
02395         {
02396             m_popupMenu->AddItem(tr("Add this Group to Playlist"),
02397                                  SLOT(togglePlayListTitle()));
02398         }
02399         else if (pginfo)
02400         {
02401             m_popupMenu->AddItem(tr("Add this recording to Playlist"),
02402                                  SLOT(togglePlayListItem()));
02403         }
02404     }
02405 
02406     m_popupMenu->AddItem(tr("Help (Status Icons)"), SLOT(showIconHelp()));
02407 
02408     DisplayPopupMenu();
02409 }
02410 
02411 bool PlaybackBox::Play(
02412     const ProgramInfo &rec,
02413     bool inPlaylist, bool ignoreBookmark, bool underNetworkControl)
02414 {
02415     bool playCompleted = false;
02416 
02417     if (m_player)
02418         return true;
02419 
02420     if ((asAvailable != rec.GetAvailableStatus()) || !rec.GetFilesize() ||
02421         !rec.IsPathSet())
02422     {
02423         m_helper.CheckAvailability(
02424             rec, (inPlaylist) ? kCheckForPlaylistAction : kCheckForPlayAction);
02425         return false;
02426     }
02427 
02428     for (uint i = 0; i < sizeof(m_artImage) / sizeof(MythUIImage*); i++)
02429     {
02430         if (!m_artImage[i])
02431             continue;
02432 
02433         m_artTimer[i]->stop();
02434         m_artImage[i]->Reset();
02435     }
02436 
02437     ProgramInfo tvrec(rec);
02438 
02439     m_playingSomething = true;
02440     int initIndex = m_recordingList->StopLoad();
02441 
02442     uint flags =
02443         (inPlaylist          ? kStartTVInPlayList       : kStartTVNoFlags) |
02444         (underNetworkControl ? kStartTVByNetworkCommand : kStartTVNoFlags) |
02445         (ignoreBookmark      ? kStartTVIgnoreBookmark   : kStartTVNoFlags);
02446 
02447     playCompleted = TV::StartTV(&tvrec, flags);
02448 
02449     m_playingSomething = false;
02450     m_recordingList->LoadInBackground(initIndex);
02451 
02452     if (inPlaylist && !m_playListPlay.empty())
02453     {
02454         QCoreApplication::postEvent(
02455             this, new MythEvent("PLAY_PLAYLIST"));
02456     }
02457     else
02458     {
02459         // User may have saved or deleted a bookmark,
02460         // requiring update of preview..
02461         ProgramInfo *pginfo = m_programInfoCache.GetProgramInfo(
02462             tvrec.GetChanID(), tvrec.GetRecordingStartTime());
02463         if (pginfo)
02464             UpdateUIListItem(pginfo, true);
02465     }
02466 
02467     if (m_needUpdate)
02468         ScheduleUpdateUIList();
02469 
02470     return playCompleted;
02471 }
02472 
02473 void PlaybackBox::RemoveProgram(
02474     uint chanid, const QDateTime &recstartts,
02475     bool forgetHistory, bool forceMetadataDelete)
02476 {
02477     ProgramInfo *delItem = FindProgramInUILists(chanid, recstartts);
02478 
02479     if (!delItem)
02480         return;
02481 
02482     if (!forceMetadataDelete &&
02483         ((delItem->GetAvailableStatus() == asPendingDelete) ||
02484          !delItem->QueryIsDeleteCandidate()))
02485     {
02486         return;
02487     }
02488 
02489     if (m_playList.filter(delItem->MakeUniqueKey()).size())
02490         togglePlayListItem(delItem);
02491 
02492     if (!forceMetadataDelete)
02493         delItem->UpdateLastDelete(true);
02494 
02495     delItem->SetAvailableStatus(asPendingDelete, "RemoveProgram");
02496     m_helper.DeleteRecording(
02497         delItem->GetChanID(), delItem->GetRecordingStartTime(),
02498         forceMetadataDelete, forgetHistory);
02499 
02500     // if the item is in the current recording list UI then delete it.
02501     MythUIButtonListItem *uiItem =
02502         m_recordingList->GetItemByData(qVariantFromValue(delItem));
02503     if (uiItem)
02504         m_recordingList->RemoveItem(uiItem);
02505 }
02506 
02507 void PlaybackBox::fanartLoad(void)
02508 {
02509     m_artImage[kArtworkFanart]->Load();
02510 }
02511 
02512 void PlaybackBox::bannerLoad(void)
02513 {
02514     m_artImage[kArtworkBanner]->Load();
02515 }
02516 
02517 void PlaybackBox::coverartLoad(void)
02518 {
02519     m_artImage[kArtworkCoverart]->Load();
02520 }
02521 
02522 void PlaybackBox::ShowDeletePopup(DeletePopupType type)
02523 {
02524     QString label;
02525     switch (type)
02526     {
02527         case kDeleteRecording:
02528             label = tr("Are you sure you want to delete:"); break;
02529         case kForceDeleteRecording:
02530             label = tr("Recording file does not exist.\n"
02531                        "Are you sure you want to delete:");
02532             break;
02533         case kStopRecording:
02534             label = tr("Are you sure you want to stop:"); break;
02535     }
02536 
02537     ProgramInfo *delItem = NULL;
02538     if (m_delList.empty() && (delItem = CurrentItem()))
02539     {
02540         push_onto_del(m_delList, *delItem);
02541     }
02542     else if (m_delList.size() >= 4)
02543     {
02544         delItem = FindProgramInUILists(
02545             m_delList[0].toUInt(),
02546             QDateTime::fromString(m_delList[1], Qt::ISODate));
02547     }
02548 
02549     if (!delItem)
02550         return;
02551 
02552     uint other_delete_cnt = (m_delList.size() / 4) - 1;
02553 
02554     label += CreateProgramInfoString(*delItem);
02555 
02556     m_popupMenu = new MythMenu(label, this, "deletemenu");
02557 
02558     QString tmpmessage;
02559     const char *tmpslot = NULL;
02560 
02561     if ((kDeleteRecording == type) &&
02562         delItem->GetRecordingGroup() != "Deleted" &&
02563         delItem->GetRecordingGroup() != "LiveTV")
02564     {
02565         tmpmessage = tr("Yes, and allow re-record");
02566         tmpslot = SLOT(DeleteForgetHistory());
02567         m_popupMenu->AddItem(tmpmessage, tmpslot);
02568     }
02569 
02570     switch (type)
02571     {
02572         case kDeleteRecording:
02573             tmpmessage = tr("Yes, delete it");
02574             tmpslot = SLOT(Delete());
02575             break;
02576         case kForceDeleteRecording:
02577             tmpmessage = tr("Yes, delete it");
02578             tmpslot = SLOT(DeleteForce());
02579             break;
02580         case kStopRecording:
02581             tmpmessage = tr("Yes, stop recording");
02582             tmpslot = SLOT(StopSelected());
02583             break;
02584     }
02585 
02586     bool defaultIsYes =
02587         ((kDeleteRecording      != type) &&
02588          (kForceDeleteRecording != type) &&
02589          (delItem->QueryAutoExpire() != kDisableAutoExpire));
02590 
02591     m_popupMenu->AddItem(tmpmessage, tmpslot, NULL, defaultIsYes);
02592 
02593     if ((kForceDeleteRecording == type) && other_delete_cnt)
02594     {
02595         tmpmessage = tr("Yes, delete it and the remaining %1 list items")
02596             .arg(other_delete_cnt);
02597         tmpslot = SLOT(DeleteForceAllRemaining());
02598         m_popupMenu->AddItem(tmpmessage, tmpslot);
02599     }
02600 
02601     switch (type)
02602     {
02603         case kDeleteRecording:
02604         case kForceDeleteRecording:
02605             tmpmessage = tr("No, keep it");
02606             tmpslot = SLOT(DeleteIgnore());
02607             break;
02608         case kStopRecording:
02609             tmpmessage = tr("No, continue recording");
02610             tmpslot = SLOT(DeleteIgnore());
02611             break;
02612     }
02613     m_popupMenu->AddItem(tmpmessage, tmpslot, NULL, !defaultIsYes);
02614 
02615     if ((type == kForceDeleteRecording) && other_delete_cnt)
02616     {
02617         tmpmessage = tr("No, and keep the remaining %1 list items")
02618             .arg(other_delete_cnt);
02619         tmpslot = SLOT(DeleteIgnoreAllRemaining());
02620         m_popupMenu->AddItem(tmpmessage, tmpslot);
02621     }
02622 
02623     DisplayPopupMenu();
02624 }
02625 
02626 void PlaybackBox::ShowAvailabilityPopup(const ProgramInfo &pginfo)
02627 {
02628     QString msg = pginfo.toString(ProgramInfo::kTitleSubtitle, " ");
02629     msg += "\n";
02630 
02631     QString byWho;
02632     switch (pginfo.GetAvailableStatus())
02633     {
02634         case asAvailable:
02635             if (pginfo.QueryIsInUse(byWho))
02636             {
02637                 ShowOkPopup(tr("Recording Available\n") + msg +
02638                             tr("This recording is currently in "
02639                                "use by:") + "\n" + byWho);
02640             }
02641             else
02642             {
02643                 ShowOkPopup(tr("Recording Available\n") + msg +
02644                             tr("This recording is currently "
02645                                "Available"));
02646             }
02647             break;
02648         case asPendingDelete:
02649             ShowOkPopup(tr("Recording Unavailable\n") + msg +
02650                         tr("This recording is currently being "
02651                            "deleted and is unavailable"));
02652             break;
02653         case asDeleted:
02654             ShowOkPopup(tr("Recording Unavailable\n") + msg +
02655                         tr("This recording has been "
02656                            "deleted and is unavailable"));
02657             break;
02658         case asFileNotFound:
02659             ShowOkPopup(tr("Recording Unavailable\n") + msg +
02660                         tr("The file for this recording can "
02661                            "not be found"));
02662             break;
02663         case asZeroByte:
02664             ShowOkPopup(tr("Recording Unavailable\n") + msg +
02665                         tr("The file for this recording is "
02666                            "empty."));
02667             break;
02668         case asNotYetAvailable:
02669             ShowOkPopup(tr("Recording Unavailable\n") + msg +
02670                         tr("This recording is not yet "
02671                            "available."));
02672     }
02673 }
02674 
02675 MythMenu* PlaybackBox::createPlaylistMenu(void)
02676 {
02677     QString label = tr("There is %n item(s) in the playlist. Actions affect "
02678            "all items in the playlist", "", m_playList.size());
02679 
02680     MythMenu *menu = new MythMenu(label, this, "slotmenu");
02681 
02682     menu->AddItem(tr("Play"), SLOT(doPlayList()));
02683     menu->AddItem(tr("Shuffle Play"), SLOT(doPlayListRandom()));
02684     menu->AddItem(tr("Clear Playlist"), SLOT(doClearPlaylist()));
02685 
02686     if (GetFocusWidget() == m_groupList)
02687     {
02688         if ((m_viewMask & VIEW_TITLES))
02689             menu->AddItem(tr("Toggle playlist for this Category/Title"),
02690                           SLOT(togglePlayListTitle()));
02691         else
02692             menu->AddItem(tr("Toggle playlist for this Group"),
02693                           SLOT(togglePlayListTitle()));
02694     }
02695     else
02696         menu->AddItem(tr("Toggle playlist for this recording"),
02697                       SLOT(togglePlayListItem()));
02698 
02699     menu->AddItem(tr("Storage Options"), NULL, createPlaylistStorageMenu());
02700     menu->AddItem(tr("Job Options"), NULL, createPlaylistJobMenu());
02701     menu->AddItem(tr("Delete"), SLOT(PlaylistDelete()));
02702     menu->AddItem(tr("Delete, and allow re-record"),
02703                   SLOT(PlaylistDeleteForgetHistory()));
02704 
02705     return menu;
02706 }
02707 
02708 MythMenu* PlaybackBox::createPlaylistStorageMenu()
02709 {
02710     QString label = tr("There is %n item(s) in the playlist. Actions affect "
02711            "all items in the playlist", "", m_playList.size());
02712 
02713     MythMenu *menu = new MythMenu(label, this, "slotmenu");
02714 
02715     menu->AddItem(tr("Change Recording Group"), SLOT(ShowRecGroupChangerUsePlaylist()));
02716     menu->AddItem(tr("Change Playback Group"), SLOT(ShowPlayGroupChangerUsePlaylist()));
02717     menu->AddItem(tr("Disable Auto Expire"), SLOT(doPlaylistExpireSetOff()));
02718     menu->AddItem(tr("Enable Auto Expire"), SLOT(doPlaylistExpireSetOn()));
02719     menu->AddItem(tr("Mark As Watched"), SLOT(doPlaylistWatchedSetOn()));
02720     menu->AddItem(tr("Mark As Unwatched"), SLOT(doPlaylistWatchedSetOff()));
02721 
02722     return menu;
02723 }
02724 
02725 MythMenu* PlaybackBox::createPlaylistJobMenu(void)
02726 {
02727     QString label = tr("There is %n item(s) in the playlist. Actions affect "
02728            "all items in the playlist", "", m_playList.size());
02729 
02730     MythMenu *menu = new MythMenu(label, this, "slotmenu");
02731 
02732     QString jobTitle;
02733     QString command;
02734     QStringList::Iterator it;
02735     ProgramInfo *tmpItem;
02736     bool isTranscoding = true;
02737     bool isFlagging = true;
02738     bool isMetadataLookup = true;
02739     bool isRunningUserJob1 = true;
02740     bool isRunningUserJob2 = true;
02741     bool isRunningUserJob3 = true;
02742     bool isRunningUserJob4 = true;
02743 
02744     for(it = m_playList.begin(); it != m_playList.end(); ++it)
02745     {
02746         tmpItem = FindProgramInUILists(*it);
02747         if (tmpItem)
02748         {
02749             if (!JobQueue::IsJobQueuedOrRunning(
02750                     JOB_TRANSCODE,
02751                     tmpItem->GetChanID(), tmpItem->GetRecordingStartTime()))
02752                 isTranscoding = false;
02753             if (!JobQueue::IsJobQueuedOrRunning(
02754                     JOB_COMMFLAG,
02755                     tmpItem->GetChanID(), tmpItem->GetRecordingStartTime()))
02756                 isFlagging = false;
02757             if (!JobQueue::IsJobQueuedOrRunning(
02758                     JOB_METADATA,
02759                     tmpItem->GetChanID(), tmpItem->GetRecordingStartTime()))
02760                 isMetadataLookup = false;
02761             if (!JobQueue::IsJobQueuedOrRunning(
02762                     JOB_USERJOB1,
02763                     tmpItem->GetChanID(), tmpItem->GetRecordingStartTime()))
02764                 isRunningUserJob1 = false;
02765             if (!JobQueue::IsJobQueuedOrRunning(
02766                     JOB_USERJOB2,
02767                     tmpItem->GetChanID(), tmpItem->GetRecordingStartTime()))
02768                 isRunningUserJob2 = false;
02769             if (!JobQueue::IsJobQueuedOrRunning(
02770                     JOB_USERJOB3,
02771                     tmpItem->GetChanID(), tmpItem->GetRecordingStartTime()))
02772                 isRunningUserJob3 = false;
02773             if (!JobQueue::IsJobQueuedOrRunning(
02774                     JOB_USERJOB4,
02775                     tmpItem->GetChanID(), tmpItem->GetRecordingStartTime()))
02776                 isRunningUserJob4 = false;
02777             if (!isTranscoding && !isFlagging && !isRunningUserJob1 &&
02778                 !isRunningUserJob2 && !isRunningUserJob3 && !isRunningUserJob4)
02779                 break;
02780         }
02781     }
02782 
02783     if (!isTranscoding)
02784         menu->AddItem(tr("Begin Transcoding"), SLOT(doPlaylistBeginTranscoding()));
02785     else
02786         menu->AddItem(tr("Stop Transcoding"), SLOT(stopPlaylistTranscoding()));
02787 
02788     if (!isFlagging)
02789         menu->AddItem(tr("Begin Commercial Detection"), SLOT(doPlaylistBeginFlagging()));
02790     else
02791         menu->AddItem(tr("Stop Commercial Detection"), SLOT(stopPlaylistFlagging()));
02792 
02793     if (!isMetadataLookup)
02794         menu->AddItem(tr("Begin Metadata Lookup"), SLOT(doPlaylistBeginLookup()));
02795     else
02796         menu->AddItem(tr("Stop Metadata Lookup"), SLOT(stopPlaylistLookup()));
02797 
02798     command = gCoreContext->GetSetting("UserJob1", "");
02799     if (!command.isEmpty())
02800     {
02801         jobTitle = gCoreContext->GetSetting("UserJobDesc1");
02802 
02803         if (!isRunningUserJob1)
02804             menu->AddItem(tr("Begin") + ' ' + jobTitle,
02805                           SLOT(doPlaylistBeginUserJob1()));
02806         else
02807             menu->AddItem(tr("Stop") + ' ' + jobTitle,
02808                           SLOT(stopPlaylistUserJob1()));
02809     }
02810 
02811     command = gCoreContext->GetSetting("UserJob2", "");
02812     if (!command.isEmpty())
02813     {
02814         jobTitle = gCoreContext->GetSetting("UserJobDesc2");
02815 
02816         if (!isRunningUserJob2)
02817             menu->AddItem(tr("Begin") + ' ' + jobTitle,
02818                           SLOT(doPlaylistBeginUserJob2()));
02819         else
02820             menu->AddItem(tr("Stop") + ' ' + jobTitle,
02821                           SLOT(stopPlaylistUserJob2()));
02822     }
02823 
02824     command = gCoreContext->GetSetting("UserJob3", "");
02825     if (!command.isEmpty())
02826     {
02827         jobTitle = gCoreContext->GetSetting("UserJobDesc3");
02828 
02829         if (!isRunningUserJob3)
02830             menu->AddItem(tr("Begin") + ' ' + jobTitle,
02831                           SLOT(doPlaylistBeginUserJob3()));
02832         else
02833             menu->AddItem(tr("Stop") + ' ' + jobTitle,
02834                           SLOT(stopPlaylistUserJob3()));
02835     }
02836 
02837     command = gCoreContext->GetSetting("UserJob4", "");
02838     if (!command.isEmpty())
02839     {
02840         jobTitle = gCoreContext->GetSetting("UserJobDesc4");
02841 
02842         if (!isRunningUserJob4)
02843             menu->AddItem(QString("%1 %2").arg(tr("Begin")).arg(jobTitle),
02844                           SLOT(doPlaylistBeginUserJob4()));
02845         else
02846             menu->AddItem(QString("%1 %2").arg(tr("Stop")).arg(jobTitle),
02847                           SLOT(stopPlaylistUserJob4()));
02848     }
02849 
02850     return menu;
02851 }
02852 
02853 void PlaybackBox::DisplayPopupMenu(void)
02854 {
02855     if (m_menuDialog || !m_popupMenu)
02856         return;
02857 
02858     m_menuDialog = new MythDialogBox(m_popupMenu, m_popupStack, "pbbmainmenupopup");
02859 
02860     if (m_menuDialog->Create())
02861     {
02862         m_popupStack->AddScreen(m_menuDialog);
02863         connect(m_menuDialog, SIGNAL(Closed(QString,int)), SLOT(popupClosed(QString,int)));
02864     }
02865     else
02866         delete m_menuDialog;
02867 }
02868 
02869 void PlaybackBox::ShowMenu()
02870 {
02871     if (m_menuDialog)
02872         return;
02873 
02874     if (GetFocusWidget() == m_groupList)
02875         ShowGroupPopup();
02876     else
02877     {
02878         ProgramInfo *pginfo = CurrentItem();
02879         if (pginfo)
02880         {
02881             m_helper.CheckAvailability(
02882                 *pginfo, kCheckForMenuAction);
02883 
02884             if ((asPendingDelete == pginfo->GetAvailableStatus()) ||
02885                 (asPendingDelete == pginfo->GetAvailableStatus()))
02886             {
02887                 ShowAvailabilityPopup(*pginfo);
02888             }
02889             else
02890             {
02891                 ShowActionPopup(*pginfo);
02892             }
02893         }
02894         else
02895             ShowGroupPopup();
02896     }
02897 }
02898 
02899 MythMenu* PlaybackBox::createPlayFromMenu()
02900 {
02901     ProgramInfo *pginfo = CurrentItem();
02902     if (!pginfo)
02903         return NULL;
02904 
02905     QString title = tr("Play Options") + CreateProgramInfoString(*pginfo);
02906 
02907     MythMenu *menu = new MythMenu(title, this, "slotmenu");
02908 
02909     menu->AddItem(tr("Play from bookmark"),  SLOT(PlayFromBookmark()));
02910     menu->AddItem(tr("Play from beginning"), SLOT(PlayFromBeginning()));
02911 
02912     return menu;
02913 }
02914 
02915 MythMenu* PlaybackBox::createStorageMenu()
02916 {
02917     ProgramInfo *pginfo = CurrentItem();
02918     if (!pginfo)
02919         return NULL;
02920 
02921     QString title = tr("Storage Options") + CreateProgramInfoString(*pginfo);
02922     QString autoExpireText = (pginfo->IsAutoExpirable()) ?
02923         tr("Disable Auto Expire") : tr("Enable Auto Expire");
02924     QString preserveText = (pginfo->IsPreserved()) ?
02925         tr("Do not preserve this episode") : tr("Preserve this episode");
02926 
02927     MythMenu *menu = new MythMenu(title, this, "slotmenu");
02928     menu->AddItem(tr("Change Recording Group"), SLOT(ShowRecGroupChanger()));
02929     menu->AddItem(tr("Change Playback Group"), SLOT(ShowPlayGroupChanger()));
02930     menu->AddItem(autoExpireText, SLOT(toggleAutoExpire()));
02931     menu->AddItem(preserveText, SLOT(togglePreserveEpisode()));
02932 
02933     return menu;
02934 }
02935 
02936 MythMenu* PlaybackBox::createRecordingMenu(void)
02937 {
02938     ProgramInfo *pginfo = CurrentItem();
02939     if (!pginfo)
02940         return NULL;
02941 
02942     QString title = tr("Scheduling Options") + CreateProgramInfoString(*pginfo);
02943 
02944     MythMenu *menu = new MythMenu(title, this, "slotmenu");
02945 
02946     menu->AddItem(tr("Edit Recording Schedule"), SLOT(doEditScheduled()));
02947 
02948     menu->AddItem(tr("Allow this episode to re-record"), SLOT(doAllowRerecord()));
02949 
02950     menu->AddItem(tr("Show Recording Details"), SLOT(showProgramDetails()));
02951 
02952     menu->AddItem(tr("Change Recording Metadata"), SLOT(showMetadataEditor()));
02953 
02954     menu->AddItem(tr("Custom Edit"), SLOT(customEdit()));
02955 
02956     return menu;
02957 }
02958 
02959 MythMenu* PlaybackBox::createJobMenu()
02960 {
02961     ProgramInfo *pginfo = CurrentItem();
02962     if (!pginfo)
02963         return NULL;
02964 
02965     QString title = tr("Job Options") + CreateProgramInfoString(*pginfo);
02966 
02967     MythMenu *menu = new MythMenu(title, this, "slotmenu");
02968 
02969     QString jobTitle;
02970     QString command;
02971 
02972     bool add[7] =
02973     {
02974         true,
02975         true,
02976         true,
02977         !gCoreContext->GetSetting("UserJob1", "").isEmpty(),
02978         !gCoreContext->GetSetting("UserJob2", "").isEmpty(),
02979         !gCoreContext->GetSetting("UserJob3", "").isEmpty(),
02980         !gCoreContext->GetSetting("UserJob4", "").isEmpty(),
02981     };
02982     int jobs[7] =
02983     {
02984         JOB_TRANSCODE,
02985         JOB_COMMFLAG,
02986         JOB_METADATA,
02987         JOB_USERJOB1,
02988         JOB_USERJOB2,
02989         JOB_USERJOB3,
02990         JOB_USERJOB4,
02991     };
02992     QString desc[14] =
02993     {
02994         // stop                         start
02995         tr("Stop Transcoding"),         tr("Begin Transcoding"),
02996         tr("Stop Commercial Detection"), tr("Begin Commercial Detection"),
02997         tr("Stop Metadata Lookup"),      tr("Begin Metadata Lookup"),
02998         "1",                            "1",
02999         "2",                            "2",
03000         "3",                            "3",
03001         "4",                            "4",
03002     };
03003     const char *myslots[14] =
03004     {   // stop                         start
03005         SLOT(doBeginTranscoding()),     SLOT(createTranscodingProfilesMenu()),
03006         SLOT(doBeginFlagging()),        SLOT(doBeginFlagging()),
03007         SLOT(doBeginLookup()),          SLOT(doBeginLookup()),
03008         SLOT(doBeginUserJob1()),        SLOT(doBeginUserJob1()),
03009         SLOT(doBeginUserJob2()),        SLOT(doBeginUserJob2()),
03010         SLOT(doBeginUserJob3()),        SLOT(doBeginUserJob3()),
03011         SLOT(doBeginUserJob4()),        SLOT(doBeginUserJob4()),
03012     };
03013 
03014     for (uint i = 0; i < sizeof(add) / sizeof(bool); i++)
03015     {
03016         if (!add[i])
03017             continue;
03018 
03019         QString stop_desc  = desc[i*2+0];
03020         QString start_desc = desc[i*2+1];
03021 
03022         if (start_desc.toUInt())
03023         {
03024             QString jobTitle = gCoreContext->GetSetting(
03025                 "UserJobDesc"+start_desc, tr("User Job") + " #" + start_desc);
03026             stop_desc  = tr("Stop")  + ' ' + jobTitle;
03027             start_desc = tr("Begin") + ' ' + jobTitle;
03028         }
03029 
03030         bool running = JobQueue::IsJobQueuedOrRunning(
03031             jobs[i], pginfo->GetChanID(), pginfo->GetRecordingStartTime());
03032 
03033         const char *slot = myslots[i * 2 + (running ? 0 : 1)];
03034         MythMenu *submenu = (slot == myslots[1] ? createTranscodingProfilesMenu() : NULL);
03035 
03036         menu->AddItem((running) ? stop_desc : start_desc, slot, submenu);
03037     }
03038 
03039     return menu;
03040 }
03041 
03042 MythMenu* PlaybackBox::createTranscodingProfilesMenu()
03043 {
03044     QString label = tr("Transcoding profiles");
03045 
03046     MythMenu *menu = new MythMenu(label, this, "transcode");
03047 
03048     menu->AddItem(tr("Default"), qVariantFromValue(-1));
03049     menu->AddItem(tr("Autodetect"), qVariantFromValue(0));
03050 
03051     MSqlQuery query(MSqlQuery::InitCon());
03052     query.prepare("SELECT r.name, r.id "
03053                   "FROM recordingprofiles r, profilegroups p "
03054                   "WHERE p.name = 'Transcoders' "
03055                   "AND r.profilegroup = p.id "
03056                   "AND r.name != 'RTjpeg/MPEG4' "
03057                   "AND r.name != 'MPEG2' ");
03058 
03059     if (!query.exec())
03060     {
03061         MythDB::DBError(LOC + "unable to query transcoders", query);
03062         return NULL;
03063     }
03064 
03065     while (query.next())
03066     {
03067         QString transcoder_name = query.value(0).toString();
03068         int transcoder_id = query.value(1).toInt();
03069 
03070         // Translatable strings for known profiles
03071         if (transcoder_name == "High Quality")
03072             transcoder_name = tr("High Quality");
03073         else if (transcoder_name == "Medium Quality")
03074             transcoder_name = tr("Medium Quality");
03075         else if (transcoder_name == "Low Quality")
03076             transcoder_name = tr("Low Quality");
03077 
03078         menu->AddItem(transcoder_name, qVariantFromValue(transcoder_id));
03079     }
03080 
03081     return menu;
03082 }
03083 
03084 void PlaybackBox::changeProfileAndTranscode(int id)
03085 {
03086     ProgramInfo *pginfo = CurrentItem();
03087 
03088     if (!pginfo)
03089         return;
03090 
03091     if (id >= 0)
03092     {
03093         RecordingInfo ri(*pginfo);
03094         ri.ApplyTranscoderProfileChangeById(id);
03095     }
03096     doBeginTranscoding();
03097 }
03098 
03099 void PlaybackBox::ShowActionPopup(const ProgramInfo &pginfo)
03100 {
03101     QString label =
03102         (asFileNotFound == pginfo.GetAvailableStatus()) ?
03103         tr("Recording file cannot be found") :
03104         (asZeroByte     == pginfo.GetAvailableStatus()) ?
03105         tr("Recording file contains no data") :
03106         tr("Recording Options");
03107 
03108     m_popupMenu = new MythMenu(label + CreateProgramInfoString(pginfo), this, "actionmenu");
03109 
03110     if ((asFileNotFound  == pginfo.GetAvailableStatus()) ||
03111         (asZeroByte      == pginfo.GetAvailableStatus()))
03112     {
03113         m_popupMenu->AddItem(tr("Show Recording Details"), SLOT(showProgramDetails()));
03114         m_popupMenu->AddItem(tr("Delete"), SLOT(askDelete()));
03115 
03116         if (m_playList.filter(pginfo.MakeUniqueKey()).size())
03117         {
03118             m_popupMenu->AddItem(tr("Remove from Playlist"), SLOT(togglePlayListItem()));
03119         }
03120         else
03121         {
03122             m_popupMenu->AddItem(tr("Add to Playlist"), SLOT(togglePlayListItem()));
03123         }
03124 
03125         DisplayPopupMenu();
03126 
03127         return;
03128     }
03129 
03130     bool sameProgram = false;
03131 
03132     if (m_player)
03133         sameProgram = m_player->IsSameProgram(0, &pginfo);
03134 
03135     TVState tvstate = kState_None;
03136 
03137     if (!sameProgram)
03138     {
03139         if (pginfo.IsBookmarkSet())
03140             m_popupMenu->AddItem(tr("Play from..."), NULL, createPlayFromMenu());
03141         else
03142             m_popupMenu->AddItem(tr("Play"), SLOT(PlayFromBookmark()));
03143     }
03144 
03145     if (!m_player)
03146     {
03147         if (m_playList.filter(pginfo.MakeUniqueKey()).size())
03148             m_popupMenu->AddItem(tr("Remove from Playlist"),
03149                                  SLOT(togglePlayListItem()));
03150         else
03151             m_popupMenu->AddItem(tr("Add to Playlist"),
03152                                  SLOT(togglePlayListItem()));
03153         if (m_playList.size())
03154         {
03155             m_popupMenu->AddItem(tr("Playlist options"), NULL, createPlaylistMenu());
03156         }
03157     }
03158 
03159     if (pginfo.GetRecordingStatus() == rsRecording &&
03160         (!(sameProgram &&
03161            (tvstate == kState_WatchingLiveTV ||
03162             tvstate == kState_WatchingRecording))))
03163     {
03164         m_popupMenu->AddItem(tr("Stop Recording"), SLOT(askStop()));
03165     }
03166 
03167     if (pginfo.IsWatched())
03168         m_popupMenu->AddItem(tr("Mark as Unwatched"), SLOT(toggleWatched()));
03169     else
03170         m_popupMenu->AddItem(tr("Mark as Watched"), SLOT(toggleWatched()));
03171 
03172     m_popupMenu->AddItem(tr("Storage Options"), NULL, createStorageMenu());
03173     m_popupMenu->AddItem(tr("Recording Options"), NULL, createRecordingMenu());
03174     m_popupMenu->AddItem(tr("Job Options"), NULL, createJobMenu());
03175 
03176     if (!sameProgram)
03177     {
03178         if (pginfo.GetRecordingGroup() == "Deleted")
03179         {
03180             push_onto_del(m_delList, pginfo);
03181             m_popupMenu->AddItem(tr("Undelete"), SLOT(Undelete()));
03182             m_popupMenu->AddItem(tr("Delete Forever"), SLOT(Delete()));
03183         }
03184         else
03185         {
03186             m_popupMenu->AddItem(tr("Delete"), SLOT(askDelete()));
03187         }
03188     }
03189 
03190     DisplayPopupMenu();
03191 }
03192 
03193 QString PlaybackBox::CreateProgramInfoString(const ProgramInfo &pginfo) const
03194 {
03195     QDateTime recstartts = pginfo.GetRecordingStartTime();
03196     QDateTime recendts   = pginfo.GetRecordingEndTime();
03197 
03198     QString timedate = QString("%1 - %2")
03199         .arg(MythDateTimeToString(recstartts, kDateTimeFull | kSimplify))
03200         .arg(MythDateTimeToString(recendts, kTime));
03201 
03202     QString title = pginfo.GetTitle();
03203 
03204     QString extra;
03205 
03206     if (!pginfo.GetSubtitle().isEmpty())
03207     {
03208         extra = QString('\n') + pginfo.GetSubtitle();
03209         int maxll = max(title.length(), 20);
03210         if (extra.length() > maxll)
03211             extra = extra.left(maxll - 3) + "...";
03212     }
03213 
03214     return QString("\n%1%2\n%3").arg(title).arg(extra).arg(timedate);
03215 }
03216 
03217 void PlaybackBox::doClearPlaylist(void)
03218 {
03219     QStringList::Iterator it;
03220     for (it = m_playList.begin(); it != m_playList.end(); ++it)
03221     {
03222         ProgramInfo *tmpItem = FindProgramInUILists(*it);
03223 
03224         if (!tmpItem)
03225             continue;
03226 
03227         MythUIButtonListItem *item =
03228             m_recordingList->GetItemByData(qVariantFromValue(tmpItem));
03229 
03230         if (item)
03231             item->DisplayState("no", "playlist");
03232     }
03233     m_playList.clear();
03234 }
03235 
03236 void PlaybackBox::doPlayList(void)
03237 {
03238     playSelectedPlaylist(false);
03239 }
03240 
03241 
03242 void PlaybackBox::doPlayListRandom(void)
03243 {
03244     playSelectedPlaylist(true);
03245 }
03246 
03247 void PlaybackBox::askStop(void)
03248 {
03249     ProgramInfo *pginfo = CurrentItem();
03250     if (pginfo)
03251     {
03252         push_onto_del(m_delList, *pginfo);
03253         ShowDeletePopup(kStopRecording);
03254     }
03255 }
03256 
03257 void PlaybackBox::showProgramDetails()
03258 {
03259     ProgramInfo *pginfo = CurrentItem();
03260     if (pginfo)
03261         ShowDetails(pginfo);
03262 }
03263 
03264 void PlaybackBox::doEditScheduled()
03265 {
03266     ProgramInfo *pginfo = CurrentItem();
03267     if (pginfo)
03268         EditScheduled(pginfo);
03269 }
03270 
03277 void PlaybackBox::doAllowRerecord()
03278 {
03279    ProgramInfo *pginfo = CurrentItem();
03280 
03281     if (!pginfo)
03282         return;
03283 
03284     RecordingInfo ri(*pginfo);
03285     ri.ForgetHistory();
03286     *pginfo = ri;
03287 }
03288 
03289 void PlaybackBox::doJobQueueJob(int jobType, int jobFlags)
03290 {
03291    ProgramInfo *pginfo = CurrentItem();
03292 
03293     if (!pginfo)
03294         return;
03295 
03296     ProgramInfo *tmpItem = FindProgramInUILists(*pginfo);
03297 
03298     if (JobQueue::IsJobQueuedOrRunning(
03299             jobType, pginfo->GetChanID(), pginfo->GetRecordingStartTime()))
03300     {
03301         JobQueue::ChangeJobCmds(
03302             jobType, pginfo->GetChanID(), pginfo->GetRecordingStartTime(),
03303             JOB_STOP);
03304         if ((jobType & JOB_COMMFLAG) && (tmpItem))
03305         {
03306             tmpItem->SetEditing(false);
03307             tmpItem->SetFlagging(false);
03308         }
03309     }
03310     else
03311     {
03312         QString jobHost;
03313         if (gCoreContext->GetNumSetting("JobsRunOnRecordHost", 0))
03314             jobHost = pginfo->GetHostname();
03315 
03316         JobQueue::QueueJob(jobType, pginfo->GetChanID(),
03317                            pginfo->GetRecordingStartTime(), "", "", jobHost,
03318                            jobFlags);
03319     }
03320 }
03321 
03322 void PlaybackBox::doBeginFlagging()
03323 {
03324     doJobQueueJob(JOB_COMMFLAG);
03325 }
03326 
03327 void PlaybackBox::doBeginLookup()
03328 {
03329     doJobQueueJob(JOB_METADATA);
03330 }
03331 
03332 void PlaybackBox::doPlaylistJobQueueJob(int jobType, int jobFlags)
03333 {
03334     ProgramInfo *tmpItem;
03335     QStringList::Iterator it;
03336 
03337     for (it = m_playList.begin(); it != m_playList.end(); ++it)
03338     {
03339         tmpItem = FindProgramInUILists(*it);
03340         if (tmpItem &&
03341             (!JobQueue::IsJobQueuedOrRunning(
03342                 jobType,
03343                 tmpItem->GetChanID(), tmpItem->GetRecordingStartTime())))
03344     {
03345             QString jobHost;
03346             if (gCoreContext->GetNumSetting("JobsRunOnRecordHost", 0))
03347                 jobHost = tmpItem->GetHostname();
03348 
03349             JobQueue::QueueJob(jobType, tmpItem->GetChanID(),
03350                                tmpItem->GetRecordingStartTime(),
03351                                "", "", jobHost, jobFlags);
03352         }
03353     }
03354 }
03355 
03356 void PlaybackBox::stopPlaylistJobQueueJob(int jobType)
03357 {
03358     ProgramInfo *tmpItem;
03359     QStringList::Iterator it;
03360 
03361     for (it = m_playList.begin(); it != m_playList.end(); ++it)
03362     {
03363         tmpItem = FindProgramInUILists(*it);
03364         if (tmpItem &&
03365             (JobQueue::IsJobQueuedOrRunning(
03366                 jobType,
03367                 tmpItem->GetChanID(), tmpItem->GetRecordingStartTime())))
03368         {
03369             JobQueue::ChangeJobCmds(
03370                 jobType, tmpItem->GetChanID(),
03371                 tmpItem->GetRecordingStartTime(), JOB_STOP);
03372 
03373             if ((jobType & JOB_COMMFLAG) && (tmpItem))
03374             {
03375                 tmpItem->SetEditing(false);
03376                 tmpItem->SetFlagging(false);
03377             }
03378         }
03379     }
03380 }
03381 
03382 void PlaybackBox::askDelete()
03383 {
03384     ProgramInfo *pginfo = CurrentItem();
03385     if (pginfo)
03386     {
03387         push_onto_del(m_delList, *pginfo);
03388         ShowDeletePopup(kDeleteRecording);
03389     }
03390 }
03391 
03392 void PlaybackBox::PlaylistDelete(bool forgetHistory)
03393 {
03394     QString forceDeleteStr("0");
03395 
03396     QStringList::const_iterator it;
03397     QStringList list;
03398     for (it = m_playList.begin(); it != m_playList.end(); ++it)
03399     {
03400         ProgramInfo *tmpItem = FindProgramInUILists(*it);
03401         if (tmpItem && tmpItem->QueryIsDeleteCandidate())
03402         {
03403             tmpItem->SetAvailableStatus(asPendingDelete, "PlaylistDelete");
03404             list.push_back(QString::number(tmpItem->GetChanID()));
03405             list.push_back(tmpItem->GetRecordingStartTime(ISODate));
03406             list.push_back(forceDeleteStr);
03407             list.push_back(forgetHistory ? "1" : "0");
03408 
03409             // if the item is in the current recording list UI then delete it.
03410             MythUIButtonListItem *uiItem =
03411                 m_recordingList->GetItemByData(qVariantFromValue(tmpItem));
03412             if (uiItem)
03413                 m_recordingList->RemoveItem(uiItem);
03414         }
03415     }
03416     m_playList.clear();
03417 
03418     if (!list.empty())
03419         m_helper.DeleteRecordings(list);
03420 
03421     doClearPlaylist();
03422 }
03423 
03424 void PlaybackBox::Undelete(void)
03425 {
03426     uint chanid;
03427     QDateTime recstartts;
03428     if (extract_one_del(m_delList, chanid, recstartts))
03429         m_helper.UndeleteRecording(chanid, recstartts);
03430 }
03431 
03432 void PlaybackBox::Delete(DeleteFlags flags)
03433 {
03434     uint chanid;
03435     QDateTime recstartts;
03436     while (extract_one_del(m_delList, chanid, recstartts))
03437     {
03438         if (flags & kIgnore)
03439             continue;
03440 
03441         RemoveProgram(chanid, recstartts,
03442                       flags & kForgetHistory, flags & kForce);
03443 
03444         if (!(flags & kAllRemaining))
03445             break;
03446     }
03447 
03448     if (!m_delList.empty())
03449     {
03450         MythEvent *e = new MythEvent("DELETE_FAILURES", m_delList);
03451         m_delList.clear();
03452         QCoreApplication::postEvent(this, e);
03453     }
03454 }
03455 
03456 ProgramInfo *PlaybackBox::FindProgramInUILists(const ProgramInfo &pginfo)
03457 {
03458     return FindProgramInUILists(
03459         pginfo.GetChanID(), pginfo.GetRecordingStartTime(),
03460         pginfo.GetRecordingGroup());
03461 }
03462 
03466 ProgramInfo *PlaybackBox::FindProgramInUILists(const QString &key)
03467 {
03468     uint chanid;
03469     QDateTime recstartts;
03470     if (ProgramInfo::ExtractKey(key, chanid, recstartts))
03471         return FindProgramInUILists(chanid, recstartts);
03472 
03473     LOG(VB_GENERAL, LOG_ERR, LOC +
03474         QString("FindProgramInUILists(%1) called with invalid key").arg(key));
03475 
03476     return NULL;
03477 }
03478 
03479 ProgramInfo *PlaybackBox::FindProgramInUILists(
03480     uint chanid, const QDateTime &recstartts,
03481     QString recgroup)
03482 {
03483     // LiveTV ProgramInfo's are not in the aggregated list
03484     ProgramList::iterator _it[2] = {
03485         m_progLists[tr("Live TV").toLower()].begin(), m_progLists[""].begin() };
03486     ProgramList::iterator _end[2] = {
03487         m_progLists[tr("Live TV").toLower()].end(),   m_progLists[""].end()   };
03488 
03489     if (recgroup != "LiveTV")
03490     {
03491         swap( _it[0],  _it[1]);
03492         swap(_end[0], _end[1]);
03493     }
03494 
03495     for (uint i = 0; i < 2; i++)
03496     {
03497         ProgramList::iterator it = _it[i], end = _end[i];
03498         for (; it != end; ++it)
03499         {
03500             if ((*it)->GetRecordingStartTime() == recstartts &&
03501                 (*it)->GetChanID() == chanid)
03502             {
03503                 return *it;
03504             }
03505         }
03506     }
03507 
03508     return NULL;
03509 }
03510 
03511 void PlaybackBox::toggleWatched(void)
03512 {
03513     MythUIButtonListItem *item = m_recordingList->GetItemCurrent();
03514 
03515     if (!item)
03516         return;
03517 
03518     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
03519 
03520     if (!pginfo)
03521         return;
03522 
03523     if (pginfo)
03524     {
03525         bool on = !pginfo->IsWatched();
03526         pginfo->SaveWatched(on);
03527         item->DisplayState((on)?"yes":"on", "watched");
03528         updateIcons(pginfo);
03529 
03530         // A refill affects the responsiveness of the UI and we only
03531         // need to rebuild the list if the watch list is displayed
03532         if (m_viewMask & VIEW_WATCHLIST)
03533             UpdateUILists();
03534     }
03535 }
03536 
03537 void PlaybackBox::toggleAutoExpire()
03538 {
03539     MythUIButtonListItem *item = m_recordingList->GetItemCurrent();
03540 
03541     if (!item)
03542         return;
03543 
03544     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
03545 
03546     if (!pginfo)
03547         return;
03548 
03549     if (pginfo)
03550     {
03551         bool on = !pginfo->IsAutoExpirable();
03552         pginfo->SaveAutoExpire(
03553             (on) ? kNormalAutoExpire : kDisableAutoExpire, true);
03554         item->DisplayState((on)?"yes":"no", "autoexpire");
03555         updateIcons(pginfo);
03556     }
03557 }
03558 
03559 void PlaybackBox::togglePreserveEpisode()
03560 {
03561     MythUIButtonListItem *item = m_recordingList->GetItemCurrent();
03562 
03563     if (!item)
03564         return;
03565 
03566     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
03567 
03568     if (!pginfo)
03569         return;
03570 
03571     if (pginfo)
03572     {
03573         bool on = !pginfo->IsPreserved();
03574         pginfo->SavePreserve(on);
03575         item->DisplayState(on?"yes":"no", "preserve");
03576         updateIcons(pginfo);
03577     }
03578 }
03579 
03580 void PlaybackBox::toggleView(ViewMask itemMask, bool setOn)
03581 {
03582     if (setOn)
03583         m_viewMask = (ViewMask)(m_viewMask | itemMask);
03584     else
03585         m_viewMask = (ViewMask)(m_viewMask & ~itemMask);
03586 
03587     UpdateUILists();
03588 }
03589 
03590 void PlaybackBox::togglePlayListTitle(void)
03591 {
03592     QString groupname = m_groupList->GetItemCurrent()->GetData().toString();
03593 
03594     ProgramList::iterator it = m_progLists[groupname].begin();
03595     ProgramList::iterator end = m_progLists[groupname].end();
03596     for (; it != end; ++it)
03597     {
03598         if (*it && ((*it)->GetAvailableStatus() == asAvailable))
03599             togglePlayListItem(*it);
03600     }
03601 }
03602 
03603 void PlaybackBox::togglePlayListItem(void)
03604 {
03605     MythUIButtonListItem *item = m_recordingList->GetItemCurrent();
03606 
03607     if (!item)
03608         return;
03609 
03610     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
03611 
03612     if (!pginfo)
03613         return;
03614 
03615     togglePlayListItem(pginfo);
03616 
03617     if (GetFocusWidget() == m_recordingList)
03618         m_recordingList->MoveDown(MythUIButtonList::MoveItem);
03619 }
03620 
03621 void PlaybackBox::togglePlayListItem(ProgramInfo *pginfo)
03622 {
03623     if (!pginfo)
03624         return;
03625 
03626     QString key = pginfo->MakeUniqueKey();
03627 
03628     MythUIButtonListItem *item =
03629                     m_recordingList->GetItemByData(qVariantFromValue(pginfo));
03630 
03631     if (m_playList.filter(key).size())
03632     {
03633         if (item)
03634             item->DisplayState("no", "playlist");
03635 
03636         QStringList tmpList;
03637         QStringList::Iterator it;
03638 
03639         tmpList = m_playList;
03640         m_playList.clear();
03641 
03642         for (it = tmpList.begin(); it != tmpList.end(); ++it )
03643         {
03644             if (*it != key)
03645                 m_playList << *it;
03646         }
03647     }
03648     else
03649     {
03650         if (item)
03651           item->DisplayState("yes", "playlist");
03652         m_playList << key;
03653     }
03654 }
03655 
03656 void PlaybackBox::processNetworkControlCommands(void)
03657 {
03658     int commands = 0;
03659     QString command;
03660 
03661     m_ncLock.lock();
03662     commands = m_networkControlCommands.size();
03663     m_ncLock.unlock();
03664 
03665     while (commands)
03666     {
03667         m_ncLock.lock();
03668         command = m_networkControlCommands.front();
03669         m_networkControlCommands.pop_front();
03670         m_ncLock.unlock();
03671 
03672         processNetworkControlCommand(command);
03673 
03674         m_ncLock.lock();
03675         commands = m_networkControlCommands.size();
03676         m_ncLock.unlock();
03677     }
03678 }
03679 
03680 void PlaybackBox::processNetworkControlCommand(const QString &command)
03681 {
03682     QStringList tokens = command.simplified().split(" ");
03683 
03684     if (tokens.size() >= 4 && (tokens[1] == "PLAY" || tokens[1] == "RESUME"))
03685     {
03686         if (tokens.size() == 6 && tokens[2] == "PROGRAM")
03687         {
03688             int clientID = tokens[5].toInt();
03689 
03690             LOG(VB_GENERAL, LOG_INFO, LOC +
03691                 QString("NetworkControl: Trying to %1 program '%2' @ '%3'")
03692                     .arg(tokens[1]).arg(tokens[3]).arg(tokens[4]));
03693 
03694             if (m_playingSomething)
03695             {
03696                 LOG(VB_GENERAL, LOG_ERR, LOC +
03697                     "NetworkControl: Already playing");
03698 
03699                 QString msg = QString(
03700                     "NETWORK_CONTROL RESPONSE %1 ERROR: Unable to play, "
03701                     "player is already playing another recording.")
03702                     .arg(clientID);
03703 
03704                 MythEvent me(msg);
03705                 gCoreContext->dispatch(me);
03706                 return;
03707             }
03708 
03709             uint chanid = tokens[3].toUInt();
03710             QDateTime recstartts = myth_dt_from_string(tokens[4]);
03711             ProgramInfo pginfo(chanid, recstartts);
03712 
03713             if (pginfo.GetChanID())
03714             {
03715                 QString msg = QString("NETWORK_CONTROL RESPONSE %1 OK")
03716                                       .arg(clientID);
03717                 MythEvent me(msg);
03718                 gCoreContext->dispatch(me);
03719 
03720                 pginfo.SetPathname(pginfo.GetPlaybackURL());
03721 
03722                 PlayX(pginfo, true, true);
03723             }
03724             else
03725             {
03726                 QString message = QString("NETWORK_CONTROL RESPONSE %1 "
03727                                           "ERROR: Could not find recording for "
03728                                           "chanid %2 @ %3").arg(clientID)
03729                                           .arg(tokens[3]).arg(tokens[4]);
03730                 MythEvent me(message);
03731                 gCoreContext->dispatch(me);
03732             }
03733         }
03734     }
03735 }
03736 
03737 bool PlaybackBox::keyPressEvent(QKeyEvent *event)
03738 {
03739     // This should be an impossible keypress we've simulated
03740     if ((event->key() == Qt::Key_LaunchMedia) &&
03741         (event->modifiers() ==
03742          (Qt::ShiftModifier |
03743           Qt::ControlModifier |
03744           Qt::AltModifier |
03745           Qt::MetaModifier |
03746           Qt::KeypadModifier)))
03747     {
03748         event->accept();
03749         m_ncLock.lock();
03750         int commands = m_networkControlCommands.size();
03751         m_ncLock.unlock();
03752         if (commands)
03753             processNetworkControlCommands();
03754         return true;
03755     }
03756 
03757     if (GetFocusWidget()->keyPressEvent(event))
03758         return true;
03759 
03760     bool handled = false;
03761     QStringList actions;
03762     handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend",
03763                                                      event, actions);
03764 
03765     for (int i = 0; i < actions.size() && !handled; ++i)
03766     {
03767         QString action = actions[i];
03768         handled = true;
03769 
03770         if (action == ACTION_1 || action == "HELP")
03771             showIconHelp();
03772         else if (action == "MENU")
03773         {
03774             ShowMenu();
03775         }
03776         else if (action == "NEXTFAV")
03777         {
03778             if (GetFocusWidget() == m_groupList)
03779                 togglePlayListTitle();
03780             else
03781                 togglePlayListItem();
03782         }
03783         else if (action == "TOGGLEFAV")
03784         {
03785             m_playList.clear();
03786             UpdateUILists();
03787         }
03788         else if (action == ACTION_TOGGLERECORD)
03789         {
03790             m_viewMask = m_viewMaskToggle(m_viewMask, VIEW_TITLES);
03791             UpdateUILists();
03792         }
03793         else if (action == ACTION_PAGERIGHT)
03794         {
03795             QString nextGroup;
03796             m_recGroupsLock.lock();
03797             if (m_recGroupIdx >= 0 && !m_recGroups.empty())
03798             {
03799                 if (++m_recGroupIdx >= m_recGroups.size())
03800                     m_recGroupIdx = 0;
03801                 nextGroup = m_recGroups[m_recGroupIdx];
03802             }
03803             m_recGroupsLock.unlock();
03804 
03805             if (!nextGroup.isEmpty())
03806                 displayRecGroup(nextGroup);
03807         }
03808         else if (action == ACTION_PAGELEFT)
03809         {
03810             QString nextGroup;
03811             m_recGroupsLock.lock();
03812             if (m_recGroupIdx >= 0 && !m_recGroups.empty())
03813             {
03814                 if (--m_recGroupIdx < 0)
03815                     m_recGroupIdx = m_recGroups.size() - 1;
03816                 nextGroup = m_recGroups[m_recGroupIdx];
03817             }
03818             m_recGroupsLock.unlock();
03819 
03820             if (!nextGroup.isEmpty())
03821                 displayRecGroup(nextGroup);
03822         }
03823         else if (action == "CHANGERECGROUP")
03824             showGroupFilter();
03825         else if (action == "CHANGEGROUPVIEW")
03826             showViewChanger();
03827         else if (action == "EDIT")
03828             doEditScheduled();
03829         else if (m_titleList.size() > 1)
03830         {
03831             if (action == "DELETE")
03832                 deleteSelected(m_recordingList->GetItemCurrent());
03833             else if (action == ACTION_PLAYBACK)
03834                 PlayFromBookmark();
03835             else if (action == "DETAILS" || action == "INFO")
03836                 details();
03837             else if (action == "CUSTOMEDIT")
03838                 customEdit();
03839             else if (action == "UPCOMING")
03840                 upcoming();
03841             else if (action == ACTION_VIEWSCHEDULED)
03842                 upcomingScheduled();
03843             else
03844                 handled = false;
03845         }
03846         else
03847             handled = false;
03848     }
03849 
03850     if (!handled && MythScreenType::keyPressEvent(event))
03851         handled = true;
03852 
03853     return handled;
03854 }
03855 
03856 void PlaybackBox::customEvent(QEvent *event)
03857 {
03858     if (event->type() == DialogCompletionEvent::kEventType)
03859     {
03860         DialogCompletionEvent *dce = dynamic_cast<DialogCompletionEvent*>(event);
03861 
03862         if (!dce)
03863             return;
03864 
03865         QString resultid = dce->GetId();
03866 
03867         if (resultid == "transcode" && dce->GetResult() >= 0)
03868             changeProfileAndTranscode(dce->GetData().toInt());
03869     }
03870     else if ((MythEvent::Type)(event->type()) == MythEvent::MythEventMessage)
03871     {
03872         MythEvent *me = (MythEvent *)event;
03873         QString message = me->Message();
03874 
03875         if (message.left(21) == "RECORDING_LIST_CHANGE")
03876         {
03877             QStringList tokens = message.simplified().split(" ");
03878             uint chanid = 0;
03879             QDateTime recstartts;
03880             if (tokens.size() >= 4)
03881             {
03882                 chanid = tokens[2].toUInt();
03883                 recstartts = QDateTime::fromString(tokens[3], Qt::ISODate);
03884             }
03885 
03886             if ((tokens.size() >= 2) && tokens[1] == "UPDATE")
03887             {
03888                 ProgramInfo evinfo(me->ExtraDataList());
03889                 if (evinfo.HasPathname() || evinfo.GetChanID())
03890                     HandleUpdateProgramInfoEvent(evinfo);
03891             }
03892             else if (chanid && recstartts.isValid() && (tokens[1] == "ADD"))
03893             {
03894                 ProgramInfo evinfo(chanid, recstartts);
03895                 if (evinfo.GetChanID())
03896                 {
03897                     evinfo.SetRecordingStatus(rsRecording);
03898                     HandleRecordingAddEvent(evinfo);
03899                 }
03900             }
03901             else if (chanid && recstartts.isValid() && (tokens[1] == "DELETE"))
03902             {
03903                 if (chanid && recstartts.isValid())
03904                     HandleRecordingRemoveEvent(chanid, recstartts);
03905             }
03906             else
03907             {
03908                 m_programInfoCache.ScheduleLoad();
03909             }
03910         }
03911         else if (message.left(15) == "NETWORK_CONTROL")
03912         {
03913             QStringList tokens = message.simplified().split(" ");
03914             if ((tokens[1] != "ANSWER") && (tokens[1] != "RESPONSE"))
03915             {
03916                 m_ncLock.lock();
03917                 m_networkControlCommands.push_back(message);
03918                 m_ncLock.unlock();
03919 
03920                 // This should be an impossible keypress we're simulating
03921                 QKeyEvent *keyevent;
03922                 Qt::KeyboardModifiers modifiers =
03923                     Qt::ShiftModifier |
03924                     Qt::ControlModifier |
03925                     Qt::AltModifier |
03926                     Qt::MetaModifier |
03927                     Qt::KeypadModifier;
03928                 keyevent = new QKeyEvent(QEvent::KeyPress,
03929                                          Qt::Key_LaunchMedia, modifiers);
03930                 QCoreApplication::postEvent((QObject*)(GetMythMainWindow()),
03931                                             keyevent);
03932 
03933                 keyevent = new QKeyEvent(QEvent::KeyRelease,
03934                                          Qt::Key_LaunchMedia, modifiers);
03935                 QCoreApplication::postEvent((QObject*)(GetMythMainWindow()),
03936                                             keyevent);
03937             }
03938         }
03939         else if (message.left(16) == "UPDATE_FILE_SIZE")
03940         {
03941             QStringList tokens = message.simplified().split(" ");
03942             bool ok = false;
03943             uint chanid = 0;
03944             QDateTime recstartts;
03945             uint64_t filesize = 0ULL;
03946             if (tokens.size() >= 4)
03947             {
03948                 chanid     = tokens[1].toUInt();
03949                 recstartts = QDateTime::fromString(tokens[2], Qt::ISODate);
03950                 filesize   = tokens[3].toLongLong(&ok);
03951             }
03952             if (chanid && recstartts.isValid() && ok)
03953             {
03954                 HandleUpdateProgramInfoFileSizeEvent(
03955                     chanid, recstartts, filesize);
03956             }
03957         }
03958         else if (message == "UPDATE_UI_LIST")
03959         {
03960             if (m_playingSomething)
03961                 m_needUpdate = true;
03962             else
03963             {
03964                 UpdateUILists();
03965                 m_helper.ForceFreeSpaceUpdate();
03966             }
03967         }
03968         else if (message == "UPDATE_USAGE_UI")
03969         {
03970             UpdateUsageUI();
03971         }
03972         else if (message == "RECONNECT_SUCCESS")
03973         {
03974             m_programInfoCache.ScheduleLoad();
03975         }
03976         else if (message == "LOCAL_PBB_DELETE_RECORDINGS")
03977         {
03978             QStringList list;
03979             for (uint i = 0; i+3 < (uint)me->ExtraDataList().size(); i+=4)
03980             {
03981                 ProgramInfo *pginfo = m_programInfoCache.GetProgramInfo(
03982                         me->ExtraDataList()[i+0].toUInt(),
03983                         QDateTime::fromString(
03984                             me->ExtraDataList()[i+1], Qt::ISODate));
03985 
03986                 if (!pginfo)
03987                     continue;
03988 
03989                 QString forceDeleteStr = me->ExtraDataList()[i+2];
03990                 QString forgetHistoryStr = me->ExtraDataList()[i+3];
03991 
03992                 list.push_back(QString::number(pginfo->GetChanID()));
03993                 list.push_back(pginfo->GetRecordingStartTime(ISODate));
03994                 list.push_back(forceDeleteStr);
03995                 list.push_back(forgetHistoryStr);
03996                 pginfo->SetAvailableStatus(asPendingDelete,
03997                                            "LOCAL_PBB_DELETE_RECORDINGS");
03998 
03999                 // if the item is in the current recording list UI
04000                 // then delete it.
04001                 MythUIButtonListItem *uiItem =
04002                     m_recordingList->GetItemByData(qVariantFromValue(pginfo));
04003                 if (uiItem)
04004                     m_recordingList->RemoveItem(uiItem);
04005             }
04006             if (!list.empty())
04007                 m_helper.DeleteRecordings(list);
04008         }
04009         else if (message == "DELETE_SUCCESSES")
04010         {
04011             m_helper.ForceFreeSpaceUpdate();
04012         }
04013         else if (message == "DELETE_FAILURES")
04014         {
04015             if (me->ExtraDataList().size() < 4)
04016                 return;
04017 
04018             for (uint i = 0; i+3 < (uint)me->ExtraDataList().size(); i += 4)
04019             {
04020                 ProgramInfo *pginfo = m_programInfoCache.GetProgramInfo(
04021                         me->ExtraDataList()[i+0].toUInt(),
04022                         QDateTime::fromString(
04023                             me->ExtraDataList()[i+1], Qt::ISODate));
04024                 if (pginfo)
04025                 {
04026                     pginfo->SetAvailableStatus(asAvailable, "DELETE_FAILURES");
04027                     m_helper.CheckAvailability(*pginfo, kCheckForCache);
04028                 }
04029             }
04030 
04031             bool forceDelete = me->ExtraDataList()[2].toUInt();
04032             if (!forceDelete)
04033             {
04034                 m_delList = me->ExtraDataList();
04035                 if (!m_menuDialog)
04036                 {
04037                     ShowDeletePopup(kForceDeleteRecording);
04038                     return;
04039                 }
04040                 else
04041                 {
04042                     LOG(VB_GENERAL, LOG_WARNING, LOC +
04043                         "Delete failures not handled due to "
04044                         "pre-existing popup.");
04045                 }
04046             }
04047 
04048             // Since we deleted items from the UI after we set
04049             // asPendingDelete, we need to put them back now..
04050             ScheduleUpdateUIList();
04051         }
04052         else if (message == "PREVIEW_SUCCESS")
04053         {
04054             HandlePreviewEvent(me->ExtraDataList());
04055         }
04056         else if (message == "PREVIEW_FAILED" && me->ExtraDataCount() >= 5)
04057         {
04058             for (uint i = 4; i < (uint) me->ExtraDataCount(); i++)
04059             {
04060                 QString token = me->ExtraData(i);
04061                 QSet<QString>::iterator it = m_preview_tokens.find(token);
04062                 if (it != m_preview_tokens.end())
04063                     m_preview_tokens.erase(it);
04064             }
04065         }
04066         else if (message == "AVAILABILITY" && me->ExtraDataCount() == 8)
04067         {
04068             const uint kMaxUIWaitTime = 10000; // ms
04069             QStringList list = me->ExtraDataList();
04070             QString key = list[0];
04071             CheckAvailabilityType cat =
04072                 (CheckAvailabilityType) list[1].toInt();
04073             AvailableStatusType availableStatus =
04074                 (AvailableStatusType) list[2].toInt();
04075             uint64_t fs = list[3].toULongLong();
04076             QTime tm;
04077             tm.setHMS(list[4].toUInt(), list[5].toUInt(),
04078                       list[6].toUInt(), list[7].toUInt());
04079             QTime now = QTime::currentTime();
04080             int time_elapsed = tm.msecsTo(now);
04081             if (time_elapsed < 0)
04082                 time_elapsed += 24 * 60 * 60 * 1000;
04083 
04084             AvailableStatusType old_avail = availableStatus;
04085             ProgramInfo *pginfo = FindProgramInUILists(key);
04086             if (pginfo)
04087             {
04088                 pginfo->SetFilesize(max(pginfo->GetFilesize(), fs));
04089                 old_avail = pginfo->GetAvailableStatus();
04090                 pginfo->SetAvailableStatus(availableStatus, "AVAILABILITY");
04091             }
04092 
04093             if ((uint)time_elapsed >= kMaxUIWaitTime)
04094                 m_playListPlay.clear();
04095 
04096             bool playnext = ((kCheckForPlaylistAction == cat) &&
04097                              !m_playListPlay.empty());
04098 
04099 
04100             if (((kCheckForPlayAction     == cat) ||
04101                  (kCheckForPlaylistAction == cat)) &&
04102                 ((uint)time_elapsed < kMaxUIWaitTime))
04103             {
04104                 if (asAvailable != availableStatus)
04105                 {
04106                     if (kCheckForPlayAction == cat && pginfo)
04107                         ShowAvailabilityPopup(*pginfo);
04108                 }
04109                 else if (pginfo)
04110                 {
04111                     playnext = false;
04112                     Play(*pginfo, kCheckForPlaylistAction == cat, false, false);
04113                 }
04114             }
04115 
04116             if (playnext)
04117             {
04118                 // failed to play this item, instead
04119                 // play the next item on the list..
04120                 QCoreApplication::postEvent(
04121                     this, new MythEvent("PLAY_PLAYLIST"));
04122             }
04123 
04124             if (old_avail != availableStatus)
04125                 UpdateUIListItem(pginfo, true);
04126         }
04127         else if ((message == "PLAY_PLAYLIST") && !m_playListPlay.empty())
04128         {
04129             QString key = m_playListPlay.front();
04130             m_playListPlay.pop_front();
04131 
04132             if (!m_playListPlay.empty())
04133             {
04134                 const ProgramInfo *pginfo =
04135                     FindProgramInUILists(m_playListPlay.front());
04136                 if (pginfo)
04137                     m_helper.CheckAvailability(*pginfo, kCheckForCache);
04138             }
04139 
04140             ProgramInfo *pginfo = FindProgramInUILists(key);
04141             if (pginfo)
04142                 Play(*pginfo, true, false, false);
04143         }
04144         else if ((message == "SET_PLAYBACK_URL") && (me->ExtraDataCount() == 2))
04145         {
04146             QString piKey = me->ExtraData(0);
04147             ProgramInfo *info = m_programInfoCache.GetProgramInfo(piKey);
04148             if (info)
04149                 info->SetPathname(me->ExtraData(1));
04150         }
04151         else if ((message == "FOUND_ARTWORK") && (me->ExtraDataCount() >= 5))
04152         {
04153             VideoArtworkType type      = (VideoArtworkType) me->ExtraData(2).toInt();
04154             QString          pikey     = me->ExtraData(3);
04155             QString          group     = me->ExtraData(4);
04156             QString          fn        = me->ExtraData(5);
04157 
04158             if (!pikey.isEmpty())
04159             {
04160                 ProgramInfo *pginfo = m_programInfoCache.GetProgramInfo(pikey);
04161                 if (pginfo &&
04162                     m_recordingList->GetItemByData(qVariantFromValue(pginfo)) ==
04163                     m_recordingList->GetItemCurrent() &&
04164                     m_artImage[(uint)type]->GetFilename() != fn)
04165                 {
04166                     m_artImage[(uint)type]->SetFilename(fn);
04167                     m_artTimer[(uint)type]->start(s_artDelay[(uint)type]);
04168                 }
04169             }
04170             else if (!group.isEmpty() &&
04171                      (m_currentGroup == group) &&
04172                      m_artImage[type] &&
04173                      m_groupList->GetItemCurrent() &&
04174                      m_artImage[(uint)type]->GetFilename() != fn)
04175             {
04176                 m_artImage[(uint)type]->SetFilename(fn);
04177                 m_artTimer[(uint)type]->start(s_artDelay[(uint)type]);
04178             }
04179         }
04180         else if (message == "EXIT_TO_MENU" ||
04181                  message == "CANCEL_PLAYLIST")
04182         {
04183             m_playListPlay.clear();
04184         }
04185     }
04186     else
04187         ScheduleCommon::customEvent(event);
04188 }
04189 
04190 void PlaybackBox::HandleRecordingRemoveEvent(
04191     uint chanid, const QDateTime &recstartts)
04192 {
04193     if (!m_programInfoCache.Remove(chanid, recstartts))
04194     {
04195         LOG(VB_GENERAL, LOG_WARNING, LOC +
04196             QString("Failed to remove %1:%2, reloading list")
04197                 .arg(chanid).arg(recstartts.toString(Qt::ISODate)));
04198         m_programInfoCache.ScheduleLoad();
04199         return;
04200     }
04201 
04202     MythUIButtonListItem *sel_item = m_groupList->GetItemCurrent();
04203     QString groupname;
04204     if (sel_item)
04205         groupname = sel_item->GetData().toString();
04206 
04207     ProgramMap::iterator git = m_progLists.begin();
04208     while (git != m_progLists.end())
04209     {
04210         ProgramList::iterator pit = (*git).begin();
04211         while (pit != (*git).end())
04212         {
04213             if ((*pit)->GetChanID()             == chanid &&
04214                 (*pit)->GetRecordingStartTime() == recstartts)
04215             {
04216                 if (!git.key().isEmpty() && git.key() == groupname)
04217                 {
04218                     MythUIButtonListItem *item_by_data =
04219                         m_recordingList->GetItemByData(
04220                             qVariantFromValue(*pit));
04221                     MythUIButtonListItem *item_cur =
04222                         m_recordingList->GetItemCurrent();
04223 
04224                     if (item_cur && (item_by_data == item_cur))
04225                     {
04226                         MythUIButtonListItem *item_next =
04227                             m_recordingList->GetItemNext(item_cur);
04228                         if (item_next)
04229                             m_recordingList->SetItemCurrent(item_next);
04230                     }
04231 
04232                     m_recordingList->RemoveItem(item_by_data);
04233                 }
04234                 pit = (*git).erase(pit);
04235             }
04236             else
04237             {
04238                 ++pit;
04239             }
04240         }
04241 
04242         if ((*git).empty())
04243         {
04244             if (!groupname.isEmpty() && (git.key() == groupname))
04245             {
04246                 MythUIButtonListItem *next_item =
04247                     m_groupList->GetItemNext(sel_item);
04248                 if (next_item)
04249                     m_groupList->SetItemCurrent(next_item);
04250 
04251                 m_groupList->RemoveItem(sel_item);
04252 
04253                 sel_item = next_item;
04254                 groupname = "";
04255                 if (sel_item)
04256                     groupname = sel_item->GetData().toString();
04257             }
04258             git = m_progLists.erase(git);
04259         }
04260         else
04261         {
04262             ++git;
04263         }
04264     }
04265 
04266     m_helper.ForceFreeSpaceUpdate();
04267 }
04268 
04269 void PlaybackBox::HandleRecordingAddEvent(const ProgramInfo &evinfo)
04270 {
04271     m_programInfoCache.Add(evinfo);
04272     ScheduleUpdateUIList();
04273 }
04274 
04275 void PlaybackBox::HandleUpdateProgramInfoEvent(const ProgramInfo &evinfo)
04276 {
04277     QString old_recgroup = m_programInfoCache.GetRecGroup(
04278         evinfo.GetChanID(), evinfo.GetRecordingStartTime());
04279 
04280     if (!m_programInfoCache.Update(evinfo))
04281         return;
04282 
04283     // If the recording group has changed, reload lists from the recently
04284     // updated cache; if not, only update UI for the updated item
04285     if (evinfo.GetRecordingGroup() == old_recgroup)
04286     {
04287         ProgramInfo *dst = FindProgramInUILists(evinfo);
04288         if (dst)
04289             UpdateUIListItem(dst, true);
04290         return;
04291     }
04292 
04293     ScheduleUpdateUIList();
04294 }
04295 
04296 void PlaybackBox::HandleUpdateProgramInfoFileSizeEvent(
04297     uint chanid, const QDateTime &recstartts, uint64_t filesize)
04298 {
04299     m_programInfoCache.UpdateFileSize(chanid, recstartts, filesize);
04300 
04301     ProgramInfo *dst = FindProgramInUILists(chanid, recstartts);
04302     if (dst)
04303         UpdateUIListItem(dst, false);
04304 }
04305 
04306 void PlaybackBox::ScheduleUpdateUIList(void)
04307 {
04308     if (!m_programInfoCache.IsLoadInProgress())
04309         QCoreApplication::postEvent(this, new MythEvent("UPDATE_UI_LIST"));
04310 }
04311 
04312 void PlaybackBox::showIconHelp(void)
04313 {
04314     HelpPopup *helpPopup = new HelpPopup(m_popupStack);
04315 
04316     if (helpPopup->Create())
04317         m_popupStack->AddScreen(helpPopup);
04318     else
04319         delete helpPopup;
04320 }
04321 
04322 void PlaybackBox::showViewChanger(void)
04323 {
04324     ChangeView *viewPopup = new ChangeView(m_popupStack, this, m_viewMask);
04325 
04326     if (viewPopup->Create())
04327     {
04328         connect(viewPopup, SIGNAL(save()), SLOT(saveViewChanges()));
04329         m_popupStack->AddScreen(viewPopup);
04330     }
04331     else
04332         delete viewPopup;
04333 }
04334 
04335 void PlaybackBox::saveViewChanges()
04336 {
04337     if (m_viewMask == VIEW_NONE)
04338         m_viewMask = VIEW_TITLES;
04339     gCoreContext->SaveSetting("DisplayGroupDefaultViewMask", (int)m_viewMask);
04340     gCoreContext->SaveSetting("PlaybackWatchList",
04341                                             (bool)(m_viewMask & VIEW_WATCHLIST));
04342 }
04343 
04344 void PlaybackBox::showGroupFilter(void)
04345 {
04346     QString dispGroup = ProgramInfo::i18n(m_recGroup);
04347 
04348     QStringList groupNames;
04349     QStringList displayNames;
04350     QStringList groups;
04351     QStringList displayGroups;
04352 
04353     MSqlQuery query(MSqlQuery::InitCon());
04354 
04355     m_recGroupType.clear();
04356 
04357     uint items = 0;
04358     uint totalItems = 0;
04359 
04360     // Add the group entries
04361     displayNames.append(QString("------- %1 -------").arg(tr("Groups")));
04362     groupNames.append("");
04363 
04364     // Find each recording group, and the number of recordings in each
04365     query.prepare("SELECT recgroup, COUNT(title) FROM recorded "
04366                   "WHERE deletepending = 0 AND watched <= :WATCHED "
04367                   "GROUP BY recgroup");
04368     query.bindValue(":WATCHED", (m_viewMask & VIEW_WATCHED));
04369     if (query.exec())
04370     {
04371         while (query.next())
04372         {
04373             dispGroup = query.value(0).toString();
04374             items     = query.value(1).toInt();
04375 
04376             if ((dispGroup != "LiveTV" || (m_viewMask & VIEW_LIVETVGRP)) &&
04377                 (dispGroup != "Deleted"))
04378                 totalItems += items;
04379 
04380             groupNames.append(dispGroup);
04381 
04382             dispGroup = (dispGroup == "Default") ? tr("Default") : dispGroup;
04383             dispGroup = (dispGroup == "Deleted") ? tr("Deleted") : dispGroup;
04384             dispGroup = (dispGroup == "LiveTV")  ? tr("Live TV")  : dispGroup;
04385 
04386             displayNames.append(tr("%1 [%n item(s)]", 0, items).arg(dispGroup));
04387 
04388             m_recGroupType[query.value(0).toString()] = "recgroup";
04389         }
04390     }
04391 
04392     // Create and add the "All Programs" entry
04393     displayNames.push_front(tr("%1 [%n item(s)]", 0, totalItems)
04394                             .arg(ProgramInfo::i18n("All Programs")));
04395     groupNames.push_front("All Programs");
04396     m_recGroupType["All Programs"] = "recgroup";
04397 
04398     // Find each category, and the number of recordings in each
04399     query.prepare("SELECT DISTINCT category, COUNT(title) FROM recorded "
04400                   "WHERE deletepending = 0 AND watched <= :WATCHED "
04401                   "GROUP BY category");
04402     query.bindValue(":WATCHED", (m_viewMask & VIEW_WATCHED));
04403     if (query.exec())
04404     {
04405         int unknownCount = 0;
04406         while (query.next())
04407         {
04408             items     = query.value(1).toInt();
04409             dispGroup = query.value(0).toString();
04410             if (dispGroup.isEmpty())
04411             {
04412                 unknownCount += items;
04413                 dispGroup = tr("Unknown");
04414             }
04415             else if (dispGroup == tr("Unknown"))
04416                 unknownCount += items;
04417 
04418             if ((!m_recGroupType.contains(dispGroup)) &&
04419                 (dispGroup != tr("Unknown")))
04420             {
04421                 displayGroups += tr("%1 [%n item(s)]", 0, items).arg(dispGroup);
04422                 groups += dispGroup;
04423 
04424                 m_recGroupType[dispGroup] = "category";
04425             }
04426         }
04427 
04428         if (unknownCount > 0)
04429         {
04430             dispGroup = tr("Unknown");
04431             items     = unknownCount;
04432             displayGroups += tr("%1 [%n item(s)]", 0, items).arg(dispGroup);
04433             groups += dispGroup;
04434 
04435             m_recGroupType[dispGroup] = "category";
04436         }
04437     }
04438 
04439     // Add the category entries
04440     displayNames.append(QString("------- %1 -------").arg(tr("Categories")));
04441     groupNames.append("");
04442     groups.sort();
04443     displayGroups.sort();
04444     QStringList::iterator it;
04445     for (it = displayGroups.begin(); it != displayGroups.end(); ++it)
04446         displayNames.append(*it);
04447     for (it = groups.begin(); it != groups.end(); ++it)
04448         groupNames.append(*it);
04449 
04450     QString label = tr("Change Filter");
04451 
04452     GroupSelector *recGroupPopup = new GroupSelector(m_popupStack, label,
04453                                                      displayNames, groupNames,
04454                                                      m_recGroup);
04455 
04456     if (recGroupPopup->Create())
04457     {
04458         connect(recGroupPopup, SIGNAL(result(QString)),
04459                 SLOT(displayRecGroup(QString)));
04460         connect(recGroupPopup, SIGNAL(Exiting()),
04461                 SLOT(groupSelectorClosed()));
04462         m_popupStack->AddScreen(recGroupPopup);
04463     }
04464     else
04465         delete recGroupPopup;
04466 }
04467 
04468 void PlaybackBox::groupSelectorClosed(void)
04469 {
04470     if ((gCoreContext->GetNumSetting("QueryInitialFilter", 0) == 1) &&
04471         ((m_titleList.size() <= 1)))
04472         Close();
04473 }
04474 
04475 void PlaybackBox::setGroupFilter(const QString &recGroup)
04476 {
04477     QString newRecGroup = recGroup;
04478 
04479     if (newRecGroup.isEmpty())
04480         return;
04481 
04482     if (newRecGroup == ProgramInfo::i18n("Default"))
04483         newRecGroup = "Default";
04484     else if (newRecGroup == ProgramInfo::i18n("All Programs"))
04485         newRecGroup = "All Programs";
04486     else if (newRecGroup == ProgramInfo::i18n("LiveTV"))
04487         newRecGroup = "LiveTV";
04488     else if (newRecGroup == ProgramInfo::i18n("Deleted"))
04489         newRecGroup = "Deleted";
04490 
04491     m_curGroupPassword = m_recGroupPwCache[recGroup];
04492 
04493     m_recGroup = newRecGroup;
04494 
04495     if (m_groupnameAsAllProg)
04496         m_groupDisplayName = ProgramInfo::i18n(m_recGroup);
04497 
04498     // Since the group filter is changing, the current position in the lists
04499     // is meaningless -- so reset the lists so the position won't be saved.
04500     m_recordingList->Reset();
04501     m_groupList->Reset();
04502 
04503     UpdateUILists();
04504 
04505     if (gCoreContext->GetNumSetting("RememberRecGroup",1))
04506         gCoreContext->SaveSetting("DisplayRecGroup", m_recGroup);
04507 
04508     if (m_recGroupType[m_recGroup] == "recgroup")
04509         gCoreContext->SaveSetting("DisplayRecGroupIsCategory", 0);
04510     else
04511         gCoreContext->SaveSetting("DisplayRecGroupIsCategory", 1);
04512 }
04513 
04514 QString PlaybackBox::getRecGroupPassword(const QString &group)
04515 {
04516     return m_recGroupPwCache[group];
04517 }
04518 
04519 void PlaybackBox::fillRecGroupPasswordCache(void)
04520 {
04521     m_recGroupPwCache.clear();
04522 
04523     MSqlQuery query(MSqlQuery::InitCon());
04524     query.prepare("SELECT recgroup, password FROM recgrouppassword "
04525                   "WHERE password IS NOT NULL AND password <> '';");
04526 
04527     if (query.exec())
04528     {
04529         while (query.next())
04530         {
04531             QString recgroup = query.value(0).toString();
04532 
04533             if (recgroup == ProgramInfo::i18n("Default"))
04534                 recgroup = "Default";
04535             else if (recgroup == ProgramInfo::i18n("All Programs"))
04536                 recgroup = "All Programs";
04537             else if (recgroup == ProgramInfo::i18n("LiveTV"))
04538                 recgroup = "LiveTV";
04539             else if (m_recGroup == ProgramInfo::i18n("Deleted"))
04540                 recgroup = "Deleted";
04541 
04542             m_recGroupPwCache[recgroup] = query.value(1).toString();
04543         }
04544     }
04545 }
04546 
04548 void PlaybackBox::ShowRecGroupChanger(bool use_playlist)
04549 {
04550     m_op_on_playlist = use_playlist;
04551 
04552     ProgramInfo *pginfo = NULL;
04553     if (use_playlist)
04554     {
04555         if (!m_playList.empty())
04556             pginfo = FindProgramInUILists(m_playList[0]);
04557     }
04558     else
04559         pginfo = CurrentItem();
04560 
04561     if (!pginfo)
04562         return;
04563 
04564     MSqlQuery query(MSqlQuery::InitCon());
04565     query.prepare(
04566         "SELECT recgroup, COUNT(title) FROM recorded "
04567         "WHERE deletepending = 0 GROUP BY recgroup ORDER BY recgroup");
04568 
04569     QStringList displayNames(tr("Add New"));
04570     QStringList groupNames("addnewgroup");
04571 
04572     if (!query.exec())
04573         return;
04574 
04575     while (query.next())
04576     {
04577         QString dispGroup = query.value(0).toString();
04578         groupNames.push_back(dispGroup);
04579 
04580         if (dispGroup == "Default")
04581             dispGroup = tr("Default");
04582         else if (dispGroup == "LiveTV")
04583             dispGroup = tr("Live TV");
04584         else if (dispGroup == "Deleted")
04585             dispGroup = tr("Deleted");
04586 
04587         displayNames.push_back(tr("%1 [%n item(s)]", "", query.value(1).toInt())
04588                                .arg(dispGroup));
04589     }
04590 
04591     QString label = tr("Select Recording Group") +
04592         CreateProgramInfoString(*pginfo);
04593 
04594     GroupSelector *rgChanger = new GroupSelector(
04595         m_popupStack, label, displayNames, groupNames,
04596         pginfo->GetRecordingGroup());
04597 
04598     if (rgChanger->Create())
04599     {
04600         connect(rgChanger, SIGNAL(result(QString)), SLOT(setRecGroup(QString)));
04601         m_popupStack->AddScreen(rgChanger);
04602     }
04603     else
04604         delete rgChanger;
04605 }
04606 
04608 void PlaybackBox::ShowPlayGroupChanger(bool use_playlist)
04609 {
04610     m_op_on_playlist = use_playlist;
04611 
04612     ProgramInfo *pginfo = NULL;
04613     if (use_playlist)
04614     {
04615         if (!m_playList.empty())
04616             pginfo = FindProgramInUILists(m_playList[0]);
04617     }
04618     else
04619         pginfo = CurrentItem();
04620 
04621     if (!pginfo)
04622         return;
04623 
04624     QStringList groupNames(tr("Default"));
04625     QStringList displayNames("Default");
04626 
04627     QStringList list = PlayGroup::GetNames();
04628     QStringList::const_iterator it = list.begin();
04629     for (; it != list.end(); ++it)
04630     {
04631         displayNames.push_back(*it);
04632         groupNames.push_back(*it);
04633     }
04634 
04635     QString label = tr("Select Playback Group") +
04636         CreateProgramInfoString(*pginfo);
04637 
04638     GroupSelector *pgChanger = new GroupSelector(
04639         m_popupStack, label,displayNames, groupNames,
04640         pginfo->GetPlaybackGroup());
04641 
04642     if (pgChanger->Create())
04643     {
04644         connect(pgChanger, SIGNAL(result(QString)),
04645                 SLOT(setPlayGroup(QString)));
04646         m_popupStack->AddScreen(pgChanger);
04647     }
04648     else
04649         delete pgChanger;
04650 }
04651 
04652 void PlaybackBox::doPlaylistExpireSetting(bool turnOn)
04653 {
04654     ProgramInfo *tmpItem;
04655     QStringList::Iterator it;
04656 
04657     for (it = m_playList.begin(); it != m_playList.end(); ++it)
04658     {
04659         if ((tmpItem = FindProgramInUILists(*it)))
04660         {
04661             if (!tmpItem->IsAutoExpirable() && turnOn)
04662                 tmpItem->SaveAutoExpire(kNormalAutoExpire, true);
04663             else if (tmpItem->IsAutoExpirable() && !turnOn)
04664                 tmpItem->SaveAutoExpire(kDisableAutoExpire, true);
04665         }
04666     }
04667 }
04668 
04669 void PlaybackBox::doPlaylistWatchedSetting(bool turnOn)
04670 {
04671     ProgramInfo *tmpItem;
04672     QStringList::Iterator it;
04673 
04674     for (it = m_playList.begin(); it != m_playList.end(); ++it)
04675     {
04676         if ((tmpItem = FindProgramInUILists(*it)))
04677         {
04678             tmpItem->SaveWatched(turnOn);
04679         }
04680     }
04681 
04682     doClearPlaylist();
04683     UpdateUILists();
04684 }
04685 
04686 void PlaybackBox::showMetadataEditor()
04687 {
04688     ProgramInfo *pgInfo = CurrentItem();
04689 
04690     MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
04691 
04692     RecMetadataEdit *editMetadata = new RecMetadataEdit(mainStack, pgInfo);
04693 
04694     if (editMetadata->Create())
04695     {
04696         connect(editMetadata, SIGNAL(result(const QString &, const QString &,
04697                 const QString &, const QString &, uint, uint)), SLOT(
04698                 saveRecMetadata(const QString &, const QString &,
04699                 const QString &, const QString &, uint, uint)));
04700         mainStack->AddScreen(editMetadata);
04701     }
04702     else
04703         delete editMetadata;
04704 }
04705 
04706 void PlaybackBox::saveRecMetadata(const QString &newTitle,
04707                                   const QString &newSubtitle,
04708                                   const QString &newDescription,
04709                                   const QString &newInetref,
04710                                   uint newSeason,
04711                                   uint newEpisode)
04712 {
04713     MythUIButtonListItem *item = m_recordingList->GetItemCurrent();
04714 
04715     if (!item)
04716         return;
04717 
04718     ProgramInfo *pginfo = qVariantValue<ProgramInfo *>(item->GetData());
04719 
04720     if (!pginfo)
04721         return;
04722 
04723     QString groupname = m_groupList->GetItemCurrent()->GetData().toString();
04724 
04725     if (groupname == pginfo->GetTitle().toLower() &&
04726         newTitle != pginfo->GetTitle())
04727     {
04728         m_recordingList->RemoveItem(item);
04729     }
04730     else
04731     {
04732         QString tempSubTitle = newTitle;
04733         if (!newSubtitle.trimmed().isEmpty())
04734             tempSubTitle = QString("%1 - \"%2\"")
04735                             .arg(tempSubTitle).arg(newSubtitle);
04736 
04737         QString seasone;
04738         QString seasonx;
04739         QString season;
04740         QString episode;
04741         if (newSeason > 0 || newEpisode > 0)
04742         {
04743             season = GetDisplaySeasonEpisode(newSeason, 1);
04744             episode = GetDisplaySeasonEpisode(newEpisode, 1);
04745             seasone = QString("s%1e%2").arg(GetDisplaySeasonEpisode
04746                                                 (newSeason, 2))
04747                             .arg(GetDisplaySeasonEpisode(newEpisode, 2));
04748             seasonx = QString("%1x%2").arg(GetDisplaySeasonEpisode
04749                                                 (newSeason, 1))
04750                             .arg(GetDisplaySeasonEpisode(newEpisode, 2));
04751         }
04752 
04753         item->SetText(tempSubTitle, "titlesubtitle");
04754         item->SetText(newTitle, "title");
04755         item->SetText(newSubtitle, "subtitle");
04756         item->SetText(newInetref, "inetref");
04757         item->SetText(seasonx, "00x00");
04758         item->SetText(seasone, "s00e00");
04759         item->SetText(season, "season");
04760         item->SetText(episode, "episode");
04761         if (newDescription != NULL)
04762             item->SetText(newDescription, "description");
04763     }
04764 
04765     pginfo->SaveInetRef(newInetref);
04766     pginfo->SaveSeasonEpisode(newSeason, newEpisode);
04767 
04768     RecordingInfo ri(*pginfo);
04769     ri.ApplyRecordRecTitleChange(newTitle, newSubtitle, newDescription);
04770     *pginfo = ri;
04771 }
04772 
04773 void PlaybackBox::setRecGroup(QString newRecGroup)
04774 {
04775     newRecGroup = newRecGroup.simplified();
04776 
04777     if (newRecGroup.isEmpty())
04778         return;
04779 
04780     if (newRecGroup == "addnewgroup")
04781     {
04782         MythScreenStack *popupStack =
04783             GetMythMainWindow()->GetStack("popup stack");
04784 
04785         MythTextInputDialog *newgroup = new MythTextInputDialog(
04786             popupStack, tr("New Recording Group"));
04787 
04788         connect(newgroup, SIGNAL(haveResult(QString)),
04789                 SLOT(setRecGroup(QString)));
04790 
04791         if (newgroup->Create())
04792             popupStack->AddScreen(newgroup, false);
04793         else
04794             delete newgroup;
04795         return;
04796     }
04797 
04798     RecordingRule record;
04799     record.LoadTemplate("Default");
04800     uint defaultAutoExpire = record.m_autoExpire;
04801 
04802     if (m_op_on_playlist)
04803     {
04804         QStringList::const_iterator it;
04805         for (it = m_playList.begin(); it != m_playList.end(); ++it )
04806         {
04807             ProgramInfo *p = FindProgramInUILists(*it);
04808             if (!p)
04809                 continue;
04810 
04811             if ((p->GetRecordingGroup() == "LiveTV") &&
04812                 (newRecGroup != "LiveTV"))
04813             {
04814                 p->SaveAutoExpire((AutoExpireType)defaultAutoExpire);
04815             }
04816             else if ((p->GetRecordingGroup() != "LiveTV") &&
04817                      (newRecGroup == "LiveTV"))
04818             {
04819                 p->SaveAutoExpire(kLiveTVAutoExpire);
04820             }
04821 
04822             RecordingInfo ri(*p);
04823             ri.ApplyRecordRecGroupChange(newRecGroup);
04824             *p = ri;
04825         }
04826         doClearPlaylist();
04827         UpdateUILists();
04828         return;
04829     }
04830 
04831     ProgramInfo *p = CurrentItem();
04832     if (!p)
04833         return;
04834 
04835     if ((p->GetRecordingGroup() == "LiveTV") && (newRecGroup != "LiveTV"))
04836         p->SaveAutoExpire((AutoExpireType)defaultAutoExpire);
04837     else if ((p->GetRecordingGroup() != "LiveTV") && (newRecGroup == "LiveTV"))
04838         p->SaveAutoExpire(kLiveTVAutoExpire);
04839 
04840     RecordingInfo ri(*p);
04841     ri.ApplyRecordRecGroupChange(newRecGroup);
04842     *p = ri;
04843     UpdateUILists();
04844 }
04845 
04846 void PlaybackBox::setPlayGroup(QString newPlayGroup)
04847 {
04848     ProgramInfo *tmpItem = CurrentItem();
04849 
04850     if (newPlayGroup.isEmpty() || !tmpItem)
04851         return;
04852 
04853     if (newPlayGroup == tr("Default"))
04854         newPlayGroup = "Default";
04855 
04856     if (m_op_on_playlist)
04857     {
04858         QStringList::Iterator it;
04859 
04860         for (it = m_playList.begin(); it != m_playList.end(); ++it )
04861         {
04862             tmpItem = FindProgramInUILists(*it);
04863             if (tmpItem)
04864             {
04865                 RecordingInfo ri(*tmpItem);
04866                 ri.ApplyRecordPlayGroupChange(newPlayGroup);
04867                 *tmpItem = ri;
04868             }
04869         }
04870         doClearPlaylist();
04871     }
04872     else if (tmpItem)
04873     {
04874         RecordingInfo ri(*tmpItem);
04875         ri.ApplyRecordPlayGroupChange(newPlayGroup);
04876         *tmpItem = ri;
04877     }
04878 }
04879 
04880 void PlaybackBox::showRecGroupPasswordChanger(void)
04881 {
04882     MythUIButtonListItem *item = m_groupList->GetItemCurrent();
04883 
04884     if (!item)
04885         return;
04886 
04887     QString recgroup = item->GetData().toString();
04888     QString currentPassword = getRecGroupPassword(m_recGroup);
04889 
04890     PasswordChange *pwChanger = new PasswordChange(m_popupStack,
04891                                                    currentPassword);
04892 
04893     if (pwChanger->Create())
04894     {
04895         connect(pwChanger, SIGNAL(result(const QString &)),
04896                 SLOT(SetRecGroupPassword(const QString &)));
04897         m_popupStack->AddScreen(pwChanger);
04898     }
04899     else
04900         delete pwChanger;
04901 }
04902 
04903 void PlaybackBox::SetRecGroupPassword(const QString &newPassword)
04904 {
04905     MSqlQuery query(MSqlQuery::InitCon());
04906 
04907     query.prepare("DELETE FROM recgrouppassword "
04908                         "WHERE recgroup = :RECGROUP ;");
04909     query.bindValue(":RECGROUP", m_recGroup);
04910 
04911     if (!query.exec())
04912         MythDB::DBError("PlaybackBox::SetRecGroupPassword -- delete",
04913                         query);
04914 
04915     if (!newPassword.isEmpty())
04916     {
04917         query.prepare("INSERT INTO recgrouppassword "
04918                         "(recgroup, password) VALUES "
04919                         "( :RECGROUP , :PASSWD )");
04920         query.bindValue(":RECGROUP", m_recGroup);
04921         query.bindValue(":PASSWD", newPassword);
04922 
04923         if (!query.exec())
04924             MythDB::DBError("PlaybackBox::SetRecGroupPassword -- insert",
04925                             query);
04926     }
04927 
04928     m_recGroupPwCache[m_recGroup] = newPassword;
04929 }
04930 
04932 
04933 GroupSelector::GroupSelector(MythScreenStack *lparent, const QString &label,
04934                              const QStringList &list, const QStringList &data,
04935                              const QString &selected)
04936             : MythScreenType(lparent, "groupselector"), m_label(label),
04937               m_List(list), m_Data(data), m_selected(selected)
04938 {
04939 }
04940 
04941 bool GroupSelector::Create()
04942 {
04943     if (!LoadWindowFromXML("recordings-ui.xml", "groupselector", this))
04944         return false;
04945 
04946     MythUIText *labelText = dynamic_cast<MythUIText*> (GetChild("label"));
04947     MythUIButtonList *groupList = dynamic_cast<MythUIButtonList*>
04948                                         (GetChild("groups"));
04949 
04950     if (!groupList)
04951     {
04952         LOG(VB_GENERAL, LOG_ERR, LOC +
04953             "Theme is missing 'groups' button list.");
04954         return false;
04955     }
04956 
04957     if (labelText)
04958         labelText->SetText(m_label);
04959 
04960     for (int i = 0; i < m_List.size(); ++i)
04961     {
04962         new MythUIButtonListItem(groupList, m_List.at(i),
04963                                  qVariantFromValue(m_Data.at(i)));
04964     }
04965 
04966     // Set the current position in the list
04967     groupList->SetValueByData(qVariantFromValue(m_selected));
04968 
04969     BuildFocusList();
04970 
04971     connect(groupList, SIGNAL(itemClicked(MythUIButtonListItem *)),
04972             SLOT(AcceptItem(MythUIButtonListItem *)));
04973 
04974     return true;
04975 }
04976 
04977 void GroupSelector::AcceptItem(MythUIButtonListItem *item)
04978 {
04979     if (!item)
04980         return;
04981 
04982     // ignore the dividers
04983     if (item->GetData().toString().isEmpty())
04984         return;
04985 
04986     QString group = item->GetData().toString();
04987     emit result(group);
04988     Close();
04989 }
04990 
04992 
04993 ChangeView::ChangeView(MythScreenStack *lparent, MythScreenType *parentscreen,
04994                        int viewMask)
04995                 : MythScreenType(lparent, "changeview"),
04996                    m_parentScreen(parentscreen), m_viewMask(viewMask)
04997 {
04998 }
04999 
05000 bool ChangeView::Create()
05001 {
05002     if (!LoadWindowFromXML("recordings-ui.xml", "changeview", this))
05003         return false;
05004 
05005     MythUICheckBox *checkBox;
05006 
05007     checkBox = dynamic_cast<MythUICheckBox*>(GetChild("titles"));
05008     if (checkBox)
05009     {
05010         if (m_viewMask & PlaybackBox::VIEW_TITLES)
05011             checkBox->SetCheckState(MythUIStateType::Full);
05012         connect(checkBox, SIGNAL(toggled(bool)),
05013                 m_parentScreen, SLOT(toggleTitleView(bool)));
05014     }
05015 
05016     checkBox = dynamic_cast<MythUICheckBox*>(GetChild("categories"));
05017     if (checkBox)
05018     {
05019         if (m_viewMask & PlaybackBox::VIEW_CATEGORIES)
05020             checkBox->SetCheckState(MythUIStateType::Full);
05021         connect(checkBox, SIGNAL(toggled(bool)),
05022                 m_parentScreen, SLOT(toggleCategoryView(bool)));
05023     }
05024 
05025     checkBox = dynamic_cast<MythUICheckBox*>(GetChild("recgroups"));
05026     if (checkBox)
05027     {
05028         if (m_viewMask & PlaybackBox::VIEW_RECGROUPS)
05029             checkBox->SetCheckState(MythUIStateType::Full);
05030         connect(checkBox, SIGNAL(toggled(bool)),
05031                 m_parentScreen, SLOT(toggleRecGroupView(bool)));
05032     }
05033 
05034     // TODO Do we need two separate settings to determine whether the watchlist
05035     //      is shown? The filter setting be enough?
05036         checkBox = dynamic_cast<MythUICheckBox*>(GetChild("watchlist"));
05037         if (checkBox)
05038         {
05039             if (m_viewMask & PlaybackBox::VIEW_WATCHLIST)
05040                 checkBox->SetCheckState(MythUIStateType::Full);
05041             connect(checkBox, SIGNAL(toggled(bool)),
05042                     m_parentScreen, SLOT(toggleWatchListView(bool)));
05043         }
05044     //
05045 
05046     checkBox = dynamic_cast<MythUICheckBox*>(GetChild("searches"));
05047     if (checkBox)
05048     {
05049         if (m_viewMask & PlaybackBox::VIEW_SEARCHES)
05050             checkBox->SetCheckState(MythUIStateType::Full);
05051         connect(checkBox, SIGNAL(toggled(bool)),
05052                 m_parentScreen, SLOT(toggleSearchView(bool)));
05053     }
05054 
05055     // TODO Do we need two separate settings to determine whether livetv
05056     //      recordings are shown? Same issue as the watchlist above
05057         checkBox = dynamic_cast<MythUICheckBox*>(GetChild("livetv"));
05058         if (checkBox)
05059         {
05060             if (m_viewMask & PlaybackBox::VIEW_LIVETVGRP)
05061                 checkBox->SetCheckState(MythUIStateType::Full);
05062             connect(checkBox, SIGNAL(toggled(bool)),
05063                     m_parentScreen, SLOT(toggleLiveTVView(bool)));
05064         }
05065     //
05066 
05067     checkBox = dynamic_cast<MythUICheckBox*>(GetChild("watched"));
05068     if (checkBox)
05069     {
05070         if (m_viewMask & PlaybackBox::VIEW_WATCHED)
05071             checkBox->SetCheckState(MythUIStateType::Full);
05072         connect(checkBox, SIGNAL(toggled(bool)),
05073                 m_parentScreen, SLOT(toggleWatchedView(bool)));
05074     }
05075 
05076     MythUIButton *savebutton = dynamic_cast<MythUIButton*>(GetChild("save"));
05077     connect(savebutton, SIGNAL(Clicked()), SLOT(SaveChanges()));
05078 
05079     BuildFocusList();
05080 
05081     return true;
05082 }
05083 
05084 void ChangeView::SaveChanges()
05085 {
05086     emit save();
05087     Close();
05088 }
05089 
05091 
05092 PasswordChange::PasswordChange(MythScreenStack *lparent, QString oldpassword)
05093                 : MythScreenType(lparent, "passwordchanger"),
05094                     m_oldPassword(oldpassword)
05095 {
05096     m_oldPasswordEdit = m_newPasswordEdit = NULL;
05097     m_okButton = NULL;
05098 }
05099 
05100 bool PasswordChange::Create()
05101 {
05102     if (!LoadWindowFromXML("recordings-ui.xml", "passwordchanger", this))
05103         return false;
05104 
05105     m_oldPasswordEdit = dynamic_cast<MythUITextEdit *>(GetChild("oldpassword"));
05106     m_newPasswordEdit = dynamic_cast<MythUITextEdit *>(GetChild("newpassword"));
05107     m_okButton = dynamic_cast<MythUIButton *>(GetChild("ok"));
05108 
05109     if (!m_oldPasswordEdit || !m_newPasswordEdit || !m_okButton)
05110     {
05111         LOG(VB_GENERAL, LOG_ERR, LOC +
05112             "Window 'passwordchanger' is missing required elements.");
05113         return false;
05114     }
05115 
05116     m_oldPasswordEdit->SetPassword(true);
05117     m_oldPasswordEdit->SetMaxLength(10);
05118     m_newPasswordEdit->SetPassword(true);
05119     m_newPasswordEdit->SetMaxLength(10);
05120 
05121     BuildFocusList();
05122 
05123     connect(m_oldPasswordEdit, SIGNAL(valueChanged()),
05124                                SLOT(OldPasswordChanged()));
05125     connect(m_okButton, SIGNAL(Clicked()), SLOT(SendResult()));
05126 
05127     return true;
05128 }
05129 
05130 void PasswordChange::OldPasswordChanged()
05131 {
05132     QString newText = m_oldPasswordEdit->GetText();
05133     bool ok = (newText == m_oldPassword);
05134     m_okButton->SetEnabled(ok);
05135 }
05136 
05137 
05138 void PasswordChange::SendResult()
05139 {
05140     emit result(m_newPasswordEdit->GetText());
05141     Close();
05142 }
05143 
05145 
05146 RecMetadataEdit::RecMetadataEdit(MythScreenStack *lparent, ProgramInfo *pginfo)
05147                 : MythScreenType(lparent, "recmetadataedit"),
05148                     m_progInfo(pginfo)
05149 {
05150     m_titleEdit = m_subtitleEdit = m_descriptionEdit = m_inetrefEdit = NULL;
05151     m_seasonSpin = m_episodeSpin = NULL;
05152 }
05153 
05154 bool RecMetadataEdit::Create()
05155 {
05156     if (!LoadWindowFromXML("recordings-ui.xml", "editmetadata", this))
05157         return false;
05158 
05159     m_titleEdit = dynamic_cast<MythUITextEdit*>(GetChild("title"));
05160     m_subtitleEdit = dynamic_cast<MythUITextEdit*>(GetChild("subtitle"));
05161     m_descriptionEdit = dynamic_cast<MythUITextEdit*>(GetChild("description"));
05162     m_inetrefEdit = dynamic_cast<MythUITextEdit*>(GetChild("inetref"));
05163     m_seasonSpin = dynamic_cast<MythUISpinBox*>(GetChild("season"));
05164     m_episodeSpin = dynamic_cast<MythUISpinBox*>(GetChild("episode"));
05165     MythUIButton *okButton = dynamic_cast<MythUIButton*>(GetChild("ok"));
05166 
05167     if (!m_titleEdit || !m_subtitleEdit || !m_inetrefEdit || !m_seasonSpin ||
05168         !m_episodeSpin || !okButton)
05169     {
05170         LOG(VB_GENERAL, LOG_ERR, LOC +
05171             "Window 'editmetadata' is missing required elements.");
05172         return false;
05173     }
05174 
05175     m_titleEdit->SetText(m_progInfo->GetTitle());
05176     m_titleEdit->SetMaxLength(128);
05177     m_subtitleEdit->SetText(m_progInfo->GetSubtitle());
05178     m_subtitleEdit->SetMaxLength(128);
05179     if (m_descriptionEdit)
05180     {
05181         m_descriptionEdit->SetText(m_progInfo->GetDescription());
05182         m_descriptionEdit->SetMaxLength(255);
05183     }
05184     m_inetrefEdit->SetText(m_progInfo->GetInetRef());
05185     m_inetrefEdit->SetMaxLength(255);
05186     m_seasonSpin->SetRange(0,9999,1,5);
05187     m_seasonSpin->SetValue(m_progInfo->GetSeason());
05188     m_episodeSpin->SetRange(0,9999,1,10);
05189     m_episodeSpin->SetValue(m_progInfo->GetEpisode());
05190 
05191     connect(okButton, SIGNAL(Clicked()), SLOT(SaveChanges()));
05192 
05193     BuildFocusList();
05194 
05195     return true;
05196 }
05197 
05198 void RecMetadataEdit::SaveChanges()
05199 {
05200     QString newRecTitle = m_titleEdit->GetText();
05201     QString newRecSubtitle = m_subtitleEdit->GetText();
05202     QString newRecDescription = NULL;
05203     QString newRecInetref = NULL;
05204     uint newRecSeason = 0, newRecEpisode = 0;
05205     if (m_descriptionEdit)
05206         newRecDescription = m_descriptionEdit->GetText();
05207     newRecInetref = m_inetrefEdit->GetText();
05208     newRecSeason = m_seasonSpin->GetIntValue();
05209     newRecEpisode = m_episodeSpin->GetIntValue();
05210 
05211     if (newRecTitle.isEmpty())
05212         return;
05213 
05214     emit result(newRecTitle, newRecSubtitle, newRecDescription,
05215                 newRecInetref, newRecSeason, newRecEpisode);
05216     Close();
05217 }
05218 
05220 
05221 HelpPopup::HelpPopup(MythScreenStack *lparent)
05222                 : MythScreenType(lparent, "helppopup"),
05223                   m_iconList(NULL)
05224 {
05225 
05226 }
05227 
05228 bool HelpPopup::Create()
05229 {
05230     if (!LoadWindowFromXML("recordings-ui.xml", "iconhelp", this))
05231         return false;
05232 
05233     m_iconList = dynamic_cast<MythUIButtonList*>(GetChild("iconlist"));
05234 
05235     if (!m_iconList)
05236     {
05237         LOG(VB_GENERAL, LOG_ERR, LOC +
05238             "Window 'iconhelp' is missing required elements.");
05239         return false;
05240     }
05241 
05242     BuildFocusList();
05243 
05244     addItem("commflagged", tr("Commercials are flagged"));
05245     addItem("cutlist",     tr("An editing cutlist is present"));
05246     addItem("autoexpire",  tr("The program is able to auto-expire"));
05247     addItem("processing",  tr("Commercials are being flagged"));
05248     addItem("bookmark",    tr("A bookmark is set"));
05249 #if 0
05250     addItem("inuse",       tr("Recording is in use"));
05251     addItem("transcoded",  tr("Recording has been transcoded"));
05252 #endif
05253 
05254     addItem("mono",        tr("Recording is in Mono"));
05255     addItem("stereo",      tr("Recording is in Stereo"));
05256     addItem("surround",    tr("Recording is in Surround Sound"));
05257     addItem("dolby",       tr("Recording is in Dolby Surround Sound"));
05258 
05259     addItem("cc",          tr("Recording is Closed Captioned"));
05260     addItem("subtitles",   tr("Recording has Subtitles Available"));
05261     addItem("onscreensub", tr("Recording is Subtitled"));
05262 
05263     addItem("hd1080",      tr("Recording is in 1080i/p High Definition"));
05264     addItem("hd720",       tr("Recording is in 720p High Definition"));
05265     addItem("hdtv",        tr("Recording is in High Definition"));
05266     addItem("widescreen",  tr("Recording is Widescreen"));
05267     addItem("avchd",       tr("Recording is in HD using H.264 codec"));
05268 
05269     addItem("watched",     tr("Recording has been watched"));
05270 //    addItem("preserved",   tr("Recording is preserved"));
05271 
05272     return true;
05273 }
05274 
05275 void HelpPopup::addItem(const QString &state, const QString &text)
05276 {
05277     MythUIButtonListItem *item = new MythUIButtonListItem(m_iconList, text);
05278     item->DisplayState(state, "icons");
05279 }
05280 
05281 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends