MythTV  0.25-pre
mythmainwindow.cpp
Go to the documentation of this file.
00001 #include "mythmainwindow.h"
00002 #include "mythmainwindow_internal.h"
00003 
00004 // C headers
00005 #include <cmath>
00006 
00007 // C++ headers
00008 #include <algorithm>
00009 #include <vector>
00010 using namespace std;
00011 
00012 // QT headers
00013 #ifdef USE_OPENGL_PAINTER
00014 #include <QGLWidget>
00015 #endif
00016 
00017 #include <QWaitCondition>
00018 #include <QApplication>
00019 #include <QTimer>
00020 #include <QDesktopWidget>
00021 #include <QHash>
00022 #include <QFile>
00023 #include <QDir>
00024 #include <QEvent>
00025 #include <QKeyEvent>
00026 #include <QKeySequence>
00027 #include <QSize>
00028 
00029 // Platform headers
00030 #include "unistd.h"
00031 #ifdef QWS
00032 #include <qwindowsystem_qws.h>
00033 #endif
00034 #ifdef Q_WS_MACX_OLDQT
00035 #include <HIToolbox/Menus.h>   // For GetMBarHeight()
00036 #endif
00037 
00038 // libmythbase headers
00039 #include "mythdb.h"
00040 #include "mythlogging.h"
00041 #include "mythevent.h"
00042 #include "mythdirs.h"
00043 #include "compat.h"
00044 #include "mythsignalingtimer.h"
00045 #include "mythcorecontext.h"
00046 #include "mythmedia.h"
00047 
00048 // libmythui headers
00049 #include "myththemebase.h"
00050 #include "screensaver.h"
00051 #include "lirc.h"
00052 #include "lircevent.h"
00053 #include "mythudplistener.h"
00054 #include "mythrender_base.h"
00055 #include "mythuistatetracker.h"
00056 
00057 #ifdef USING_APPLEREMOTE
00058 #include "AppleRemoteListener.h"
00059 #endif
00060 
00061 #ifdef USE_JOYSTICK_MENU
00062 #include "jsmenu.h"
00063 #include "jsmenuevent.h"
00064 #endif
00065 
00066 #ifdef USING_LIBCEC
00067 #include "cecadapter.h"
00068 #endif
00069 
00070 #include "mythscreentype.h"
00071 #include "mythpainter.h"
00072 #ifdef USE_OPENGL_PAINTER
00073 #include "mythpainter_ogl.h"
00074 #endif
00075 #include "mythpainter_qt.h"
00076 #include "mythgesture.h"
00077 #include "mythuihelper.h"
00078 #include "mythdialogbox.h"
00079 
00080 #ifdef USING_MINGW
00081 #include "mythpainter_d3d9.h"
00082 #endif
00083 
00084 #define GESTURE_TIMEOUT 1000
00085 #define STANDBY_TIMEOUT 30 // Minutes
00086 
00087 #define LOC      QString("MythMainWindow: ")
00088 
00089 class KeyContext
00090 {
00091   public:
00092     void AddMapping(int key, QString action)
00093     {
00094         actionMap[key].append(action);
00095     }
00096 
00097     bool GetMapping(int key, QStringList &actions)
00098     {
00099         if (actionMap.count(key) > 0)
00100         {
00101             actions += actionMap[key];
00102             return true;
00103         }
00104         return false;
00105     }
00106 
00107     QMap<int, QStringList> actionMap;
00108 };
00109 
00110 struct JumpData
00111 {
00112     void (*callback)(void);
00113     QString destination;
00114     QString description;
00115     bool exittomain;
00116     QString localAction;
00117 };
00118 
00119 struct MPData {
00120     QString description;
00121     MediaPlayCallback playFn;
00122 };
00123 
00124 class MythMainWindowPrivate
00125 {
00126   public:
00127     MythMainWindowPrivate() :
00128         wmult(1.0f), hmult(1.0f),
00129         screenwidth(0), screenheight(0),
00130         xbase(0), ybase(0),
00131         does_fill_screen(false),
00132         ignore_lirc_keys(false),
00133         ignore_joystick_keys(false),
00134         lircThread(NULL),
00135 #ifdef USE_JOYSTICK_MENU
00136         joystickThread(NULL),
00137 #endif
00138 
00139 #ifdef USING_APPLEREMOTE
00140         appleRemoteListener(NULL),
00141         appleRemote(NULL),
00142 #endif
00143 
00144 #ifdef USING_LIBCEC
00145         cecAdapter(NULL),
00146 #endif
00147 
00148         exitingtomain(false),
00149         popwindows(false),
00150 
00151         m_useDB(true),
00152 
00153         exitmenucallback(NULL),
00154 
00155         exitmenumediadevicecallback(NULL),
00156         mediadeviceforcallback(NULL),
00157 
00158         escapekey(0),
00159 
00160         sysEventHandler(NULL),
00161 
00162         drawInterval(1000 / 70),
00163         drawTimer(NULL),
00164         mainStack(NULL),
00165 
00166         painter(NULL),
00167         render(NULL),
00168 
00169         AllowInput(true),
00170 
00171         gesture(MythGesture()),
00172         gestureTimer(NULL),
00173         hideMouseTimer(NULL),
00174 
00175         paintwin(NULL),
00176 
00177         oldpaintwin(NULL),
00178         oldpainter(NULL),
00179         oldrender(NULL),
00180 
00181         m_drawDisabledDepth(0),
00182         m_drawEnabled(true),
00183 
00184         m_themeBase(NULL),
00185 
00186         m_udpListener(NULL),
00187 
00188         m_pendingUpdate(false),
00189 
00190         idleTimer(NULL),
00191         standby(false)
00192     {
00193     }
00194 
00195     int TranslateKeyNum(QKeyEvent *e);
00196 
00197     float wmult, hmult;
00198     int screenwidth, screenheight;
00199 
00200     QRect screenRect;
00201     QRect uiScreenRect;
00202 
00203     int xbase, ybase;
00204     bool does_fill_screen;
00205 
00206     bool ignore_lirc_keys;
00207     bool ignore_joystick_keys;
00208 
00209     LIRC *lircThread;
00210 
00211 #ifdef USE_JOYSTICK_MENU
00212     JoystickMenuThread *joystickThread;
00213 #endif
00214 
00215 #ifdef USING_APPLEREMOTE
00216     AppleRemoteListener *appleRemoteListener;
00217     AppleRemote         *appleRemote;
00218 #endif
00219 
00220 #ifdef USING_LIBCEC
00221     CECAdapter* cecAdapter;
00222 #endif
00223 
00224     bool exitingtomain;
00225     bool popwindows;
00226 
00227     bool m_useDB;              
00228 
00229     QHash<QString, KeyContext *> keyContexts;
00230     QMap<int, JumpData*> jumpMap;
00231     QMap<QString, JumpData> destinationMap;
00232     QMap<QString, MPData> mediaPluginMap;
00233 
00234     void (*exitmenucallback)(void);
00235 
00236     void (*exitmenumediadevicecallback)(MythMediaDevice* mediadevice);
00237     MythMediaDevice * mediadeviceforcallback;
00238 
00239     int escapekey;
00240 
00241     QObject *sysEventHandler;
00242 
00243     int drawInterval;
00244     MythSignalingTimer *drawTimer;
00245     QVector<MythScreenStack *> stackList;
00246     MythScreenStack *mainStack;
00247 
00248     MythPainter *painter;
00249     MythRender  *render;
00250 
00251 
00252     bool AllowInput;
00253 
00254     QRegion repaintRegion;
00255 
00256     MythGesture gesture;
00257     QTimer *gestureTimer;
00258     QTimer *hideMouseTimer;
00259 
00260     /* compatibility only, FIXME remove */
00261     std::vector<QWidget *> widgetList;
00262 
00263     QWidget *paintwin;
00264 
00265     QWidget *oldpaintwin;
00266     MythPainter *oldpainter;
00267     MythRender *oldrender;
00268 
00269     QMutex m_drawDisableLock;
00270     QMutex m_setDrawEnabledLock;
00271     QWaitCondition m_setDrawEnabledWait;
00272     uint m_drawDisabledDepth;
00273     bool m_drawEnabled;
00274 
00275     MythThemeBase *m_themeBase;
00276     MythUDPListener *m_udpListener;
00277 
00278     bool m_pendingUpdate;
00279 
00280     QTimer *idleTimer;
00281     bool standby;
00282 };
00283 
00284 // Make keynum in QKeyEvent be equivalent to what's in QKeySequence
00285 int MythMainWindowPrivate::TranslateKeyNum(QKeyEvent* e)
00286 {
00287     int keynum = e->key();
00288 
00289     if ((keynum != Qt::Key_Shift  ) && (keynum !=Qt::Key_Control   ) &&
00290         (keynum != Qt::Key_Meta   ) && (keynum !=Qt::Key_Alt       ) &&
00291         (keynum != Qt::Key_Super_L) && (keynum !=Qt::Key_Super_R   ) &&
00292         (keynum != Qt::Key_Hyper_L) && (keynum !=Qt::Key_Hyper_R   ) &&
00293         (keynum != Qt::Key_AltGr  ) && (keynum !=Qt::Key_CapsLock  ) &&
00294         (keynum != Qt::Key_NumLock) && (keynum !=Qt::Key_ScrollLock ))
00295     {
00296         Qt::KeyboardModifiers modifiers;
00297         // if modifiers have been pressed, rebuild keynum
00298         if ((modifiers = e->modifiers()) != Qt::NoModifier)
00299         {
00300             int modnum = (((modifiers & Qt::ShiftModifier) &&
00301                             (keynum > 0x7f) &&
00302                             (keynum != Qt::Key_Backtab)) ? Qt::SHIFT : 0) |
00303                          ((modifiers & Qt::ControlModifier) ? Qt::CTRL : 0) |
00304                          ((modifiers & Qt::MetaModifier) ? Qt::META : 0) |
00305                          ((modifiers & Qt::AltModifier) ? Qt::ALT : 0);
00306             modnum &= ~Qt::UNICODE_ACCEL;
00307             return (keynum |= modnum);
00308         }
00309     }
00310 
00311     return keynum;
00312 }
00313 
00314 static MythMainWindow *mainWin = NULL;
00315 static QMutex mainLock;
00316 
00325 MythMainWindow *MythMainWindow::getMainWindow(const bool useDB)
00326 {
00327     if (mainWin)
00328         return mainWin;
00329 
00330     QMutexLocker lock(&mainLock);
00331 
00332     if (!mainWin)
00333     {
00334         mainWin = new MythMainWindow(useDB);
00335         gCoreContext->SetGUIObject(mainWin);
00336     }
00337 
00338     return mainWin;
00339 }
00340 
00341 void MythMainWindow::destroyMainWindow(void)
00342 {
00343     gCoreContext->SetGUIObject(NULL);
00344     delete mainWin;
00345     mainWin = NULL;
00346 }
00347 
00348 MythMainWindow *GetMythMainWindow(void)
00349 {
00350     return MythMainWindow::getMainWindow();
00351 }
00352 
00353 bool HasMythMainWindow(void)
00354 {
00355     return (mainWin);
00356 }
00357 
00358 void DestroyMythMainWindow(void)
00359 {
00360     MythMainWindow::destroyMainWindow();
00361 }
00362 
00363 MythPainter *GetMythPainter(void)
00364 {
00365     return MythMainWindow::getMainWindow()->GetCurrentPainter();
00366 }
00367 
00368 #ifdef USE_OPENGL_PAINTER
00369 MythPainterWindowGL::MythPainterWindowGL(MythMainWindow *win,
00370                                          MythMainWindowPrivate *priv,
00371                                          MythRenderOpenGL *rend)
00372                    : QGLWidget(rend, win),
00373                      parent(win), d(priv), render(rend)
00374 {
00375     setAutoBufferSwap(false);
00376 }
00377 
00378 void MythPainterWindowGL::paintEvent(QPaintEvent *pe)
00379 {
00380     d->repaintRegion = d->repaintRegion.unite(pe->region());
00381     parent->drawScreen();
00382 }
00383 #endif
00384 
00385 #ifdef USING_MINGW
00386 MythPainterWindowD3D9::MythPainterWindowD3D9(MythMainWindow *win,
00387                                              MythMainWindowPrivate *priv)
00388                    : QGLWidget(win),
00389                      parent(win), d(priv)
00390 {
00391     setAutoBufferSwap(false);
00392 }
00393 
00394 void MythPainterWindowD3D9::paintEvent(QPaintEvent *pe)
00395 {
00396     d->repaintRegion = d->repaintRegion.unite(pe->region());
00397     parent->drawScreen();
00398 }
00399 #endif
00400 
00401 MythPainterWindowQt::MythPainterWindowQt(MythMainWindow *win,
00402                                          MythMainWindowPrivate *priv)
00403                    : QWidget(win),
00404                      parent(win), d(priv)
00405 {
00406 }
00407 
00408 void MythPainterWindowQt::paintEvent(QPaintEvent *pe)
00409 {
00410     d->repaintRegion = d->repaintRegion.unite(pe->region());
00411     parent->drawScreen();
00412 }
00413 
00414 MythMainWindow::MythMainWindow(const bool useDB)
00415               : QWidget(NULL)
00416 {
00417     d = new MythMainWindowPrivate;
00418 
00419     setObjectName("mainwindow");
00420 
00421     d->AllowInput = false;
00422 
00423     // This prevents database errors from RegisterKey() when there is no DB:
00424     d->m_useDB = useDB;
00425     d->painter = NULL;
00426     d->paintwin = NULL;
00427     d->oldpainter = NULL;
00428     d->oldpaintwin = NULL;
00429     d->oldrender = NULL;
00430 
00431     //Init();
00432 
00433     d->ignore_lirc_keys = false;
00434     d->ignore_joystick_keys = false;
00435     d->exitingtomain = false;
00436     d->popwindows = true;
00437     d->exitmenucallback = false;
00438     d->exitmenumediadevicecallback = false;
00439     d->mediadeviceforcallback = NULL;
00440     d->escapekey = Qt::Key_Escape;
00441     d->mainStack = NULL;
00442     d->sysEventHandler = NULL;
00443 
00444     installEventFilter(this);
00445 
00446     d->lircThread = NULL;
00447     StartLIRC();
00448 
00449 #ifdef USE_JOYSTICK_MENU
00450     d->ignore_joystick_keys = false;
00451 
00452     QString joy_config_file = GetConfDir() + "/joystickmenurc";
00453 
00454     d->joystickThread = NULL;
00455     d->joystickThread = new JoystickMenuThread(this);
00456     if (!d->joystickThread->Init(joy_config_file))
00457         d->joystickThread->start();
00458 #endif
00459 
00460 #ifdef USING_APPLEREMOTE
00461     d->appleRemoteListener = new AppleRemoteListener(this);
00462     d->appleRemote         = AppleRemote::Get();
00463 
00464     d->appleRemote->setListener(d->appleRemoteListener);
00465     d->appleRemote->startListening();
00466     if (d->appleRemote->isListeningToRemote())
00467         d->appleRemote->start();
00468 #endif
00469 
00470 #ifdef USING_LIBCEC
00471     d->cecAdapter = new CECAdapter();
00472     if (!d->cecAdapter->IsValid())
00473     {
00474         delete d->cecAdapter;
00475         d->cecAdapter = NULL;
00476     }
00477 #endif
00478 
00479     d->m_udpListener = new MythUDPListener();
00480 
00481     InitKeys();
00482 
00483     d->gestureTimer = new QTimer(this);
00484     connect(d->gestureTimer, SIGNAL(timeout()), this, SLOT(mouseTimeout()));
00485     d->hideMouseTimer = new QTimer(this);
00486     d->hideMouseTimer->setSingleShot(true);
00487     d->hideMouseTimer->setInterval(3000); // 3 seconds
00488     connect(d->hideMouseTimer, SIGNAL(timeout()), SLOT(HideMouseTimeout()));
00489 
00490     d->drawTimer = new MythSignalingTimer(this, SLOT(animate()));
00491     d->drawTimer->start(d->drawInterval);
00492 
00493     d->AllowInput = true;
00494 
00495     d->repaintRegion = QRegion(QRect(0,0,0,0));
00496 
00497     d->m_drawEnabled = true;
00498 
00499     connect(this, SIGNAL(signalRemoteScreenShot(QString,int,int)),
00500             this, SLOT(doRemoteScreenShot(QString,int,int)),
00501             Qt::BlockingQueuedConnection);
00502 
00503     // We need to listen for playback start/end events
00504     gCoreContext->addListener(this);
00505 
00506     int idletime = gCoreContext->GetNumSetting("FrontendIdleTimeout",
00507                                                STANDBY_TIMEOUT);
00508     if (idletime <= 0)
00509         idletime = STANDBY_TIMEOUT;
00510 
00511     d->idleTimer = new QTimer(this);
00512     d->idleTimer->setSingleShot(true);
00513     d->idleTimer->setInterval(1000 * 60 * idletime); // 30 minutes
00514     connect(d->idleTimer, SIGNAL(timeout()), SLOT(IdleTimeout()));
00515     d->idleTimer->start();
00516 }
00517 
00518 MythMainWindow::~MythMainWindow()
00519 {
00520     d->drawTimer->stop();
00521 
00522     while (!d->stackList.isEmpty())
00523     {
00524         MythScreenStack *stack = d->stackList.back();
00525         d->stackList.pop_back();
00526 
00527         if (stack == d->mainStack)
00528             d->mainStack = NULL;
00529 
00530         delete stack;
00531     }
00532 
00533     delete d->m_themeBase;
00534 
00535     while (!d->keyContexts.isEmpty())
00536     {
00537         KeyContext *context = *d->keyContexts.begin();
00538         d->keyContexts.erase(d->keyContexts.begin());
00539         delete context;
00540     }
00541 
00542 #ifdef USE_LIRC
00543     if (d->lircThread)
00544     {
00545         d->lircThread->deleteLater();
00546         d->lircThread = NULL;
00547     }
00548 #endif
00549 
00550 #ifdef USE_JOYSTICK_MENU
00551     if (d->joystickThread)
00552     {
00553         if (d->joystickThread->isRunning())
00554         {
00555             d->joystickThread->Stop();
00556             d->joystickThread->wait();
00557         }
00558 
00559         delete d->joystickThread;
00560         d->joystickThread = NULL;
00561     }
00562 #endif
00563 
00564 #ifdef USING_APPLEREMOTE
00565     // We don't delete this, just disable its plumbing. If we create another
00566     // MythMainWindow later, AppleRemote::get() will retrieve the instance.
00567     if (d->appleRemote->isRunning())
00568         d->appleRemote->stopListening();
00569 
00570     delete d->appleRemoteListener;
00571 #endif
00572 
00573 #ifdef USING_LIBCEC
00574     if (d->cecAdapter)
00575         delete d->cecAdapter;
00576 #endif
00577 
00578     delete d;
00579 }
00580 
00581 MythPainter *MythMainWindow::GetCurrentPainter(void)
00582 {
00583     return d->painter;
00584 }
00585 
00586 QWidget *MythMainWindow::GetPaintWindow(void)
00587 {
00588     return d->paintwin;
00589 }
00590 
00591 void MythMainWindow::ShowPainterWindow(void)
00592 {
00593     if (d->paintwin)
00594         d->paintwin->show();
00595     if (d->render)
00596         d->render->Release();
00597 }
00598 
00599 void MythMainWindow::HidePainterWindow(void)
00600 {
00601     if (d->paintwin)
00602     {
00603         d->paintwin->clearMask();
00604         if (!(d->render && d->render->IsShared()))
00605             d->paintwin->hide();
00606     }
00607 }
00608 
00609 void MythMainWindow::ResizePainterWindow(const QSize &size)
00610 {
00611     if (!d->paintwin)
00612         return;
00613     d->paintwin->setFixedSize(size);
00614     d->paintwin->resize(size);
00615 }
00616 
00617 MythRender *MythMainWindow::GetRenderDevice()
00618 {
00619     return d->render;
00620 }
00621 
00622 void MythMainWindow::AddScreenStack(MythScreenStack *stack, bool main)
00623 {
00624     d->stackList.push_back(stack);
00625     if (main)
00626         d->mainStack = stack;
00627 }
00628 
00629 void MythMainWindow::PopScreenStack()
00630 {
00631     delete d->stackList.back();
00632     d->stackList.pop_back();
00633 }
00634 
00635 int MythMainWindow::GetStackCount(void)
00636 {
00637     return d->stackList.size();
00638 }
00639 
00640 MythScreenStack *MythMainWindow::GetMainStack(void)
00641 {
00642     return d->mainStack;
00643 }
00644 
00645 MythScreenStack *MythMainWindow::GetStack(const QString &stackname)
00646 {
00647     QVector<MythScreenStack *>::Iterator it;
00648     for (it = d->stackList.begin(); it != d->stackList.end(); ++it)
00649     {
00650         if ((*it)->objectName() == stackname)
00651             return *it;
00652     }
00653     return NULL;
00654 }
00655 
00656 MythScreenStack* MythMainWindow::GetStackAt(int pos)
00657 {
00658     if (pos >= 0 && pos < d->stackList.size())
00659         return d->stackList.at(pos);
00660 
00661     return NULL;
00662 }
00663 
00664 void MythMainWindow::animate(void)
00665 {
00666     /* FIXME: remove */
00667     if (currentWidget() || !d->m_drawEnabled)
00668         return;
00669 
00670     if (!d->paintwin)
00671         return;
00672 
00673     d->drawTimer->blockSignals(true);
00674 
00675     bool redraw = false;
00676 
00677     if (!d->repaintRegion.isEmpty())
00678         redraw = true;
00679 
00680     QVector<MythScreenStack *>::Iterator it;
00681     for (it = d->stackList.begin(); it != d->stackList.end(); ++it)
00682     {
00683         QVector<MythScreenType *> drawList;
00684         (*it)->GetDrawOrder(drawList);
00685 
00686         QVector<MythScreenType *>::Iterator screenit;
00687         for (screenit = drawList.begin(); screenit != drawList.end();
00688              ++screenit)
00689         {
00690             (*screenit)->Pulse();
00691 
00692             if ((*screenit)->NeedsRedraw())
00693             {
00694                 QRegion topDirty = (*screenit)->GetDirtyArea();
00695                 (*screenit)->ResetNeedsRedraw();
00696                 d->repaintRegion = d->repaintRegion.unite(topDirty);
00697                 redraw = true;
00698             }
00699         }
00700     }
00701 
00702     if (redraw && !(d->render && d->render->IsShared()))
00703         d->paintwin->update(d->repaintRegion);
00704 
00705     for (it = d->stackList.begin(); it != d->stackList.end(); ++it)
00706         (*it)->ScheduleInitIfNeeded();
00707 
00708     d->drawTimer->blockSignals(false);
00709 }
00710 
00711 void MythMainWindow::drawScreen(void)
00712 {
00713     /* FIXME: remove */
00714     if (currentWidget() || !d->m_drawEnabled)
00715         return;
00716 
00717     if (!d->painter->SupportsClipping())
00718         d->repaintRegion = d->repaintRegion.unite(d->uiScreenRect);
00719     else
00720     {
00721         // Ensure that the region is not larger than the screen which
00722         // can happen with bad themes
00723         d->repaintRegion = d->repaintRegion.intersect(d->uiScreenRect);
00724 
00725         // Check for any widgets that have been updated since we built
00726         // the dirty region list in ::animate()
00727         QVector<MythScreenStack *>::Iterator it;
00728         for (it = d->stackList.begin(); it != d->stackList.end(); ++it)
00729         {
00730             QVector<MythScreenType *> redrawList;
00731             (*it)->GetDrawOrder(redrawList);
00732 
00733             QVector<MythScreenType *>::Iterator screenit;
00734             for (screenit = redrawList.begin(); screenit != redrawList.end();
00735                  ++screenit)
00736             {
00737                 if ((*screenit)->NeedsRedraw())
00738                 {
00739                     QRegion topDirty = (*screenit)->GetDirtyArea();
00740                     QVector<QRect> wrects = topDirty.rects();
00741                     for (int i = 0; i < wrects.size(); i++)
00742                     {
00743                         bool foundThisRect = false;
00744                         QVector<QRect> drects = d->repaintRegion.rects();
00745                         for (int j = 0; j < drects.size(); j++)
00746                         {
00747                             if (drects[j].contains(wrects[i]))
00748                             {
00749                                 foundThisRect = true;
00750                                 break;
00751                             }
00752                         }
00753 
00754                         if (!foundThisRect)
00755                             return;
00756                     }
00757                 }
00758             }
00759         }
00760     }
00761 
00762     if (!(d->render && d->render->IsShared()))
00763         draw();
00764 
00765     d->repaintRegion = QRegion(QRect(0, 0, 0, 0));
00766 }
00767 
00768 void MythMainWindow::draw(void)
00769 {
00770     if (!d->painter)
00771         return;
00772 
00773     d->painter->Begin(d->paintwin);
00774 
00775     QVector<QRect> rects = d->repaintRegion.rects();
00776 
00777     for (int i = 0; i < rects.size(); i++)
00778     {
00779         if (rects[i].width() == 0 || rects[i].height() == 0)
00780             continue;
00781 
00782         if (rects[i] != d->uiScreenRect)
00783             d->painter->SetClipRect(rects[i]);
00784 
00785         QVector<MythScreenStack *>::Iterator it;
00786         for (it = d->stackList.begin(); it != d->stackList.end(); ++it)
00787         {
00788             QVector<MythScreenType *> redrawList;
00789             (*it)->GetDrawOrder(redrawList);
00790 
00791             QVector<MythScreenType *>::Iterator screenit;
00792             for (screenit = redrawList.begin(); screenit != redrawList.end();
00793                  ++screenit)
00794             {
00795                 (*screenit)->Draw(d->painter, 0, 0, 255, rects[i]);
00796             }
00797         }
00798     }
00799 
00800     d->painter->End();
00801 }
00802 
00803 void MythMainWindow::closeEvent(QCloseEvent *e)
00804 {
00805     if (e->spontaneous())
00806     {
00807         QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, d->escapekey,
00808                                    Qt::NoModifier);
00809         QCoreApplication::postEvent(this, key);
00810         e->ignore();
00811     }
00812     else
00813         QWidget::closeEvent(e);
00814 }
00815 
00816 void MythMainWindow::GrabWindow(QImage &image)
00817 {
00818     WId winid;
00819     QWidget *active = QApplication::activeWindow();
00820     if (active)
00821         winid = active->winId();
00822     else
00823         winid = QApplication::desktop()->winId();
00824 
00825     QPixmap p = QPixmap::grabWindow(winid);
00826     image = p.toImage();
00827 }
00828 
00829 /* This is required to allow a screenshot to be requested by another thread
00830  * other than the UI thread, and to wait for the screenshot before returning.
00831  * It is used by mythweb for the remote access screenshots
00832  */
00833 void MythMainWindow::doRemoteScreenShot(QString filename, int x, int y)
00834 {
00835     // This will be running in the UI thread, as is required by QPixmap
00836     QStringList args;
00837     args << QString::number(x);
00838     args << QString::number(y);
00839     args << filename;
00840 
00841     MythEvent me(MythEvent::MythEventMessage, ACTION_SCREENSHOT, args);
00842     qApp->sendEvent(this, &me);
00843 }
00844 
00845 void MythMainWindow::RemoteScreenShot(QString filename, int x, int y)
00846 {
00847     // This will be running in a non-UI thread and is used to trigger a
00848     // function in the UI thread, and waits for completion of that handler
00849     emit signalRemoteScreenShot(filename, x, y);
00850 }
00851 
00852 bool MythMainWindow::SaveScreenShot(const QImage &image, QString filename)
00853 {
00854     if (filename.isEmpty())
00855     {
00856         QString fpath = GetMythDB()->GetSetting("ScreenShotPath", "/tmp");
00857         filename = QString("%1/myth-screenshot-%2.png").arg(fpath)
00858          .arg(QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss.zzz"));
00859     }
00860 
00861     QString extension = filename.section('.', -1, -1);
00862     if (extension == "jpg")
00863         extension = "JPEG";
00864     else
00865         extension = "PNG";
00866 
00867     LOG(VB_GENERAL, LOG_INFO, QString("Saving screenshot to %1 (%2x%3)")
00868                        .arg(filename).arg(image.width()).arg(image.height()));
00869 
00870     if (image.save(filename, extension.toAscii(), 100))
00871     {
00872         LOG(VB_GENERAL, LOG_INFO, "MythMainWindow::screenShot succeeded");
00873         return true;
00874     }
00875 
00876     LOG(VB_GENERAL, LOG_INFO, "MythMainWindow::screenShot Failed!");
00877     return false;
00878 }
00879 
00880 bool MythMainWindow::ScreenShot(int w, int h, QString filename)
00881 {
00882     QImage img;
00883     GrabWindow(img);
00884     if (w <= 0)
00885         w = img.width();
00886     if (h <= 0)
00887         h = img.height();
00888 
00889     img = img.scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation);
00890     return SaveScreenShot(img, filename);
00891 }
00892 
00893 bool MythMainWindow::event(QEvent *e)
00894 {
00895     if (!updatesEnabled() && (e->type() == QEvent::UpdateRequest))
00896         d->m_pendingUpdate = true;
00897 
00898     if (e->type() == QEvent::Show && !e->spontaneous())
00899     {
00900         QCoreApplication::postEvent(
00901             this, new QEvent(MythEvent::kMythPostShowEventType));
00902     }
00903 
00904     if (e->type() == MythEvent::kMythPostShowEventType)
00905     {
00906         raise();
00907         activateWindow();
00908         return true;
00909     }
00910 
00911 #ifdef USING_APPLEREMOTE
00912     if (d->appleRemote)
00913     {
00914         if (e->type() == QEvent::WindowActivate)
00915             d->appleRemote->startListening();
00916 
00917         if (e->type() == QEvent::WindowDeactivate)
00918             d->appleRemote->stopListening();
00919     }
00920 #endif
00921 
00922     return QWidget::event(e);
00923 }
00924 
00925 void MythMainWindow::Init(void)
00926 {
00927     GetMythUI()->GetScreenSettings(d->xbase, d->screenwidth, d->wmult,
00928                                    d->ybase, d->screenheight, d->hmult);
00929 
00930     if (GetMythDB()->GetNumSetting("GuiOffsetX") > 0 ||
00931         GetMythDB()->GetNumSetting("GuiWidth")   > 0 ||
00932         GetMythDB()->GetNumSetting("GuiOffsetY") > 0 ||
00933         GetMythDB()->GetNumSetting("GuiHeight")  > 0)
00934         d->does_fill_screen = false;
00935     else
00936         d->does_fill_screen = true;
00937 
00938     // Set window border based on fullscreen attribute
00939     Qt::WindowFlags flags = Qt::Window;
00940 
00941     if (!GetMythDB()->GetNumSetting("RunFrontendInWindow", 0))
00942     {
00943         LOG(VB_GENERAL, LOG_INFO, "Using Frameless Window");
00944         flags |= Qt::FramelessWindowHint;
00945     }
00946 
00947     // Workaround Qt/Windows playback bug?
00948 #ifdef _WIN32
00949     flags |= Qt::MSWindowsOwnDC;
00950 #endif
00951 
00952     setWindowFlags(flags);
00953 
00954     if (d->does_fill_screen && !GetMythUI()->IsGeometryOverridden())
00955     {
00956         LOG(VB_GENERAL, LOG_INFO, "Using Full Screen Window");
00957         setWindowState(Qt::WindowFullScreen);
00958     }
00959 
00960     d->screenRect = QRect(d->xbase, d->ybase, d->screenwidth, d->screenheight);
00961     d->uiScreenRect = QRect(0, 0, d->screenwidth, d->screenheight);
00962 
00963     setGeometry(d->xbase, d->ybase, d->screenwidth, d->screenheight);
00964     setFixedSize(QSize(d->screenwidth, d->screenheight));
00965 
00966     GetMythUI()->ThemeWidget(this);
00967     Show();
00968 
00969     if (!GetMythDB()->GetNumSetting("HideMouseCursor", 0))
00970         setMouseTracking(true); // Required for mouse cursor auto-hide
00971     // Set cursor call must come after Show() to work on some systems.
00972     ShowMouseCursor(false);
00973 
00974     move(d->xbase, d->ybase);
00975 
00976     if (d->paintwin)
00977     {
00978         d->oldpaintwin = d->paintwin;
00979         d->paintwin = NULL;
00980         d->drawTimer->stop();
00981     }
00982 
00983     if (d->painter)
00984     {
00985         d->oldpainter = d->painter;
00986         d->painter = NULL;
00987     }
00988 
00989     if (d->render)
00990     {
00991         d->oldrender = d->render;
00992         d->render = NULL;
00993     }
00994 
00995     QString painter = GetMythDB()->GetSetting("UIPainter", "auto");
00996 #ifdef USING_MINGW
00997     if (painter == "auto" || painter == "d3d9")
00998     {
00999         LOG(VB_GENERAL, LOG_INFO, "Using the D3D9 painter");
01000         d->painter = new MythD3D9Painter();
01001         d->paintwin = new MythPainterWindowD3D9(this, d);
01002     }
01003 #endif
01004 #ifdef USE_OPENGL_PAINTER
01005     if ((painter == "auto" && (!d->painter && !d->paintwin)) ||
01006         painter.contains("opengl"))
01007     {
01008         LOG(VB_GENERAL, LOG_INFO, "Trying the OpenGL painter");
01009         d->painter = new MythOpenGLPainter();
01010         d->render = MythRenderOpenGL::Create(painter);
01011         MythRenderOpenGL *gl = dynamic_cast<MythRenderOpenGL*>(d->render);
01012         d->paintwin = new MythPainterWindowGL(this, d, gl);
01013         QGLWidget *qgl = dynamic_cast<QGLWidget *>(d->paintwin);
01014         if (qgl)
01015         {
01016             bool teardown = false;
01017             if (!qgl->isValid())
01018             {
01019                 LOG(VB_GENERAL, LOG_ERR, "Failed to create OpenGL painter. "
01020                                          "Check your OpenGL installation.");
01021                 teardown = true;
01022             }
01023             else if (painter == "auto" && gl && !gl->IsRecommendedRenderer())
01024             {
01025                 LOG(VB_GENERAL, LOG_WARNING,
01026                     "OpenGL painter not recommended with this system's "
01027                     "hardware/drivers. Falling back to Qt painter.");
01028                 teardown = true;
01029             }
01030             if (teardown)
01031             {
01032                 delete d->painter;
01033                 d->painter = NULL;
01034                 delete d->paintwin;
01035                 d->paintwin = NULL;
01036                 d->render = NULL; // deleted by the painterwindow
01037             }
01038             else
01039                 gl->Init();
01040         }
01041     }
01042 #endif
01043 
01044     if (!d->painter && !d->paintwin)
01045     {
01046         LOG(VB_GENERAL, LOG_INFO, "Using the Qt painter");
01047         d->painter = new MythQtPainter();
01048         d->paintwin = new MythPainterWindowQt(this, d);
01049     }
01050 
01051     if (!d->paintwin)
01052     {
01053         LOG(VB_GENERAL, LOG_ERR, "MythMainWindow failed to create a "
01054                                  "painter window.");
01055         return;
01056     }
01057 
01058     if (d->painter->GetName() != "Qt")
01059     {
01060         setAttribute(Qt::WA_NoSystemBackground);
01061         setAutoFillBackground(false);
01062     }
01063 
01064     d->paintwin->move(0, 0);
01065     ResizePainterWindow(size());
01066     d->paintwin->raise();
01067     ShowPainterWindow();
01068     if (!GetMythDB()->GetNumSetting("HideMouseCursor", 0))
01069         d->paintwin->setMouseTracking(true); // Required for mouse cursor auto-hide
01070 
01071     GetMythUI()->UpdateImageCache();
01072     if (d->m_themeBase)
01073         d->m_themeBase->Reload();
01074     else
01075         d->m_themeBase = new MythThemeBase();
01076 }
01077 
01078 void MythMainWindow::InitKeys()
01079 {
01080     RegisterKey("Global", ACTION_UP, QT_TRANSLATE_NOOP("MythControls",
01081         "Up Arrow"),               "Up");
01082     RegisterKey("Global", ACTION_DOWN, QT_TRANSLATE_NOOP("MythControls",
01083         "Down Arrow"),           "Down");
01084     RegisterKey("Global", ACTION_LEFT, QT_TRANSLATE_NOOP("MythControls",
01085         "Left Arrow"),           "Left");
01086     RegisterKey("Global", ACTION_RIGHT, QT_TRANSLATE_NOOP("MythControls",
01087         "Right Arrow"),         "Right");
01088     RegisterKey("Global", "NEXT", QT_TRANSLATE_NOOP("MythControls",
01089         "Move to next widget"),   "Tab");
01090     RegisterKey("Global", "PREVIOUS", QT_TRANSLATE_NOOP("MythControls",
01091         "Move to preview widget"), "Backtab");
01092     RegisterKey("Global", ACTION_SELECT, QT_TRANSLATE_NOOP("MythControls",
01093         "Select"), "Return,Enter,Space");
01094     RegisterKey("Global", "BACKSPACE", QT_TRANSLATE_NOOP("MythControls",
01095         "Backspace"),       "Backspace");
01096     RegisterKey("Global", "ESCAPE", QT_TRANSLATE_NOOP("MythControls",
01097         "Escape"),                "Esc");
01098     RegisterKey("Global", "MENU", QT_TRANSLATE_NOOP("MythControls",
01099         "Pop-up menu"),             "M");
01100     RegisterKey("Global", "INFO", QT_TRANSLATE_NOOP("MythControls",
01101         "More information"),        "I");
01102     RegisterKey("Global", "DELETE", QT_TRANSLATE_NOOP("MythControls",
01103         "Delete"),                  "D");
01104     RegisterKey("Global", "EDIT", QT_TRANSLATE_NOOP("MythControls",
01105         "Edit"),                    "E");
01106     RegisterKey("Global", ACTION_SCREENSHOT, QT_TRANSLATE_NOOP("MythControls",
01107          "Save screenshot"), "");
01108     RegisterKey("Global", ACTION_HANDLEMEDIA, QT_TRANSLATE_NOOP("MythControls",
01109          "Play a media resource"), "");
01110 
01111     RegisterKey("Global", "PAGEUP", QT_TRANSLATE_NOOP("MythControls",
01112         "Page Up"),              "PgUp");
01113     RegisterKey("Global", "PAGEDOWN", QT_TRANSLATE_NOOP("MythControls",
01114         "Page Down"),          "PgDown");
01115     RegisterKey("Global", "PAGETOP", QT_TRANSLATE_NOOP("MythControls",
01116         "Page to top of list"),      "");
01117     RegisterKey("Global", "PAGEMIDDLE", QT_TRANSLATE_NOOP("MythControls",
01118         "Page to middle of list"),   "");
01119     RegisterKey("Global", "PAGEBOTTOM", QT_TRANSLATE_NOOP("MythControls",
01120         "Page to bottom of list"),   "");
01121 
01122     RegisterKey("Global", "PREVVIEW", QT_TRANSLATE_NOOP("MythControls",
01123         "Previous View"),        "Home");
01124     RegisterKey("Global", "NEXTVIEW", QT_TRANSLATE_NOOP("MythControls",
01125         "Next View"),             "End");
01126 
01127     RegisterKey("Global", "HELP", QT_TRANSLATE_NOOP("MythControls",
01128         "Help"),                   "F1");
01129     RegisterKey("Global", "EJECT", QT_TRANSLATE_NOOP("MythControls"
01130         ,"Eject Removable Media"),   "");
01131 
01132     RegisterKey("Global", "CUT", QT_TRANSLATE_NOOP("MythControls",
01133         "Cut text from textedit"), "Ctrl+X");
01134     RegisterKey("Global", "COPY", QT_TRANSLATE_NOOP("MythControls"
01135         ,"Copy text from textedit"), "Ctrl+C");
01136     RegisterKey("Global", "PASTE", QT_TRANSLATE_NOOP("MythControls",
01137         "Paste text into textedit"), "Ctrl+V");
01138     RegisterKey("Global", "UNDO", QT_TRANSLATE_NOOP("MythControls",
01139         "Undo"), "Ctrl+Z");
01140     RegisterKey("Global", "REDO", QT_TRANSLATE_NOOP("MythControls",
01141         "Redo"), "Ctrl+Y");
01142     RegisterKey("Global", "SEARCH", QT_TRANSLATE_NOOP("MythControls",
01143         "Show incremental search dialog"), "Ctrl+S");
01144 
01145     RegisterKey("Global", ACTION_0, QT_TRANSLATE_NOOP("MythControls","0"), "0");
01146     RegisterKey("Global", ACTION_1, QT_TRANSLATE_NOOP("MythControls","1"), "1");
01147     RegisterKey("Global", ACTION_2, QT_TRANSLATE_NOOP("MythControls","2"), "2");
01148     RegisterKey("Global", ACTION_3, QT_TRANSLATE_NOOP("MythControls","3"), "3");
01149     RegisterKey("Global", ACTION_4, QT_TRANSLATE_NOOP("MythControls","4"), "4");
01150     RegisterKey("Global", ACTION_5, QT_TRANSLATE_NOOP("MythControls","5"), "5");
01151     RegisterKey("Global", ACTION_6, QT_TRANSLATE_NOOP("MythControls","6"), "6");
01152     RegisterKey("Global", ACTION_7, QT_TRANSLATE_NOOP("MythControls","7"), "7");
01153     RegisterKey("Global", ACTION_8, QT_TRANSLATE_NOOP("MythControls","8"), "8");
01154     RegisterKey("Global", ACTION_9, QT_TRANSLATE_NOOP("MythControls","9"), "9");
01155 
01156     RegisterKey("Global", ACTION_TVPOWERON,  QT_TRANSLATE_NOOP("MythControls",
01157         "Turn the display on"),   "");
01158     RegisterKey("Global", ACTION_TVPOWEROFF, QT_TRANSLATE_NOOP("MythControls",
01159         "Turn the display off"),  "");
01160 
01161     RegisterKey("Global", "SYSEVENT01", QT_TRANSLATE_NOOP("MythControls",
01162         "Trigger System Key Event #1"), "");
01163     RegisterKey("Global", "SYSEVENT02", QT_TRANSLATE_NOOP("MythControls",
01164         "Trigger System Key Event #2"), "");
01165     RegisterKey("Global", "SYSEVENT03", QT_TRANSLATE_NOOP("MythControls",
01166         "Trigger System Key Event #3"), "");
01167     RegisterKey("Global", "SYSEVENT04", QT_TRANSLATE_NOOP("MythControls",
01168         "Trigger System Key Event #4"), "");
01169     RegisterKey("Global", "SYSEVENT05", QT_TRANSLATE_NOOP("MythControls",
01170         "Trigger System Key Event #5"), "");
01171     RegisterKey("Global", "SYSEVENT06", QT_TRANSLATE_NOOP("MythControls",
01172         "Trigger System Key Event #6"), "");
01173     RegisterKey("Global", "SYSEVENT07", QT_TRANSLATE_NOOP("MythControls",
01174         "Trigger System Key Event #7"), "");
01175     RegisterKey("Global", "SYSEVENT08", QT_TRANSLATE_NOOP("MythControls",
01176         "Trigger System Key Event #8"), "");
01177     RegisterKey("Global", "SYSEVENT09", QT_TRANSLATE_NOOP("MythControls",
01178         "Trigger System Key Event #9"), "");
01179     RegisterKey("Global", "SYSEVENT10", QT_TRANSLATE_NOOP("MythControls",
01180         "Trigger System Key Event #10"), "");
01181 
01182     // these are for the html viewer widget (MythUIWebBrowser)
01183     RegisterKey("Browser", "ZOOMIN",          QT_TRANSLATE_NOOP("MythControls",
01184         "Zoom in on browser window"),           ".,>");
01185     RegisterKey("Browser", "ZOOMOUT",         QT_TRANSLATE_NOOP("MythControls",
01186         "Zoom out on browser window"),          ",,<");
01187     RegisterKey("Browser", "TOGGLEINPUT",     QT_TRANSLATE_NOOP("MythControls",
01188         "Toggle where keyboard input goes to"),  "F1");
01189 
01190     RegisterKey("Browser", "MOUSEUP",         QT_TRANSLATE_NOOP("MythControls",
01191         "Move mouse pointer up"),                 "2");
01192     RegisterKey("Browser", "MOUSEDOWN",       QT_TRANSLATE_NOOP("MythControls",
01193         "Move mouse pointer down"),               "8");
01194     RegisterKey("Browser", "MOUSELEFT",       QT_TRANSLATE_NOOP("MythControls",
01195         "Move mouse pointer left"),               "4");
01196     RegisterKey("Browser", "MOUSERIGHT",      QT_TRANSLATE_NOOP("MythControls",
01197         "Move mouse pointer right"),              "6");
01198     RegisterKey("Browser", "MOUSELEFTBUTTON", QT_TRANSLATE_NOOP("MythControls",
01199         "Mouse Left button click"),               "5");
01200 
01201     RegisterKey("Browser", "PAGEDOWN",        QT_TRANSLATE_NOOP("MythControls",
01202         "Scroll down half a page"),               "9");
01203     RegisterKey("Browser", "PAGEUP",          QT_TRANSLATE_NOOP("MythControls",
01204         "Scroll up half a page"),                 "3");
01205     RegisterKey("Browser", "PAGELEFT",        QT_TRANSLATE_NOOP("MythControls",
01206         "Scroll left half a page"),               "7");
01207     RegisterKey("Browser", "PAGERIGHT",       QT_TRANSLATE_NOOP("MythControls",
01208         "Scroll right half a page"),              "1");
01209 
01210     RegisterKey("Browser", "NEXTLINK",        QT_TRANSLATE_NOOP("MythControls",
01211         "Move selection to next link"),           "Z");
01212     RegisterKey("Browser", "PREVIOUSLINK",    QT_TRANSLATE_NOOP("MythControls",
01213         "Move selection to previous link"),       "Q");
01214     RegisterKey("Browser", "FOLLOWLINK",      QT_TRANSLATE_NOOP("MythControls",
01215         "Follow selected link"),            "Return,Space,Enter");
01216     RegisterKey("Browser", "HISTORYBACK",     QT_TRANSLATE_NOOP("MythControls",
01217         "Go back to previous page"),        "R,Backspace");
01218     RegisterKey("Browser", "HISTORYFORWARD",  QT_TRANSLATE_NOOP("MythControls",
01219         "Go forward to previous page"),     "F");
01220 
01221     RegisterKey("Main Menu",    "EXIT",       QT_TRANSLATE_NOOP("MythControls",
01222         "System Exit"),                     "");
01223     RegisterKey("Main Menu",    "EXITPROMPT", QT_TRANSLATE_NOOP("MythControls",
01224         "Display System Exit Prompt"),      "Esc");
01225 }
01226 
01227 void MythMainWindow::ReloadKeys()
01228 {
01229     ClearKeyContext("Global");
01230     ClearKeyContext("Browser");
01231     ClearKeyContext("Main Menu");
01232     InitKeys();
01233 }
01234 
01235 void MythMainWindow::ReinitDone(void)
01236 {
01237     delete d->oldpainter;
01238     d->oldpainter = NULL;
01239 
01240     delete d->oldpaintwin;
01241     d->oldpaintwin = NULL;
01242 
01243     // For OpenGL contexts (at least), deleting the painter window also
01244     // deletes the render context
01245     d->oldrender = NULL;
01246 
01247     d->paintwin->move(0, 0);
01248     d->paintwin->setFixedSize(size());
01249     d->paintwin->raise();
01250     ShowPainterWindow();
01251 
01252     d->drawTimer->start(1000 / 70);
01253 }
01254 
01255 void MythMainWindow::Show(void)
01256 {
01257     show();
01258 #ifdef Q_WS_MACX_OLDQT
01259     if (d->does_fill_screen)
01260         HideMenuBar();
01261     else
01262         ShowMenuBar();
01263 #endif
01264 }
01265 
01266 /* FIXME compatibility only */
01267 void MythMainWindow::attach(QWidget *child)
01268 {
01269 #ifdef USING_MINGW
01270 # ifdef _MSC_VER
01271 #  pragma message( "TODO FIXME MythMainWindow::attach() does not always work on MS Windows!")
01272 # else
01273 #  warning TODO FIXME MythMainWindow::attach() does not always work on MS Windows!
01274 # endif
01275 
01276     // if windows are created on different threads,
01277     // or if setFocus() is called from a thread other than the main UI thread,
01278     // setFocus() hangs the thread that called it
01279     LOG(VB_GENERAL, LOG_ERR,
01280             QString("MythMainWindow::attach old: %1, new: %2, thread: %3")
01281             .arg(currentWidget() ? currentWidget()->objectName() : "none")
01282             .arg(child->objectName())
01283             .arg(::GetCurrentThreadId()));
01284 #endif
01285     if (currentWidget())
01286         currentWidget()->setEnabled(false);
01287 
01288     d->widgetList.push_back(child);
01289     child->winId();
01290     child->raise();
01291     child->setFocus();
01292     child->setMouseTracking(true);
01293 }
01294 
01295 void MythMainWindow::detach(QWidget *child)
01296 {
01297     std::vector<QWidget*>::iterator it =
01298         std::find(d->widgetList.begin(), d->widgetList.end(), child);
01299 
01300     if (it == d->widgetList.end())
01301     {
01302         LOG(VB_GENERAL, LOG_ERR, "Could not find widget to detach");
01303         return;
01304     }
01305 
01306     d->widgetList.erase(it);
01307     QWidget *current = currentWidget();
01308 
01309     if (current)
01310     {
01311         current->setEnabled(true);
01312         current->setFocus();
01313         current->setMouseTracking(true);
01314     }
01315 
01316     if (d->exitingtomain)
01317     {
01318         QCoreApplication::postEvent(
01319             this, new QEvent(MythEvent::kExitToMainMenuEventType));
01320     }
01321 }
01322 
01323 QWidget *MythMainWindow::currentWidget(void)
01324 {
01325     if (d->widgetList.size() > 0)
01326         return d->widgetList.back();
01327     return NULL;
01328 }
01329 /* FIXME: end compatibility */
01330 
01331 uint MythMainWindow::PushDrawDisabled(void)
01332 {
01333     QMutexLocker locker(&d->m_drawDisableLock);
01334     d->m_drawDisabledDepth++;
01335     if (d->m_drawDisabledDepth && d->m_drawEnabled)
01336         SetDrawEnabled(false);
01337     return d->m_drawDisabledDepth;
01338 }
01339 
01340 uint MythMainWindow::PopDrawDisabled(void)
01341 {
01342     QMutexLocker locker(&d->m_drawDisableLock);
01343     if (d->m_drawDisabledDepth)
01344     {
01345         d->m_drawDisabledDepth--;
01346         if (!d->m_drawDisabledDepth && !d->m_drawEnabled)
01347             SetDrawEnabled(true);
01348     }
01349     return d->m_drawDisabledDepth;
01350 }
01351 
01352 void MythMainWindow::SetDrawEnabled(bool enable)
01353 {
01354     QMutexLocker locker(&d->m_setDrawEnabledLock);
01355 
01356     if (!gCoreContext->IsUIThread())
01357     {
01358         QCoreApplication::postEvent(
01359             this, new MythEvent(
01360                 (enable) ?
01361                 MythEvent::kEnableDrawingEventType :
01362                 MythEvent::kDisableDrawingEventType));
01363 
01364         while (QCoreApplication::hasPendingEvents())
01365             d->m_setDrawEnabledWait.wait(&d->m_setDrawEnabledLock);
01366 
01367         return;
01368     }
01369 
01370     setUpdatesEnabled(enable);
01371     d->m_drawEnabled = enable;
01372 
01373     if (enable)
01374     {
01375         if (d->m_pendingUpdate)
01376         {
01377             QApplication::postEvent(this, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
01378             d->m_pendingUpdate = false;
01379         }
01380         d->drawTimer->start(1000 / 70);
01381         ShowPainterWindow();
01382     }
01383     else
01384     {
01385         HidePainterWindow();
01386         d->drawTimer->stop();
01387     }
01388 
01389     d->m_setDrawEnabledWait.wakeAll();
01390 }
01391 
01392 void MythMainWindow::SetEffectsEnabled(bool enable)
01393 {
01394     QVector<MythScreenStack *>::Iterator it;
01395     for (it = d->stackList.begin(); it != d->stackList.end(); ++it)
01396     {
01397         if (enable)
01398             (*it)->EnableEffects();
01399         else
01400             (*it)->DisableEffects();
01401     }
01402 }
01403 
01404 bool MythMainWindow::IsExitingToMain(void) const
01405 {
01406     return d->exitingtomain;
01407 }
01408 
01409 void MythMainWindow::ExitToMainMenu(void)
01410 {
01411     bool jumpdone = !(d->popwindows);
01412 
01413     d->exitingtomain = true;
01414 
01415     /* compatibility code, remove, FIXME */
01416     QWidget *current = currentWidget();
01417     if (current && d->exitingtomain && d->popwindows)
01418     {
01419         if (current->objectName() != QString("mainmenu"))
01420         {
01421             if (current->objectName() == QString("video playback window"))
01422             {
01423                 MythEvent *me = new MythEvent("EXIT_TO_MENU");
01424                 QCoreApplication::postEvent(current, me);
01425             }
01426             else if (current->inherits("MythDialog"))
01427             {
01428                 QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, d->escapekey,
01429                                                Qt::NoModifier);
01430                 QObject *key_target = getTarget(*key);
01431                 QCoreApplication::postEvent(key_target, key);
01432             }
01433             return;
01434         }
01435         else
01436             jumpdone = true;
01437     }
01438 
01439     MythScreenStack *toplevel = GetMainStack();
01440     if (toplevel && d->popwindows)
01441     {
01442         MythScreenType *screen = toplevel->GetTopScreen();
01443         if (screen && screen->objectName() != QString("mainmenu"))
01444         {
01445             if (screen->objectName() == QString("video playback window"))
01446             {
01447                 MythEvent *me = new MythEvent("EXIT_TO_MENU");
01448                 QCoreApplication::postEvent(screen, me);
01449             }
01450             else
01451             {
01452                 QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, d->escapekey,
01453                                                Qt::NoModifier);
01454                 QCoreApplication::postEvent(this, key);
01455             }
01456             return;
01457         }
01458         else
01459             jumpdone = true;
01460     }
01461 
01462     if (jumpdone)
01463     {
01464         d->exitingtomain = false;
01465         d->popwindows = true;
01466         if (d->exitmenucallback)
01467         {
01468             void (*callback)(void) = d->exitmenucallback;
01469             d->exitmenucallback = NULL;
01470             callback();
01471         }
01472         else if (d->exitmenumediadevicecallback)
01473         {
01474             void (*callback)(MythMediaDevice*) = d->exitmenumediadevicecallback;
01475             MythMediaDevice * mediadevice = d->mediadeviceforcallback;
01476             d->mediadeviceforcallback = NULL;
01477             callback(mediadevice);
01478         }
01479     }
01480 }
01481 
01492 bool MythMainWindow::TranslateKeyPress(const QString &context,
01493                                        QKeyEvent *e, QStringList &actions,
01494                                        bool allowJumps)
01495 {
01496     actions.clear();
01497 
01498     // Special case for custom QKeyEvent where the action is embedded directly
01499     // in the QKeyEvent text property. Used by MythFEXML http extension
01500     if (e->key() == 0 && !e->text().isEmpty() &&
01501         e->modifiers() == Qt::NoModifier)
01502     {
01503         QString action = e->text();
01504         // check if it is a jumppoint
01505         if (!d->destinationMap.contains(action))
01506         {
01507             actions.append(action);
01508             return false;
01509         }
01510 
01511         if (allowJumps)
01512         {
01513             // This does not filter the jump based on the current location but
01514             // is consistent with handling of other actions that do not need
01515             // a keybinding. The network control code tries to match
01516             // GetCurrentLocation with the jumppoint but matching is utterly
01517             // inconsistent e.g. mainmenu<->Main Menu, Playback<->Live TV
01518             JumpTo(action);
01519             return true;
01520         }
01521 
01522         return false;
01523     }
01524 
01525     int keynum = d->TranslateKeyNum(e);
01526 
01527     QStringList localActions;
01528     if (allowJumps && (d->jumpMap.count(keynum) > 0) &&
01529         (!d->jumpMap[keynum]->localAction.isEmpty()) &&
01530         (d->keyContexts.value(context)) &&
01531         (d->keyContexts.value(context)->GetMapping(keynum, localActions)))
01532     {
01533         if (localActions.contains(d->jumpMap[keynum]->localAction))
01534             allowJumps = false;
01535     }
01536 
01537     if (allowJumps && d->jumpMap.count(keynum) > 0 &&
01538             !d->jumpMap[keynum]->exittomain && d->exitmenucallback == NULL)
01539     {
01540         void (*callback)(void) = d->jumpMap[keynum]->callback;
01541         callback();
01542         return true;
01543     }
01544 
01545     if (allowJumps &&
01546         d->jumpMap.count(keynum) > 0 && d->exitmenucallback == NULL)
01547     {
01548         d->exitingtomain = true;
01549         d->exitmenucallback = d->jumpMap[keynum]->callback;
01550         QCoreApplication::postEvent(
01551             this, new QEvent(MythEvent::kExitToMainMenuEventType));
01552         return true;
01553     }
01554 
01555     if (d->keyContexts.value(context))
01556         d->keyContexts.value(context)->GetMapping(keynum, actions);
01557 
01558     if (context != "Global")
01559         d->keyContexts.value("Global")->GetMapping(keynum, actions);
01560 
01561     return false;
01562 }
01563 
01564 void MythMainWindow::ClearKey(const QString &context, const QString &action)
01565 {
01566     KeyContext * keycontext = d->keyContexts.value(context);
01567     if (keycontext == NULL) return;
01568 
01569     QMutableMapIterator<int, QStringList> it(keycontext->actionMap);
01570     while (it.hasNext())
01571     {
01572         it.next();
01573         QStringList list = it.value();
01574 
01575         list.removeAll(action);
01576         if (list.isEmpty())
01577             it.remove();
01578     }
01579 }
01580 
01581 void MythMainWindow::ClearKeyContext(const QString &context)
01582 {
01583     KeyContext *keycontext = d->keyContexts.value(context);
01584     if (keycontext != NULL)
01585         keycontext->actionMap.clear();
01586 }
01587 
01588 void MythMainWindow::BindKey(const QString &context, const QString &action,
01589                              const QString &key)
01590 {
01591     QKeySequence keyseq(key);
01592 
01593     if (!d->keyContexts.contains(context))
01594         d->keyContexts.insert(context, new KeyContext());
01595 
01596     for (unsigned int i = 0; i < keyseq.count(); i++)
01597     {
01598         int keynum = keyseq[i];
01599         keynum &= ~Qt::UNICODE_ACCEL;
01600 
01601         QStringList dummyaction("");
01602         if (d->keyContexts.value(context)->GetMapping(keynum, dummyaction))
01603         {
01604             LOG(VB_GENERAL, LOG_WARNING,
01605                 QString("Key %1 is bound to multiple actions in context %2.")
01606                     .arg(key).arg(context));
01607         }
01608 
01609         d->keyContexts.value(context)->AddMapping(keynum, action);
01610 #if 0
01611         LOG(VB_GENERAL, LOG_DEBUG, QString("Binding: %1 to action: %2 (%3)")
01612                                    .arg(key).arg(action).arg(context));
01613 #endif
01614 
01615         if (action == "ESCAPE" && context == "Global" && i == 0)
01616             d->escapekey = keynum;
01617     }
01618 }
01619 
01620 void MythMainWindow::RegisterKey(const QString &context, const QString &action,
01621                                  const QString &description, const QString &key)
01622 {
01623     QString keybind = key;
01624 
01625     MSqlQuery query(MSqlQuery::InitCon());
01626 
01627     if (d->m_useDB && query.isConnected())
01628     {
01629         query.prepare("SELECT keylist, description FROM keybindings WHERE "
01630                       "context = :CONTEXT AND action = :ACTION AND "
01631                       "hostname = :HOSTNAME ;");
01632         query.bindValue(":CONTEXT", context);
01633         query.bindValue(":ACTION", action);
01634         query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
01635 
01636         if (query.exec() && query.next())
01637         {
01638             keybind = query.value(0).toString();
01639             QString db_description = query.value(1).toString();
01640 
01641             // Update keybinding description if changed
01642             if (db_description != description)
01643             {
01644                 LOG(VB_GENERAL, LOG_NOTICE,
01645                     "Updating keybinding description...");
01646                 query.prepare(
01647                     "UPDATE keybindings "
01648                     "SET description = :DESCRIPTION "
01649                     "WHERE context   = :CONTEXT AND "
01650                     "      action    = :ACTION  AND "
01651                     "      hostname  = :HOSTNAME");
01652 
01653                 query.bindValue(":DESCRIPTION", description);
01654                 query.bindValue(":CONTEXT",     context);
01655                 query.bindValue(":ACTION",      action);
01656                 query.bindValue(":HOSTNAME",    GetMythDB()->GetHostName());
01657 
01658                 if (!query.exec() && !(GetMythDB()->SuppressDBMessages()))
01659                 {
01660                     MythDB::DBError("Update Keybinding", query);
01661                 }
01662             }
01663         }
01664         else
01665         {
01666             QString inskey = keybind;
01667 
01668             query.prepare("INSERT INTO keybindings (context, action, "
01669                           "description, keylist, hostname) VALUES "
01670                           "( :CONTEXT, :ACTION, :DESCRIPTION, :KEYLIST, "
01671                           ":HOSTNAME );");
01672             query.bindValue(":CONTEXT", context);
01673             query.bindValue(":ACTION", action);
01674             query.bindValue(":DESCRIPTION", description);
01675             query.bindValue(":KEYLIST", inskey);
01676             query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
01677 
01678             if (!query.exec() && !(GetMythDB()->SuppressDBMessages()))
01679             {
01680                 MythDB::DBError("Insert Keybinding", query);
01681             }
01682         }
01683     }
01684 
01685     BindKey(context, action, keybind);
01686 }
01687 
01688 QString MythMainWindow::GetKey(const QString &context,
01689                                const QString &action) const
01690 {
01691     MSqlQuery query(MSqlQuery::InitCon());
01692     if (!query.isConnected())
01693         return "?";
01694 
01695     query.prepare("SELECT keylist "
01696                   "FROM keybindings "
01697                   "WHERE context  = :CONTEXT AND "
01698                   "      action   = :ACTION  AND "
01699                   "      hostname = :HOSTNAME");
01700     query.bindValue(":CONTEXT", context);
01701     query.bindValue(":ACTION", action);
01702     query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
01703 
01704     if (!query.exec() || !query.isActive() || !query.next())
01705         return "?";
01706 
01707     return query.value(0).toString();
01708 }
01709 
01710 void MythMainWindow::ClearJump(const QString &destination)
01711 {
01712     /* make sure that the jump point exists (using [] would add it)*/
01713     if (d->destinationMap.find(destination) == d->destinationMap.end())
01714     {
01715        LOG(VB_GENERAL, LOG_ERR,
01716            "Cannot clear ficticious jump point" + destination);
01717        return;
01718     }
01719 
01720     QMutableMapIterator<int, JumpData*> it(d->jumpMap);
01721     while (it.hasNext())
01722     {
01723         it.next();
01724         JumpData *jd = it.value();
01725         if (jd->destination == destination)
01726             it.remove();
01727     }
01728 }
01729 
01730 
01731 void MythMainWindow::BindJump(const QString &destination, const QString &key)
01732 {
01733     /* make sure the jump point exists */
01734     if (d->destinationMap.find(destination) == d->destinationMap.end())
01735     {
01736        LOG(VB_GENERAL, LOG_ERR,
01737            "Cannot bind to ficticious jump point" + destination);
01738        return;
01739     }
01740 
01741     QKeySequence keyseq(key);
01742 
01743     for (unsigned int i = 0; i < keyseq.count(); i++)
01744     {
01745         int keynum = keyseq[i];
01746         keynum &= ~Qt::UNICODE_ACCEL;
01747 
01748         if (d->jumpMap.count(keynum) == 0)
01749         {
01750 #if 0
01751             LOG(VB_GENERAL, LOG_DEBUG, QString("Binding: %1 to JumpPoint: %2")
01752                                        .arg(keybind).arg(destination));
01753 #endif
01754 
01755             d->jumpMap[keynum] = &d->destinationMap[destination];
01756         }
01757         else
01758         {
01759             LOG(VB_GENERAL, LOG_WARNING,
01760                 QString("Key %1 is already bound to a jump point.").arg(key));
01761         }
01762     }
01763 #if 0
01764     else
01765         LOG(VB_GENERAL, LOG_DEBUG,
01766             QString("JumpPoint: %2 exists, no keybinding") .arg(destination));
01767 #endif
01768 }
01769 
01770 void MythMainWindow::RegisterJump(const QString &destination,
01771                                   const QString &description,
01772                                   const QString &key, void (*callback)(void),
01773                                   bool exittomain, QString localAction)
01774 {
01775     QString keybind = key;
01776 
01777     MSqlQuery query(MSqlQuery::InitCon());
01778     if (query.isConnected())
01779     {
01780         query.prepare("SELECT keylist FROM jumppoints WHERE "
01781                       "destination = :DEST and hostname = :HOST ;");
01782         query.bindValue(":DEST", destination);
01783         query.bindValue(":HOST", GetMythDB()->GetHostName());
01784 
01785         if (query.exec() && query.next())
01786         {
01787             keybind = query.value(0).toString();
01788         }
01789         else
01790         {
01791             QString inskey = keybind;
01792 
01793             query.prepare("INSERT INTO jumppoints (destination, description, "
01794                           "keylist, hostname) VALUES ( :DEST, :DESC, :KEYLIST, "
01795                           ":HOST );");
01796             query.bindValue(":DEST", destination);
01797             query.bindValue(":DESC", description);
01798             query.bindValue(":KEYLIST", inskey);
01799             query.bindValue(":HOST", GetMythDB()->GetHostName());
01800 
01801             if (!query.exec() || !query.isActive())
01802             {
01803                 MythDB::DBError("Insert Jump Point", query);
01804             }
01805         }
01806     }
01807 
01808     JumpData jd =
01809         { callback, destination, description, exittomain, localAction };
01810     d->destinationMap[destination] = jd;
01811 
01812     BindJump(destination, keybind);
01813 }
01814 
01815 void MythMainWindow::ClearAllJumps()
01816 {
01817     QList<QString> destinations = d->destinationMap.keys();
01818     QList<QString>::Iterator it;
01819     for (it = destinations.begin(); it != destinations.end(); ++it)
01820         ClearJump(*it);
01821 }
01822 
01823 void MythMainWindow::JumpTo(const QString& destination, bool pop)
01824 {
01825     if (d->destinationMap.count(destination) > 0 && d->exitmenucallback == NULL)
01826     {
01827         d->exitingtomain = true;
01828         d->popwindows = pop;
01829         d->exitmenucallback = d->destinationMap[destination].callback;
01830         QCoreApplication::postEvent(
01831             this, new QEvent(MythEvent::kExitToMainMenuEventType));
01832         return;
01833     }
01834 }
01835 
01836 bool MythMainWindow::DestinationExists(const QString& destination) const
01837 {
01838     return (d->destinationMap.count(destination) > 0) ? true : false;
01839 }
01840 
01841 QStringList MythMainWindow::EnumerateDestinations(void) const
01842 {
01843     return d->destinationMap.keys();
01844 }
01845 
01846 void MythMainWindow::RegisterMediaPlugin(const QString &name,
01847                                          const QString &desc,
01848                                          MediaPlayCallback fn)
01849 {
01850     if (d->mediaPluginMap.count(name) == 0)
01851     {
01852         LOG(VB_GENERAL, LOG_NOTICE,
01853             QString("Registering %1 as a media playback plugin.").arg(name));
01854         MPData mpd = {desc, fn};
01855         d->mediaPluginMap[name] = mpd;
01856     }
01857     else
01858     {
01859         LOG(VB_GENERAL, LOG_NOTICE,
01860             QString("%1 is already registered as a media playback plugin.")
01861                 .arg(name));
01862     }
01863 }
01864 
01865 bool MythMainWindow::HandleMedia(const QString &handler, const QString &mrl,
01866                                  const QString &plot, const QString &title,
01867                                  const QString &subtitle,
01868                                  const QString &director, int season,
01869                                  int episode, const QString &inetref,
01870                                  int lenMins, const QString &year,
01871                                  const QString &id, bool useBookmarks)
01872 {
01873     QString lhandler(handler);
01874     if (lhandler.isEmpty())
01875         lhandler = "Internal";
01876 
01877     // Let's see if we have a plugin that matches the handler name...
01878     if (d->mediaPluginMap.count(lhandler))
01879     {
01880         d->mediaPluginMap[lhandler].playFn(mrl, plot, title, subtitle,
01881                                            director, season, episode,
01882                                            inetref, lenMins, year, id,
01883                                            useBookmarks);
01884         return true;
01885     }
01886 
01887     return false;
01888 }
01889 
01890 void MythMainWindow::HandleTVPower(bool poweron)
01891 {
01892 #ifdef USING_LIBCEC
01893     if (d->cecAdapter)
01894         d->cecAdapter->Action((poweron) ? ACTION_TVPOWERON : ACTION_TVPOWEROFF);
01895 #else
01896     (void) poweron;
01897 #endif
01898 }
01899 
01900 void MythMainWindow::AllowInput(bool allow)
01901 {
01902     d->AllowInput = allow;
01903 }
01904 
01905 void MythMainWindow::mouseTimeout(void)
01906 {
01907     MythGestureEvent *e;
01908 
01909     /* complete the stroke if its our first timeout */
01910     if (d->gesture.recording())
01911     {
01912         d->gesture.stop();
01913     }
01914 
01915     /* get the last gesture */
01916     e = d->gesture.gesture();
01917 
01918     if (e->gesture() < MythGestureEvent::Click)
01919         QCoreApplication::postEvent(this, e);
01920 }
01921 
01922 bool MythMainWindow::eventFilter(QObject *, QEvent *e)
01923 {
01924     MythGestureEvent *ge;
01925 
01926     /* Don't let anything through if input is disallowed. */
01927     if (!d->AllowInput)
01928         return true;
01929 
01930     switch (e->type())
01931     {
01932         case QEvent::KeyPress:
01933         {
01934             ResetIdleTimer();
01935             QKeyEvent *ke = dynamic_cast<QKeyEvent*>(e);
01936 
01937             // Work around weird GCC run-time bug. Only manifest on Mac OS X
01938             if (!ke)
01939                 ke = static_cast<QKeyEvent *>(e);
01940 
01941             if (currentWidget())
01942             {
01943                 ke->accept();
01944                 QWidget *current = currentWidget();
01945                 if (current && current->isEnabled())
01946                     qApp->notify(current, ke);
01947 
01948                 break;
01949             }
01950 
01951             QVector<MythScreenStack *>::Iterator it;
01952             for (it = d->stackList.end()-1; it != d->stackList.begin()-1; --it)
01953             {
01954                 MythScreenType *top = (*it)->GetTopScreen();
01955                 if (top)
01956                 {
01957                     if (top->keyPressEvent(ke))
01958                         return true;
01959 
01960                     // Note:  The following break prevents keypresses being
01961                     //        sent to windows below popups
01962                     if ((*it)->objectName() == "popup stack")
01963                         break;
01964                 }
01965             }
01966             break;
01967         }
01968         case QEvent::MouseButtonPress:
01969         {
01970             ResetIdleTimer();
01971             ShowMouseCursor(true);
01972             if (!d->gesture.recording())
01973             {
01974                 d->gesture.start();
01975                 d->gesture.record(dynamic_cast<QMouseEvent*>(e)->pos());
01976 
01977                 /* start a single shot timer */
01978                 d->gestureTimer->start(GESTURE_TIMEOUT);
01979 
01980                 return true;
01981             }
01982             break;
01983         }
01984         case QEvent::MouseButtonRelease:
01985         {
01986             ResetIdleTimer();
01987             ShowMouseCursor(true);
01988             if (d->gestureTimer->isActive())
01989                 d->gestureTimer->stop();
01990 
01991             if (currentWidget())
01992                 break;
01993 
01994             if (d->gesture.recording())
01995             {
01996                 d->gesture.stop();
01997                 ge = d->gesture.gesture();
01998 
01999                 /* handle clicks separately */
02000                 if (ge->gesture() == MythGestureEvent::Click)
02001                 {
02002                     QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(e);
02003                     if (!mouseEvent)
02004                         return false;
02005 
02006                     QVector<MythScreenStack *>::iterator it;
02007                     QPoint p = mouseEvent->pos();
02008 
02009                     ge->SetPosition(p);
02010 
02011                     MythGestureEvent::Button button = MythGestureEvent::NoButton;
02012                     switch (mouseEvent->button())
02013                     {
02014                         case Qt::LeftButton :
02015                             button = MythGestureEvent::LeftButton;
02016                             break;
02017                         case Qt::RightButton :
02018                             button = MythGestureEvent::RightButton;
02019                             break;
02020                         case Qt::MidButton :
02021                             button = MythGestureEvent::MiddleButton;
02022                             break;
02023                         case Qt::XButton1 :
02024                             button = MythGestureEvent::Aux1Button;
02025                             break;
02026                         case Qt::XButton2 :
02027                             button = MythGestureEvent::Aux2Button;
02028                             break;
02029                         default :
02030                             button = MythGestureEvent::NoButton;
02031                     }
02032 
02033                     ge->SetButton(button);
02034 
02035                     for (it = d->stackList.end()-1; it != d->stackList.begin()-1;
02036                          --it)
02037                     {
02038                         MythScreenType *screen = (*it)->GetTopScreen();
02039 
02040                         if (!screen || !screen->ContainsPoint(p))
02041                             continue;
02042 
02043                         if (screen->gestureEvent(ge))
02044                             break;
02045 
02046                         // Note:  The following break prevents clicks being
02047                         //        sent to windows below popups
02048                         //
02049                         //        we want to permit this in some cases, e.g.
02050                         //        when the music miniplayer is on screen or a
02051                         //        non-interactive alert/news scroller. So these
02052                         //        things need to be in a third or more stack
02053                         if ((*it)->objectName() == "popup stack")
02054                             break;
02055                     }
02056 
02057                     delete ge;
02058                 }
02059                 else
02060                     QCoreApplication::postEvent(this, ge);
02061 
02062                 return true;
02063             }
02064             break;
02065         }
02066         case QEvent::MouseMove:
02067         {
02068             ResetIdleTimer();
02069             ShowMouseCursor(true);
02070             if (d->gesture.recording())
02071             {
02072                 /* reset the timer */
02073                 d->gestureTimer->stop();
02074                 d->gestureTimer->start(GESTURE_TIMEOUT);
02075 
02076                 d->gesture.record(dynamic_cast<QMouseEvent*>(e)->pos());
02077                 return true;
02078             }
02079             break;
02080         }
02081         case QEvent::Wheel:
02082         {
02083             ResetIdleTimer();
02084             ShowMouseCursor(true);
02085             QWheelEvent* qmw = dynamic_cast<QWheelEvent*>(e);
02086             int delta = qmw->delta();
02087             if (delta>0)
02088             {
02089                 qmw->accept();
02090                 QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
02091                                                Qt::NoModifier);
02092                 QObject *key_target = getTarget(*key);
02093                 if (!key_target)
02094                     QCoreApplication::postEvent(this, key);
02095                 else
02096                     QCoreApplication::postEvent(key_target, key);
02097             }
02098             if (delta<0)
02099             {
02100                 qmw->accept();
02101                 QKeyEvent *key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
02102                                                Qt::NoModifier);
02103                 QObject *key_target = getTarget(*key);
02104                 if (!key_target)
02105                     QCoreApplication::postEvent(this, key);
02106                 else
02107                     QCoreApplication::postEvent(key_target, key);
02108             }
02109             break;
02110         }
02111         default:
02112             break;
02113     }
02114 
02115     return false;
02116 }
02117 
02118 void MythMainWindow::customEvent(QEvent *ce)
02119 {
02120     if (ce->type() == MythGestureEvent::kEventType)
02121     {
02122         MythGestureEvent *ge = static_cast<MythGestureEvent*>(ce);
02123         MythScreenStack *toplevel = GetMainStack();
02124         if (toplevel && !currentWidget())
02125         {
02126             MythScreenType *screen = toplevel->GetTopScreen();
02127             if (screen)
02128                 screen->gestureEvent(ge);
02129         }
02130         LOG(VB_GUI, LOG_DEBUG, QString("Gesture: %1") .arg(QString(*ge)));
02131     }
02132     else if (ce->type() == MythEvent::kExitToMainMenuEventType &&
02133              d->exitingtomain)
02134     {
02135         ExitToMainMenu();
02136     }
02137     else if (ce->type() == ExternalKeycodeEvent::kEventType)
02138     {
02139         ExternalKeycodeEvent *eke = static_cast<ExternalKeycodeEvent *>(ce);
02140         int keycode = eke->getKeycode();
02141 
02142         QKeyEvent key(QEvent::KeyPress, keycode, Qt::NoModifier);
02143 
02144         QObject *key_target = getTarget(key);
02145         if (!key_target)
02146             QCoreApplication::sendEvent(this, &key);
02147         else
02148             QCoreApplication::sendEvent(key_target, &key);
02149     }
02150 #if defined(USE_LIRC) || defined(USING_APPLEREMOTE)
02151     else if (ce->type() == LircKeycodeEvent::kEventType &&
02152              !d->ignore_lirc_keys)
02153     {
02154         LircKeycodeEvent *lke = static_cast<LircKeycodeEvent *>(ce);
02155 
02156         if (LircKeycodeEvent::kLIRCInvalidKeyCombo == lke->modifiers())
02157         {
02158             LOG(VB_GENERAL, LOG_WARNING,
02159                     QString("Attempt to convert LIRC key sequence '%1' "
02160                             "to a Qt key sequence failed.")
02161                     .arg(lke->lirctext()));
02162         }
02163         else
02164         {
02165             GetMythUI()->ResetScreensaver();
02166             if (GetMythUI()->GetScreenIsAsleep())
02167                 return;
02168 
02169             QKeyEvent key(lke->keytype(),   lke->key(),
02170                           lke->modifiers(), lke->text());
02171 
02172             QObject *key_target = getTarget(key);
02173             if (!key_target)
02174                 QCoreApplication::sendEvent(this, &key);
02175             else
02176                 QCoreApplication::sendEvent(key_target, &key);
02177         }
02178     }
02179 #endif
02180 #ifdef USE_JOYSTICK_MENU
02181     else if (ce->type() == JoystickKeycodeEvent::kEventType &&
02182              !d->ignore_joystick_keys)
02183     {
02184         JoystickKeycodeEvent *jke = static_cast<JoystickKeycodeEvent *>(ce);
02185         int keycode = jke->getKeycode();
02186 
02187         if (keycode)
02188         {
02189             GetMythUI()->ResetScreensaver();
02190             if (GetMythUI()->GetScreenIsAsleep())
02191                 return;
02192 
02193             Qt::KeyboardModifiers mod = Qt::KeyboardModifiers(keycode & Qt::MODIFIER_MASK);
02194             int k = (keycode & ~Qt::MODIFIER_MASK); /* trim off the mod */
02195             QString text;
02196 
02197             if (k & Qt::UNICODE_ACCEL)
02198             {
02199                 QChar c(k & ~Qt::UNICODE_ACCEL);
02200                 text = QString(c);
02201             }
02202 
02203             QKeyEvent key(jke->isKeyDown() ? QEvent::KeyPress :
02204                           QEvent::KeyRelease, k, mod, text);
02205 
02206             QObject *key_target = getTarget(key);
02207             if (!key_target)
02208                 QCoreApplication::sendEvent(this, &key);
02209             else
02210                 QCoreApplication::sendEvent(key_target, &key);
02211         }
02212         else
02213         {
02214             LOG(VB_GENERAL, LOG_WARNING,
02215                     QString("attempt to convert '%1' to a key sequence failed. "
02216                             "Fix your key mappings.")
02217                     .arg(jke->getJoystickMenuText()));
02218         }
02219     }
02220 #endif
02221     else if (ce->type() == MythMediaEvent::kEventType)
02222     {
02223         MythMediaEvent *me = static_cast<MythMediaEvent*>(ce);
02224 
02225         // A listener based system might be more efficient, but we should never
02226         // have that many screens open at once so impact should be minimal.
02227         //
02228         // This approach is simpler for everyone to follow. Plugin writers
02229         // don't have to worry about adding their screens to the list because
02230         // all screens receive media events.
02231         //
02232         // Events are even sent to hidden or backgrounded screens, this avoids
02233         // the need for those to poll for changes when they become visible again
02234         // however this needs to be kept in mind if media changes trigger
02235         // actions which would not be appropriate when the screen doesn't have
02236         // focus. It is the programmers responsibility to ignore events when
02237         // necessary.
02238         QVector<MythScreenStack *>::Iterator it;
02239         for (it = d->stackList.begin(); it != d->stackList.end(); ++it)
02240         {
02241             QVector<MythScreenType *> screenList;
02242             (*it)->GetScreenList(screenList);
02243             QVector<MythScreenType *>::Iterator sit;
02244             for (sit = screenList.begin(); sit != screenList.end(); ++sit)
02245             {
02246                 MythScreenType *screen = (*sit);
02247                 if (screen)
02248                     screen->mediaEvent(me);
02249             }
02250         }
02251 
02252         // Debugging
02253         MythMediaDevice *device = me->getDevice();
02254         if (device)
02255         {
02256             LOG(VB_GENERAL, LOG_DEBUG, QString("Media Event: %1 - %2")
02257                     .arg(device->getDevicePath()).arg(device->getStatus()));
02258         }
02259     }
02260     else if (ce->type() == ScreenSaverEvent::kEventType)
02261     {
02262         ScreenSaverEvent *sse = static_cast<ScreenSaverEvent *>(ce);
02263         switch (sse->getSSEventType())
02264         {
02265             case ScreenSaverEvent::ssetDisable:
02266             {
02267                 GetMythUI()->DoDisableScreensaver();
02268                 break;
02269             }
02270             case ScreenSaverEvent::ssetRestore:
02271             {
02272                 GetMythUI()->DoRestoreScreensaver();
02273                 break;
02274             }
02275             case ScreenSaverEvent::ssetReset:
02276             {
02277                 GetMythUI()->DoResetScreensaver();
02278                 break;
02279             }
02280             default:
02281             {
02282                 LOG(VB_GENERAL, LOG_ERR,
02283                         QString("Unknown ScreenSaverEvent type: %1")
02284                         .arg(sse->getSSEventType()));
02285             }
02286         }
02287     }
02288     else if (ce->type() == MythEvent::kPushDisableDrawingEventType)
02289     {
02290         PushDrawDisabled();
02291     }
02292     else if (ce->type() == MythEvent::kPopDisableDrawingEventType)
02293     {
02294         PopDrawDisabled();
02295     }
02296     else if (ce->type() == MythEvent::kDisableDrawingEventType)
02297     {
02298         SetDrawEnabled(false);
02299     }
02300     else if (ce->type() == MythEvent::kEnableDrawingEventType)
02301     {
02302         SetDrawEnabled(true);
02303     }
02304     else if (ce->type() == MythEvent::kLockInputDevicesEventType)
02305     {
02306         LockInputDevices(true);
02307     }
02308     else if (ce->type() == MythEvent::kUnlockInputDevicesEventType)
02309     {
02310         LockInputDevices(false);
02311     }
02312     else if ((MythEvent::Type)(ce->type()) == MythEvent::MythEventMessage)
02313     {
02314         MythEvent *me = (MythEvent *)ce;
02315         QString message = me->Message();
02316 
02317         if (message.startsWith(ACTION_HANDLEMEDIA))
02318         {
02319             if (me->ExtraDataCount() == 1)
02320                 HandleMedia("Internal", me->ExtraData(0));
02321             else if (me->ExtraDataCount() == 11)
02322                 HandleMedia("Internal", me->ExtraData(0),
02323                     me->ExtraData(1), me->ExtraData(2),
02324                     me->ExtraData(3), me->ExtraData(4),
02325                     me->ExtraData(5).toInt(), me->ExtraData(6).toInt(),
02326                     me->ExtraData(7), me->ExtraData(8).toInt(),
02327                     me->ExtraData(9), me->ExtraData(10), true);
02328             else
02329                 LOG(VB_GENERAL, LOG_ERR, "Failed to handle media");
02330         }
02331         else if (message.startsWith(ACTION_SCREENSHOT))
02332         {
02333             int width = 0;
02334             int height = 0;
02335             QString filename;
02336 
02337             if (me->ExtraDataCount() >= 2)
02338             {
02339                 width  = me->ExtraData(0).toInt();
02340                 height = me->ExtraData(1).toInt();
02341 
02342                 if (me->ExtraDataCount() == 3)
02343                     filename = me->ExtraData(2);
02344             }
02345             ScreenShot(width, height, filename);
02346         }
02347         else if (message == ACTION_GETSTATUS)
02348         {
02349             QVariantMap state;
02350             state.insert("state", "idle");
02351             state.insert("menutheme",
02352                  GetMythDB()->GetSetting("menutheme", "defaultmenu"));
02353             state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
02354             MythUIStateTracker::SetState(state);
02355         }
02356         else if (message.startsWith("PLAYBACK_START"))
02357         {
02358             PauseIdleTimer(true);
02359         }
02360         else if (message.startsWith("PLAYBACK_END"))
02361         {
02362             PauseIdleTimer(false);
02363         }
02364     }
02365     else if ((MythEvent::Type)(ce->type()) == MythEvent::MythUserMessage)
02366     {
02367         MythEvent *me = (MythEvent *)ce;
02368         QString message = me->Message();
02369 
02370         if (!message.isEmpty())
02371             ShowOkPopup(message);
02372     }
02373 }
02374 
02375 QObject *MythMainWindow::getTarget(QKeyEvent &key)
02376 {
02377     QObject *key_target = NULL;
02378 
02379     if (!currentWidget())
02380         return key_target;
02381 
02382     key_target = QWidget::keyboardGrabber();
02383 
02384     if (!key_target)
02385     {
02386         QWidget *focus_widget = qApp->focusWidget();
02387         if (focus_widget && focus_widget->isEnabled())
02388         {
02389             key_target = focus_widget;
02390 
02391             // Yes this is special code for handling the
02392             // the escape key.
02393             if (key.key() == d->escapekey && focus_widget->topLevelWidget())
02394                 key_target = focus_widget->topLevelWidget();
02395         }
02396     }
02397 
02398     if (!key_target)
02399         key_target = this;
02400 
02401     return key_target;
02402 }
02403 
02404 int MythMainWindow::NormalizeFontSize(int pointSize)
02405 {
02406     float floatSize = pointSize;
02407     float desired = 100.0;
02408 
02409 #ifdef USING_MINGW
02410     // logicalDpiY not supported in QT3/win.
02411     int logicalDpiY = 100;
02412     HDC hdc = GetDC(NULL);
02413     if (hdc)
02414     {
02415         logicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
02416         ReleaseDC(NULL, hdc);
02417     }
02418 #else
02419     int logicalDpiY = this->logicalDpiY();
02420 #endif
02421 
02422     // adjust for screen resolution relative to 100 dpi
02423     floatSize = floatSize * desired / logicalDpiY;
02424     // adjust for myth GUI size relative to 800x600
02425     floatSize = floatSize * d->hmult;
02426     // round to the nearest point size
02427     pointSize = (int)(floatSize + 0.5);
02428 
02429     return pointSize;
02430 }
02431 
02432 MythRect MythMainWindow::NormRect(const MythRect &rect)
02433 {
02434     MythRect ret;
02435     ret.setWidth((int)(rect.width() * d->wmult));
02436     ret.setHeight((int)(rect.height() * d->hmult));
02437     ret.moveTopLeft(QPoint((int)(rect.x() * d->wmult),
02438                            (int)(rect.y() * d->hmult)));
02439     ret = ret.normalized();
02440 
02441     return ret;
02442 }
02443 
02444 QPoint MythMainWindow::NormPoint(const QPoint &point)
02445 {
02446     QPoint ret;
02447     ret.setX((int)(point.x() * d->wmult));
02448     ret.setY((int)(point.y() * d->hmult));
02449 
02450     return ret;
02451 }
02452 
02453 QSize MythMainWindow::NormSize(const QSize &size)
02454 {
02455     QSize ret;
02456     ret.setWidth((int)(size.width() * d->wmult));
02457     ret.setHeight((int)(size.height() * d->hmult));
02458 
02459     return ret;
02460 }
02461 
02462 int MythMainWindow::NormX(const int x)
02463 {
02464     return (int)(x * d->wmult);
02465 }
02466 
02467 int MythMainWindow::NormY(const int y)
02468 {
02469     return (int)(y * d->hmult);
02470 }
02471 
02472 void MythMainWindow::SetScalingFactors(float wmult, float hmult)
02473 {
02474     d->wmult = wmult;
02475     d->hmult = hmult;
02476 }
02477 
02478 QRect MythMainWindow::GetUIScreenRect(void)
02479 {
02480     return d->uiScreenRect;
02481 }
02482 
02483 void MythMainWindow::SetUIScreenRect(QRect &rect)
02484 {
02485     d->uiScreenRect = rect;
02486 }
02487 
02488 int MythMainWindow::GetDrawInterval() const
02489 {
02490     return d->drawInterval;
02491 }
02492 
02493 void MythMainWindow::StartLIRC(void)
02494 {
02495 #ifdef USE_LIRC
02496     if (d->lircThread)
02497     {
02498         d->lircThread->deleteLater();
02499         d->lircThread = NULL;
02500     }
02501 
02502     QString config_file = GetConfDir() + "/lircrc";
02503     if (!QFile::exists(config_file))
02504         config_file = QDir::homePath() + "/.lircrc";
02505 
02506     /* lircd socket moved from /dev/ to /var/run/lirc/ in lirc 0.8.6 */
02507     QString lirc_socket = "/dev/lircd";
02508     if (!QFile::exists(lirc_socket))
02509         lirc_socket = "/var/run/lirc/lircd";
02510 
02511     d->lircThread = new LIRC(
02512         this,
02513         GetMythDB()->GetSetting("LircSocket", lirc_socket),
02514         "mythtv", config_file);
02515 
02516     if (d->lircThread->Init())
02517     {
02518         d->lircThread->start();
02519     }
02520     else
02521     {
02522         d->lircThread->deleteLater();
02523         d->lircThread = NULL;
02524     }
02525 #endif
02526 }
02527 
02528 void MythMainWindow::LockInputDevices( bool locked )
02529 {
02530     if( locked )
02531         LOG(VB_GENERAL, LOG_INFO, "Locking input devices");
02532     else
02533         LOG(VB_GENERAL, LOG_INFO, "Unlocking input devices");
02534 
02535 #ifdef USE_LIRC
02536     d->ignore_lirc_keys = locked;
02537 #endif
02538 
02539 #ifdef USE_JOYSTICK_MENU
02540     d->ignore_joystick_keys = locked;
02541 #endif
02542 }
02543 
02544 void MythMainWindow::ShowMouseCursor(bool show)
02545 {
02546     if (show && GetMythDB()->GetNumSetting("HideMouseCursor", 0))
02547         return;
02548 #ifdef QWS
02549     QWSServer::setCursorVisible(show);
02550 #endif
02551     // Set cursor call must come after Show() to work on some systems.
02552     setCursor(show ? (Qt::ArrowCursor) : (Qt::BlankCursor));
02553 
02554     if (show)
02555         d->hideMouseTimer->start();
02556 }
02557 
02558 void MythMainWindow::HideMouseTimeout(void)
02559 {
02560     ShowMouseCursor(false);
02561 }
02562 
02563 void MythMainWindow::ResetIdleTimer(void)
02564 {
02565     // If the timer isn't active then it's been paused
02566     if (!d->idleTimer->isActive() && !d->standby)
02567         return;
02568 
02569     if (d->standby)
02570         ExitStandby();
02571 
02572     d->idleTimer->start();
02573 }
02574 
02575 void MythMainWindow::PauseIdleTimer(bool pause)
02576 {
02577     if (pause)
02578         d->idleTimer->stop();
02579     else
02580         d->idleTimer->start();
02581 
02582     ResetIdleTimer();
02583 }
02584 
02585 void MythMainWindow::IdleTimeout(void)
02586 {
02587     EnterStandby();
02588 }
02589 
02590 void MythMainWindow::EnterStandby()
02591 {
02592     int idletimeout = gCoreContext->GetNumSetting("FrontendIdleTimeout",
02593                                                   STANDBY_TIMEOUT);
02594     LOG(VB_GENERAL, LOG_NOTICE, QString("Entering standby mode after "
02595                                         "%1 minutes of inactivity")
02596                                         .arg(idletimeout));
02597     //JumpTo("Main Menu");
02598     d->standby = true;
02599     gCoreContext->AllowShutdown();
02600 }
02601 
02602 void MythMainWindow::ExitStandby()
02603 {
02604     LOG(VB_GENERAL, LOG_NOTICE, "Leaving standby mode");
02605     d->standby = false;
02606     gCoreContext->BlockShutdown();
02607 }
02608 
02609 
02610 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends