00001
00002 #include "guidegrid.h"
00003
00004
00005 #include <math.h>
00006 #include <unistd.h>
00007 #include <iostream>
00008 #include <algorithm>
00009 using namespace std;
00010
00011
00012 #include <QCoreApplication>
00013 #include <QKeyEvent>
00014 #include <QDateTime>
00015
00016
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
00148 int i = m_listener->FindChannel(0, m_entry, false);
00149 if (i >= 0)
00150 {
00151
00152 m_timer->start(kJumpToChannelTimeout);
00153
00154
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 {
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
00171 if (changrpid == -2)
00172 changrpid = gCoreContext->GetNumSetting("ChannelGroupDefault", -1);
00173
00174
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
00389
00390 if (m_player && m_allowFinder)
00391 {
00392 QString message = QString("EPG_EXITING");
00393 qApp->postEvent(m_player, new MythEvent(message));
00394 }
00395
00396
00397
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
00426
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
00521
00522
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
00891 db_chan_list_t tmp;
00892 tmp.push_back(val);
00893 m_channelInfos.push_back(tmp);
00894 }
00895
00896
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
01744 ChannelGroup::ToggleChannel(chanid, grpid, false);
01745 else
01746
01747 ChannelGroup::ToggleChannel(chanid, grpid, true);
01748
01749
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
01948 GetScreenStack()->PopScreen(this, false);
01949
01950 epgIsVisibleCond.wakeAll();
01951 }
01952
01953 void GuideGrid::Close()
01954 {
01955
01956
01957 if (GetMythMainWindow()->GetStack("popup stack")->TotalScreens() > 0)
01958 return;
01959
01960 if (m_updateTimer)
01961 m_updateTimer->stop();
01962
01963
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