MythTV  0.26-pre
guidegrid.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends