|
MythTV
0.26-pre
|
00001 00002 #include "guidegrid.h" 00003 00004 // c/c++ 00005 #include <math.h> 00006 #include <unistd.h> 00007 #include <iostream> 00008 #include <algorithm> 00009 using namespace std; 00010 00011 //qt 00012 #include <QCoreApplication> 00013 #include <QKeyEvent> 00014 #include <QDateTime> 00015 00016 // myth 00017 #include "mythcorecontext.h" 00018 #include "mythdbcon.h" 00019 #include "mythlogging.h" 00020 #include "dbchannelinfo.h" 00021 #include "programinfo.h" 00022 #include "recordingrule.h" 00023 #include "tv_play.h" 00024 #include "tv_rec.h" 00025 #include "customedit.h" 00026 #include "mythmiscutil.h" 00027 #include "remoteutil.h" 00028 #include "channelutil.h" 00029 #include "cardutil.h" 00030 #include "mythuibuttonlist.h" 00031 #include "mythuiguidegrid.h" 00032 #include "mythdialogbox.h" 00033 #include "progfind.h" 00034 00035 QWaitCondition epgIsVisibleCond; 00036 00037 #define LOC QString("GuideGrid: ") 00038 #define LOC_ERR QString("GuideGrid, Error: ") 00039 #define LOC_WARN QString("GuideGrid, Warning: ") 00040 00041 const QString kUnknownTitle = QObject::tr("Unknown"); 00042 const QString kUnknownCategory = QObject::tr("Unknown"); 00043 00044 JumpToChannel::JumpToChannel( 00045 JumpToChannelListener *parent, const QString &start_entry, 00046 int start_chan_idx, int cur_chan_idx, uint rows_disp) : 00047 m_listener(parent), 00048 m_entry(start_entry), 00049 m_previous_start_channel_index(start_chan_idx), 00050 m_previous_current_channel_index(cur_chan_idx), 00051 m_rows_displayed(rows_disp), 00052 m_timer(new QTimer(this)) 00053 { 00054 if (parent && m_timer) 00055 { 00056 connect(m_timer, SIGNAL(timeout()), SLOT(deleteLater())); 00057 m_timer->setSingleShot(true); 00058 } 00059 Update(); 00060 } 00061 00062 00063 void JumpToChannel::deleteLater(void) 00064 { 00065 if (m_listener) 00066 { 00067 m_listener->SetJumpToChannel(NULL); 00068 m_listener = NULL; 00069 } 00070 00071 if (m_timer) 00072 { 00073 m_timer->stop(); 00074 m_timer = NULL; 00075 } 00076 00077 QObject::deleteLater(); 00078 } 00079 00080 00081 static bool has_action(QString action, const QStringList &actions) 00082 { 00083 QStringList::const_iterator it; 00084 for (it = actions.begin(); it != actions.end(); ++it) 00085 { 00086 if (action == *it) 00087 return true; 00088 } 00089 return false; 00090 } 00091 00092 bool JumpToChannel::ProcessEntry(const QStringList &actions, const QKeyEvent *e) 00093 { 00094 if (!m_listener) 00095 return false; 00096 00097 if (has_action("ESCAPE", actions)) 00098 { 00099 m_listener->GoTo(m_previous_start_channel_index, 00100 m_previous_current_channel_index); 00101 deleteLater(); 00102 return true; 00103 } 00104 00105 if (has_action("DELETE", actions)) 00106 { 00107 if (!m_entry.isEmpty()) 00108 m_entry = m_entry.left(m_entry.length()-1); 00109 Update(); 00110 return true; 00111 } 00112 00113 if (has_action(ACTION_SELECT, actions)) 00114 { 00115 if (Update()) 00116 deleteLater(); 00117 return true; 00118 } 00119 00120 QString txt = e->text(); 00121 bool isUInt; 00122 txt.toUInt(&isUInt); 00123 if (isUInt) 00124 { 00125 m_entry += txt; 00126 Update(); 00127 return true; 00128 } 00129 00130 if (!m_entry.isEmpty() && (txt=="_" || txt=="-" || txt=="#" || txt==".")) 00131 { 00132 m_entry += txt; 00133 Update(); 00134 return true; 00135 } 00136 00137 return false; 00138 } 00139 00140 bool JumpToChannel::Update(void) 00141 { 00142 if (!m_timer || !m_listener) 00143 return false; 00144 00145 m_timer->stop(); 00146 00147 // find the closest channel ... 00148 int i = m_listener->FindChannel(0, m_entry, false); 00149 if (i >= 0) 00150 { 00151 // setup the timeout timer for jump mode 00152 m_timer->start(kJumpToChannelTimeout); 00153 00154 // rows_displayed to center 00155 int start = i - m_rows_displayed/2; 00156 int cur = m_rows_displayed/2; 00157 m_listener->GoTo(start, cur); 00158 return true; 00159 } 00160 else 00161 { // prefix must be invalid.. reset entry.. 00162 deleteLater(); 00163 return false; 00164 } 00165 } 00166 00167 void GuideGrid::RunProgramGuide(uint chanid, const QString &channum, 00168 TV *player, bool embedVideo, bool allowFinder, int changrpid) 00169 { 00170 // which channel group should we default to 00171 if (changrpid == -2) 00172 changrpid = gCoreContext->GetNumSetting("ChannelGroupDefault", -1); 00173 00174 // check there are some channels setup 00175 DBChanList channels = ChannelUtil::GetChannels( 00176 0, true, "", (changrpid<0) ? 0 : changrpid); 00177 if (!channels.size()) 00178 { 00179 QString message; 00180 if (changrpid == -1) 00181 { 00182 message = tr("You don't have any channels defined in the database." 00183 "\n\t\t\tThe program guide will have nothing to show you."); 00184 } 00185 else 00186 { 00187 message = tr("Channel group '%1' doesn't have any channels defined." 00188 "\n\t\t\tThe program guide will have nothing to show you.") 00189 .arg(ChannelGroup::GetChannelGroupName(changrpid)); 00190 } 00191 00192 LOG(VB_GENERAL, LOG_WARNING, LOC + message); 00193 00194 if (!player) 00195 ShowOkPopup(message); 00196 else 00197 { 00198 if (player && allowFinder) 00199 { 00200 message = QString("EPG_EXITING"); 00201 qApp->postEvent(player, new MythEvent(message)); 00202 } 00203 } 00204 00205 return; 00206 } 00207 00208 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack(); 00209 GuideGrid *gg = new GuideGrid(mainStack, 00210 chanid, channum, 00211 player, embedVideo, allowFinder, 00212 changrpid); 00213 00214 if (gg->Create()) 00215 mainStack->AddScreen(gg, (player == NULL)); 00216 else 00217 delete gg; 00218 } 00219 00220 GuideGrid::GuideGrid(MythScreenStack *parent, 00221 uint chanid, QString channum, 00222 TV *player, bool embedVideo, 00223 bool allowFinder, int changrpid) 00224 : ScheduleCommon(parent, "guidegrid"), 00225 m_allowFinder(allowFinder), 00226 m_player(player), 00227 m_usingNullVideo(false), m_embedVideo(embedVideo), 00228 m_previewVideoRefreshTimer(new QTimer(this)), 00229 m_updateTimer(NULL), 00230 m_jumpToChannelLock(QMutex::Recursive), 00231 m_jumpToChannel(NULL) 00232 { 00233 connect(m_previewVideoRefreshTimer, SIGNAL(timeout()), 00234 this, SLOT(refreshVideo())); 00235 00236 m_channelCount = 5; 00237 m_timeCount = 30; 00238 m_currentStartChannel = 0; 00239 m_changrpid = changrpid; 00240 m_changrplist = ChannelGroup::GetChannelGroups(false); 00241 00242 m_sortReverse = gCoreContext->GetNumSetting("EPGSortReverse", 0); 00243 m_selectRecThreshold = gCoreContext->GetNumSetting("SelChangeRecThreshold", 16); 00244 00245 m_channelOrdering = gCoreContext->GetSetting("ChannelOrdering", "channum"); 00246 00247 for (uint i = 0; i < MAX_DISPLAY_CHANS; i++) 00248 m_programs.push_back(NULL); 00249 00250 for (int x = 0; x < MAX_DISPLAY_TIMES; ++x) 00251 { 00252 for (int y = 0; y < MAX_DISPLAY_CHANS; ++y) 00253 m_programInfos[y][x] = NULL; 00254 } 00255 00256 m_originalStartTime = QDateTime::currentDateTime(); 00257 00258 int secsoffset = -((m_originalStartTime.time().minute() % 30) * 60 + 00259 m_originalStartTime.time().second()); 00260 m_currentStartTime = m_originalStartTime.addSecs(secsoffset); 00261 m_startChanID = chanid; 00262 m_startChanNum = channum; 00263 } 00264 00265 bool GuideGrid::Create() 00266 { 00267 QString windowName = "programguide"; 00268 00269 if (m_embedVideo) 00270 windowName = "programguide-video"; 00271 00272 if (!LoadWindowFromXML("schedule-ui.xml", windowName, this)) 00273 return false; 00274 00275 bool err = false; 00276 UIUtilE::Assign(this, m_timeList, "timelist", &err); 00277 UIUtilE::Assign(this, m_channelList, "channellist", &err); 00278 UIUtilE::Assign(this, m_guideGrid, "guidegrid", &err); 00279 UIUtilW::Assign(this, m_dateText, "datetext"); 00280 UIUtilW::Assign(this, m_longdateText, "longdatetext"); 00281 UIUtilW::Assign(this, m_changroupname, "channelgroup"); 00282 UIUtilW::Assign(this, m_channelImage, "channelicon"); 00283 UIUtilW::Assign(this, m_jumpToText, "jumptotext"); 00284 00285 if (err) 00286 { 00287 LOG(VB_GENERAL, LOG_ERR, 00288 QString("Cannot load screen '%1'").arg(windowName)); 00289 return false; 00290 } 00291 00292 BuildFocusList(); 00293 00294 MythUIImage *videoImage = dynamic_cast<MythUIImage *>(GetChild("video")); 00295 if (videoImage && m_embedVideo) 00296 m_videoRect = videoImage->GetArea(); 00297 else 00298 m_videoRect = QRect(0,0,0,0); 00299 00300 m_channelCount = m_guideGrid->getChannelCount(); 00301 m_timeCount = m_guideGrid->getTimeCount() * 6; 00302 m_verticalLayout = m_guideGrid->isVerticalLayout(); 00303 00304 m_currentEndTime = m_currentStartTime.addSecs(m_timeCount * 60 * 5); 00305 00306 LoadInBackground(); 00307 return true; 00308 } 00309 00310 void GuideGrid::Load(void) 00311 { 00312 LoadFromScheduler(m_recList); 00313 fillChannelInfos(); 00314 00315 int maxchannel = max((int)GetChannelCount() - 1, 0); 00316 setStartChannel((int)(m_currentStartChannel) - (int)(m_channelCount / 2)); 00317 m_channelCount = min(m_channelCount, maxchannel + 1); 00318 00319 for (int y = 0; y < m_channelCount; ++y) 00320 { 00321 int chanNum = y + m_currentStartChannel; 00322 if (chanNum >= (int) m_channelInfos.size()) 00323 chanNum -= (int) m_channelInfos.size(); 00324 if (chanNum >= (int) m_channelInfos.size()) 00325 continue; 00326 00327 if (chanNum < 0) 00328 chanNum = 0; 00329 00330 delete m_programs[y]; 00331 m_programs[y] = getProgramListFromProgram(chanNum); 00332 } 00333 } 00334 00335 void GuideGrid::Init(void) 00336 { 00337 m_currentRow = (int)(m_channelCount / 2); 00338 m_currentCol = 0; 00339 00340 fillTimeInfos(); 00341 00342 updateChannels(); 00343 00344 fillProgramInfos(true); 00345 updateInfo(); 00346 00347 m_updateTimer = new QTimer(this); 00348 connect(m_updateTimer, SIGNAL(timeout()), SLOT(updateTimeout()) ); 00349 m_updateTimer->start(60 * 1000); 00350 00351 updateDateText(); 00352 00353 QString changrpname = ChannelGroup::GetChannelGroupName(m_changrpid); 00354 00355 if (m_changroupname) 00356 m_changroupname->SetText(changrpname); 00357 00358 gCoreContext->addListener(this); 00359 } 00360 00361 GuideGrid::~GuideGrid() 00362 { 00363 gCoreContext->removeListener(this); 00364 00365 while (!m_programs.empty()) 00366 { 00367 if (m_programs.back()) 00368 delete m_programs.back(); 00369 m_programs.pop_back(); 00370 } 00371 00372 m_channelInfos.clear(); 00373 00374 if (m_updateTimer) 00375 { 00376 m_updateTimer->disconnect(this); 00377 m_updateTimer = NULL; 00378 } 00379 00380 if (m_previewVideoRefreshTimer) 00381 { 00382 m_previewVideoRefreshTimer->disconnect(this); 00383 m_previewVideoRefreshTimer = NULL; 00384 } 00385 00386 gCoreContext->SaveSetting("EPGSortReverse", m_sortReverse ? "1" : "0"); 00387 00388 // if we have a player and we are returning to it we need 00389 // to tell it to stop embedding and return to fullscreen 00390 if (m_player && m_allowFinder) 00391 { 00392 QString message = QString("EPG_EXITING"); 00393 qApp->postEvent(m_player, new MythEvent(message)); 00394 } 00395 00396 // maybe the user selected a different channel group, 00397 // tell the player to update its channel list just in case 00398 if (m_player) 00399 m_player->UpdateChannelList(m_changrpid); 00400 00401 if (gCoreContext->GetNumSetting("ChannelGroupRememberLast", 0)) 00402 gCoreContext->SaveSetting("ChannelGroupDefault", m_changrpid); 00403 } 00404 00405 bool GuideGrid::keyPressEvent(QKeyEvent *event) 00406 { 00407 QStringList actions; 00408 bool handled = false; 00409 handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend", event, actions); 00410 00411 if (handled) 00412 return true; 00413 00414 if (actions.size()) 00415 { 00416 QMutexLocker locker(&m_jumpToChannelLock); 00417 00418 if (!m_jumpToChannel) 00419 { 00420 QString chanNum = actions[0]; 00421 bool isNum; 00422 chanNum.toInt(&isNum); 00423 if (isNum) 00424 { 00425 // see if we can find a matching channel before creating the JumpToChannel otherwise 00426 // JumpToChannel will delete itself in the ctor leading to a segfault 00427 int i = FindChannel(0, chanNum, false); 00428 if (i >= 0) 00429 { 00430 m_jumpToChannel = new JumpToChannel(this, chanNum, 00431 m_currentStartChannel, 00432 m_currentRow, m_channelCount); 00433 updateJumpToChannel(); 00434 } 00435 00436 handled = true; 00437 } 00438 } 00439 00440 if (m_jumpToChannel && !handled) 00441 handled = m_jumpToChannel->ProcessEntry(actions, event); 00442 } 00443 00444 for (int i = 0; i < actions.size() && !handled; ++i) 00445 { 00446 QString action = actions[i]; 00447 handled = true; 00448 if (action == ACTION_UP) 00449 { 00450 if (m_verticalLayout) 00451 cursorLeft(); 00452 else 00453 cursorUp(); 00454 } 00455 else if (action == ACTION_DOWN) 00456 { 00457 if (m_verticalLayout) 00458 cursorRight(); 00459 else 00460 cursorDown(); 00461 } 00462 else if (action == ACTION_LEFT) 00463 { 00464 if (m_verticalLayout) 00465 cursorUp(); 00466 else 00467 cursorLeft(); 00468 } 00469 else if (action == ACTION_RIGHT) 00470 { 00471 if (m_verticalLayout) 00472 cursorDown(); 00473 else 00474 cursorRight(); 00475 } 00476 else if (action == "PAGEUP") 00477 { 00478 if (m_verticalLayout) 00479 moveLeftRight(kPageLeft); 00480 else 00481 moveUpDown(kPageUp); 00482 } 00483 else if (action == "PAGEDOWN") 00484 { 00485 if (m_verticalLayout) 00486 moveLeftRight(kPageRight); 00487 else 00488 moveUpDown(kPageDown); 00489 } 00490 else if (action == ACTION_PAGELEFT) 00491 { 00492 if (m_verticalLayout) 00493 moveUpDown(kPageUp); 00494 else 00495 moveLeftRight(kPageLeft); 00496 } 00497 else if (action == ACTION_PAGERIGHT) 00498 { 00499 if (m_verticalLayout) 00500 moveUpDown(kPageDown); 00501 else 00502 moveLeftRight(kPageRight); 00503 } 00504 else if (action == ACTION_DAYLEFT) 00505 moveLeftRight(kDayLeft); 00506 else if (action == ACTION_DAYRIGHT) 00507 moveLeftRight(kDayRight); 00508 else if (action == "NEXTFAV") 00509 toggleGuideListing(); 00510 else if (action == ACTION_FINDER) 00511 showProgFinder(); 00512 else if (action == "MENU") 00513 ShowMenu(); 00514 else if (action == "ESCAPE" || action == ACTION_GUIDE) 00515 Close(); 00516 else if (action == ACTION_SELECT) 00517 { 00518 if (m_player && (m_player->GetState(-1) == kState_WatchingLiveTV)) 00519 { 00520 // See if this show is far enough into the future that it's 00521 // probable that the user wanted to schedule it to record 00522 // instead of changing the channel. 00523 ProgramInfo *pginfo = 00524 m_programInfos[m_currentRow][m_currentCol]; 00525 int secsTillStart = 00526 (pginfo) ? QDateTime::currentDateTime().secsTo( 00527 pginfo->GetScheduledStartTime()) : 0; 00528 if (pginfo && (pginfo->GetTitle() != kUnknownTitle) && 00529 ((secsTillStart / 60) >= m_selectRecThreshold)) 00530 { 00531 editRecSchedule(); 00532 } 00533 else 00534 { 00535 enter(); 00536 } 00537 } 00538 else 00539 editRecSchedule(); 00540 } 00541 else if (action == "EDIT") 00542 editSchedule(); 00543 else if (action == "CUSTOMEDIT") 00544 customEdit(); 00545 else if (action == "DELETE") 00546 deleteRule(); 00547 else if (action == "UPCOMING") 00548 upcoming(); 00549 else if (action == "DETAILS" || action == "INFO") 00550 details(); 00551 else if (action == ACTION_TOGGLERECORD) 00552 quickRecord(); 00553 else if (action == ACTION_TOGGLEFAV) 00554 { 00555 if (m_changrpid == -1) 00556 ChannelGroupMenu(0); 00557 else 00558 toggleChannelFavorite(); 00559 } 00560 else if (action == "CHANUPDATE") 00561 channelUpdate(); 00562 else if (action == ACTION_VOLUMEUP) 00563 volumeUpdate(true); 00564 else if (action == ACTION_VOLUMEDOWN) 00565 volumeUpdate(false); 00566 else if (action == "CYCLEAUDIOCHAN") 00567 toggleMute(true); 00568 else if (action == ACTION_MUTEAUDIO) 00569 toggleMute(); 00570 else if (action == ACTION_TOGGLEPGORDER) 00571 { 00572 m_sortReverse = !m_sortReverse; 00573 generateListings(); 00574 updateChannels(); 00575 } 00576 else 00577 handled = false; 00578 } 00579 00580 if (!handled && MythScreenType::keyPressEvent(event)) 00581 handled = true; 00582 00583 return handled; 00584 } 00585 00586 void GuideGrid::ShowMenu(void) 00587 { 00588 QString label = tr("Guide Options"); 00589 00590 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 00591 MythDialogBox *menuPopup = new MythDialogBox(label, popupStack, 00592 "guideMenuPopup"); 00593 00594 if (menuPopup->Create()) 00595 { 00596 menuPopup->SetReturnEvent(this, "guidemenu"); 00597 00598 if (m_player && (m_player->GetState(-1) == kState_WatchingLiveTV)) 00599 menuPopup->AddButton(tr("Change to Channel")); 00600 00601 menuPopup->AddButton(tr("Record This")); 00602 00603 menuPopup->AddButton(tr("Recording Options"), NULL, true); 00604 00605 menuPopup->AddButton(tr("Program Details")); 00606 00607 menuPopup->AddButton(tr("Jump to Time"), NULL, true); 00608 00609 menuPopup->AddButton(tr("Reverse Channel Order")); 00610 00611 if (!m_changrplist.empty()) 00612 { 00613 menuPopup->AddButton(tr("Choose Channel Group")); 00614 00615 if (m_changrpid == -1) 00616 menuPopup->AddButton(tr("Add To Channel Group"), NULL, true); 00617 else 00618 menuPopup->AddButton(tr("Remove from Channel Group"), NULL, true); 00619 } 00620 00621 popupStack->AddScreen(menuPopup); 00622 } 00623 else 00624 { 00625 delete menuPopup; 00626 } 00627 } 00628 00629 void GuideGrid::ShowRecordingMenu(void) 00630 { 00631 QString label = tr("Recording Options"); 00632 00633 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 00634 MythDialogBox *menuPopup = new MythDialogBox(label, popupStack, 00635 "recMenuPopup"); 00636 00637 if (menuPopup->Create()) 00638 { 00639 menuPopup->SetReturnEvent(this, "recmenu"); 00640 00641 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 00642 00643 if (pginfo && pginfo->GetRecordingRuleID()) 00644 menuPopup->AddButton(tr("Edit Recording Status")); 00645 menuPopup->AddButton(tr("Edit Schedule")); 00646 menuPopup->AddButton(tr("Show Upcoming")); 00647 menuPopup->AddButton(tr("Custom Edit")); 00648 00649 if (pginfo && pginfo->GetRecordingRuleID()) 00650 menuPopup->AddButton(tr("Delete Rule")); 00651 00652 popupStack->AddScreen(menuPopup); 00653 } 00654 else 00655 { 00656 delete menuPopup; 00657 } 00658 } 00659 00660 DBChannel *GuideGrid::GetChannelInfo(uint chan_idx, int sel) 00661 { 00662 sel = (sel >= 0) ? sel : m_channelInfoIdx[chan_idx]; 00663 00664 if (chan_idx >= GetChannelCount()) 00665 return NULL; 00666 00667 if (sel >= (int) m_channelInfos[chan_idx].size()) 00668 return NULL; 00669 00670 return &(m_channelInfos[chan_idx][sel]); 00671 } 00672 00673 const DBChannel *GuideGrid::GetChannelInfo(uint chan_idx, int sel) const 00674 { 00675 return ((GuideGrid*)this)->GetChannelInfo(chan_idx, sel); 00676 } 00677 00678 uint GuideGrid::GetChannelCount(void) const 00679 { 00680 return m_channelInfos.size(); 00681 } 00682 00683 int GuideGrid::GetStartChannelOffset(int row) const 00684 { 00685 uint cnt = GetChannelCount(); 00686 if (!cnt) 00687 return -1; 00688 00689 row = (row < 0) ? m_currentRow : row; 00690 return (row + m_currentStartChannel) % cnt; 00691 } 00692 00693 ProgramList GuideGrid::GetProgramList(uint chanid) const 00694 { 00695 ProgramList proglist; 00696 MSqlBindings bindings; 00697 QString querystr = 00698 "WHERE program.chanid = :CHANID AND " 00699 " program.endtime >= :STARTTS AND " 00700 " program.starttime <= :ENDTS AND " 00701 " program.manualid = 0 "; 00702 bindings[":STARTTS"] = 00703 m_currentStartTime.addSecs(0 - m_currentStartTime.time().second()); 00704 bindings[":ENDTS"] = 00705 m_currentEndTime.addSecs(0 - m_currentEndTime.time().second()); 00706 bindings[":CHANID"] = chanid; 00707 00708 ProgramList dummy; 00709 LoadFromProgram(proglist, querystr, bindings, dummy); 00710 00711 return proglist; 00712 } 00713 00714 uint GuideGrid::GetAlternateChannelIndex( 00715 uint chan_idx, bool with_same_channum) const 00716 { 00717 uint si = m_channelInfoIdx[chan_idx]; 00718 const DBChannel *chinfo = GetChannelInfo(chan_idx, si); 00719 00720 PlayerContext *ctx = m_player->GetPlayerReadLock(-1, __FILE__, __LINE__); 00721 00722 const uint cnt = (ctx && chinfo) ? m_channelInfos[chan_idx].size() : 0; 00723 for (uint i = 0; i < cnt; ++i) 00724 { 00725 if (i == si) 00726 continue; 00727 00728 const DBChannel *ciinfo = GetChannelInfo(chan_idx, i); 00729 if (!ciinfo) 00730 continue; 00731 00732 bool same_channum = ciinfo->channum == chinfo->channum; 00733 00734 if (with_same_channum != same_channum) 00735 continue; 00736 00737 if (!m_player->IsTunable(ctx, ciinfo->chanid, true)) 00738 continue; 00739 00740 if (with_same_channum) 00741 { 00742 si = i; 00743 break; 00744 } 00745 00746 ProgramList proglist = GetProgramList(chinfo->chanid); 00747 ProgramList ch_proglist = GetProgramList(ciinfo->chanid); 00748 00749 if (proglist.empty() || 00750 proglist.size() != ch_proglist.size()) 00751 continue; 00752 00753 bool isAlt = true; 00754 for (uint j = 0; j < proglist.size(); ++j) 00755 { 00756 isAlt &= proglist[j]->IsSameProgramTimeslot(*ch_proglist[j]); 00757 } 00758 00759 if (isAlt) 00760 { 00761 si = i; 00762 break; 00763 } 00764 } 00765 00766 m_player->ReturnPlayerLock(ctx); 00767 00768 return si; 00769 } 00770 00771 00772 #define MKKEY(IDX,SEL) ((((uint64_t)IDX) << 32) | SEL) 00773 DBChanList GuideGrid::GetSelection(void) const 00774 { 00775 DBChanList selected; 00776 00777 int idx = GetStartChannelOffset(); 00778 if (idx < 0) 00779 return selected; 00780 00781 uint si = m_channelInfoIdx[idx]; 00782 00783 vector<uint64_t> sel; 00784 sel.push_back( MKKEY(idx, si) ); 00785 00786 const DBChannel *ch = GetChannelInfo(sel[0]>>32, sel[0]&0xffff); 00787 if (!ch) 00788 return selected; 00789 00790 selected.push_back(*ch); 00791 if (m_channelInfos[idx].size() <= 1) 00792 return selected; 00793 00794 ProgramList proglist = GetProgramList(selected[0].chanid); 00795 00796 if (proglist.empty()) 00797 return selected; 00798 00799 for (uint i = 0; i < m_channelInfos[idx].size(); ++i) 00800 { 00801 const DBChannel *ci = GetChannelInfo(idx, i); 00802 if (ci && (i != si) && 00803 (ci->callsign == ch->callsign) && (ci->channum == ch->channum)) 00804 { 00805 sel.push_back( MKKEY(idx, i) ); 00806 } 00807 } 00808 00809 for (uint i = 0; i < m_channelInfos[idx].size(); ++i) 00810 { 00811 const DBChannel *ci = GetChannelInfo(idx, i); 00812 if (ci && (i != si) && 00813 (ci->callsign == ch->callsign) && (ci->channum != ch->channum)) 00814 { 00815 sel.push_back( MKKEY(idx, i) ); 00816 } 00817 } 00818 00819 for (uint i = 0; i < m_channelInfos[idx].size(); ++i) 00820 { 00821 const DBChannel *ci = GetChannelInfo(idx, i); 00822 if ((i != si) && (ci->callsign != ch->callsign)) 00823 { 00824 sel.push_back( MKKEY(idx, i) ); 00825 } 00826 } 00827 00828 for (uint i = 1; i < sel.size(); ++i) 00829 { 00830 const DBChannel *ci = GetChannelInfo(sel[i]>>32, sel[i]&0xffff); 00831 const ProgramList ch_proglist = GetProgramList(ch->chanid); 00832 00833 if (!ci || proglist.size() != ch_proglist.size()) 00834 continue; 00835 00836 bool isAlt = true; 00837 for (uint j = 0; j < proglist.size(); ++j) 00838 { 00839 isAlt &= proglist[j]->IsSameProgramTimeslot(*ch_proglist[j]); 00840 } 00841 00842 if (isAlt) 00843 selected.push_back(*ci); 00844 } 00845 00846 return selected; 00847 } 00848 #undef MKKEY 00849 00850 void GuideGrid::updateTimeout(void) 00851 { 00852 m_updateTimer->stop(); 00853 fillProgramInfos(); 00854 m_updateTimer->start((int)(60 * 1000)); 00855 } 00856 00857 void GuideGrid::fillChannelInfos(bool gotostartchannel) 00858 { 00859 m_channelInfos.clear(); 00860 m_channelInfoIdx.clear(); 00861 m_currentStartChannel = 0; 00862 00863 DBChanList channels = ChannelUtil::GetChannels( 00864 0, true, "", (m_changrpid < 0) ? 0 : m_changrpid); 00865 ChannelUtil::SortChannels(channels, m_channelOrdering, false); 00866 00867 typedef vector<uint> uint_list_t; 00868 QMap<QString,uint_list_t> channum_to_index_map; 00869 QMap<QString,uint_list_t> callsign_to_index_map; 00870 00871 for (uint i = 0; i < channels.size(); ++i) 00872 { 00873 uint chan = i; 00874 if (m_sortReverse) 00875 { 00876 chan = channels.size() - i - 1; 00877 } 00878 00879 bool ndup = channum_to_index_map[channels[chan].channum].size(); 00880 bool cdup = callsign_to_index_map[channels[chan].callsign].size(); 00881 00882 if (ndup && cdup) 00883 continue; 00884 00885 DBChannel val(channels[chan]); 00886 00887 channum_to_index_map[val.channum].push_back(GetChannelCount()); 00888 callsign_to_index_map[val.callsign].push_back(GetChannelCount()); 00889 00890 // add the new channel to the list 00891 db_chan_list_t tmp; 00892 tmp.push_back(val); 00893 m_channelInfos.push_back(tmp); 00894 } 00895 00896 // handle duplicates 00897 for (uint i = 0; i < channels.size(); ++i) 00898 { 00899 const uint_list_t &ndups = channum_to_index_map[channels[i].channum]; 00900 for (uint j = 0; j < ndups.size(); ++j) 00901 { 00902 if (channels[i].chanid != m_channelInfos[ndups[j]][0].chanid && 00903 channels[i].callsign == m_channelInfos[ndups[j]][0].callsign) 00904 m_channelInfos[ndups[j]].push_back(channels[i]); 00905 } 00906 00907 const uint_list_t &cdups = callsign_to_index_map[channels[i].callsign]; 00908 for (uint j = 0; j < cdups.size(); ++j) 00909 { 00910 if (channels[i].chanid != m_channelInfos[cdups[j]][0].chanid) 00911 m_channelInfos[cdups[j]].push_back(channels[i]); 00912 } 00913 } 00914 00915 if (gotostartchannel) 00916 { 00917 int ch = FindChannel(m_startChanID, m_startChanNum); 00918 m_currentStartChannel = (uint) max(0, ch); 00919 } 00920 00921 if (m_channelInfos.empty()) 00922 { 00923 LOG(VB_GENERAL, LOG_ERR, "GuideGrid: " 00924 "\n\t\t\tYou don't have any channels defined in the database." 00925 "\n\t\t\tGuide grid will have nothing to show you."); 00926 } 00927 } 00928 00929 int GuideGrid::FindChannel(uint chanid, const QString &channum, 00930 bool exact) const 00931 { 00932 static QMutex chanSepRegExpLock; 00933 static QRegExp chanSepRegExp(ChannelUtil::kATSCSeparators); 00934 00935 // first check chanid 00936 uint i = (chanid) ? 0 : GetChannelCount(); 00937 for (; i < GetChannelCount(); ++i) 00938 { 00939 if (m_channelInfos[i][0].chanid == chanid) 00940 return i; 00941 } 00942 00943 // then check for chanid in duplicates 00944 i = (chanid) ? 0 : GetChannelCount(); 00945 for (; i < GetChannelCount(); ++i) 00946 { 00947 for (uint j = 1; j < m_channelInfos[i].size(); ++j) 00948 { 00949 if (m_channelInfos[i][j].chanid == chanid) 00950 return i; 00951 } 00952 } 00953 00954 // then check channum, first only 00955 i = (channum.isEmpty()) ? GetChannelCount() : 0; 00956 for (; i < GetChannelCount(); ++i) 00957 { 00958 if (m_channelInfos[i][0].channum == channum) 00959 return i; 00960 } 00961 00962 // then check channum duplicates 00963 i = (channum.isEmpty()) ? GetChannelCount() : 0; 00964 for (; i < GetChannelCount(); ++i) 00965 { 00966 for (uint j = 1; j < m_channelInfos[i].size(); ++j) 00967 { 00968 if (m_channelInfos[i][j].channum == channum) 00969 return i; 00970 } 00971 } 00972 00973 if (exact || channum.isEmpty()) 00974 return -1; 00975 00976 // then check partial channum, first only 00977 for (i = 0; i < GetChannelCount(); ++i) 00978 { 00979 if (m_channelInfos[i][0].channum.left(channum.length()) == channum) 00980 return i; 00981 } 00982 00983 // then check all partial channum 00984 for (i = 0; i < GetChannelCount(); ++i) 00985 { 00986 for (uint j = 0; j < m_channelInfos[i].size(); ++j) 00987 { 00988 if (m_channelInfos[i][j].channum.left(channum.length()) == channum) 00989 return i; 00990 } 00991 } 00992 00993 // then check all channum with "_" for subchannels 00994 QMutexLocker locker(&chanSepRegExpLock); 00995 QString tmpchannum = channum; 00996 if (tmpchannum.contains(chanSepRegExp)) 00997 { 00998 tmpchannum.replace(chanSepRegExp, "_"); 00999 } 01000 else if (channum.length() >= 2) 01001 { 01002 tmpchannum = channum.left(channum.length() - 1) + '_' + 01003 channum.right(1); 01004 } 01005 else 01006 { 01007 return -1; 01008 } 01009 01010 for (i = 0; i < GetChannelCount(); ++i) 01011 { 01012 for (uint j = 0; j < m_channelInfos[i].size(); ++j) 01013 { 01014 QString tmp = m_channelInfos[i][j].channum; 01015 tmp.replace(chanSepRegExp, "_"); 01016 if (tmp == tmpchannum) 01017 return i; 01018 } 01019 } 01020 01021 return -1; 01022 } 01023 01024 void GuideGrid::fillTimeInfos() 01025 { 01026 m_timeList->Reset(); 01027 01028 QDateTime starttime = m_currentStartTime; 01029 01030 m_firstTime = m_currentStartTime; 01031 m_lastTime = m_firstTime.addSecs(m_timeCount * 60 * 4); 01032 01033 for (int x = 0; x < m_timeCount; ++x) 01034 { 01035 int mins = starttime.time().minute(); 01036 mins = 5 * (mins / 5); 01037 if (mins % 30 == 0) 01038 { 01039 QString timeStr = MythDateTimeToString(starttime, kTime); 01040 01041 InfoMap infomap; 01042 infomap["starttime"] = timeStr; 01043 01044 QTime endtime = starttime.time().addSecs(60 * 30); 01045 01046 infomap["endtime"] = MythTimeToString(endtime, kTime); 01047 01048 MythUIButtonListItem *item = 01049 new MythUIButtonListItem(m_timeList, timeStr); 01050 item->SetTextFromMap(infomap); 01051 } 01052 01053 starttime = starttime.addSecs(5 * 60); 01054 } 01055 m_currentEndTime = starttime; 01056 } 01057 01058 void GuideGrid::fillProgramInfos(bool useExistingData) 01059 { 01060 m_guideGrid->ResetData(); 01061 01062 for (int y = 0; y < m_channelCount; ++y) 01063 { 01064 fillProgramRowInfos(y, useExistingData); 01065 } 01066 } 01067 01068 ProgramList *GuideGrid::getProgramListFromProgram(int chanNum) 01069 { 01070 ProgramList *proglist = new ProgramList(); 01071 01072 if (proglist) 01073 { 01074 MSqlBindings bindings; 01075 QString querystr = "WHERE program.chanid = :CHANID " 01076 " AND program.endtime >= :STARTTS " 01077 " AND program.starttime <= :ENDTS " 01078 " AND program.manualid = 0 "; 01079 bindings[":CHANID"] = GetChannelInfo(chanNum)->chanid; 01080 bindings[":STARTTS"] = 01081 m_currentStartTime.addSecs(0 - m_currentStartTime.time().second()); 01082 bindings[":ENDTS"] = 01083 m_currentEndTime.addSecs(0 - m_currentEndTime.time().second()); 01084 01085 LoadFromProgram(*proglist, querystr, bindings, m_recList); 01086 } 01087 01088 return proglist; 01089 } 01090 01091 void GuideGrid::fillProgramRowInfos(unsigned int row, bool useExistingData) 01092 { 01093 m_guideGrid->ResetRow(row); 01094 01095 // never divide by zero.. 01096 if (!m_guideGrid->getChannelCount() || !m_timeCount) 01097 return; 01098 01099 for (int x = 0; x < m_timeCount; ++x) 01100 { 01101 m_programInfos[row][x] = NULL; 01102 } 01103 01104 if (m_channelInfos.empty()) 01105 return; 01106 01107 int chanNum = row + m_currentStartChannel; 01108 if (chanNum >= (int) m_channelInfos.size()) 01109 chanNum -= (int) m_channelInfos.size(); 01110 if (chanNum >= (int) m_channelInfos.size()) 01111 return; 01112 01113 if (chanNum < 0) 01114 chanNum = 0; 01115 01116 if (!useExistingData) 01117 { 01118 delete m_programs[row]; 01119 m_programs[row] = getProgramListFromProgram(chanNum); 01120 } 01121 01122 ProgramList *proglist = m_programs[row]; 01123 if (!proglist) 01124 return; 01125 01126 QDateTime ts = m_currentStartTime; 01127 01128 QDateTime tnow = QDateTime::currentDateTime(); 01129 int progPast = 0; 01130 if (tnow > m_currentEndTime) 01131 progPast = 100; 01132 else if (tnow < m_currentStartTime) 01133 progPast = 0; 01134 else 01135 { 01136 int played = m_currentStartTime.secsTo(tnow); 01137 int length = m_currentStartTime.secsTo(m_currentEndTime); 01138 if (length) 01139 progPast = played * 100 / length; 01140 } 01141 01142 m_guideGrid->SetProgPast(progPast); 01143 01144 ProgramList::iterator program = proglist->begin(); 01145 vector<ProgramInfo*> unknownlist; 01146 bool unknown = false; 01147 ProgramInfo *proginfo = NULL; 01148 for (int x = 0; x < m_timeCount; ++x) 01149 { 01150 if (program != proglist->end() && 01151 (ts >= (*program)->GetScheduledEndTime())) 01152 { 01153 ++program; 01154 } 01155 01156 if ((program == proglist->end()) || 01157 (ts < (*program)->GetScheduledStartTime())) 01158 { 01159 if (unknown) 01160 { 01161 proginfo->spread++; 01162 proginfo->SetScheduledEndTime( 01163 proginfo->GetScheduledEndTime().addSecs(5 * 60)); 01164 } 01165 else 01166 { 01167 proginfo = new ProgramInfo(kUnknownTitle, kUnknownCategory, 01168 ts, ts.addSecs(5*60)); 01169 unknownlist.push_back(proginfo); 01170 proginfo->startCol = x; 01171 proginfo->spread = 1; 01172 unknown = true; 01173 } 01174 } 01175 else 01176 { 01177 if (proginfo && proginfo == *program) 01178 { 01179 proginfo->spread++; 01180 } 01181 else 01182 { 01183 proginfo = *program; 01184 if (proginfo) 01185 { 01186 proginfo->startCol = x; 01187 proginfo->spread = 1; 01188 unknown = false; 01189 } 01190 } 01191 } 01192 m_programInfos[row][x] = proginfo; 01193 ts = ts.addSecs(5 * 60); 01194 } 01195 01196 vector<ProgramInfo*>::iterator it = unknownlist.begin(); 01197 for (; it != unknownlist.end(); ++it) 01198 proglist->push_back(*it); 01199 01200 MythRect programRect = m_guideGrid->GetArea(); 01201 01203 double ydifference = 0.0, xdifference = 0.0; 01204 01205 if (m_verticalLayout) 01206 { 01207 ydifference = programRect.width() / 01208 (double) m_guideGrid->getChannelCount(); 01209 xdifference = programRect.height() / 01210 (double) m_timeCount; 01211 } 01212 else 01213 { 01214 ydifference = programRect.height() / 01215 (double) m_guideGrid->getChannelCount(); 01216 xdifference = programRect.width() / 01217 (double) m_timeCount; 01218 } 01219 01220 int arrow = 0; 01221 int cnt = 0; 01222 int spread = 1; 01223 QDateTime lastprog; 01224 QRect tempRect; 01225 bool isCurrent = false; 01226 01227 for (int x = 0; x < m_timeCount; ++x) 01228 { 01229 ProgramInfo *pginfo = m_programInfos[row][x]; 01230 if (!pginfo) 01231 continue; 01232 01233 spread = 1; 01234 if (pginfo->GetScheduledStartTime() != lastprog) 01235 { 01236 arrow = 0; 01237 if (pginfo->GetScheduledStartTime() < m_firstTime.addSecs(-300)) 01238 arrow = arrow + 1; 01239 if (pginfo->GetScheduledEndTime() > m_lastTime.addSecs(2100)) 01240 arrow = arrow + 2; 01241 01242 if (pginfo->spread != -1) 01243 { 01244 spread = pginfo->spread; 01245 } 01246 else 01247 { 01248 for (int z = x + 1; z < m_timeCount; ++z) 01249 { 01250 ProgramInfo *test = m_programInfos[row][z]; 01251 if (test && (test->GetScheduledStartTime() == 01252 pginfo->GetScheduledStartTime())) 01253 spread++; 01254 } 01255 pginfo->spread = spread; 01256 pginfo->startCol = x; 01257 01258 for (int z = x + 1; z < x + spread; ++z) 01259 { 01260 ProgramInfo *test = m_programInfos[row][z]; 01261 if (test) 01262 { 01263 test->spread = spread; 01264 test->startCol = x; 01265 } 01266 } 01267 } 01268 01269 if (m_verticalLayout) 01270 { 01271 tempRect = QRect((int)(row * ydifference), 01272 (int)(x * xdifference), 01273 (int)(ydifference), 01274 (int)(xdifference * pginfo->spread)); 01275 } 01276 else 01277 { 01278 tempRect = QRect((int)(x * xdifference), 01279 (int)(row * ydifference), 01280 (int)(xdifference * pginfo->spread), 01281 (int)ydifference); 01282 } 01283 01284 // snap to right edge for last entry. 01285 if (tempRect.right() + 2 >= programRect.width()) 01286 tempRect.setRight(programRect.width()); 01287 if (tempRect.bottom() + 2 >= programRect.bottom()) 01288 tempRect.setBottom(programRect.bottom()); 01289 01290 if (m_currentRow == (int)row && (m_currentCol >= x) && 01291 (m_currentCol < (x + spread))) 01292 isCurrent = true; 01293 else 01294 isCurrent = false; 01295 01296 int recFlag; 01297 switch (pginfo->GetRecordingRuleType()) 01298 { 01299 case kSingleRecord: 01300 recFlag = 1; 01301 break; 01302 case kTimeslotRecord: 01303 recFlag = 2; 01304 break; 01305 case kChannelRecord: 01306 recFlag = 3; 01307 break; 01308 case kAllRecord: 01309 recFlag = 4; 01310 break; 01311 case kWeekslotRecord: 01312 recFlag = 5; 01313 break; 01314 case kFindOneRecord: 01315 case kFindDailyRecord: 01316 case kFindWeeklyRecord: 01317 recFlag = 6; 01318 break; 01319 case kOverrideRecord: 01320 case kDontRecord: 01321 recFlag = 7; 01322 break; 01323 case kNotRecording: 01324 default: 01325 recFlag = 0; 01326 break; 01327 } 01328 01329 int recStat; 01330 if (pginfo->GetRecordingStatus() == rsConflict || 01331 pginfo->GetRecordingStatus() == rsOffLine) 01332 recStat = 2; 01333 else if (pginfo->GetRecordingStatus() <= rsWillRecord) 01334 recStat = 1; 01335 else 01336 recStat = 0; 01337 01338 m_guideGrid->SetProgramInfo( 01339 row, cnt, tempRect, pginfo->GetTitle(), 01340 pginfo->GetCategory(), arrow, recFlag, 01341 recStat, isCurrent); 01342 01343 cnt++; 01344 } 01345 01346 lastprog = pginfo->GetScheduledStartTime(); 01347 } 01348 } 01349 01350 void GuideGrid::customEvent(QEvent *event) 01351 { 01352 if ((MythEvent::Type)(event->type()) == MythEvent::MythEventMessage) 01353 { 01354 MythEvent *me = (MythEvent *)event; 01355 QString message = me->Message(); 01356 01357 if (message == "SCHEDULE_CHANGE") 01358 { 01359 LoadFromScheduler(m_recList); 01360 fillProgramInfos(); 01361 updateInfo(); 01362 } 01363 else if (message == "STOP_VIDEO_REFRESH_TIMER") 01364 { 01365 m_previewVideoRefreshTimer->stop(); 01366 } 01367 else if (message == "START_VIDEO_REFRESH_TIMER") 01368 { 01369 m_previewVideoRefreshTimer->start(66); 01370 } 01371 } 01372 else if (event->type() == DialogCompletionEvent::kEventType) 01373 { 01374 DialogCompletionEvent *dce = (DialogCompletionEvent*)(event); 01375 01376 QString resultid = dce->GetId(); 01377 QString resulttext = dce->GetResultText(); 01378 int buttonnum = dce->GetResult(); 01379 01380 if (resultid == "deleterule") 01381 { 01382 RecordingRule *record = 01383 qVariantValue<RecordingRule *>(dce->GetData()); 01384 if (record) 01385 { 01386 if ((buttonnum > 0) && !record->Delete()) 01387 LOG(VB_GENERAL, LOG_ERR, "Failed to delete recording rule"); 01388 delete record; 01389 } 01390 } 01391 else if (resultid == "guidemenu") 01392 { 01393 if (resulttext == tr("Record This")) 01394 { 01395 quickRecord(); 01396 } 01397 else if (resulttext == tr("Change to Channel")) 01398 { 01399 enter(); 01400 } 01401 else if (resulttext == tr("Program Details")) 01402 { 01403 details(); 01404 } 01405 else if (resulttext == tr("Reverse Channel Order")) 01406 { 01407 m_sortReverse = !m_sortReverse; 01408 generateListings(); 01409 updateChannels(); 01410 } 01411 else if (resulttext == tr("Add To Channel Group")) 01412 { 01413 if (m_changrpid == -1) 01414 ChannelGroupMenu(0); 01415 } 01416 else if (resulttext == tr("Remove from Channel Group")) 01417 { 01418 toggleChannelFavorite(); 01419 } 01420 else if (resulttext == tr("Choose Channel Group")) 01421 { 01422 ChannelGroupMenu(1); 01423 } 01424 else if (resulttext == tr("Recording Options")) 01425 { 01426 ShowRecordingMenu(); 01427 } 01428 else if (resulttext == tr("Jump to Time")) 01429 { 01430 ShowJumpToTime(); 01431 } 01432 } 01433 else if (resultid == "recmenu") 01434 { 01435 if (resulttext == tr("Edit Recording Status")) 01436 { 01437 editRecSchedule(); 01438 } 01439 else if (resulttext == tr("Edit Schedule")) 01440 { 01441 editSchedule(); 01442 } 01443 else if (resulttext == tr("Upcoming")) 01444 { 01445 upcoming(); 01446 } 01447 else if (resulttext == tr("Custom Edit")) 01448 { 01449 customEdit(); 01450 } 01451 else if (resulttext == tr("Delete Rule")) 01452 { 01453 deleteRule(); 01454 } 01455 01456 } 01457 else if (resultid == "channelgrouptogglemenu") 01458 { 01459 int changroupid; 01460 changroupid = ChannelGroup::GetChannelGroupId(resulttext); 01461 01462 if (changroupid > 0) 01463 toggleChannelFavorite(changroupid); 01464 } 01465 else if (resultid == "channelgroupmenu") 01466 { 01467 if (buttonnum >= 0) 01468 { 01469 int changroupid; 01470 01471 if (resulttext == QObject::tr("All Channels")) 01472 changroupid = -1; 01473 else 01474 changroupid = ChannelGroup::GetChannelGroupId(resulttext); 01475 01476 m_changrpid = changroupid; 01477 generateListings(); 01478 updateChannels(); 01479 updateInfo(); 01480 01481 QString changrpname; 01482 changrpname = ChannelGroup::GetChannelGroupName(m_changrpid); 01483 01484 if (m_changroupname) 01485 m_changroupname->SetText(changrpname); 01486 } 01487 } 01488 else if (resultid == "jumptotime") 01489 { 01490 QDateTime datetime = dce->GetData().toDateTime(); 01491 moveToTime(datetime); 01492 } 01493 else 01494 ScheduleCommon::customEvent(event); 01495 } 01496 } 01497 01498 void GuideGrid::updateDateText(void) 01499 { 01500 if (m_dateText) 01501 m_dateText->SetText(MythDateTimeToString(m_currentStartTime, kDateShort)); 01502 if (m_longdateText) 01503 m_longdateText->SetText(MythDateTimeToString(m_currentStartTime, 01504 (kDateFull | kSimplify))); 01505 } 01506 01507 void GuideGrid::updateChannels(void) 01508 { 01509 m_channelList->Reset(); 01510 01511 DBChannel *chinfo = GetChannelInfo(m_currentStartChannel); 01512 01513 if (m_player) 01514 m_player->ClearTunableCache(); 01515 01516 for (unsigned int y = 0; (y < (unsigned int)m_channelCount) && chinfo; ++y) 01517 { 01518 unsigned int chanNumber = y + m_currentStartChannel; 01519 if (chanNumber >= m_channelInfos.size()) 01520 chanNumber -= m_channelInfos.size(); 01521 if (chanNumber >= m_channelInfos.size()) 01522 break; 01523 01524 chinfo = GetChannelInfo(chanNumber); 01525 01526 bool unavailable = false, try_alt = false; 01527 01528 if (m_player) 01529 { 01530 const PlayerContext *ctx = m_player->GetPlayerReadLock( 01531 -1, __FILE__, __LINE__); 01532 if (ctx && chinfo) 01533 try_alt = !m_player->IsTunable(ctx, chinfo->chanid, true); 01534 m_player->ReturnPlayerLock(ctx); 01535 } 01536 01537 if (try_alt) 01538 { 01539 unavailable = true; 01540 01541 // Try alternates with same channum if applicable 01542 uint alt = GetAlternateChannelIndex(chanNumber, true); 01543 if (alt != m_channelInfoIdx[chanNumber]) 01544 { 01545 unavailable = false; 01546 m_channelInfoIdx[chanNumber] = alt; 01547 chinfo = GetChannelInfo(chanNumber); 01548 } 01549 01550 // Try alternates with different channum if applicable 01551 if (unavailable && chinfo && 01552 !GetProgramList(chinfo->chanid).empty()) 01553 { 01554 alt = GetAlternateChannelIndex(chanNumber, false); 01555 unavailable = (alt == m_channelInfoIdx[chanNumber]); 01556 } 01557 } 01558 01559 MythUIButtonListItem *item = 01560 new MythUIButtonListItem(m_channelList, 01561 chinfo ? chinfo->GetFormatted(DBChannel::kChannelShort) : QString()); 01562 01563 QString state = "available"; 01564 if (unavailable) 01565 state = (m_changrpid == -1) ? "unavailable" : "favunavailable"; 01566 else 01567 state = (m_changrpid == -1) ? "available" : "favourite"; 01568 01569 item->SetFontState(state); 01570 item->DisplayState(state, "chanstatus"); 01571 01572 if (chinfo) 01573 { 01574 InfoMap infomap; 01575 chinfo->ToMap(infomap); 01576 item->SetTextFromMap(infomap); 01577 01578 if (!chinfo->icon.isEmpty()) 01579 { 01580 QString iconurl = 01581 gCoreContext->GetMasterHostPrefix("ChannelIcons", 01582 chinfo->icon); 01583 item->SetImage(iconurl, "channelicon"); 01584 } 01585 } 01586 } 01587 } 01588 01589 void GuideGrid::updateInfo(void) 01590 { 01591 if (m_currentRow < 0 || m_currentCol < 0) 01592 return; 01593 01594 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 01595 if (!pginfo) 01596 return; 01597 01598 InfoMap infoMap; 01599 01600 int chanNum = m_currentRow + m_currentStartChannel; 01601 if (chanNum >= (int)m_channelInfos.size()) 01602 chanNum -= (int)m_channelInfos.size(); 01603 if (chanNum >= (int)m_channelInfos.size()) 01604 return; 01605 if (chanNum < 0) 01606 chanNum = 0; 01607 01608 DBChannel *chinfo = GetChannelInfo(chanNum); 01609 01610 if (m_channelImage) 01611 { 01612 m_channelImage->Reset(); 01613 if (!chinfo->icon.isEmpty()) 01614 { 01615 QString iconurl = gCoreContext->GetMasterHostPrefix("ChannelIcons", 01616 chinfo->icon); 01617 01618 m_channelImage->SetFilename(iconurl); 01619 m_channelImage->Load(); 01620 } 01621 } 01622 01623 chinfo->ToMap(infoMap); 01624 pginfo->ToMap(infoMap); 01625 SetTextFromMap(infoMap); 01626 01627 MythUIStateType *ratingState = dynamic_cast<MythUIStateType*> 01628 (GetChild("ratingstate")); 01629 if (ratingState) 01630 { 01631 QString rating = QString::number(pginfo->GetStars(10)); 01632 ratingState->DisplayState(rating); 01633 } 01634 } 01635 01636 void GuideGrid::toggleGuideListing() 01637 { 01638 int oldchangrpid = m_changrpid; 01639 01640 m_changrpid = ChannelGroup::GetNextChannelGroup(m_changrplist, oldchangrpid); 01641 01642 if (oldchangrpid != m_changrpid) 01643 generateListings(); 01644 01645 updateChannels(); 01646 updateInfo(); 01647 01648 QString changrpname = ChannelGroup::GetChannelGroupName(m_changrpid); 01649 01650 if (m_changroupname) 01651 m_changroupname->SetText(changrpname); 01652 } 01653 01654 void GuideGrid::generateListings() 01655 { 01656 m_currentStartChannel = 0; 01657 m_currentRow = 0; 01658 01659 int maxchannel = 0; 01660 fillChannelInfos(); 01661 maxchannel = max((int)GetChannelCount() - 1, 0); 01662 m_channelCount = min(m_guideGrid->getChannelCount(), maxchannel + 1); 01663 01664 LoadFromScheduler(m_recList); 01665 fillProgramInfos(); 01666 } 01667 01668 void GuideGrid::ChannelGroupMenu(int mode) 01669 { 01670 if (m_changrplist.empty()) 01671 { 01672 QString message = tr("You don't have any channel groups defined"); 01673 01674 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 01675 01676 MythConfirmationDialog *okPopup = new MythConfirmationDialog(popupStack, 01677 message, false); 01678 if (okPopup->Create()) 01679 popupStack->AddScreen(okPopup); 01680 else 01681 delete okPopup; 01682 01683 return; 01684 } 01685 01686 QString label = tr("Select Channel Group"); 01687 01688 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 01689 MythDialogBox *menuPopup = new MythDialogBox(label, popupStack, "menuPopup"); 01690 01691 if (menuPopup->Create()) 01692 { 01693 if (mode == 0) 01694 { 01695 // add channel to group menu 01696 menuPopup->SetReturnEvent(this, "channelgrouptogglemenu"); 01697 ChannelGroupList channels = ChannelGroup::GetChannelGroups(true); 01698 for (uint i = 0; i < channels.size(); ++i) 01699 menuPopup->AddButton(channels[i].name); 01700 } 01701 else 01702 { 01703 // switch to channel group menu 01704 menuPopup->SetReturnEvent(this, "channelgroupmenu"); 01705 menuPopup->AddButton(QObject::tr("All Channels")); 01706 for (uint i = 0; i < m_changrplist.size(); ++i) 01707 menuPopup->AddButton(m_changrplist[i].name); 01708 } 01709 01710 popupStack->AddScreen(menuPopup); 01711 } 01712 else 01713 { 01714 delete menuPopup; 01715 } 01716 } 01717 01718 void GuideGrid::toggleChannelFavorite(int grpid) 01719 { 01720 MSqlQuery query(MSqlQuery::InitCon()); 01721 01722 if (grpid == -1) 01723 { 01724 if (m_changrpid == -1) 01725 return; 01726 else 01727 grpid = m_changrpid; 01728 } 01729 01730 // Get current channel id, and make sure it exists... 01731 int chanNum = m_currentRow + m_currentStartChannel; 01732 if (chanNum >= (int)m_channelInfos.size()) 01733 chanNum -= (int)m_channelInfos.size(); 01734 if (chanNum >= (int)m_channelInfos.size()) 01735 return; 01736 if (chanNum < 0) 01737 chanNum = 0; 01738 01739 DBChannel *ch = GetChannelInfo(chanNum); 01740 uint chanid = ch->chanid; 01741 01742 if (m_changrpid == -1) 01743 // If currently viewing all channels, allow to add only not delete 01744 ChannelGroup::ToggleChannel(chanid, grpid, false); 01745 else 01746 // Only allow delete if viewing the favorite group in question 01747 ChannelGroup::ToggleChannel(chanid, grpid, true); 01748 01749 // If viewing favorites, refresh because a channel was removed 01750 if (m_changrpid != -1) 01751 { 01752 generateListings(); 01753 updateChannels(); 01754 updateInfo(); 01755 } 01756 } 01757 01758 void GuideGrid::cursorLeft() 01759 { 01760 ProgramInfo *test = m_programInfos[m_currentRow][m_currentCol]; 01761 01762 if (!test) 01763 { 01764 moveLeftRight(kScrollLeft); 01765 return; 01766 } 01767 01768 int startCol = test->startCol; 01769 m_currentCol = startCol - 1; 01770 01771 if (m_currentCol < 0) 01772 { 01773 m_currentCol = 0; 01774 moveLeftRight(kScrollLeft); 01775 } 01776 else 01777 { 01778 fillProgramRowInfos(m_currentRow); 01779 m_guideGrid->SetRedraw(); 01780 updateInfo(); 01781 } 01782 } 01783 01784 void GuideGrid::cursorRight() 01785 { 01786 ProgramInfo *test = m_programInfos[m_currentRow][m_currentCol]; 01787 01788 if (!test) 01789 { 01790 moveLeftRight(kScrollRight); 01791 return; 01792 } 01793 01794 int spread = test->spread; 01795 int startCol = test->startCol; 01796 01797 m_currentCol = startCol + spread; 01798 01799 if (m_currentCol > m_timeCount - 1) 01800 { 01801 m_currentCol = m_timeCount - 1; 01802 moveLeftRight(kScrollRight); 01803 } 01804 else 01805 { 01806 fillProgramRowInfos(m_currentRow); 01807 m_guideGrid->SetRedraw(); 01808 updateInfo(); 01809 } 01810 } 01811 01812 void GuideGrid::cursorDown() 01813 { 01814 m_currentRow++; 01815 01816 if (m_currentRow > m_channelCount - 1) 01817 { 01818 m_currentRow = m_channelCount - 1; 01819 moveUpDown(kScrollDown); 01820 } 01821 else 01822 { 01823 fillProgramRowInfos(m_currentRow); 01824 m_guideGrid->SetRedraw(); 01825 updateInfo(); 01826 updateChannels(); 01827 } 01828 } 01829 01830 void GuideGrid::cursorUp() 01831 { 01832 m_currentRow--; 01833 01834 if (m_currentRow < 0) 01835 { 01836 m_currentRow = 0; 01837 moveUpDown(kScrollUp); 01838 } 01839 else 01840 { 01841 fillProgramRowInfos(m_currentRow); 01842 m_guideGrid->SetRedraw(); 01843 updateInfo(); 01844 updateChannels(); 01845 } 01846 } 01847 01848 void GuideGrid::moveLeftRight(MoveVector movement) 01849 { 01850 switch (movement) 01851 { 01852 case kScrollLeft : 01853 m_currentStartTime = m_currentStartTime.addSecs(-30 * 60); 01854 break; 01855 case kScrollRight : 01856 m_currentStartTime = m_currentStartTime.addSecs(30 * 60); 01857 break; 01858 case kPageLeft : 01859 m_currentStartTime = m_currentStartTime.addSecs(-5 * 60 * m_timeCount); 01860 break; 01861 case kPageRight : 01862 m_currentStartTime = m_currentStartTime.addSecs(5 * 60 * m_timeCount); 01863 break; 01864 case kDayLeft : 01865 m_currentStartTime = m_currentStartTime.addSecs(-24 * 60 * 60); 01866 break; 01867 case kDayRight : 01868 m_currentStartTime = m_currentStartTime.addSecs(24 * 60 * 60); 01869 break; 01870 default : 01871 break; 01872 } 01873 01874 fillTimeInfos(); 01875 fillProgramInfos(); 01876 m_guideGrid->SetRedraw(); 01877 updateInfo(); 01878 updateDateText(); 01879 } 01880 01881 void GuideGrid::moveUpDown(MoveVector movement) 01882 { 01883 switch (movement) 01884 { 01885 case kScrollDown : 01886 setStartChannel(m_currentStartChannel + 1); 01887 break; 01888 case kScrollUp : 01889 setStartChannel((int)(m_currentStartChannel) - 1); 01890 break; 01891 case kPageDown : 01892 setStartChannel(m_currentStartChannel + m_channelCount); 01893 break; 01894 case kPageUp : 01895 setStartChannel((int)(m_currentStartChannel) - m_channelCount); 01896 break; 01897 default : 01898 break; 01899 } 01900 01901 fillProgramInfos(); 01902 m_guideGrid->SetRedraw(); 01903 updateInfo(); 01904 updateChannels(); 01905 } 01906 01907 void GuideGrid::moveToTime(QDateTime datetime) 01908 { 01909 if (!datetime.isValid()) 01910 return; 01911 01912 m_currentStartTime = datetime; 01913 01914 fillTimeInfos(); 01915 fillProgramInfos(); 01916 m_guideGrid->SetRedraw(); 01917 updateInfo(); 01918 updateDateText(); 01919 } 01920 01921 void GuideGrid::setStartChannel(int newStartChannel) 01922 { 01923 if (newStartChannel < 0) 01924 m_currentStartChannel = newStartChannel + GetChannelCount(); 01925 else if (newStartChannel >= (int) GetChannelCount()) 01926 m_currentStartChannel = newStartChannel - GetChannelCount(); 01927 else 01928 m_currentStartChannel = newStartChannel; 01929 } 01930 01931 void GuideGrid::showProgFinder() 01932 { 01933 if (m_allowFinder) 01934 RunProgramFinder(m_player, m_embedVideo, false); 01935 } 01936 01937 void GuideGrid::enter() 01938 { 01939 if (!m_player) 01940 return; 01941 01942 if (m_updateTimer) 01943 m_updateTimer->stop(); 01944 01945 channelUpdate(); 01946 01947 // Don't perform transition effects when guide is being used during playback 01948 GetScreenStack()->PopScreen(this, false); 01949 01950 epgIsVisibleCond.wakeAll(); 01951 } 01952 01953 void GuideGrid::Close() 01954 { 01955 // HACK: Do not allow exit if we have a popup menu open, not convinced 01956 // that this is the right solution 01957 if (GetMythMainWindow()->GetStack("popup stack")->TotalScreens() > 0) 01958 return; 01959 01960 if (m_updateTimer) 01961 m_updateTimer->stop(); 01962 01963 // don't fade the screen if we are returning to the player 01964 if (m_player) 01965 GetScreenStack()->PopScreen(this, false); 01966 else 01967 GetScreenStack()->PopScreen(this, true); 01968 01969 epgIsVisibleCond.wakeAll(); 01970 } 01971 01972 void GuideGrid::quickRecord() 01973 { 01974 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 01975 01976 if (!pginfo) 01977 return; 01978 01979 if (pginfo->GetTitle() == kUnknownTitle) 01980 return; 01981 01982 RecordingInfo ri(*pginfo); 01983 ri.ToggleRecord(); 01984 *pginfo = ri; 01985 01986 LoadFromScheduler(m_recList); 01987 fillProgramInfos(); 01988 updateInfo(); 01989 } 01990 01991 void GuideGrid::editRecSchedule() 01992 { 01993 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 01994 01995 if (!pginfo) 01996 return; 01997 01998 if (pginfo->GetTitle() == kUnknownTitle) 01999 return; 02000 02001 EditRecording(pginfo); 02002 } 02003 02004 void GuideGrid::editSchedule() 02005 { 02006 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 02007 02008 if (!pginfo) 02009 return; 02010 02011 if (pginfo->GetTitle() == kUnknownTitle) 02012 return; 02013 02014 EditScheduled(pginfo); 02015 } 02016 02017 void GuideGrid::customEdit() 02018 { 02019 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 02020 02021 EditCustom(pginfo); 02022 } 02023 02024 void GuideGrid::deleteRule() 02025 { 02026 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 02027 02028 if (!pginfo || !pginfo->GetRecordingRuleID()) 02029 return; 02030 02031 RecordingRule *record = new RecordingRule(); 02032 if (!record->LoadByProgram(pginfo)) 02033 { 02034 delete record; 02035 return; 02036 } 02037 02038 QString message = tr("Delete '%1' %2 rule?").arg(record->m_title) 02039 .arg(toString(pginfo->GetRecordingRuleType())); 02040 02041 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 02042 02043 MythConfirmationDialog *okPopup = new MythConfirmationDialog(popupStack, 02044 message, true); 02045 02046 okPopup->SetReturnEvent(this, "deleterule"); 02047 okPopup->SetData(qVariantFromValue(record)); 02048 02049 if (okPopup->Create()) 02050 popupStack->AddScreen(okPopup); 02051 else 02052 delete okPopup; 02053 } 02054 02055 void GuideGrid::upcoming() 02056 { 02057 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 02058 02059 if (!pginfo) 02060 return; 02061 02062 if (pginfo->GetTitle() == kUnknownTitle) 02063 return; 02064 02065 ShowUpcoming(pginfo); 02066 } 02067 02068 void GuideGrid::details() 02069 { 02070 ProgramInfo *pginfo = m_programInfos[m_currentRow][m_currentCol]; 02071 02072 if (!pginfo) 02073 return; 02074 02075 if (pginfo->GetTitle() == kUnknownTitle) 02076 return; 02077 02078 ShowDetails(pginfo); 02079 } 02080 02081 void GuideGrid::channelUpdate(void) 02082 { 02083 if (!m_player) 02084 return; 02085 02086 DBChanList sel = GetSelection(); 02087 02088 if (sel.size()) 02089 { 02090 PlayerContext *ctx = m_player->GetPlayerReadLock(-1, __FILE__, __LINE__); 02091 m_player->ChangeChannel(ctx, sel); 02092 m_player->ReturnPlayerLock(ctx); 02093 } 02094 } 02095 02096 void GuideGrid::volumeUpdate(bool up) 02097 { 02098 if (m_player) 02099 { 02100 PlayerContext *ctx = m_player->GetPlayerReadLock(-1, __FILE__, __LINE__); 02101 m_player->ChangeVolume(ctx, up); 02102 m_player->ReturnPlayerLock(ctx); 02103 } 02104 } 02105 02106 void GuideGrid::toggleMute(const bool muteIndividualChannels) 02107 { 02108 if (m_player) 02109 { 02110 PlayerContext *ctx = m_player->GetPlayerReadLock(-1, __FILE__, __LINE__); 02111 m_player->ToggleMute(ctx, muteIndividualChannels); 02112 m_player->ReturnPlayerLock(ctx); 02113 } 02114 } 02115 02116 void GuideGrid::GoTo(int start, int cur_row) 02117 { 02118 setStartChannel(start); 02119 m_currentRow = cur_row % m_channelCount; 02120 updateChannels(); 02121 fillProgramInfos(); 02122 updateInfo(); 02123 updateJumpToChannel(); 02124 } 02125 02126 void GuideGrid::updateJumpToChannel(void) 02127 { 02128 QString txt; 02129 { 02130 QMutexLocker locker(&m_jumpToChannelLock); 02131 if (m_jumpToChannel) 02132 txt = m_jumpToChannel->GetEntry(); 02133 } 02134 02135 if (txt.isEmpty()) 02136 return; 02137 02138 if (m_jumpToText) 02139 m_jumpToText->SetText(txt); 02140 } 02141 02142 void GuideGrid::SetJumpToChannel(JumpToChannel *ptr) 02143 { 02144 QMutexLocker locker(&m_jumpToChannelLock); 02145 m_jumpToChannel = ptr; 02146 02147 if (!m_jumpToChannel) 02148 { 02149 if (m_jumpToText) 02150 m_jumpToText->Reset(); 02151 02152 updateDateText(); 02153 } 02154 } 02155 02156 void GuideGrid::HideTVWindow(void) 02157 { 02158 GetMythMainWindow()->GetPaintWindow()->clearMask(); 02159 } 02160 02161 void GuideGrid::EmbedTVWindow(void) 02162 { 02163 MythEvent *me = new MythEvent("STOP_VIDEO_REFRESH_TIMER"); 02164 qApp->postEvent(this, me); 02165 02166 m_usingNullVideo = !m_player->StartEmbedding(m_videoRect); 02167 if (!m_usingNullVideo) 02168 { 02169 QRegion r1 = QRegion(m_Area); 02170 QRegion r2 = QRegion(m_videoRect); 02171 GetMythMainWindow()->GetPaintWindow()->setMask(r1.xored(r2)); 02172 m_player->DrawUnusedRects(); 02173 } 02174 else 02175 { 02176 me = new MythEvent("START_VIDEO_REFRESH_TIMER"); 02177 qApp->postEvent(this, me); 02178 } 02179 } 02180 02181 void GuideGrid::refreshVideo(void) 02182 { 02183 if (m_player && m_usingNullVideo) 02184 { 02185 GetMythMainWindow()->GetPaintWindow()->update(m_videoRect); 02186 } 02187 } 02188 02189 void GuideGrid::aboutToHide(void) 02190 { 02191 if (m_player) 02192 HideTVWindow(); 02193 02194 MythScreenType::aboutToHide(); 02195 } 02196 02197 void GuideGrid::aboutToShow(void) 02198 { 02199 if (m_player) 02200 EmbedTVWindow(); 02201 02202 MythScreenType::aboutToShow(); 02203 } 02204 02205 void GuideGrid::ShowJumpToTime(void) 02206 { 02207 QString message = tr("Jump to a specific date and time in the guide"); 02208 int flags = (MythTimeInputDialog::kAllDates | MythTimeInputDialog::kDay | 02209 MythTimeInputDialog::kHours); 02210 02211 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 02212 MythTimeInputDialog *timedlg = new MythTimeInputDialog(popupStack, message, 02213 flags); 02214 02215 if (timedlg->Create()) 02216 { 02217 timedlg->SetReturnEvent(this, "jumptotime"); 02218 popupStack->AddScreen(timedlg); 02219 } 02220 else 02221 delete timedlg; 02222 } 02223
1.7.6.1