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