00001
00002 #include "playbackbox.h"
00003
00004
00005 #include <QCoreApplication>
00006 #include <QWaitCondition>
00007 #include <QDateTime>
00008 #include <QTimer>
00009 #include <QMap>
00010
00011
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
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
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
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
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
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() );
00335 list.push_back(QString());
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
00382 m_artHostOverride(),
00383
00384 m_type(ltype),
00385 m_watchListAutoExpire(false),
00386 m_watchListMaxAge(60), m_watchListBlackOut(2),
00387 m_groupnameAsAllProg(false),
00388 m_listOrder(1),
00389
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
00397 m_menuDialog(NULL),
00398 m_popupMenu(NULL),
00399 m_doToggleMenu(true),
00400
00401 m_progsInDB(0),
00402
00403 m_op_on_playlist(false),
00404 m_programInfoCache(this), m_playingSomething(false),
00405
00406 m_needUpdate(false),
00407
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
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
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
00451
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
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
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
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
00801
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
00818 item->SetFontState(state);
00819 item->DisplayState(state, "status");
00820
00821
00822 QString job = extract_job_state(*pginfo);
00823 item->DisplayState(job, "jobstate");
00824
00825
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
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
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
00977 QString job = extract_job_state(*pginfo);
00978 item->DisplayState(job, "jobstate");
00979
00980
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
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
01249
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
01297
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;
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
01364
01365
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
01487
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
01497 QString groupname = prefSelGroup->GetData().toString();
01498
01499
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
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
01563 QStringList groupSelPref, itemSelPref, itemTopPref;
01564 if (!save_position(m_groupList, m_recordingList,
01565 groupSelPref, itemSelPref, itemTopPref))
01566 {
01567
01568
01569 if (m_watchListStart && (m_viewMask & VIEW_WATCHLIST))
01570 groupSelPref.push_back(m_watchGroupLabel);
01571 }
01572
01573
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
01596
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) &&
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")
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())
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 {
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
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
01878 delHours[recid] = 1000;
01879 if (last_delete.isValid())
01880 delHours[recid] = last_delete.secsTo(now) / 3600 + 1;
01881
01882
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
01898 if (spanHours[recid] == 0)
01899 {
01900 spanHours[recid] = 1000;
01901 delHours[recid] = 1000;
01902 }
01903
01904
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
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
01927 if (hrs < 42)
01928 {
01929 (*pit)->SetRecordingPriority2(
01930 (*pit)->GetRecordingPriority2() + 42 - hrs);
01931 }
01932
01933
01934 (*pit)->SetRecordingPriority2(
01935 (*pit)->GetRecordingPriority2() + abs((hrs % 24) - 12) * 2);
01936
01937
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
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
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
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
02054
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
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
02460
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
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
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 {
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
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
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
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
03531
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
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
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
04000
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
04049
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;
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
04119
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
04284
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
04361 displayNames.append(QString("------- %1 -------").arg(tr("Groups")));
04362 groupNames.append("");
04363
04364
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
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
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
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
04499
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
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
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
05035
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
05056
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
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