00001 #include <qapplication.h>
00002 #include <qsqldatabase.h>
00003 #include <qimage.h>
00004 #include <qpixmap.h>
00005 #include <qdir.h>
00006 #include <qpainter.h>
00007 #include <unistd.h>
00008 #include <qsqldatabase.h>
00009 #include <qurl.h>
00010 #include <qnetwork.h>
00011 #include <qwaitcondition.h>
00012 #include <qregexp.h>
00013
00014 #include <qhostaddress.h>
00015
00016 #include <cmath>
00017 #include <queue>
00018
00019 #include "config.h"
00020 #include "mythcontext.h"
00021 #include "exitcodes.h"
00022 #include "oldsettings.h"
00023 #include "util.h"
00024 #include "remotefile.h"
00025 #include "dialogbox.h"
00026 #include "mythdialogs.h"
00027 #include "mythplugin.h"
00028 #include "screensaver.h"
00029 #include "DisplayRes.h"
00030 #include "backendselect.h"
00031 #include "dbsettings.h"
00032 #include "langsettings.h"
00033 #include "mythdbcon.h"
00034 #include "util-x11.h"
00035 #include "mythsocket.h"
00036 #include "themeinfo.h"
00037
00038 #include "libmythui/mythmainwindow.h"
00039 #include "libmythupnp/mythxmlclient.h"
00040 #include "libmythupnp/upnp.h"
00041
00042 #ifdef USING_MINGW
00043 #include <winsock2.h>
00044 #undef DialogBox
00045 #include "compat.h"
00046 #endif
00047
00048
00049
00050 #ifdef CONFIG_DARWIN
00051 const QString kPluginLibPrefix = "lib";
00052 const QString kPluginLibSuffix = ".dylib";
00053 #elif USING_MINGW
00054 const QString kPluginLibPrefix = "lib";
00055 const QString kPluginLibSuffix = ".dll";
00056 #else
00057 const QString kPluginLibPrefix = "lib";
00058 const QString kPluginLibSuffix = ".so";
00059 #endif
00060
00061 MythContext *gContext = NULL;
00062 QMutex MythContext::verbose_mutex(true);
00063 QString MythContext::x11_display = QString::null;
00064
00065 unsigned int print_verbose_messages = VB_IMPORTANT | VB_GENERAL;
00066 QString verboseString = QString(" important general");
00067
00068 QMutex avcodeclock(true);
00069
00070
00071 const QString gBackendURI = "urn:schemas-mythtv-org:device:MasterMediaServer:1";
00072 const QString kDefaultBE = "UPnP/MythFrontend/DefaultBackend/";
00073 const QString kDefaultPIN = kDefaultBE + "SecurityPin";
00074 const QString kDefaultUSN = kDefaultBE + "USN";
00075
00076
00077 int parse_verbose_arg(QString arg)
00078 {
00079 QString option;
00080 bool reverseOption;
00081
00082 if (arg.startsWith("-"))
00083 {
00084 cerr << "Invalid or missing argument to -v/--verbose option\n";
00085 return GENERIC_EXIT_INVALID_CMDLINE;
00086 }
00087 else
00088 {
00089 QStringList verboseOpts = QStringList::split(',', arg);
00090 for (QStringList::Iterator it = verboseOpts.begin();
00091 it != verboseOpts.end(); ++it )
00092 {
00093 option = *it;
00094 reverseOption = false;
00095
00096 if (option != "none" && option.left(2) == "no")
00097 {
00098 reverseOption = true;
00099 option = option.right(option.length() - 2);
00100 }
00101
00102 if (!strcmp(option,"help"))
00103 {
00104 QString m_verbose = verboseString;
00105 m_verbose.replace(QRegExp(" "), ",");
00106 m_verbose.replace(QRegExp("^,"), "");
00107 cerr <<
00108 "Verbose debug levels.\n" <<
00109 "Accepts any combination (separated by comma) of:\n\n" <<
00110
00111 #define VERBOSE_ARG_HELP(ARG_ENUM, ARG_VALUE, ARG_STR, ARG_ADDITIVE, ARG_HELP) \
00112 QString(" %1").arg(ARG_STR).leftJustify(15, ' ', true) << \
00113 " - " << ARG_HELP << "\n" <<
00114
00115 VERBOSE_MAP(VERBOSE_ARG_HELP)
00116
00117 "\n" <<
00118 "The default for this program appears to be: '-v " <<
00119 m_verbose << "'\n\n" <<
00120 "Most options are additive except for none, all, and important.\n" <<
00121 "These three are semi-exclusive and take precedence over any\n" <<
00122 "prior options given. You can however use something like\n" <<
00123 "'-v none,jobqueue' to get only JobQueue related messages\n" <<
00124 "and override the default verbosity level.\n" <<
00125 "\n" <<
00126 "The additive options may also be subtracted from 'all' by \n" <<
00127 "prefixing them with 'no', so you may use '-v all,nodatabase'\n" <<
00128 "to view all but database debug messages.\n" <<
00129 "\n" <<
00130 "Some debug levels may not apply to this program.\n" <<
00131 endl;
00132 return GENERIC_EXIT_INVALID_CMDLINE;
00133 }
00134
00135 #define VERBOSE_ARG_CHECKS(ARG_ENUM, ARG_VALUE, ARG_STR, ARG_ADDITIVE, ARG_HELP) \
00136 else if (option == ARG_STR) \
00137 { \
00138 if (reverseOption) \
00139 { \
00140 print_verbose_messages &= ~(ARG_VALUE); \
00141 verboseString = verboseString + " no" + ARG_STR; \
00142 } \
00143 else \
00144 { \
00145 if (ARG_ADDITIVE) \
00146 { \
00147 print_verbose_messages |= ARG_VALUE; \
00148 verboseString = verboseString + " " + ARG_STR; \
00149 } \
00150 else \
00151 { \
00152 print_verbose_messages = ARG_VALUE; \
00153 verboseString = ARG_STR; \
00154 } \
00155 } \
00156 }
00157
00158 VERBOSE_MAP(VERBOSE_ARG_CHECKS)
00159
00160 else
00161 {
00162 cerr << "Unknown argument for -v/--verbose: "
00163 << option << endl;;
00164 }
00165 }
00166 }
00167
00168 return GENERIC_EXIT_OK;
00169 }
00170
00171
00172 QString safe_eno_to_string(int errnum)
00173 {
00174 return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum);
00175 }
00176
00177
00178 class MythContextPrivate
00179 {
00180 public:
00181 MythContextPrivate(MythContext *lparent);
00182 ~MythContextPrivate();
00183
00184 bool Init (const bool gui, UPnp *UPnPclient,
00185 const bool prompt, const bool noPrompt);
00186 bool FindDatabase(const bool prompt, const bool noPrompt);
00187
00188 bool IsWideMode() const {return (m_baseWidth == 1280);}
00189 void SetWideMode() {m_baseWidth = 1280; m_baseHeight = 720;}
00190 bool IsSquareMode() const {return (m_baseWidth == 800);}
00191 void SetSquareMode() {m_baseWidth = 800; m_baseHeight = 600;}
00192
00193 void TempMainWindow(bool languagePrompt = true);
00194 void EndTempWindow(void);
00195
00196 void GetScreenBounds(void);
00197 void StoreGUIsettings(void);
00198
00199 void LoadLogSettings(void);
00200 void LoadDatabaseSettings(void);
00201
00202 bool LoadSettingsFile(void);
00203 bool WriteSettingsFile(const DatabaseParams ¶ms,
00204 bool overwrite = false);
00205 bool FindSettingsProbs(void);
00206
00207 QString getResponse(const QString &query, const QString &def);
00208 int intResponse(const QString &query, int def);
00209 bool PromptForDatabaseParams(const QString &error);
00210 QString TestDBconnection(void);
00211 void ResetDatabase(void);
00212
00213 bool InitUPnP(void);
00214 void DeleteUPnP(void);
00215 int ChooseBackend(const QString &error);
00216 int UPnPautoconf(const int milliSeconds = 2000);
00217 void StoreConnectionInfo(void);
00218 bool DefaultUPnP(QString &error);
00219 bool UPnPconnect(const DeviceLocation *device, const QString &PIN);
00220
00221
00222 MythContext *parent;
00223
00224 Settings *m_settings;
00225 Settings *m_qtThemeSettings;
00226
00227 QString m_installprefix;
00228
00229 QString m_libname;
00230
00231 bool m_gui;
00232 bool m_backend;
00233
00234 bool m_themeloaded;
00235 QString m_menuthemepathname;
00236 QString m_themepathname;
00237 QPixmap *m_backgroundimage;
00238 QPalette m_palette;
00239
00240
00241
00242
00243 int m_xbase, m_ybase;
00244 int m_height, m_width;
00245
00246
00247 int m_baseWidth, m_baseHeight;
00248
00249
00250 QMutex m_hostnamelock;
00251 QString m_localhostname;
00252
00253 DatabaseParams m_DBparams;
00254 QString m_DBhostCp;
00255
00256 UPnp *m_UPnP;
00257 XmlConfiguration *m_XML;
00258 HttpServer *m_HTTP;
00259
00260 QMutex serverSockLock;
00261 bool attemptingToConnect;
00262
00263 MDBManager m_dbmanager;
00264
00265 QMap<QString, QImage> imageCache;
00266
00267 QString language;
00268
00269 MythMainWindow *mainWindow;
00270
00271 float m_wmult, m_hmult;
00272
00273
00274
00275 int m_screenxbase, m_screenybase;
00276 int m_screenwidth, m_screenheight;
00277
00278
00279 int m_geometry_x, m_geometry_y;
00280 int m_geometry_w, m_geometry_h;
00281
00282 QString themecachedir;
00283
00284 int bigfontsize, mediumfontsize, smallfontsize;
00285
00286 MythSocket *serverSock;
00287 MythSocket *eventSock;
00288
00289 bool disablelibrarypopup;
00290
00291 MythPluginManager *pluginmanager;
00292
00293 ScreenSaverControl *screensaver;
00294
00295 int m_logenable, m_logmaxcount, m_logprintlevel;
00296 QMap<QString,int> lastLogCounts;
00297 QMap<QString,QString> lastLogStrings;
00298
00299 bool screensaverEnabled;
00300
00301 DisplayRes *display_res;
00302
00303 QMutex m_priv_mutex;
00304 queue<MythPrivRequest> m_priv_requests;
00305 QWaitCondition m_priv_queued;
00306
00307 bool useSettingsCache;
00308 QMutex settingsCacheLock;
00309 QMap <QString, QString> settingsCache;
00310 QMap <QString, QString> overriddenSettings;
00311 };
00312
00313
00314 MythContextPrivate::MythContextPrivate(MythContext *lparent)
00315 : parent(lparent),
00316 m_settings(new Settings()), m_qtThemeSettings(new Settings()),
00317 m_installprefix(RUNPREFIX), m_libname(LIBDIRNAME),
00318 m_gui(false), m_backend(false), m_themeloaded(false),
00319 m_menuthemepathname(QString::null), m_themepathname(QString::null),
00320 m_backgroundimage(NULL),
00321 m_xbase(0), m_ybase(0), m_height(0), m_width(0),
00322 m_baseWidth(800), m_baseHeight(600),
00323 m_localhostname(QString::null),
00324 m_UPnP(NULL), m_XML(NULL), m_HTTP(NULL),
00325 serverSockLock(false),
00326 attemptingToConnect(false),
00327 language(""),
00328 mainWindow(NULL),
00329 m_wmult(1.0), m_hmult(1.0),
00330 m_screenxbase(0), m_screenybase(0), m_screenwidth(0), m_screenheight(0),
00331 m_geometry_x(0), m_geometry_y(0), m_geometry_w(0), m_geometry_h(0),
00332 themecachedir(QString::null),
00333 bigfontsize(0), mediumfontsize(0), smallfontsize(0),
00334 serverSock(NULL), eventSock(NULL),
00335 disablelibrarypopup(false),
00336 pluginmanager(NULL),
00337 screensaver(NULL),
00338 m_logenable(-1), m_logmaxcount(-1), m_logprintlevel(-1),
00339 screensaverEnabled(false),
00340 display_res(NULL),
00341 useSettingsCache(false)
00342 {
00343 char *tmp_installprefix = getenv("MYTHTVDIR");
00344 if (tmp_installprefix)
00345 m_installprefix = tmp_installprefix;
00346
00347 #if QT_VERSION >= 0x030200
00348 QDir prefixDir = qApp->applicationDirPath();
00349 #else
00350 QString appPath = QFileInfo(qApp->argv()[0]).absFilePath();
00351 QDir prefixDir(appPath.left(appPath.findRev("/")));
00352 #endif
00353
00354 if (QDir(m_installprefix).isRelative())
00355 {
00356
00357
00358
00359
00360 VERBOSE(VB_IMPORTANT,
00361 "Relative PREFIX! (" + m_installprefix +
00362 ")\n\t\tappDir=" + prefixDir.canonicalPath());
00363 prefixDir.cd(m_installprefix);
00364 m_installprefix = prefixDir.canonicalPath();
00365 }
00366
00367 VERBOSE(VB_IMPORTANT, "Using runtime prefix = " + m_installprefix);
00368 }
00369
00370 MythContextPrivate::~MythContextPrivate()
00371 {
00372 imageCache.clear();
00373
00374 DeleteUPnP();
00375 if (m_settings)
00376 delete m_settings;
00377 if (m_qtThemeSettings)
00378 delete m_qtThemeSettings;
00379 if (serverSock)
00380 serverSock->DownRef();
00381 if (eventSock)
00382 eventSock->DownRef();
00383 if (screensaver)
00384 delete screensaver;
00385 }
00386
00401 void MythContextPrivate::TempMainWindow(bool languagePrompt)
00402 {
00403 if (mainWindow)
00404 return;
00405
00406
00407
00408
00409 if (m_DBparams.dbHostName.length())
00410 {
00411 m_DBhostCp = m_DBparams.dbHostName;
00412 m_DBparams.dbHostName = "";
00413 }
00414
00415 m_settings->SetSetting("Theme", "blue");
00416 #ifdef Q_WS_MACX
00417
00418 m_settings->SetSetting("Style", "Windows");
00419 #endif
00420 parent->LoadQtConfig();
00421
00422 MythMainWindow *mainWindow = MythMainWindow::getMainWindow(false);
00423 mainWindow->Init();
00424 parent->SetMainWindow(mainWindow);
00425
00426 if (languagePrompt)
00427 {
00428
00429 LanguageSettings::prompt();
00430 LanguageSettings::load("mythfrontend");
00431 }
00432 }
00433
00434 void MythContextPrivate::EndTempWindow(void)
00435 {
00436 parent->SetMainWindow(NULL);
00437 DestroyMythMainWindow();
00438 }
00439
00447 void MythContextPrivate::GetScreenBounds()
00448 {
00449 if (m_geometry_w)
00450 {
00451
00452 m_xbase = m_geometry_x;
00453 m_ybase = m_geometry_y;
00454 m_width = m_geometry_w;
00455 m_height = m_geometry_h;
00456 return;
00457 }
00458
00459 QDesktopWidget * desktop = QApplication::desktop();
00460 bool hasXinerama = GetNumberOfXineramaScreens() > 1;
00461 int numScreens = desktop->numScreens();
00462 int screen;
00463
00464 if (hasXinerama)
00465 VERBOSE(VB_GENERAL,
00466 QString("Total desktop dim: %1x%2, over %3 screen[s].")
00467 .arg(desktop->width()).arg(desktop->height()).arg(numScreens));
00468 if (numScreens > 1)
00469 for (screen = 0; screen < numScreens; ++screen)
00470 {
00471 QRect dim = desktop->screenGeometry(screen);
00472 VERBOSE(VB_GENERAL,
00473 QString("Screen %1 dim: %2x%3.")
00474 .arg(screen).arg(dim.width()).arg(dim.height()));
00475 }
00476
00477 screen = desktop->primaryScreen();
00478 VERBOSE(VB_GENERAL, QString("Primary screen %1.").arg(screen));
00479
00480 if (hasXinerama)
00481 screen = parent->GetNumSetting("XineramaScreen", screen);
00482
00483 if (screen == -1)
00484 {
00485 VERBOSE(VB_GENERAL, QString("Using all %1 screens.")
00486 .arg(numScreens));
00487 m_xbase = 0;
00488 m_ybase = 0;
00489 m_width = desktop->width();
00490 m_height = desktop->height();
00491
00492 VERBOSE(VB_GENERAL, QString("Total width = %1, height = %2")
00493 .arg(m_width).arg(m_height));
00494 return;
00495 }
00496
00497 if (hasXinerama)
00498 {
00499 if (screen < 0 || screen >= numScreens)
00500 {
00501 VERBOSE(VB_IMPORTANT, QString(
00502 "Xinerama screen %1 was specified,"
00503 " but only %2 available, so using screen 0.")
00504 .arg(screen).arg(numScreens));
00505 screen = 0;
00506 }
00507 }
00508
00509
00510 {
00511 QRect bounds;
00512
00513 bool inWindow = parent->GetNumSetting("RunFrontendInWindow", 0);
00514
00515 if (inWindow)
00516 VERBOSE(VB_IMPORTANT, QString("Running in a window"));
00517
00518 if (inWindow)
00519
00520
00521 bounds = desktop->availableGeometry(screen);
00522 else
00523 bounds = desktop->screenGeometry(screen);
00524
00525 m_xbase = bounds.x();
00526 m_ybase = bounds.y();
00527 m_width = bounds.width();
00528 m_height = bounds.height();
00529
00530 VERBOSE(VB_GENERAL, QString("Using screen %1, %2x%3 at %4,%5")
00531 .arg(screen).arg(m_width).arg(m_height)
00532 .arg(m_xbase).arg(m_ybase));
00533 }
00534 }
00535
00536 bool MythContextPrivate::Init(const bool gui, UPnp *UPnPclient,
00537 const bool promptForBackend, const bool noPrompt)
00538 {
00539 m_gui = gui;
00540 if (UPnPclient)
00541 {
00542 m_UPnP = UPnPclient;
00543 #ifndef _WIN32
00544 m_XML = (XmlConfiguration *)UPnp::g_pConfig;
00545 #endif
00546 }
00547
00548
00549 if (gui)
00550 screensaver = ScreenSaverControl::get();
00551
00552
00553
00554 if (!FindDatabase(promptForBackend, noPrompt))
00555 return false;
00556
00557
00558
00559 if (gui)
00560 {
00561 GetScreenBounds();
00562 StoreGUIsettings();
00563 }
00564
00565 return true;
00566 }
00567
00580 bool MythContextPrivate::FindDatabase(const bool prompt, const bool noPrompt)
00581 {
00582
00583 bool manualSelect = prompt && !noPrompt;
00584
00585
00586 bool autoSelect = !manualSelect;
00587
00588 QString failure;
00589
00590
00591
00592 LoadDatabaseSettings();
00593
00594
00595
00596
00597 if (!manualSelect)
00598 {
00599
00600
00601
00602 if (DefaultUPnP(failure))
00603 autoSelect = manualSelect = false;
00604 else
00605 if (failure.length())
00606 VERBOSE(VB_IMPORTANT, failure);
00607
00608 failure = TestDBconnection();
00609 if (failure.isEmpty())
00610 goto DBfound;
00611 }
00612
00613
00614
00615 if (autoSelect)
00616 {
00617 int count = UPnPautoconf();
00618
00619 if (count == 0)
00620 failure = "No UPnP backends found";
00621
00622 if (count == 1)
00623 {
00624 failure = TestDBconnection();
00625 if (failure.isEmpty())
00626 goto DBfound;
00627 }
00628
00629 if (count > 1 || count == -1)
00630 manualSelect = !noPrompt;
00631 }
00632
00633 if (!m_gui)
00634 manualSelect = false;
00635
00636
00637
00638
00639 if (manualSelect)
00640 {
00641 switch (ChooseBackend(QString::null))
00642 {
00643 case -1:
00644 if (PromptForDatabaseParams(""))
00645 break;
00646 else
00647 goto NoDBfound;
00648
00649 case 0:
00650 goto NoDBfound;
00651
00652 case 1:
00653 break;
00654
00655 default:
00656 goto NoDBfound;
00657 }
00658 failure = TestDBconnection();
00659 }
00660
00661
00662
00663
00664 while (failure.length())
00665 {
00666 VERBOSE(VB_IMPORTANT, QString("%1").arg(failure));
00667 if (( manualSelect && ChooseBackend(failure)) ||
00668 (!manualSelect && PromptForDatabaseParams(failure)))
00669 {
00670 failure = TestDBconnection();
00671 if (failure.length())
00672 VERBOSE(VB_IMPORTANT, QString("%1").arg(failure));
00673 }
00674 else
00675 goto NoDBfound;
00676 }
00677
00678 DBfound:
00679
00680 StoreConnectionInfo();
00681 ResetDatabase();
00682 DeleteUPnP();
00683 return true;
00684
00685 NoDBfound:
00686
00687 DeleteUPnP();
00688 return false;
00689 }
00690
00694 void MythContextPrivate::StoreGUIsettings()
00695 {
00696 if (m_geometry_w)
00697 {
00698
00699 m_screenxbase = m_geometry_x;
00700 m_screenybase = m_geometry_y;
00701 m_screenwidth = m_geometry_w;
00702 m_screenheight = m_geometry_h;
00703 }
00704 else
00705 {
00706 m_screenxbase = parent->GetNumSetting("GuiOffsetX");
00707 m_screenybase = parent->GetNumSetting("GuiOffsetY");
00708 m_screenwidth = m_screenheight = 0;
00709 parent->GetResolutionSetting("Gui", m_screenwidth, m_screenheight);
00710 }
00711
00712
00713
00714
00715 if (!m_screenxbase)
00716 m_screenxbase = m_xbase;
00717 if (!m_screenybase)
00718 m_screenybase = m_ybase;
00719 if (!m_screenwidth)
00720 m_screenwidth = m_width;
00721 if (!m_screenheight)
00722 m_screenheight = m_height;
00723
00724 if (m_screenheight < 160 || m_screenwidth < 160)
00725 {
00726 VERBOSE(VB_IMPORTANT, "Somehow, your screen size settings are bad.");
00727 VERBOSE(VB_IMPORTANT, QString("GuiResolution: %1")
00728 .arg(parent->GetSetting("GuiResolution")));
00729 VERBOSE(VB_IMPORTANT, QString(" old GuiWidth: %1")
00730 .arg(parent->GetNumSetting("GuiWidth")));
00731 VERBOSE(VB_IMPORTANT, QString(" old GuiHeight: %1")
00732 .arg(parent->GetNumSetting("GuiHeight")));
00733 VERBOSE(VB_IMPORTANT, QString("m_width: %1").arg(m_width));
00734 VERBOSE(VB_IMPORTANT, QString("m_height: %1").arg(m_height));
00735 VERBOSE(VB_IMPORTANT, "Falling back to 640x480");
00736
00737 m_screenwidth = 640;
00738 m_screenheight = 480;
00739 }
00740
00741 m_wmult = m_screenwidth / (float)m_baseWidth;
00742 m_hmult = m_screenheight / (float)m_baseHeight;
00743
00744 QFont font = QFont("Arial");
00745 if (!font.exactMatch())
00746 font = QFont();
00747 font.setStyleHint(QFont::SansSerif, QFont::PreferAntialias);
00748 font.setPointSize((int)floor(14 * m_hmult));
00749
00750 QApplication::setFont(font);
00751
00752
00753 }
00754
00755
00756 void MythContextPrivate::LoadLogSettings(void)
00757 {
00758 m_logenable = parent->GetNumSetting("LogEnabled", 0);
00759 m_logmaxcount = parent->GetNumSetting("LogMaxCount", 0);
00760 m_logprintlevel = parent->GetNumSetting("LogPrintLevel", LP_ERROR);
00761 }
00762
00768 void MythContextPrivate::LoadDatabaseSettings(void)
00769 {
00770 if (!LoadSettingsFile())
00771 {
00772 VERBOSE(VB_IMPORTANT, "Unable to read configuration file mysql.txt");
00773
00774
00775 m_DBparams.dbHostName = "localhost";
00776 m_DBparams.dbHostPing = true;
00777 m_DBparams.dbPort = 0;
00778 m_DBparams.dbUserName = "mythtv";
00779 m_DBparams.dbPassword = "mythtv";
00780 m_DBparams.dbName = "mythconverg";
00781 m_DBparams.dbType = "QMYSQL3";
00782 m_DBparams.localEnabled = false;
00783 m_DBparams.localHostName = "my-unique-identifier-goes-here";
00784 m_DBparams.wolEnabled = false;
00785 m_DBparams.wolReconnect = 0;
00786 m_DBparams.wolRetry = 5;
00787 m_DBparams.wolCommand = "echo 'WOLsqlServerCommand not set'";
00788 }
00789
00790
00791
00792 FindSettingsProbs();
00793
00794 m_localhostname = m_DBparams.localHostName;
00795 if (m_localhostname.isEmpty() ||
00796 m_localhostname == "my-unique-identifier-goes-here")
00797 {
00798 char localhostname[1024];
00799 if (gethostname(localhostname, 1024))
00800 {
00801 VERBOSE(VB_IMPORTANT,
00802 "MCP: Error, could not determine host name." + ENO);
00803 localhostname[0] = '\0';
00804 }
00805 m_localhostname = localhostname;
00806 VERBOSE(VB_IMPORTANT, "Empty LocalHostName.");
00807 }
00808
00809 VERBOSE(VB_GENERAL, QString("Using localhost value of %1")
00810 .arg(m_localhostname));
00811 }
00812
00816 bool MythContextPrivate::LoadSettingsFile(void)
00817 {
00818 if (!m_settings->LoadSettingsFiles("mysql.txt", m_installprefix))
00819 return false;
00820
00821 m_DBparams.dbHostName = m_settings->GetSetting("DBHostName");
00822 m_DBparams.dbHostPing = m_settings->GetSetting("DBHostPing") != "no";
00823 m_DBparams.dbPort = m_settings->GetNumSetting("DBPort");
00824 m_DBparams.dbUserName = m_settings->GetSetting("DBUserName");
00825 m_DBparams.dbPassword = m_settings->GetSetting("DBPassword");
00826 m_DBparams.dbName = m_settings->GetSetting("DBName");
00827 m_DBparams.dbType = m_settings->GetSetting("DBType");
00828
00829 m_DBparams.localHostName = m_settings->GetSetting("LocalHostName");
00830 m_DBparams.localEnabled = m_DBparams.localHostName.length() > 0;
00831
00832 m_DBparams.wolReconnect
00833 = m_settings->GetNumSetting("WOLsqlReconnectWaitTime");
00834 m_DBparams.wolEnabled = m_DBparams.wolReconnect > 0;
00835
00836 m_DBparams.wolRetry = m_settings->GetNumSetting("WOLsqlConnectRetry");
00837 m_DBparams.wolCommand = m_settings->GetSetting("WOLsqlCommand");
00838
00839 return true;
00840 }
00841
00842 bool MythContextPrivate::WriteSettingsFile(const DatabaseParams ¶ms,
00843 bool overwrite)
00844 {
00845 QString path = MythContext::GetConfDir() + "/mysql.txt";
00846 QFile * f = new QFile(path);
00847
00848 if (!overwrite && f->exists())
00849 {
00850 return false;
00851 }
00852
00853 QString dirpath = MythContext::GetConfDir();
00854 QDir createDir(dirpath);
00855
00856 if (!createDir.exists())
00857 {
00858 if (!createDir.mkdir(dirpath, true))
00859 {
00860 VERBOSE(VB_IMPORTANT, QString("Could not create %1").arg(dirpath));
00861 return false;
00862 }
00863 }
00864
00865 if (!f->open(IO_WriteOnly))
00866 {
00867 VERBOSE(VB_IMPORTANT, QString("Could not open settings file %1 "
00868 "for writing").arg(path));
00869 return false;
00870 }
00871
00872 VERBOSE(VB_IMPORTANT, QString("Writing settings file %1").arg(path));
00873 QTextStream s(f);
00874 s << "DBHostName=" << params.dbHostName << endl;
00875
00876 s << "\n"
00877 << "# By default, Myth tries to ping the DB host to see if it exists.\n"
00878 << "# If your DB host or network doesn't accept pings, set this to no:\n"
00879 << "#\n";
00880
00881 if (params.dbHostPing)
00882 s << "#DBHostPing=no" << endl << endl;
00883 else
00884 s << "DBHostPing=no" << endl << endl;
00885
00886 if (params.dbPort)
00887 s << "DBPort=" << params.dbPort << endl;
00888
00889 s << "DBUserName=" << params.dbUserName << endl
00890 << "DBPassword=" << params.dbPassword << endl
00891 << "DBName=" << params.dbName << endl
00892 << "DBType=" << params.dbType << endl
00893 << endl
00894 << "# Set the following if you want to use something other than this\n"
00895 << "# machine's real hostname for identifying settings in the database.\n"
00896 << "# This is useful if your hostname changes often, as otherwise you\n"
00897 << "# will need to reconfigure mythtv (or futz with the DB) every time.\n"
00898 << "# TWO HOSTS MUST NOT USE THE SAME VALUE\n"
00899 << "#\n";
00900
00901 if (params.localEnabled)
00902 s << "LocalHostName=" << params.localHostName << endl;
00903 else
00904 s << "#LocalHostName=my-unique-identifier-goes-here\n";
00905
00906 s << endl
00907 << "# If you want your frontend to be able to wake your MySQL server\n"
00908 << "# using WakeOnLan, have a look at the following settings:\n"
00909 << "#\n"
00910 << "#\n"
00911 << "# The time the frontend waits (in seconds) between reconnect tries.\n"
00912 << "# This should be the rough time your MySQL server needs for startup\n"
00913 << "#\n";
00914
00915 if (params.wolEnabled)
00916 s << "WOLsqlReconnectWaitTime=" << params.wolReconnect << endl;
00917 else
00918 s << "#WOLsqlReconnectWaitTime=0\n";
00919
00920 s << "#\n"
00921 << "#\n"
00922 << "# This is the number of retries to wake the MySQL server\n"
00923 << "# until the frontend gives up\n"
00924 << "#\n";
00925
00926 if (params.wolEnabled)
00927 s << "WOLsqlConnectRetry=" << params.wolRetry << endl;
00928 else
00929 s << "#WOLsqlConnectRetry=5\n";
00930
00931 s << "#\n"
00932 << "#\n"
00933 << "# This is the command executed to wake your MySQL server.\n"
00934 << "#\n";
00935
00936 if (params.wolEnabled)
00937 s << "WOLsqlCommand=" << params.wolCommand << endl;
00938 else
00939 s << "#WOLsqlCommand=echo 'WOLsqlServerCommand not set'\n";
00940
00941 f->close();
00942 return true;
00943 }
00944
00945 bool MythContextPrivate::FindSettingsProbs(void)
00946 {
00947 bool problems = false;
00948
00949 if (m_DBparams.dbHostName.isEmpty())
00950 {
00951 problems = true;
00952 VERBOSE(VB_IMPORTANT, "DBHostName is not set in mysql.txt");
00953 VERBOSE(VB_IMPORTANT, "Assuming localhost");
00954 m_DBparams.dbHostName = "localhost";
00955 }
00956 if (m_DBparams.dbUserName.isEmpty())
00957 {
00958 problems = true;
00959 VERBOSE(VB_IMPORTANT, "DBUserName is not set in mysql.txt");
00960 }
00961 if (m_DBparams.dbPassword.isEmpty())
00962 {
00963 problems = true;
00964 VERBOSE(VB_IMPORTANT, "DBPassword is not set in mysql.txt");
00965 }
00966 if (m_DBparams.dbName.isEmpty())
00967 {
00968 problems = true;
00969 VERBOSE(VB_IMPORTANT, "DBName is not set in mysql.txt");
00970 }
00971 return problems;
00972 }
00973
00974 QString MythContextPrivate::getResponse(const QString &query,
00975 const QString &def)
00976 {
00977 cout << query;
00978
00979 if (def != "")
00980 cout << " [" << def << "] ";
00981 else
00982 cout << " ";
00983
00984 if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
00985 {
00986 cout << endl << "[console is not interactive, using default '"
00987 << def << "']" << endl;
00988 return def;
00989 }
00990
00991 char response[80];
00992 cin.clear();
00993 cin.getline(response, 80);
00994 if (!cin.good())
00995 {
00996 cout << endl;
00997 VERBOSE(VB_IMPORTANT, "Read from stdin failed");
00998 return NULL;
00999 }
01000
01001 QString qresponse = response;
01002
01003 if (qresponse == "")
01004 qresponse = def;
01005
01006 return qresponse;
01007 }
01008
01009 int MythContextPrivate::intResponse(const QString &query, int def)
01010 {
01011 QString str_resp = getResponse(query, QString("%1").arg(def));
01012 if (!str_resp)
01013 return false;
01014 bool ok;
01015 int resp = str_resp.toInt(&ok);
01016 return (ok ? resp : def);
01017 }
01018
01019 bool MythContextPrivate::PromptForDatabaseParams(const QString &error)
01020 {
01021 bool accepted = false;
01022 if (m_gui)
01023 {
01024 TempMainWindow();
01025
01026
01027 if (error.length())
01028 MythPopupBox::showOkPopup(mainWindow, "DB connect failure", error);
01029
01030
01031 DatabaseSettings settings(m_DBhostCp);
01032 accepted = (settings.exec() == QDialog::Accepted);
01033 if (!accepted)
01034 VERBOSE(VB_IMPORTANT, "User cancelled database configuration");
01035
01036 EndTempWindow();
01037 }
01038 else
01039 {
01040 DatabaseParams params = parent->GetDatabaseParams();
01041 QString response;
01042
01043
01044 cout << endl << error << endl << endl;
01045 response = getResponse("Would you like to configure the database "
01046 "connection now?",
01047 "yes");
01048 if (!response || response.left(1).lower() != "y")
01049 return false;
01050
01051 params.dbHostName = getResponse("Database host name:",
01052 params.dbHostName);
01053 response = getResponse("Should I test connectivity to this host "
01054 "using the ping command?", "yes");
01055 params.dbHostPing = (!response || response.left(1).lower() != "y");
01056
01057 params.dbPort = intResponse("Database non-default port:",
01058 params.dbPort);
01059 params.dbName = getResponse("Database name:",
01060 params.dbName);
01061 params.dbUserName = getResponse("Database user name:",
01062 params.dbUserName);
01063 params.dbPassword = getResponse("Database password:",
01064 params.dbPassword);
01065
01066 params.localHostName = getResponse("Unique identifier for this machine "
01067 "(if empty, the local host name "
01068 "will be used):",
01069 params.localHostName);
01070 params.localEnabled = !params.localHostName.isEmpty();
01071
01072 response = getResponse("Would you like to use Wake-On-LAN to retry "
01073 "database connections?",
01074 (params.wolEnabled ? "yes" : "no"));
01075 if (response)
01076 params.wolEnabled = (response.left(1).lower() == "y");
01077
01078 if (params.wolEnabled)
01079 {
01080 params.wolReconnect = intResponse("Seconds to wait for "
01081 "reconnection:",
01082 params.wolReconnect);
01083 params.wolRetry = intResponse("Number of times to retry:",
01084 params.wolRetry);
01085 params.wolCommand = getResponse("Command to use to wake server:",
01086 params.wolCommand);
01087 }
01088
01089 accepted = parent->SaveDatabaseParams(params);
01090 }
01091 return accepted;
01092 }
01093
01097 QString MythContextPrivate::TestDBconnection(void)
01098 {
01099 bool doPing = m_DBparams.dbHostPing;
01100 QString err;
01101 QString host = m_DBparams.dbHostName;
01102 int port = m_DBparams.dbPort;
01103
01104
01105
01106
01107
01108
01109 if (host == "localhost" || host == "127.0.0.1" || host == m_localhostname)
01110 doPing = false;
01111
01112
01113 if (doPing && m_DBparams.wolEnabled)
01114 for (int attempt = 0; attempt < m_DBparams.wolRetry; ++attempt)
01115 {
01116 int wakeupTime = m_DBparams.wolReconnect;
01117
01118 if (ping(host, wakeupTime))
01119 {
01120 doPing = false;
01121 break;
01122 }
01123
01124 VERBOSE(VB_GENERAL, QString("Trying to wake up host %1, attempt %2")
01125 .arg(host).arg(attempt));
01126 system(m_DBparams.wolCommand);
01127
01128 VERBOSE(VB_GENERAL,
01129 QString("Waiting for %1 seconds").arg(wakeupTime));
01130 sleep(m_DBparams.wolReconnect);
01131 }
01132
01133 if (doPing)
01134 {
01135 VERBOSE(VB_GENERAL,
01136 QString("Testing network connectivity to %1").arg(host));
01137 }
01138
01139 if (doPing && !ping(host, 3))
01140 {
01141
01142 m_DBhostCp = m_DBparams.dbHostName;
01143
01144
01145 m_DBparams.dbHostName = "";
01146
01147 err = QObject::tr("Cannot find (ping) database host %1 on the network");
01148 return err.arg(host);
01149 }
01150
01151
01152
01153
01154 if (port && !telnet(host, port))
01155 {
01156 err = QObject::tr("Cannot connect to port %1 on database host %2");
01157 return err.arg(port).arg(host);
01158 }
01159
01160
01161
01162
01163 if (!MSqlQuery::testDBConnection())
01164 return QObject::tr(QString("Cannot login to database?"));
01165
01166
01167 return QString::null;
01168 }
01169
01181 void MythContextPrivate::ResetDatabase(void)
01182 {
01183 m_dbmanager.CloseDatabases();
01184 parent->ClearSettingsCache();
01185 }
01186
01187
01188 bool MythContextPrivate::InitUPnP(void)
01189 {
01190 if (m_UPnP)
01191 return true;
01192
01193 VERBOSE(VB_UPNP, "Setting UPnP client for backend autodiscovery...");
01194
01195 if (!m_XML)
01196 m_XML = new XmlConfiguration("");
01197
01198 m_UPnP = new UPnp();
01199 m_UPnP->SetConfiguration(m_XML);
01200
01201 int port=6549;
01202 m_HTTP = new HttpServer(port);
01203
01204 if (!m_HTTP->ok())
01205 {
01206 VERBOSE(VB_IMPORTANT, "MCP::InitUPnP() - HttpServer Create Error");
01207 DeleteUPnP();
01208 return false;
01209 }
01210
01211 if (!m_UPnP->Initialize(port, m_HTTP))
01212 {
01213 VERBOSE(VB_IMPORTANT, "MCP::InitUPnP() - UPnp::Initialize() Error");
01214 DeleteUPnP();
01215 return false;
01216 }
01217
01218
01219 UPnpDevice &device = UPnp::g_UPnpDeviceDesc.m_rootDevice;
01220 device.m_sDeviceType = "urn:schemas-upnp-org:device:MythContextClient:1";
01221
01222 m_UPnP->Start();
01223
01224 return true;
01225 }
01226
01227 void MythContextPrivate::DeleteUPnP(void)
01228 {
01229 if (m_UPnP && !m_HTTP)
01230 return;
01231
01232 if (m_UPnP)
01233 {
01234
01235 VERBOSE(VB_GENERAL, "Deleting UPnP client...");
01236
01237 delete m_UPnP;
01238 m_UPnP = NULL;
01239 m_XML = NULL;
01240 }
01241
01242 if (m_HTTP)
01243 {
01244 delete m_HTTP;
01245 m_HTTP = NULL;
01246 }
01247 }
01248
01252 int MythContextPrivate::ChooseBackend(const QString &error)
01253 {
01254 if (!InitUPnP())
01255 return -1;
01256
01257 TempMainWindow();
01258
01259
01260 if (error.length())
01261 MythPopupBox::showOkPopup(mainWindow, "DB connect failure", error);
01262
01263 VERBOSE(VB_GENERAL, "Putting up the UPnP backend chooser");
01264
01265 BackendSelect *BEsel = new BackendSelect(mainWindow, &m_DBparams);
01266 switch (BEsel->exec())
01267 {
01268 case kDialogCodeRejected:
01269 VERBOSE(VB_IMPORTANT, "User canceled database configuration");
01270 return 0;
01271
01272 case kDialogCodeButton0:
01273 VERBOSE(VB_IMPORTANT, "User requested Manual Config");
01274 return -1;
01275 }
01276
01277
01278
01279 QStringList buttons;
01280 QString message;
01281
01282 buttons += QObject::tr("Save database details");
01283 buttons += QObject::tr("Save backend details");
01284 buttons += QObject::tr("Don't Save");
01285
01286 message = QObject::tr("Save that backend or database as the default?");
01287
01288 DialogCode selected = MythPopupBox::ShowButtonPopup(
01289 mainWindow, "Save default", message, buttons, kDialogCodeButton2);
01290 switch (selected)
01291 {
01292 case kDialogCodeButton0:
01293 WriteSettingsFile(m_DBparams, true);
01294
01295 m_XML->SetValue(kDefaultUSN, "");
01296 m_XML->Save();
01297 break;
01298 case kDialogCodeButton1:
01299 if (BEsel->m_PIN.length())
01300 m_XML->SetValue(kDefaultPIN, BEsel->m_PIN);
01301 m_XML->SetValue(kDefaultUSN, BEsel->m_USN);
01302 m_XML->Save();
01303 break;
01304 }
01305
01306 delete BEsel;
01307 EndTempWindow();
01308
01309 return 1;
01310 }
01311
01320 void MythContextPrivate::StoreConnectionInfo(void)
01321 {
01322 if (!m_XML)
01323 return;
01324
01325 m_XML->SetValue(kDefaultBE + "DBHostName", m_DBparams.dbHostName);
01326 m_XML->SetValue(kDefaultBE + "DBUserName", m_DBparams.dbUserName);
01327 m_XML->SetValue(kDefaultBE + "DBPassword", m_DBparams.dbPassword);
01328 m_XML->SetValue(kDefaultBE + "DBName", m_DBparams.dbName);
01329 m_XML->SetValue(kDefaultBE + "DBPort", m_DBparams.dbPort);
01330 m_XML->Save();
01331 }
01332
01339 int MythContextPrivate::UPnPautoconf(const int milliSeconds)
01340 {
01341 if (!InitUPnP())
01342 return 0;
01343
01344 SSDPCacheEntries *backends = NULL;
01345 int count;
01346 QString LOC = "UPnPautoconf() - ";
01347 QTime timer;
01348
01349 m_UPnP->PerformSearch(gBackendURI);
01350 for (timer.start(); timer.elapsed() < milliSeconds; usleep(25000))
01351 {
01352 backends = m_UPnP->g_SSDPCache.Find(gBackendURI);
01353 if (backends)
01354 {
01355 backends->AddRef();
01356 break;
01357 }
01358 putchar('.');
01359 }
01360 putchar('\n');
01361
01362 if (!backends)
01363 {
01364 VERBOSE(VB_GENERAL, LOC + "No UPnP backends found");
01365 return 0;
01366 }
01367
01368
01369
01370
01371
01372
01373 count = backends->Count();
01374 switch (count)
01375 {
01376 case 0:
01377 VERBOSE(VB_IMPORTANT,
01378 LOC + "No UPnP backends found, but SSDP::Find() not NULL!");
01379 break;
01380 case 1:
01381 VERBOSE(VB_GENERAL, LOC + "Found one UPnP backend");
01382 break;
01383 default:
01384 VERBOSE(VB_GENERAL,
01385 (LOC + "More than one UPnP backend found (%1)").arg(count));
01386 }
01387
01388 if (count != 1)
01389 {
01390 backends->Release();
01391 return count;
01392 }
01393
01394
01395
01396 backends->Lock();
01397 DeviceLocation *BE = backends->GetEntryMap()->begin().data();
01398 backends->Unlock();
01399 backends->Release();
01400
01401
01402
01403 if (UPnPconnect(BE, QString::null))
01404 return 1;
01405
01406 return -1;
01407 }
01408
01414 bool MythContextPrivate::DefaultUPnP(QString &error)
01415 {
01416 XmlConfiguration *XML = new XmlConfiguration("config.xml");
01417 QString loc = "MCP::DefaultUPnP() - ";
01418 QString localHostName = XML->GetValue(kDefaultBE + "LocalHostName", "");
01419 QString PIN = XML->GetValue(kDefaultPIN, "");
01420 QString USN = XML->GetValue(kDefaultUSN, "");
01421
01422 delete XML;
01423
01424 if (USN.isEmpty())
01425 {
01426 VERBOSE(VB_UPNP, loc + "No default UPnP backend");
01427 return false;
01428 }
01429
01430 VERBOSE(VB_UPNP, loc + "config.xml has default " +
01431 QString("PIN '%1' and host USN: %2")
01432 .arg(PIN).arg(USN));
01433
01434 if (!InitUPnP())
01435 {
01436 error = "UPnP is broken?";
01437 return false;
01438 }
01439
01440 m_UPnP->PerformSearch(gBackendURI);
01441 DeviceLocation *pDevLoc = m_UPnP->g_SSDPCache.Find(gBackendURI, USN);
01442 if (!pDevLoc)
01443 {
01444 error = "Cannot find default UPnP backend";
01445 return false;
01446
01447 }
01448
01449 if (UPnPconnect(pDevLoc, PIN))
01450 {
01451 if (localHostName.length())
01452 {
01453 m_DBparams.localHostName = localHostName;
01454 m_DBparams.localEnabled = true;
01455 }
01456
01457 return true;
01458 }
01459
01460 error = "Cannot connect to default backend via UPnP. Wrong saved PIN?";
01461 return false;
01462 }
01463
01467 bool MythContextPrivate::UPnPconnect(const DeviceLocation *backend,
01468 const QString &PIN)
01469 {
01470 QString error;
01471 QString LOC = "UPnPconnect() - ";
01472 QString URL = backend->m_sLocation;
01473 MythXMLClient XML(URL);
01474
01475 VERBOSE(VB_UPNP, LOC + QString("Trying host at %1").arg(URL));
01476 switch (XML.GetConnectionInfo(PIN, &m_DBparams, error))
01477 {
01478 case UPnPResult_Success:
01479 break;
01480
01481 case UPnPResult_ActionNotAuthorized:
01482
01483
01484
01485 VERBOSE(VB_UPNP, LOC + error + ". Wrong PIN?");
01486 return false;
01487
01488 default:
01489 VERBOSE(VB_UPNP, LOC + error);
01490 return false;
01491 }
01492
01493 QString DBhost = m_DBparams.dbHostName;
01494 VERBOSE(VB_UPNP, LOC + QString("Got database hostname: %1").arg(DBhost));
01495
01496 return true;
01497 }
01498
01499
01500 MythContext::MythContext(const QString &binversion)
01501 : QObject(), d(NULL), app_binary_version(binversion)
01502 {
01503 #ifdef USING_MINGW
01504 static bool WSAStarted = false;
01505 if (!WSAStarted) {
01506 WSADATA wsadata;
01507 int res = WSAStartup(MAKEWORD(2, 0), &wsadata);
01508 VERBOSE(VB_SOCKET, QString("WSAStartup returned %1").arg(res));
01509 }
01510 #endif
01511
01512 qInitNetworkProtocols();
01513
01514 d = new MythContextPrivate(this);
01515 }
01516
01517 bool MythContext::Init(const bool gui, UPnp *UPnPclient,
01518 const bool promptForBackend,
01519 const bool disableAutoDiscovery)
01520 {
01521 if (app_binary_version != MYTH_BINARY_VERSION)
01522 {
01523 QString warning =
01524 QString("This app was compiled against libmyth version: %1"
01525 "\n\t\t\tbut the library is version: %2"
01526 "\n\t\t\tYou probably want to recompile everything, and do a"
01527 "\n\t\t\t'make distclean' first.")
01528 .arg(app_binary_version).arg(MYTH_BINARY_VERSION);
01529
01530 if (gui)
01531 {
01532 d->TempMainWindow(false);
01533 MythPopupBox::showOkPopup(d->mainWindow,
01534 "Library version error", warning);
01535 }
01536 VERBOSE(VB_IMPORTANT, warning);
01537
01538 return false;
01539 }
01540
01541 #ifdef _WIN32
01542
01543
01544 char *home = getenv("HOME");
01545 if (!home)
01546 {
01547 home = getenv("LOCALAPPDATA");
01548 if (!home)
01549 home = getenv("APPDATA");
01550 if (!home)
01551 home = ".";
01552
01553 _putenv(QString("HOME=%1").arg(home));
01554 }
01555 #endif
01556
01557 if (QDir::homeDirPath() == "/" && ! getenv("MYTHCONFDIR"))
01558 {
01559 QString warning = "Cannot locate your home directory."
01560 " Please set the environment variable HOME";
01561 if (gui)
01562 {
01563 d->TempMainWindow(false);
01564 MythPopupBox::showOkPopup(d->mainWindow, "HOME error", warning);
01565 }
01566 VERBOSE(VB_IMPORTANT, warning + " or MYTHCONFDIR");
01567
01568 return false;
01569 }
01570
01571 if (!d->Init(gui, UPnPclient, promptForBackend, disableAutoDiscovery))
01572 return false;
01573
01574 ActivateSettingsCache(true);
01575
01576 return true;
01577 }
01578
01579 MythContext::~MythContext()
01580 {
01581 if (d)
01582 delete d;
01583 }
01584
01585 bool MythContext::ConnectToMasterServer(bool blockingClient)
01586 {
01587 if (gContext->IsMasterBackend())
01588 {
01589
01590
01591 VERBOSE(VB_IMPORTANT, "ERROR: Master backend tried to connect back "
01592 "to itself!");
01593 return false;
01594 }
01595
01596 QString server = gContext->GetSetting("MasterServerIP", "localhost");
01597 int port = gContext->GetNumSetting("MasterServerPort", 6543);
01598
01599 if (!d->eventSock)
01600 d->eventSock = new MythSocket();
01601
01602 if (!d->serverSock)
01603 d->serverSock = ConnectServer(d->eventSock, server,
01604 port, blockingClient);
01605
01606 if (d->serverSock)
01607 d->eventSock->setCallbacks(this);
01608
01609 return (bool) (d->serverSock);
01610 }
01611
01612 MythSocket *MythContext::ConnectServer(MythSocket *eventSock,
01613 const QString &hostname,
01614 int port,
01615 bool blockingClient)
01616 {
01617 MythSocket *serverSock = NULL;
01618 int cnt = 1;
01619
01620 int sleepTime = GetNumSetting("WOLbackendReconnectWaitTime", 0);
01621 int maxConnTry = GetNumSetting("WOLbackendConnectRetry", 1);
01622
01623 do
01624 {
01625 VERBOSE(VB_GENERAL, QString("Connecting to backend server: "
01626 "%1:%2 (try %3 of %4)")
01627 .arg(hostname).arg(port).arg(cnt)
01628 .arg(maxConnTry));
01629
01630 serverSock = new MythSocket();
01631
01632 if (!serverSock->connect(hostname, port))
01633 {
01634 serverSock->DownRef();
01635 serverSock = NULL;
01636
01637 if (d->attemptingToConnect)
01638 break;
01639 d->attemptingToConnect = true;
01640
01641
01642 if (sleepTime <= 0)
01643 {
01644 VERBOSE(
01645 VB_IMPORTANT, "Connection timed out. \n\t\t\t"
01646 "You probably should modify the Master Server \n\t\t\t"
01647 "settings in the setup program and set the \n\t\t\t"
01648 "proper IP address.");
01649 if (d->m_gui && d->m_height && d->m_width)
01650 {
01651 bool manageLock = false;
01652 if (!blockingClient && d->serverSockLock.locked())
01653 {
01654 manageLock = true;
01655 d->serverSockLock.unlock();
01656 }
01657 MythPopupBox::showOkPopup(d->mainWindow,
01658 "connection failure",
01659 tr("Could not connect to the "
01660 "master backend server -- is "
01661 "it running? Is the IP "
01662 "address set for it in the "
01663 "setup program correct?"));
01664 if (manageLock)
01665 d->serverSockLock.lock();
01666 }
01667
01668 d->attemptingToConnect = false;
01669 return false;
01670 }
01671 else
01672 {
01673 VERBOSE(VB_GENERAL, "Trying to wake up the MasterBackend "
01674 "now.");
01675 QString wol_cmd = GetSetting("WOLbackendCommand",
01676 "echo \'would run the "
01677 "WakeServerCommand now, if "
01678 "set!\'");
01679 myth_system(wol_cmd);
01680
01681 VERBOSE(VB_GENERAL, QString("Waiting for %1 seconds until I "
01682 "try to reconnect again.")
01683 .arg(sleepTime));
01684 sleep(sleepTime);
01685 ++cnt;
01686 }
01687 d->attemptingToConnect = false;
01688 }
01689 else
01690 break;
01691 }
01692 while (cnt <= maxConnTry);
01693
01694 #ifndef IGNORE_PROTO_VER_MISMATCH
01695 if (serverSock && !CheckProtoVersion(serverSock))
01696 {
01697 serverSock->DownRef();
01698 serverSock = NULL;
01699 return false;
01700 }
01701 #endif
01702
01703 if (serverSock)
01704 {
01705
01706 QString str = QString("ANN %1 %2 %3")
01707 .arg(blockingClient ? "Playback" : "Monitor")
01708 .arg(d->m_localhostname).arg(false);
01709 QStringList strlist = str;
01710 serverSock->writeStringList(strlist);
01711 serverSock->readStringList(strlist, true);
01712
01713 if (eventSock && eventSock->state() == MythSocket::Idle)
01714 {
01715
01716
01717 eventSock->connect(hostname, port);
01718
01719 eventSock->Lock();
01720
01721 QString str = QString("ANN Monitor %1 %2")
01722 .arg(d->m_localhostname).arg(true);
01723 QStringList strlist = str;
01724 eventSock->writeStringList(strlist);
01725 eventSock->readStringList(strlist);
01726
01727 eventSock->Unlock();
01728 }
01729 }
01730 return serverSock;
01731 }
01732
01733 bool MythContext::IsConnectedToMaster(void)
01734 {
01735 return d->serverSock;
01736 }
01737
01738 void MythContext::BlockShutdown(void)
01739 {
01740 QStringList strlist;
01741
01742 if (d->serverSock == NULL)
01743 return;
01744
01745 strlist << "BLOCK_SHUTDOWN";
01746 d->serverSock->writeStringList(strlist);
01747 d->serverSock->readStringList(strlist);
01748
01749 if (d->eventSock == NULL || d->eventSock->state() != MythSocket::Connected)
01750 return;
01751
01752 strlist.clear();
01753 strlist << "BLOCK_SHUTDOWN";
01754
01755 d->eventSock->Lock();
01756
01757 d->eventSock->writeStringList(strlist);
01758 d->eventSock->readStringList(strlist);
01759
01760 d->eventSock->Unlock();
01761 }
01762
01763 void MythContext::AllowShutdown(void)
01764 {
01765 QStringList strlist;
01766
01767 if (d->serverSock == NULL)
01768 return;
01769
01770 strlist << "ALLOW_SHUTDOWN";
01771 d->serverSock->writeStringList(strlist);
01772 d->serverSock->readStringList(strlist);
01773
01774 if (d->eventSock == NULL || d->eventSock->state() != MythSocket::Connected)
01775 return;
01776
01777 strlist.clear();
01778 strlist << "ALLOW_SHUTDOWN";
01779
01780 d->eventSock->Lock();
01781
01782 d->eventSock->writeStringList(strlist);
01783 d->eventSock->readStringList(strlist);
01784
01785 d->eventSock->Unlock();
01786 }
01787
01788 void MythContext::SetBackend(bool backend)
01789 {
01790 d->m_backend = backend;
01791 }
01792
01793 bool MythContext::IsBackend(void)
01794 {
01795 return d->m_backend;
01796 }
01797
01798 bool MythContext::IsMasterHost(void)
01799 {
01800 QString myip = gContext->GetSetting("BackendServerIP");
01801 QString masterip = gContext->GetSetting("MasterServerIP");
01802
01803 return (masterip == myip);
01804 }
01805
01806 bool MythContext::IsMasterBackend(void)
01807 {
01808 return (IsBackend() && IsMasterHost());
01809 }
01810
01811 bool MythContext::BackendIsRunning(void)
01812 {
01813 #if defined(CONFIG_DARWIN) || (__FreeBSD__) || defined(__OpenBSD__)
01814 char *command = "ps -ax | grep -i mythbackend | grep -v grep > /dev/null";
01815 #else
01816 char *command = "ps -ae | grep mythbackend > /dev/null";
01817 #endif
01818 bool res = myth_system(command,
01819 MYTH_SYSTEM_DONT_BLOCK_LIRC |
01820 MYTH_SYSTEM_DONT_BLOCK_JOYSTICK_MENU);
01821 return !res;
01822 }
01823
01824 bool MythContext::IsFrontendOnly(void)
01825 {
01826
01827 bool backendOnLocalhost = false;
01828
01829 QStringList strlist = "QUERY_IS_ACTIVE_BACKEND";
01830 strlist << GetHostName();
01831
01832 SendReceiveStringList(strlist);
01833
01834 if (QString(strlist[0]) == "FALSE")
01835 backendOnLocalhost = false;
01836 else
01837 backendOnLocalhost = true;
01838
01839 return !backendOnLocalhost;
01840 }
01841
01842 QString MythContext::GetMasterHostPrefix(void)
01843 {
01844 QString ret = "";
01845
01846 if (!d->serverSock)
01847 {
01848 d->serverSockLock.lock();
01849 ConnectToMasterServer();
01850 d->serverSockLock.unlock();
01851 }
01852
01853 if (d->serverSock)
01854 ret = QString("myth://%1:%2/")
01855 .arg(d->serverSock->peerAddress().toString())
01856 .arg(d->serverSock->peerPort());
01857 return ret;
01858 }
01859
01860 void MythContext::ClearSettingsCache(QString myKey, QString newVal)
01861 {
01862 if (!d)
01863 return;
01864
01865 d->settingsCacheLock.lock();
01866 if (myKey != "" && d->settingsCache.contains(myKey))
01867 {
01868 VERBOSE(VB_DATABASE, QString("Clearing Settings Cache for '%1'.")
01869 .arg(myKey));
01870 d->settingsCache.remove(myKey);
01871 d->settingsCache[myKey] = newVal;
01872 }
01873 else
01874 {
01875 VERBOSE(VB_DATABASE, "Clearing Settings Cache.");
01876 d->settingsCache.clear();
01877 }
01878 d->settingsCacheLock.unlock();
01879 }
01880
01881 void MythContext::ActivateSettingsCache(bool activate)
01882 {
01883 if (!d)
01884 return;
01885
01886 if (activate)
01887 VERBOSE(VB_DATABASE, "Enabling Settings Cache.");
01888 else
01889 VERBOSE(VB_DATABASE, "Disabling Settings Cache.");
01890
01891 d->useSettingsCache = activate;
01892 ClearSettingsCache();
01893 }
01894
01895 QString MythContext::GetHostName(void)
01896 {
01897
01898
01899 d->m_hostnamelock.lock();
01900 QString tmp = QDeepCopy<QString>(d->m_localhostname);
01901 d->m_hostnamelock.unlock();
01902 return tmp;
01903 }
01904
01905 QString MythContext::GetFilePrefix(void)
01906 {
01907 return GetSetting("RecordFilePrefix");
01908 }
01909
01910 QString MythContext::GetInstallPrefix(void)
01911 {
01912 return d->m_installprefix;
01913 }
01914
01915 QString MythContext::GetConfDir(void)
01916 {
01917 char *tmp_confdir = getenv("MYTHCONFDIR");
01918 QString dir;
01919 if (tmp_confdir)
01920 {
01921 dir = QString(tmp_confdir);
01922
01923 dir.replace("$HOME", QDir::homeDirPath());
01924 }
01925 else
01926 dir = QDir::homeDirPath() + "/.mythtv";
01927
01928
01929 return dir;
01930 }
01931
01932 QString MythContext::GetShareDir(void)
01933 {
01934 return d->m_installprefix + "/share/mythtv/";
01935 }
01936
01937 QString MythContext::GetLibraryDir(void)
01938 {
01939 return d->m_installprefix + "/" + d->m_libname + "/mythtv/";
01940 }
01941
01942 QString MythContext::GetThemesParentDir(void)
01943 {
01944 return GetShareDir() + "themes/";
01945 }
01946
01947 QString MythContext::GetPluginsDir(void)
01948 {
01949 return GetLibraryDir() + "plugins/";
01950 }
01951
01952 QString MythContext::GetPluginsNameFilter(void)
01953 {
01954 return kPluginLibPrefix + "*" + kPluginLibSuffix;
01955 }
01956
01957 QString MythContext::FindPlugin(const QString &plugname)
01958 {
01959 return GetPluginsDir() + kPluginLibPrefix + plugname + kPluginLibSuffix;
01960 }
01961
01962 QString MythContext::GetTranslationsDir(void)
01963 {
01964 return GetShareDir() + "i18n/";
01965 }
01966
01967 QString MythContext::GetTranslationsNameFilter(void)
01968 {
01969 return "mythfrontend_*.qm";
01970 }
01971
01972 QString MythContext::FindTranslation(const QString &translation)
01973 {
01974 return GetTranslationsDir()
01975 + "mythfrontend_" + translation.lower() + ".qm";
01976 }
01977
01978 QString MythContext::GetFontsDir(void)
01979 {
01980 return GetShareDir();
01981 }
01982
01983 QString MythContext::GetFontsNameFilter(void)
01984 {
01985 return "*ttf";
01986 }
01987
01988 QString MythContext::FindFont(const QString &fontname)
01989 {
01990 return GetFontsDir() + fontname + ".ttf";
01991 }
01992
01993 QString MythContext::GetFiltersDir(void)
01994 {
01995 return GetLibraryDir() + "filters/";
01996 }
01997
01998
01999 void MythContext::LoadQtConfig(void)
02000 {
02001 d->language = "";
02002 d->themecachedir = "";
02003
02004 DisplayRes *dispRes = DisplayRes::GetDisplayRes();
02005 if (dispRes && GetNumSetting("UseVideoModes", 0))
02006 {
02007 d->display_res = dispRes;
02008
02009 d->display_res->Initialize();
02010
02011
02012 d->display_res->SwitchToGUI();
02013 }
02014
02015
02016 d->GetScreenBounds();
02017
02018
02019 if (d->m_qtThemeSettings)
02020 delete d->m_qtThemeSettings;
02021
02022 d->m_qtThemeSettings = new Settings;
02023
02024 QString style = GetSetting("Style", "");
02025 if (style != "")
02026 qApp->setStyle(style);
02027
02028 QString themename = GetSetting("Theme");
02029 QString themedir = FindThemeDir(themename);
02030
02031 ThemeInfo *themeinfo = new ThemeInfo(themedir);
02032
02033 if (themeinfo && themeinfo->IsWide())
02034 {
02035 VERBOSE(VB_IMPORTANT,
02036 QString("Switching to wide mode (%1)").arg(themename));
02037 d->SetWideMode();
02038 }
02039 else
02040 {
02041 VERBOSE(VB_IMPORTANT,
02042 QString("Switching to square mode (%1)").arg(themename));
02043 d->SetSquareMode();
02044 }
02045
02046 if (themeinfo)
02047 delete themeinfo;
02048
02049
02050 d->StoreGUIsettings();
02051
02052 d->m_themepathname = themedir + "/";
02053
02054 themedir += "/qtlook.txt";
02055 d->m_qtThemeSettings->ReadSettings(themedir);
02056 d->m_themeloaded = false;
02057
02058 if (d->m_backgroundimage)
02059 delete d->m_backgroundimage;
02060 d->m_backgroundimage = NULL;
02061
02062 themename = GetSetting("MenuTheme");
02063 d->m_menuthemepathname = FindMenuThemeDir(themename) +"/";
02064
02065 d->bigfontsize = GetNumSetting("QtFontBig", 25);
02066 d->mediumfontsize = GetNumSetting("QtFontMedium", 16);
02067 d->smallfontsize = GetNumSetting("QtFontSmall", 12);
02068 }
02069
02070 void MythContext::RefreshBackendConfig(void)
02071 {
02072 d->LoadLogSettings();
02073 }
02074
02075 void MythContext::UpdateImageCache(void)
02076 {
02077 d->imageCache.clear();
02078
02079 ClearOldImageCache();
02080 CacheThemeImages();
02081 }
02082
02083 void MythContext::ClearOldImageCache(void)
02084 {
02085 QString cachedirname = MythContext::GetConfDir() + "/themecache/";
02086
02087 d->themecachedir = cachedirname + GetSetting("Theme") + "." +
02088 QString::number(d->m_screenwidth) + "." +
02089 QString::number(d->m_screenheight);
02090
02091 QDir dir(cachedirname);
02092
02093 if (!dir.exists())
02094 dir.mkdir(cachedirname);
02095
02096 QString themecachedir = d->themecachedir;
02097
02098 d->themecachedir += "/";
02099
02100 dir.setPath(themecachedir);
02101 if (!dir.exists())
02102 dir.mkdir(themecachedir);
02103
02104 dir.setPath(cachedirname);
02105
02106 const QFileInfoList *list = dir.entryInfoList();
02107 if (!list)
02108 return;
02109
02110 QFileInfoListIterator it(*list);
02111 QFileInfo *fi;
02112 QMap<QDateTime, QString> dirtimes;
02113
02114 while ((fi = it.current()) != 0)
02115 {
02116 ++it;
02117 if (fi->fileName() == "." || fi->fileName() == "..")
02118 continue;
02119 if (fi->isDir() && !fi->isSymLink())
02120 {
02121 if (fi->absFilePath() == themecachedir)
02122 continue;
02123 dirtimes[fi->lastModified()] = fi->absFilePath();
02124 }
02125 }
02126
02127 const size_t max_cached = GetNumSetting("ThemeCacheSize", 1);
02128 while (dirtimes.size() >= max_cached)
02129 {
02130 VERBOSE(VB_FILE, QString("Removing cache dir: %1")
02131 .arg(dirtimes.begin().data()));
02132 RemoveCacheDir(dirtimes.begin().data());
02133 dirtimes.erase(dirtimes.begin());
02134 }
02135
02136 QMap<QDateTime, QString>::const_iterator dit = dirtimes.begin();
02137 for (; dit != dirtimes.end(); ++dit)
02138 {
02139 VERBOSE(VB_FILE, QString("Keeping cache dir: %1")
02140 .arg((*dit).data()));
02141 }
02142 }
02143
02144 void MythContext::RemoveCacheDir(const QString &dirname)
02145 {
02146 QString cachedirname = MythContext::GetConfDir() + "/themecache/";
02147
02148 if (!dirname.startsWith(cachedirname))
02149 return;
02150
02151 VERBOSE(VB_IMPORTANT,
02152 QString("Removing stale cache dir: %1").arg(dirname));
02153
02154 QDir dir(dirname);
02155
02156 if (!dir.exists())
02157 return;
02158
02159 const QFileInfoList *list = dir.entryInfoList();
02160 if (!list)
02161 return;
02162
02163 QFileInfoListIterator it(*list);
02164 QFileInfo *fi;
02165
02166 while ((fi = it.current()) != 0)
02167 {
02168 ++it;
02169 if (fi->fileName() == "." || fi->fileName() == "..")
02170 continue;
02171 if (fi->isFile() && !fi->isSymLink())
02172 {
02173 QFile file(fi->absFilePath());
02174 file.remove();
02175 }
02176 else if (fi->isDir() && !fi->isSymLink())
02177 {
02178 RemoveCacheDir(fi->absFilePath());
02179 }
02180 }
02181
02182 dir.rmdir(dirname);
02183 }
02184
02185 void MythContext::CacheThemeImages(void)
02186 {
02187 if (d->m_screenwidth == d->m_baseWidth &&
02188 d->m_screenheight == d->m_baseHeight)
02189 return;
02190
02191 CacheThemeImagesDirectory(d->m_themepathname);
02192 if (d->IsWideMode())
02193 CacheThemeImagesDirectory(GetThemesParentDir() + "default-wide/");
02194 CacheThemeImagesDirectory(GetThemesParentDir() + "default/");
02195 }
02196
02197 void MythContext::CacheThemeImagesDirectory(const QString &dirname,
02198 const QString &subdirname)
02199 {
02200 QDir dir(dirname);
02201
02202 if (!dir.exists())
02203 return;
02204
02205 const QFileInfoList *list = dir.entryInfoList();
02206 if (!list)
02207 return;
02208
02209 QFileInfoListIterator it(*list);
02210 QFileInfo *fi;
02211
02212 MythProgressDialog *caching = NULL;
02213 if (subdirname.length() == 0)
02214 caching = new MythProgressDialog(QObject::tr("Pre-scaling theme images"),
02215 list->count());
02216
02217 int progress = 0;
02218
02219 QString destdir = d->themecachedir;
02220 if (subdirname.length() > 0)
02221 destdir += subdirname + "/";
02222
02223 while ((fi = it.current()) != 0)
02224 {
02225 if (caching)
02226 caching->setProgress(progress);
02227 progress++;
02228
02229 ++it;
02230 if (fi->fileName() == "." || fi->fileName() == "..")
02231 continue;
02232 if (fi->isDir() && subdirname.length() == 0)
02233 {
02234 QString newdirname = fi->fileName();
02235 QDir newsubdir(d->themecachedir + newdirname);
02236 if (!newsubdir.exists())
02237 newsubdir.mkdir(d->themecachedir + newdirname);
02238
02239 CacheThemeImagesDirectory(dirname + "/" + newdirname,
02240 newdirname);
02241 continue;
02242 }
02243 else if (fi->isDir())
02244 continue;
02245
02246 if (fi->extension().lower() != "png" &&
02247 fi->extension().lower() != "jpg" &&
02248 fi->extension().lower() != "gif" &&
02249 fi->extension().lower() != "jpeg")
02250 continue;
02251
02252 QString filename = fi->fileName();
02253 QFileInfo cacheinfo(destdir + filename);
02254
02255 if (!cacheinfo.exists() ||
02256 (cacheinfo.lastModified() < fi->lastModified()))
02257 {
02258 VERBOSE(VB_FILE, QString("generating cache image for: %1")
02259 .arg(fi->absFilePath()));
02260
02261 QImage *tmpimage = LoadScaleImage(fi->absFilePath(), false);
02262
02263 if (tmpimage && tmpimage->width() > 0 && tmpimage->height() > 0)
02264 {
02265 if (!tmpimage->save(destdir + filename, "PNG"))
02266 {
02267 VERBOSE(VB_IMPORTANT,
02268 QString("Failed to save cached image: %1")
02269 .arg(d->themecachedir + filename));
02270 }
02271
02272 delete tmpimage;
02273 }
02274 }
02275 }
02276
02277 if (caching)
02278 {
02279 caching->Close();
02280 caching->deleteLater();
02281 }
02282 }
02283
02284 void MythContext::GetScreenBounds(int &xbase, int &ybase,
02285 int &width, int &height)
02286 {
02287 xbase = d->m_xbase;
02288 ybase = d->m_ybase;
02289
02290 width = d->m_width;
02291 height = d->m_height;
02292 }
02293
02294 void MythContext::GetScreenSettings(float &wmult, float &hmult)
02295 {
02296 wmult = d->m_wmult;
02297 hmult = d->m_hmult;
02298 }
02299
02300 void MythContext::GetScreenSettings(int &width, float &wmult,
02301 int &height, float &hmult)
02302 {
02303 height = d->m_screenheight;
02304 width = d->m_screenwidth;
02305
02306 wmult = d->m_wmult;
02307 hmult = d->m_hmult;
02308 }
02309
02310 void MythContext::GetScreenSettings(int &xbase, int &width, float &wmult,
02311 int &ybase, int &height, float &hmult)
02312 {
02313 xbase = d->m_screenxbase;
02314 ybase = d->m_screenybase;
02315
02316 height = d->m_screenheight;
02317 width = d->m_screenwidth;
02318
02319 wmult = d->m_wmult;
02320 hmult = d->m_hmult;
02321 }
02322
02323 void MythContext::GetResolutionSetting(const QString &type,
02324 int &width, int &height,
02325 double &forced_aspect,
02326 short &refresh_rate,
02327 int index)
02328 {
02329 bool ok = false, ok0 = false, ok1 = false;
02330 QString sRes = QString("%1Resolution").arg(type);
02331 QString sRR = QString("%1RefreshRate").arg(type);
02332 QString sAspect = QString("%1ForceAspect").arg(type);
02333 QString sWidth = QString("%1Width").arg(type);
02334 QString sHeight = QString("%1Height").arg(type);
02335 if (index >= 0)
02336 {
02337 sRes = QString("%1Resolution%2").arg(type).arg(index);
02338 sRR = QString("%1RefreshRate%2").arg(type).arg(index);
02339 sAspect = QString("%1ForceAspect%2").arg(type).arg(index);
02340 sWidth = QString("%1Width%2").arg(type).arg(index);
02341 sHeight = QString("%1Height%2").arg(type).arg(index);
02342 }
02343
02344 QString res = GetSetting(sRes);
02345
02346 if ("" != res)
02347 {
02348 QStringList slist = QStringList::split("x", res);
02349 int w = width, h = height;
02350 if (2 == slist.size())
02351 {
02352 w = slist[0].toInt(&ok0);
02353 h = slist[1].toInt(&ok1);
02354 }
02355 bool ok = ok0 && ok1;
02356 if (ok)
02357 {
02358 width = w;
02359 height = h;
02360 refresh_rate = GetNumSetting(sRR);
02361 forced_aspect = GetFloatSetting(sAspect);
02362 }
02363 }
02364 else
02365
02366 if (!ok)
02367 {
02368 int tmpWidth = GetNumSetting(sWidth, width);
02369 if (tmpWidth)
02370 width = tmpWidth;
02371
02372 int tmpHeight = GetNumSetting(sHeight, height);
02373 if (tmpHeight)
02374 height = tmpHeight;
02375
02376 refresh_rate = 0;
02377 forced_aspect = 0.0;
02378
02379 }
02380 }
02381
02382 void MythContext::GetResolutionSetting(const QString &t, int &w, int &h, int i)
02383 {
02384 double forced_aspect = 0;
02385 short refresh_rate = 0;
02386 GetResolutionSetting(t, w, h, forced_aspect, refresh_rate, i);
02387 }
02388
02398 bool MythContext::ParseGeometryOverride(const QString geometry)
02399 {
02400 QRegExp sre("^(\\d+)x(\\d+)$");
02401 QRegExp lre("^(\\d+)x(\\d+)([+-]\\d+)([+-]\\d+)$");
02402 QStringList geo;
02403 bool longForm = false;
02404
02405 if (sre.exactMatch(geometry))
02406 {
02407 sre.search(geometry);
02408 geo = sre.capturedTexts();
02409 }
02410 else if (lre.exactMatch(geometry))
02411 {
02412 lre.search(geometry);
02413 geo = lre.capturedTexts();
02414 longForm = true;
02415 }
02416 else
02417 {
02418 VERBOSE(VB_IMPORTANT, "Geometry does not match either form -");
02419 VERBOSE(VB_IMPORTANT, "WIDTHxHEIGHT or WIDTHxHEIGHT+XOFF+YOFF");
02420 return false;
02421 }
02422
02423 bool parsed;
02424
02425 if (!d)
02426 {
02427 VERBOSE(VB_IMPORTANT,
02428 "MythContextPrivate not initted, can't store geometry.");
02429 return false;
02430 }
02431
02432 d->m_geometry_w = geo[1].toInt(&parsed);
02433 if (!parsed)
02434 {
02435 VERBOSE(VB_IMPORTANT, "Could not parse width of geometry override");
02436 return false;
02437 }
02438
02439 d->m_geometry_h = geo[2].toInt(&parsed);
02440 if (!parsed)
02441 {
02442 VERBOSE(VB_IMPORTANT, "Could not parse height of geometry override");
02443 return false;
02444 }
02445
02446 if (longForm)
02447 {
02448 d->m_geometry_x = geo[3].toInt(&parsed);
02449 if (!parsed)
02450 {
02451 VERBOSE(VB_IMPORTANT,
02452 "Could not parse horizontal offset of geometry override");
02453 return false;
02454 }
02455
02456 d->m_geometry_y = geo[4].toInt(&parsed);
02457 if (!parsed)
02458 {
02459 VERBOSE(VB_IMPORTANT,
02460 "Could not parse vertical offset of geometry override");
02461 return false;
02462 }
02463 }
02464
02465 VERBOSE(VB_IMPORTANT, QString("Overriding GUI, width=%1,"
02466 " height=%2 at %3,%4")
02467 .arg(d->m_geometry_w).arg(d->m_geometry_h)
02468 .arg(d->m_geometry_x).arg(d->m_geometry_y));
02469 return true;
02470 }
02471
02480 QString MythContext::FindThemeDir(const QString &themename)
02481 {
02482 QString testdir = MythContext::GetConfDir() + "/themes/" + themename;
02483
02484 QDir dir(testdir);
02485 if (dir.exists())
02486 return testdir;
02487 else
02488 VERBOSE(VB_IMPORTANT, "No theme dir: " + dir.absPath());
02489
02490
02491 testdir = GetThemesParentDir() + themename;
02492 dir.setPath(testdir);
02493 if (dir.exists())
02494 return testdir;
02495 else
02496 VERBOSE(VB_IMPORTANT, "No theme dir: " + dir.absPath());
02497
02498
02499 testdir = GetThemesParentDir() + "G.A.N.T";
02500 dir.setPath(testdir);
02501 if (dir.exists())
02502 {
02503 VERBOSE(VB_IMPORTANT, QString("Could not find theme: %1 - "
02504 "Switching to G.A.N.T").arg(themename));
02505 SaveSetting("Theme", "G.A.N.T");
02506 return testdir;
02507 }
02508 else
02509 VERBOSE(VB_IMPORTANT, "No theme dir: " + dir.absPath());
02510
02511 VERBOSE(VB_IMPORTANT, QString("Could not find theme: %1").arg(themename));
02512 return "";
02513 }
02514
02523 QString MythContext::FindMenuThemeDir(const QString &menuname)
02524 {
02525 QString testdir;
02526 QDir dir;
02527
02528 if (menuname == "default")
02529 {
02530 testdir = GetShareDir();
02531 dir.setPath(testdir);
02532 if (dir.exists())
02533 return testdir;
02534 }
02535
02536 testdir = MythContext::GetConfDir() + "/themes/" + menuname;
02537
02538 dir.setPath(testdir);
02539 if (dir.exists())
02540 return testdir;
02541
02542 testdir = GetThemesParentDir() + menuname;
02543 dir.setPath(testdir);
02544 if (dir.exists())
02545 return testdir;
02546
02547 testdir = GetShareDir();
02548 dir.setPath(testdir);
02549 if (dir.exists())
02550 {
02551 VERBOSE(VB_IMPORTANT, QString("Could not find theme: %1 - "
02552 "Switching to default").arg(menuname));
02553 SaveSetting("MenuTheme", "default");
02554 return testdir;
02555 }
02556 else {
02557 VERBOSE(VB_IMPORTANT, QString("Could not find menu theme: %1 - Fallback to default failed.").arg(menuname));
02558 }
02559
02560 return "";
02561 }
02562
02563 QString MythContext::GetMenuThemeDir(void)
02564 {
02565 return d->m_menuthemepathname;
02566 }
02567
02568 QString MythContext::GetThemeDir(void)
02569 {
02570 return d->m_themepathname;
02571 }
02572
02573 QValueList<QString> MythContext::GetThemeSearchPath(void)
02574 {
02575 QValueList<QString> searchpath;
02576
02577 searchpath.append(GetThemeDir());
02578 if (d->IsWideMode())
02579 searchpath.append(GetThemesParentDir() + "default-wide/");
02580 searchpath.append(GetThemesParentDir() + "default/");
02581 searchpath.append("/tmp/");
02582 return searchpath;
02583 }
02584
02585 MDBManager *MythContext::GetDBManager(void)
02586 {
02587 return &d->m_dbmanager;
02588 }
02589
02590 void MythContext::DBError(const QString &where, const QSqlQuery& query)
02591 {
02592 QString str = QString("DB Error (%1):\n").arg(where);
02593
02594 #if QT_VERSION >= 0x030200
02595 str += "Query was:\n";
02596 str += query.executedQuery() + "\n";
02597 str += QString::fromUtf8(DBErrorMessage(query.lastError()));
02598 #else
02599 str += "Your version of Qt is too old to provide proper debugging\n";
02600 str += "Query may have been:\n";
02601 str += QString::fromUtf8(query.lastQuery()) + "\n";
02602 str += DBErrorMessage(query.lastError());
02603 #endif
02604 VERBOSE(VB_IMPORTANT, QString("%1").arg(str));
02605 }
02606
02607 QString MythContext::DBErrorMessage(const QSqlError& err)
02608 {
02609 if (!err.type())
02610 return "No error type from QSqlError? Strange...";
02611
02612 return QString("Driver error was [%1/%2]:\n"
02613 "%3\n"
02614 "Database error was:\n"
02615 "%4\n")
02616 .arg(err.type())
02617 .arg(err.number())
02618 .arg(err.driverText())
02619 .arg(err.databaseText());
02620 }
02621
02622 Settings *MythContext::qtconfig(void)
02623 {
02624 return d->m_qtThemeSettings;
02625 }
02626
02627 void MythContext::SaveSetting(const QString &key, int newValue)
02628 {
02629 (void) SaveSettingOnHost(key,
02630 QString::number(newValue), d->m_localhostname);
02631 }
02632
02633 void MythContext::SaveSetting(const QString &key, const QString &newValue)
02634 {
02635 (void) SaveSettingOnHost(key, newValue, d->m_localhostname);
02636 }
02637
02638 bool MythContext::SaveSettingOnHost(const QString &key,
02639 const QString &newValue,
02640 const QString &host)
02641 {
02642 bool success = false;
02643
02644 MSqlQuery query(MSqlQuery::InitCon());
02645 if (query.isConnected())
02646 {
02647
02648 if ((host) && (host != ""))
02649 query.prepare("DELETE FROM settings WHERE value = :KEY "
02650 "AND hostname = :HOSTNAME ;");
02651 else
02652 query.prepare("DELETE FROM settings WHERE value = :KEY "
02653 "AND hostname is NULL;");
02654
02655 query.bindValue(":KEY", key);
02656 query.bindValue(":HOSTNAME", host);
02657
02658 if (!query.exec() || !query.isActive())
02659 MythContext::DBError("Clear setting", query);
02660
02661 if ((host) && (host != ""))
02662 query.prepare("INSERT INTO settings (value,data,hostname) "
02663 "VALUES ( :VALUE, :DATA, :HOSTNAME );");
02664 else
02665 query.prepare("INSERT INTO settings (value,data,hostname ) "
02666 "VALUES ( :VALUE, :DATA, NULL );");
02667
02668 query.bindValue(":VALUE", key);
02669 query.bindValue(":DATA", newValue);
02670 query.bindValue(":HOSTNAME", host);
02671
02672 if (!query.exec() || !query.isActive())
02673 MythContext::DBError("SaveSettingOnHost query failure: ", query);
02674 else
02675 success = true;
02676 }
02677 else
02678 {
02679 VERBOSE(VB_IMPORTANT,
02680 QString("Database not open while trying to save setting: %1")
02681 .arg(key));
02682 }
02683
02684 ClearSettingsCache(key, newValue);
02685 ClearSettingsCache(host + " " + key, newValue);
02686
02687 return success;
02688 }
02689
02690 QString MythContext::GetSetting(const QString &key, const QString &defaultval)
02691 {
02692 bool found = false;
02693 QString value;
02694
02695 if (d && d->overriddenSettings.contains(key)) {
02696 value = d->overriddenSettings[key];
02697 return value;
02698 }
02699
02700 if (d && d->useSettingsCache)
02701 {
02702 d->settingsCacheLock.lock();
02703 if (d->settingsCache.contains(key))
02704 {
02705 value = d->settingsCache[key];
02706 d->settingsCacheLock.unlock();
02707 return value;
02708 }
02709 d->settingsCacheLock.unlock();
02710 }
02711
02712 MSqlQuery query(MSqlQuery::InitCon());
02713 if (query.isConnected())
02714 {
02715 query.prepare("SELECT data FROM settings WHERE value "
02716 "= :KEY AND hostname = :HOSTNAME ;");
02717 query.bindValue(":KEY", key);
02718 query.bindValue(":HOSTNAME", d->m_localhostname);
02719 query.exec();
02720
02721 if (query.isActive() && query.size() > 0)
02722 {
02723 query.next();
02724 value = QString::fromUtf8(query.value(0).toString());
02725 found = true;
02726 }
02727 else
02728 {
02729 query.prepare("SELECT data FROM settings WHERE value = :KEY AND "
02730 "hostname IS NULL;");
02731 query.bindValue(":KEY", key);
02732 query.exec();
02733
02734 if (query.isActive() && query.size() > 0)
02735 {
02736 query.next();
02737 value = QString::fromUtf8(query.value(0).toString());
02738 found = true;
02739 }
02740 }
02741 }
02742 else
02743 {
02744 VERBOSE(VB_IMPORTANT,
02745 QString("Database not open while trying to load setting: %1")
02746 .arg(key));
02747 }
02748
02749 if (!found)
02750 return d->m_settings->GetSetting(key, defaultval);
02751
02752
02753 if (!value.isNull() && d && d->useSettingsCache)
02754 {
02755 d->settingsCacheLock.lock();
02756 d->settingsCache[key] = value;
02757 d->settingsCacheLock.unlock();
02758 }
02759
02760 return value;
02761 }
02762
02763 int MythContext::GetNumSetting(const QString &key, int defaultval)
02764 {
02765 QString val = QString::number(defaultval);
02766 QString retval = GetSetting(key, val);
02767
02768 return retval.toInt();
02769 }
02770
02771 double MythContext::GetFloatSetting(const QString &key, double defaultval)
02772 {
02773 QString val = QString::number(defaultval);
02774 QString retval = GetSetting(key, val);
02775
02776 return retval.toDouble();
02777 }
02778
02779 QString MythContext::GetSettingOnHost(const QString &key, const QString &host,
02780 const QString &defaultval)
02781 {
02782 bool found = false;
02783 QString value = defaultval;
02784 QString myKey = host + " " + key;
02785
02786 if (d)
02787 {
02788 if (d->overriddenSettings.contains(myKey))
02789 {
02790 value = d->overriddenSettings[myKey];
02791 return value;
02792 }
02793
02794 if ((host == d->m_localhostname) &&
02795 (d->overriddenSettings.contains(key)))
02796 {
02797 value = d->overriddenSettings[key];
02798 return value;
02799 }
02800 }
02801
02802 if (d && d->useSettingsCache)
02803 {
02804 d->settingsCacheLock.lock();
02805 if (d->settingsCache.contains(myKey))
02806 {
02807 value = d->settingsCache[myKey];
02808 d->settingsCacheLock.unlock();
02809 return value;
02810 }
02811 d->settingsCacheLock.unlock();
02812 }
02813
02814 MSqlQuery query(MSqlQuery::InitCon());
02815 if (query.isConnected())
02816 {
02817 query.prepare("SELECT data FROM settings WHERE value = :VALUE "
02818 "AND hostname = :HOSTNAME ;");
02819 query.bindValue(":VALUE", key);
02820 query.bindValue(":HOSTNAME", host);
02821
02822 if (query.exec() && query.isActive() && query.size() > 0)
02823 {
02824 query.next();
02825 value = query.value(0).toString();
02826 found = true;
02827 }
02828 }
02829 else
02830 {
02831 VERBOSE(VB_IMPORTANT,
02832 QString("Database not open while trying to load setting: %1")
02833 .arg(key));
02834 }
02835
02836 if (found && d && d->useSettingsCache)
02837 {
02838 d->settingsCacheLock.lock();
02839 d->settingsCache[host + " " + key] = value;
02840 d->settingsCacheLock.unlock();
02841 }
02842
02843 return value;
02844 }
02845
02846 int MythContext::GetNumSettingOnHost(const QString &key, const QString &host,
02847 int defaultval)
02848 {
02849 QString val = QString::number(defaultval);
02850 QString retval = GetSettingOnHost(key, host, val);
02851
02852 return retval.toInt();
02853 }
02854
02855 double MythContext::GetFloatSettingOnHost(
02856 const QString &key, const QString &host, double defaultval)
02857 {
02858 QString val = QString::number(defaultval);
02859 QString retval = GetSettingOnHost(key, host, val);
02860
02861 return retval.toDouble();
02862 }
02863
02864 void MythContext::SetPalette(QWidget *widget)
02865 {
02866 QPalette pal = widget->palette();
02867
02868 QColorGroup active = pal.active();
02869 QColorGroup disabled = pal.disabled();
02870 QColorGroup inactive = pal.inactive();
02871
02872 const QString names[] = { "Foreground", "Button", "Light", "Midlight",
02873 "Dark", "Mid", "Text", "BrightText", "ButtonText",
02874 "Base", "Background", "Shadow", "Highlight",
02875 "HighlightedText" };
02876
02877 QString type = "Active";
02878 for (int i = 0; i < 13; i++)
02879 {
02880 QString color = d->m_qtThemeSettings->GetSetting(type + names[i]);
02881 if (color != "")
02882 pal.setColor(QPalette::Active, (QColorGroup::ColorRole) i,
02883 QColor(color));
02884 }
02885
02886 type = "Disabled";
02887 for (int i = 0; i < 13; i++)
02888 {
02889 QString color = d->m_qtThemeSettings->GetSetting(type + names[i]);
02890 if (color != "")
02891 pal.setColor(QPalette::Disabled, (QColorGroup::ColorRole) i,
02892 QColor(color));
02893 }
02894
02895 type = "Inactive";
02896 for (int i = 0; i < 13; i++)
02897 {
02898 QString color = d->m_qtThemeSettings->GetSetting(type + names[i]);
02899 if (color != "")
02900 pal.setColor(QPalette::Inactive, (QColorGroup::ColorRole) i,
02901 QColor(color));
02902 }
02903
02904 widget->setPalette(pal);
02905 }
02906
02907 void MythContext::ThemeWidget(QWidget *widget)
02908 {
02909 if (d->m_themeloaded)
02910 {
02911 widget->setPalette(d->m_palette);
02912 if (d->m_backgroundimage && d->m_backgroundimage->width() > 0)
02913 {
02914 widget->setPaletteBackgroundPixmap(*(d->m_backgroundimage));
02915 }
02916 return;
02917 }
02918
02919 SetPalette(widget);
02920 d->m_palette = widget->palette();
02921
02922 QPixmap *bgpixmap = NULL;
02923
02924 if (d->m_qtThemeSettings->GetSetting("BackgroundPixmap") != "")
02925 {
02926 QString pmapname = d->m_themepathname +
02927 d->m_qtThemeSettings->GetSetting("BackgroundPixmap");
02928
02929 bgpixmap = LoadScalePixmap(pmapname);
02930 if (bgpixmap)
02931 {
02932 widget->setBackgroundOrigin(QWidget::AncestorOrigin);
02933 widget->setPaletteBackgroundPixmap(*bgpixmap);
02934 d->m_backgroundimage = new QPixmap(*bgpixmap);
02935 }
02936 }
02937 else if (d->m_qtThemeSettings->GetSetting("TiledBackgroundPixmap") != "")
02938 {
02939 QString pmapname = d->m_themepathname +
02940 d->m_qtThemeSettings->GetSetting("TiledBackgroundPixmap");
02941
02942 int width, height;
02943 float wmult, hmult;
02944
02945 GetScreenSettings(width, wmult, height, hmult);
02946
02947 bgpixmap = LoadScalePixmap(pmapname);
02948 if (bgpixmap)
02949 {
02950 QPixmap background(width, height);
02951 QPainter tmp(&background);
02952
02953 tmp.drawTiledPixmap(0, 0, width, height, *bgpixmap);
02954 tmp.end();
02955
02956 d->m_backgroundimage = new QPixmap(background);
02957 widget->setBackgroundOrigin(QWidget::AncestorOrigin);
02958 widget->setPaletteBackgroundPixmap(background);
02959 }
02960 }
02961
02962 d->m_themeloaded = true;
02963
02964 if (bgpixmap)
02965 delete bgpixmap;
02966 }
02967
02968 bool MythContext::FindThemeFile(QString &filename)
02969 {
02970
02971 if (QFile::exists(filename))
02972 return true;
02973
02974 int pathStart = filename.findRev('/');
02975 QString basename;
02976 if (pathStart > 0)
02977 basename = filename.mid(pathStart + 1);
02978
02979 QString file;
02980 QValueList<QString> searchpath = GetThemeSearchPath();
02981 for (QValueList<QString>::const_iterator ii = searchpath.begin();
02982 ii != searchpath.end(); ii++)
02983 {
02984 if (QFile::exists((file = *ii + filename)))
02985 goto found;
02986 if (pathStart > 0 && QFile::exists((file = *ii + basename)))
02987 goto found;
02988 }
02989
02990 return false;
02991
02992 found:
02993 filename = file;
02994 return true;
02995 }
02996
02997 QImage *MythContext::LoadScaleImage(QString filename, bool fromcache)
02998 {
02999 if (filename.left(5) == "myth:")
03000 return NULL;
03001
03002 if (d->themecachedir != "" && fromcache)
03003 {
03004 QString cachefilepath;
03005 bool bFound = false;
03006
03007
03008 if (!bFound)
03009 {
03010 if (!strcmp(filename.left(d->m_themepathname.length()),
03011 d->m_themepathname))
03012 {
03013 QString tmpfilename = filename;
03014 tmpfilename.remove(0, d->m_themepathname.length());
03015 cachefilepath = d->themecachedir + tmpfilename;
03016 QFile cachecheck(cachefilepath);
03017 if (cachecheck.exists())
03018 bFound = true;
03019 }
03020 }
03021
03022
03023 if (!bFound)
03024 {
03025 cachefilepath = d->themecachedir + filename;
03026 QFile cachecheck(cachefilepath);
03027 if (cachecheck.exists())
03028 bFound = true;
03029 }
03030
03031
03032 if (!bFound)
03033 {
03034 QFileInfo fi(filename);
03035 cachefilepath = d->themecachedir + fi.fileName();
03036 QFile cachecheck(cachefilepath);
03037 if (cachecheck.exists())
03038 bFound = true;
03039 }
03040
03041 if (bFound)
03042 {
03043 QImage *ret = new QImage(cachefilepath);
03044 if (ret)
03045 return ret;
03046 }
03047 }
03048
03049
03050 if (!FindThemeFile(filename))
03051 {
03052 VERBOSE(VB_IMPORTANT,
03053 QString("Unable to find image file: %1").arg(filename));
03054
03055 return NULL;
03056 }
03057
03058 QImage *ret = NULL;
03059
03060 int width, height;
03061 float wmult, hmult;
03062
03063 GetScreenSettings(width, wmult, height, hmult);
03064
03065 if (width != d->m_baseWidth || height != d->m_baseHeight)
03066 {
03067 QImage tmpimage;
03068
03069 if (!tmpimage.load(filename))
03070 {
03071 VERBOSE(VB_IMPORTANT,
03072 QString("Error loading image file: %1").arg(filename));
03073
03074 return NULL;
03075 }
03076 QImage tmp2 = tmpimage.smoothScale((int)(tmpimage.width() * wmult),
03077 (int)(tmpimage.height() * hmult));
03078 ret = new QImage(tmp2);
03079 }
03080 else
03081 {
03082 ret = new QImage(filename);
03083 if (ret->width() == 0)
03084 {
03085 VERBOSE(VB_IMPORTANT,
03086 QString("Error loading image file: %1").arg(filename));
03087
03088 delete ret;
03089 return NULL;
03090 }
03091 }
03092
03093 return ret;
03094 }
03095
03096 QPixmap *MythContext::LoadScalePixmap(QString filename, bool fromcache)
03097 {
03098 if (filename.left(5) == "myth:")
03099 return NULL;
03100
03101 if (d->themecachedir != "" && fromcache)
03102 {
03103 QString cachefilepath;
03104 bool bFound = false;
03105
03106
03107 if (!bFound)
03108 {
03109 if (!strcmp(filename.left(d->m_themepathname.length()),
03110 d->m_themepathname))
03111 {
03112 QString tmpfilename = filename;
03113 tmpfilename.remove(0, d->m_themepathname.length());
03114 cachefilepath = d->themecachedir + tmpfilename;
03115 QFile cachecheck(cachefilepath);
03116 if (cachecheck.exists())
03117 bFound = true;
03118 }
03119 }
03120
03121
03122 if (!bFound)
03123 {
03124 cachefilepath = d->themecachedir + filename;
03125 QFile cachecheck(cachefilepath);
03126 if (cachecheck.exists())
03127 bFound = true;
03128 }
03129
03130
03131 if (!bFound)
03132 {
03133 QFileInfo fi(filename);
03134 cachefilepath = d->themecachedir + fi.fileName();
03135 QFile cachecheck(cachefilepath);
03136 if (cachecheck.exists())
03137 bFound = true;
03138 }
03139
03140 if (bFound)
03141 {
03142 QPixmap *ret = new QPixmap(cachefilepath);
03143 if (ret)
03144 return ret;
03145 }
03146 }
03147
03148 if (!FindThemeFile(filename))
03149 {
03150 VERBOSE(VB_IMPORTANT,
03151 QString("Unable to find image file: %1").arg(filename));
03152
03153 return NULL;
03154 }
03155
03156 QPixmap *ret = new QPixmap();
03157
03158 int width, height;
03159 float wmult, hmult;
03160
03161 GetScreenSettings(width, wmult, height, hmult);
03162
03163 if (width != d->m_baseWidth || height != d->m_baseHeight)
03164 {
03165 QImage tmpimage;
03166
03167 if (!tmpimage.load(filename))
03168 {
03169 VERBOSE(VB_IMPORTANT,
03170 QString("Error loading image file: %1").arg(filename));
03171
03172 delete ret;
03173 return NULL;
03174 }
03175 QImage tmp2 = tmpimage.smoothScale((int)(tmpimage.width() * wmult),
03176 (int)(tmpimage.height() * hmult));
03177 ret->convertFromImage(tmp2);
03178 }
03179 else
03180 {
03181 if (!ret->load(filename))
03182 {
03183 VERBOSE(VB_IMPORTANT,
03184 QString("Error loading image file: %1").arg(filename));
03185
03186 delete ret;
03187 return NULL;
03188 }
03189 }
03190
03191 return ret;
03192 }
03193
03194 QImage *MythContext::CacheRemotePixmap(const QString &url, bool reCache)
03195 {
03196 QUrl qurl = url;
03197 if (qurl.host() == "")
03198 return NULL;
03199
03200 if ((d->imageCache.contains(url)) && (reCache == false))
03201 return &(d->imageCache[url]);
03202
03203 RemoteFile *rf = new RemoteFile(url, false, 0);
03204
03205 QByteArray data;
03206 bool ret = rf->SaveAs(data);
03207
03208 delete rf;
03209
03210 if (ret)
03211 {
03212 QImage image(data);
03213 if (image.width() > 0)
03214 {
03215 d->imageCache[url] = image;
03216 return &(d->imageCache[url]);
03217 }
03218 }
03219
03220 return NULL;
03221 }
03222
03223 void MythContext::SetSetting(const QString &key, const QString &newValue)
03224 {
03225 d->m_settings->SetSetting(key, newValue);
03226 ClearSettingsCache(key, newValue);
03227 }
03228
03235 void MythContext::OverrideSettingForSession(const QString &key,
03236 const QString &value)
03237 {
03238 d->overriddenSettings[key] = value;
03239 }
03240
03241 bool MythContext::SendReceiveStringList(QStringList &strlist,
03242 bool quickTimeout, bool block)
03243 {
03244 d->serverSockLock.lock();
03245
03246 if (!d->serverSock)
03247 {
03248 ConnectToMasterServer(false);
03249
03250 }
03251
03252 bool ok = false;
03253
03254 if (d->serverSock)
03255 {
03256 d->serverSock->writeStringList(strlist);
03257 ok = d->serverSock->readStringList(strlist, quickTimeout);
03258
03259 if (!ok)
03260 {
03261 VERBOSE(VB_IMPORTANT, QString("Connection to backend server lost"));
03262 d->serverSock->DownRef();
03263 d->serverSock = NULL;
03264
03265 ConnectToMasterServer(false);
03266
03267 if (d->serverSock)
03268 {
03269 d->serverSock->writeStringList(strlist);
03270 ok = d->serverSock->readStringList(strlist, quickTimeout);
03271 }
03272 }
03273
03274
03275 while (ok && strlist[0] == "BACKEND_MESSAGE")
03276 {
03277
03278 VERBOSE(VB_IMPORTANT, "SRSL you shouldn't see this!!");
03279 QString message = strlist[1];
03280 strlist.pop_front(); strlist.pop_front();
03281
03282 MythEvent me(message, strlist);
03283 dispatch(me);
03284
03285 ok = d->serverSock->readStringList(strlist, quickTimeout);
03286 }
03287
03288
03289 if (!ok)
03290 {
03291 if (d->serverSock)
03292 {
03293 d->serverSock->DownRef();
03294 d->serverSock = NULL;
03295 }
03296
03297 qApp->lock();
03298 if (!block)
03299 d->serverSockLock.unlock();
03300 VERBOSE(VB_IMPORTANT,
03301 QString("Reconnection to backend server failed"));
03302 if (d->m_height && d->m_width)
03303 MythPopupBox::showOkPopup(d->mainWindow, "connection failure",
03304 tr("The connection to the master backend "
03305 "server has gone away for some reason.. "
03306 "Is it running?"));
03307
03308 if (!block)
03309 d->serverSockLock.lock();
03310 qApp->unlock();
03311 }
03312 }
03313
03314 d->serverSockLock.unlock();
03315
03316 return ok;
03317 }
03318
03319 void MythContext::readyRead(MythSocket *sock)
03320 {
03321 (void)sock;
03322
03323 while (d->eventSock->state() == MythSocket::Connected &&
03324 d->eventSock->bytesAvailable() > 0)
03325 {
03326 QStringList strlist;
03327 if (!d->eventSock->readStringList(strlist))
03328 continue;
03329
03330 QString prefix = strlist[0];
03331 QString message = strlist[1];
03332
03333 if (prefix == "OK")
03334 {
03335 }
03336 else if (prefix != "BACKEND_MESSAGE")
03337 {
03338 VERBOSE(VB_IMPORTANT,
03339 QString("Received a: %1 message from the backend"
03340 "\n\t\t\tBut I don't know what to do with it.")
03341 .arg(prefix));
03342 }
03343 else if (message == "CLEAR_SETTINGS_CACHE")
03344 {
03345
03346 VERBOSE(VB_GENERAL, "Received a remote 'Clear Cache' request");
03347 ClearSettingsCache();
03348 }
03349 else
03350 {
03351 strlist.pop_front();
03352 strlist.pop_front();
03353 MythEvent me(message, strlist);
03354 dispatch(me);
03355 }
03356 }
03357 }
03358
03359 bool MythContext::CheckProtoVersion(MythSocket* socket)
03360 {
03361 QStringList strlist = QString("MYTH_PROTO_VERSION %1")
03362 .arg(MYTH_PROTO_VERSION);
03363 socket->writeStringList(strlist);
03364 socket->readStringList(strlist, true);
03365
03366 if (strlist.empty())
03367 {
03368 VERBOSE(VB_IMPORTANT, "Protocol version check failure. The response "
03369 "to MYTH_PROTO_VERSION was empty.");
03370
03371 return false;
03372 }
03373 else if (strlist[0] == "REJECT")
03374 {
03375 VERBOSE(VB_GENERAL, QString("Protocol version mismatch (frontend=%1,"
03376 "backend=%2)\n")
03377 .arg(MYTH_PROTO_VERSION).arg(strlist[1]));
03378
03379 if (d->m_height && d->m_width)
03380 {
03381 qApp->lock();
03382 MythPopupBox::showOkPopup(d->mainWindow,
03383 "Connection failure",
03384 tr(QString("The server uses network "
03385 "protocol version %1, "
03386 "but this client only "
03387 "understands version %2. "
03388 "Make sure you are running "
03389 "compatible versions of "
03390 "the backend and frontend."))
03391 .arg(strlist[1])
03392 .arg(MYTH_PROTO_VERSION));
03393 qApp->unlock();
03394 }
03395 return false;
03396 }
03397 else if (strlist[0] == "ACCEPT")
03398 {
03399 VERBOSE(VB_IMPORTANT, QString("Using protocol version %1")
03400 .arg(MYTH_PROTO_VERSION));
03401 return true;
03402 }
03403
03404 VERBOSE(VB_GENERAL, QString("Unexpected response to MYTH_PROTO_VERSION: %1")
03405 .arg(strlist[0]));
03406 return false;
03407 }
03408
03409 void MythContext::connected(MythSocket *sock)
03410 {
03411 (void)sock;
03412 }
03413
03414 void MythContext::connectionClosed(MythSocket *sock)
03415 {
03416 (void)sock;
03417
03418 VERBOSE(VB_IMPORTANT, QString("Event socket closed. "
03419 "No connection to the backend."));
03420
03421 d->serverSockLock.lock();
03422 if (d->serverSock)
03423 {
03424 d->serverSock->DownRef();
03425 d->serverSock = NULL;
03426 }
03427
03428 if (d->eventSock)
03429 {
03430 d->eventSock->DownRef();
03431 d->eventSock = NULL;
03432 }
03433
03434 d->serverSockLock.unlock();
03435 }
03436
03437 QFont MythContext::GetBigFont(void)
03438 {
03439 QFont font = QApplication::font();
03440 font.setPointSize(GetMythMainWindow()->NormalizeFontSize(d->bigfontsize));
03441 font.setWeight(QFont::Bold);
03442
03443 return font;
03444 }
03445
03446 QFont MythContext::GetMediumFont(void)
03447 {
03448 QFont font = QApplication::font();
03449 font.setPointSize(GetMythMainWindow()->NormalizeFontSize(d->mediumfontsize));
03450 font.setWeight(QFont::Bold);
03451
03452 return font;
03453 }
03454
03455 QFont MythContext::GetSmallFont(void)
03456 {
03457 QFont font = QApplication::font();
03458 font.setPointSize(GetMythMainWindow()->NormalizeFontSize(d->smallfontsize));
03459 font.setWeight(QFont::Bold);
03460
03461 return font;
03462 }
03463
03468 QString MythContext::GetLanguage(void)
03469 {
03470 return GetLanguageAndVariant().left(2);
03471 }
03472
03480 QString MythContext::GetLanguageAndVariant(void)
03481 {
03482 if (d->language == QString::null || d->language == "")
03483 d->language = GetSetting("Language", "EN").lower();
03484
03485 return d->language;
03486 }
03487
03488 void MythContext::SetMainWindow(MythMainWindow *mainwin)
03489 {
03490 d->mainWindow = mainwin;
03491 }
03492
03493 MythMainWindow *MythContext::GetMainWindow(void)
03494 {
03495 if (!d)
03496 return NULL;
03497 return d->mainWindow;
03498 }
03499
03510 int MythContext::PromptForSchemaUpgrade(const QString &dbver,
03511 const QString ¤t,
03512 const QString &backupResult)
03513 {
03514 bool autoUpgrade = false;
03515 bool connections = false;
03516 bool expertMode = false;
03517 QString message;
03518 int returnValue = MYTH_SCHEMA_UPGRADE;
03519 bool upgradable = (dbver.toUInt() < current.toUInt());
03520 QString warnOtherCl = tr("There are also other clients using this"
03521 " database. They should be shut down first.");
03522
03523
03524
03525 if (dbver.isEmpty() || dbver == "0")
03526 {
03527 VERBOSE(VB_GENERAL, "No current database version. Auto upgrading");
03528 return MYTH_SCHEMA_UPGRADE;
03529 }
03530
03531
03532
03533
03534
03535 switch (gContext->GetNumSetting("DBSchemaAutoUpgrade"))
03536 {
03537 case 1: autoUpgrade = true; break;
03538 case -1: expertMode = true; break;
03539 default: break;
03540 }
03541
03542
03543
03544
03545
03546
03547
03548
03549 if (autoUpgrade && upgradable && !connections)
03550 return MYTH_SCHEMA_UPGRADE;
03551
03552
03553
03554 if (upgradable)
03555 {
03556 if (autoUpgrade && connections)
03557 {
03558 message = tr("Error: MythTV cannot upgrade the schema of this"
03559 " datatase because other clients are using it.\n\n"
03560 "Please shut them down before upgrading.");
03561 returnValue = MYTH_SCHEMA_ERROR;
03562 }
03563 else
03564 {
03565 message = tr("Warning: MythTV wants to upgrade"
03566 " your database schema, from %1 to %2.");
03567 if (expertMode)
03568 message += "\n\n" +
03569 tr("You can try using the old schema,"
03570 " but that may cause problems.");
03571 }
03572 }
03573 else
03574 {
03575 if (expertMode)
03576 message = tr("Warning: MythTV database has newer"
03577 " schema (%1) than expected (%2).");
03578 else
03579 {
03580 message = tr("Error: MythTV database has newer"
03581 " schema (%1) than expected (%2).");
03582 returnValue = MYTH_SCHEMA_ERROR;
03583 }
03584 }
03585
03586 if (backupResult == "__FAILED__")
03587 message += "\n" + tr("MythTV was unable to backup your database.");
03588
03589 if (message.contains("%1"))
03590 message = message.arg(dbver).arg(current);
03591
03592
03593 if (d->m_gui)
03594 {
03595 bool createdTempWindow = false;
03596
03597 if (!d->mainWindow)
03598 {
03599 d->TempMainWindow();
03600 createdTempWindow = true;
03601 }
03602
03603 if (returnValue == MYTH_SCHEMA_ERROR)
03604 {
03605 MythPopupBox::showOkPopup(
03606 d->mainWindow, "Database Upgrade Error",
03607 message, QObject::tr("Exit"));
03608 }
03609 else
03610 {
03611 QStringList buttonNames;
03612
03613 buttonNames += QObject::tr("Exit");
03614 if (upgradable)
03615 buttonNames += QObject::tr("Upgrade");
03616 if (expertMode)
03617 buttonNames += QObject::tr("Use current schema");
03618
03619 DialogCode selected = MythPopupBox::ShowButtonPopup(
03620 d->mainWindow, "Database Upgrade", message,
03621 buttonNames, kDialogCodeButton0);
03622
03623
03624 if (kDialogCodeButton1 == selected ||
03625 kDialogCodeButton2 == selected)
03626 {
03627 if ((backupResult != "__FAILED__") &&
03628 (backupResult != ""))
03629 {
03630 int dirPos = backupResult.findRev(QChar('/'));
03631 QString dirName;
03632 QString fileName;
03633 if (dirPos > 0)
03634 {
03635 fileName = backupResult.mid(dirPos + 1);
03636 dirName = backupResult.left(dirPos);
03637 }
03638 message = tr("If your system becomes unstable, a database "
03639 "backup file called %1 is located in %2.")
03640 .arg(fileName).arg(dirName);
03641 }
03642 else
03643 message = tr("This cannot be un-done, so having a"
03644 " database backup would be a good idea.");
03645 if (connections)
03646 message += "\n\n" + warnOtherCl;
03647
03648 selected = MythPopupBox::ShowButtonPopup(
03649 d->mainWindow, "Database Upgrade", message,
03650 buttonNames, kDialogCodeButton0);
03651 }
03652
03653 switch (selected)
03654 {
03655 case kDialogCodeRejected:
03656 case kDialogCodeButton0:
03657 returnValue = MYTH_SCHEMA_EXIT; break;
03658 case kDialogCodeButton1:
03659 returnValue = upgradable ?
03660 MYTH_SCHEMA_UPGRADE:
03661 MYTH_SCHEMA_USE_EXISTING; break;
03662 case kDialogCodeButton2:
03663 returnValue = MYTH_SCHEMA_USE_EXISTING; break;
03664 default:
03665 returnValue = MYTH_SCHEMA_ERROR;
03666 }
03667 }
03668
03669 if (createdTempWindow)
03670 {
03671 d->EndTempWindow();
03672 d->m_DBparams.dbHostName = d->m_DBhostCp;
03673 }
03674
03675 return returnValue;
03676 }
03677
03678
03679
03680 if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
03681 {
03682 if (expertMode)
03683 {
03684 cout << "Console non-interactive. Using existing schema." << endl;
03685 return MYTH_SCHEMA_USE_EXISTING;
03686 }
03687
03688 cout << "Console is not interactive, cannot ask user about"
03689 << " upgrading database schema." << endl << "Upgrading." << endl;
03690 return MYTH_SCHEMA_UPGRADE;
03691 }
03692
03693
03694 QString resp;
03695
03696 cout << endl << message << endl << endl;
03697
03698 if (backupResult == "__FAILED__")
03699 cout << "WARNING: MythTV was unable to backup your database."
03700 << endl << endl;
03701 else if (backupResult != "")
03702 cout << "If your system becomes unstable, a database backup is "
03703 "located in " << backupResult << endl << endl;
03704
03705 if (expertMode)
03706 {
03707 resp = d->getResponse("Would you like to use the existing schema?",
03708 "yes");
03709 if (!resp || resp.left(1).lower() == "y")
03710 return MYTH_SCHEMA_USE_EXISTING;
03711 }
03712
03713 resp = d->getResponse("\nShall I upgrade this database?", "yes");
03714 if (resp && resp.left(1).lower() != "y")
03715 return MYTH_SCHEMA_EXIT;
03716
03717 if (connections)
03718 cout << endl << warnOtherCl <<endl;
03719
03720 if ((backupResult == "__FAILED__") ||
03721 (backupResult == ""))
03722 {
03723 resp = d->getResponse("\nA database backup might be a good idea"
03724 "\nAre you sure you want to upgrade?", "no");
03725 if (!resp || resp.left(1).lower() == "n")
03726 return MYTH_SCHEMA_EXIT;
03727 }
03728
03729 return MYTH_SCHEMA_UPGRADE;
03730 }
03731
03732 bool MythContext::TestPopupVersion(const QString &name,
03733 const QString &libversion,
03734 const QString &pluginversion)
03735 {
03736 if (libversion == pluginversion)
03737 return true;
03738
03739 QString err = "The " + name + " plugin was compiled against libmyth " +
03740 "version: " + pluginversion + ", but the installed " +
03741 "libmyth is at version: " + libversion + ". You probably " +
03742 "want to recompile the " + name + " plugin after doing a " +
03743 "make distclean.";
03744
03745 if (GetMainWindow() && !d->disablelibrarypopup)
03746 {
03747 DialogBox *dlg = new DialogBox(gContext->GetMainWindow(), err);
03748 dlg->AddButton("OK");
03749 dlg->exec();
03750 dlg->deleteLater();
03751 }
03752
03753 return false;
03754 }
03755
03756 void MythContext::SetDisableLibraryPopup(bool check)
03757 {
03758 d->disablelibrarypopup = check;
03759 }
03760
03761 void MythContext::SetPluginManager(MythPluginManager *pmanager)
03762 {
03763 d->pluginmanager = pmanager;
03764 }
03765
03766 MythPluginManager *MythContext::getPluginManager(void)
03767 {
03768 return d->pluginmanager;
03769 }
03770
03771
03772 void MythContext::DisableScreensaver(void)
03773 {
03774 QApplication::postEvent(GetMainWindow(),
03775 new ScreenSaverEvent(ScreenSaverEvent::ssetDisable));
03776 }
03777
03778 void MythContext::RestoreScreensaver(void)
03779 {
03780 QApplication::postEvent(GetMainWindow(),
03781 new ScreenSaverEvent(ScreenSaverEvent::ssetRestore));
03782 }
03783
03784 void MythContext::ResetScreensaver(void)
03785 {
03786 QApplication::postEvent(GetMainWindow(),
03787 new ScreenSaverEvent(ScreenSaverEvent::ssetReset));
03788 }
03789
03790 void MythContext::DoDisableScreensaver(void)
03791 {
03792 if (d && d->screensaver)
03793 {
03794 d->screensaver->Disable();
03795 d->screensaverEnabled = false;
03796 }
03797 }
03798
03799 void MythContext::DoRestoreScreensaver(void)
03800 {
03801 if (d && d->screensaver)
03802 {
03803 d->screensaver->Restore();
03804 d->screensaverEnabled = true;
03805 }
03806 }
03807
03808 void MythContext::DoResetScreensaver(void)
03809 {
03810 if (d && d->screensaver)
03811 {
03812 d->screensaver->Reset();
03813 d->screensaverEnabled = false;
03814 }
03815 }
03816
03817 bool MythContext::GetScreensaverEnabled(void)
03818 {
03819 if (!d)
03820 return false;
03821 return d->screensaverEnabled;
03822 }
03823
03824
03825 void MythContext::LogEntry(const QString &module, int priority,
03826 const QString &message, const QString &details)
03827 {
03828 unsigned int logid;
03829 int howmany;
03830
03831 if (d->m_logenable == -1)
03832 d->LoadLogSettings();
03833 if (d->m_logenable == 1)
03834 {
03835 QString fullMsg = message;
03836 if (!details.isEmpty())
03837 fullMsg += ": " + details;
03838
03839 if (message.left(21) != "Last message repeated")
03840 {
03841 if (fullMsg == d->lastLogStrings[module])
03842 {
03843 d->lastLogCounts[module] += 1;
03844 return;
03845 }
03846 else
03847 {
03848 if (0 < d->lastLogCounts[module])
03849 {
03850 LogEntry(module, priority,
03851 QString("Last message repeated %1 times")
03852 .arg(d->lastLogCounts[module]),
03853 d->lastLogStrings[module]);
03854 }
03855
03856 d->lastLogCounts[module] = 0;
03857 d->lastLogStrings[module] = fullMsg;
03858 }
03859 }
03860
03861
03862 MSqlQuery query(MSqlQuery::InitCon());
03863 query.prepare("INSERT INTO mythlog (module, priority, "
03864 "logdate, host, message, details) "
03865 "values (:MODULE, :PRIORITY, now(), :HOSTNAME, "
03866 ":MESSAGE, :DETAILS );");
03867
03868 query.bindValue(":MODULE", module);
03869 query.bindValue(":PRIORITY", priority);
03870 query.bindValue(":HOSTNAME", d->m_localhostname);
03871 query.bindValue(":MESSAGE", message);
03872 query.bindValue(":DETAILS", details.utf8());
03873
03874 if (!query.exec() || !query.isActive())
03875 MythContext::DBError("LogEntry", query);
03876
03877 if (d->m_logmaxcount > 0)
03878 {
03879 query.prepare("SELECT logid FROM mythlog WHERE "
03880 "module= :MODULE ORDER BY logdate ASC ;");
03881 query.bindValue(":MODULE", module);
03882 if (!query.exec() || !query.isActive())
03883 {
03884 MythContext::DBError("DelLogEntry#1", query);
03885 }
03886 else
03887 {
03888 howmany = query.size();
03889 if (howmany > d->m_logmaxcount)
03890 {
03891 MSqlQuery delquery(MSqlQuery::InitCon());
03892 while (howmany > d->m_logmaxcount)
03893 {
03894 query.next();
03895 logid = query.value(0).toUInt();
03896 delquery.prepare("DELETE FROM mythlog WHERE "
03897 "logid= :LOGID ;");
03898 delquery.bindValue(":LOGID", logid);
03899
03900 if (!delquery.exec() || !delquery.isActive())
03901 {
03902 MythContext::DBError("DelLogEntry#2", delquery);
03903 }
03904 howmany--;
03905 }
03906 }
03907 }
03908 }
03909
03910 if (priority <= d->m_logprintlevel)
03911 {
03912 VERBOSE(VB_IMPORTANT,
03913 QString("%1: %2").arg(module).arg(fullMsg));
03914 }
03915 }
03916 }
03917
03918 void MythContext::addPrivRequest(MythPrivRequest::Type t, void *data)
03919 {
03920 QMutexLocker lockit(&d->m_priv_mutex);
03921 d->m_priv_requests.push(MythPrivRequest(t, data));
03922 d->m_priv_queued.wakeAll();
03923 }
03924
03925 void MythContext::waitPrivRequest() const
03926 {
03927 d->m_priv_mutex.lock();
03928 while (d->m_priv_requests.empty())
03929 d->m_priv_queued.wait(&d->m_priv_mutex);
03930 d->m_priv_mutex.unlock();
03931 }
03932
03933 MythPrivRequest MythContext::popPrivRequest()
03934 {
03935 QMutexLocker lockit(&d->m_priv_mutex);
03936 MythPrivRequest ret_val(MythPrivRequest::PrivEnd, NULL);
03937 if (!d->m_priv_requests.empty()) {
03938 ret_val = d->m_priv_requests.front();
03939 d->m_priv_requests.pop();
03940 }
03941 return ret_val;
03942 }
03943
03944 DatabaseParams MythContext::GetDatabaseParams(void)
03945 {
03946 return d->m_DBparams;
03947 }
03948
03949 bool MythContext::SaveDatabaseParams(const DatabaseParams ¶ms)
03950 {
03951 bool ret = true;
03952 DatabaseParams cur_params = GetDatabaseParams();
03953
03954
03955 if (params.dbHostName != cur_params.dbHostName ||
03956 params.dbHostPing != cur_params.dbHostPing ||
03957 params.dbPort != cur_params.dbPort ||
03958 params.dbUserName != cur_params.dbUserName ||
03959 params.dbPassword != cur_params.dbPassword ||
03960 params.dbName != cur_params.dbName ||
03961 params.dbType != cur_params.dbType ||
03962 params.localEnabled != cur_params.localEnabled ||
03963 params.wolEnabled != cur_params.wolEnabled ||
03964 (params.localEnabled &&
03965 (params.localHostName != cur_params.localHostName)) ||
03966 (params.wolEnabled &&
03967 (params.wolReconnect != cur_params.wolReconnect ||
03968 params.wolRetry != cur_params.wolRetry ||
03969 params.wolCommand != cur_params.wolCommand)))
03970 {
03971 ret = d->WriteSettingsFile(params, true);
03972 if (ret)
03973 {
03974
03975 d->m_DBparams = params;
03976
03977
03978 d->ResetDatabase();
03979 }
03980 }
03981 return ret;
03982 }
03983
03984 void MythContext::addCurrentLocation(QString location)
03985 {
03986 QMutexLocker locker(&locationLock);
03987 if (currentLocation.last() != location)
03988 currentLocation.push_back(location);
03989 }
03990
03991 QString MythContext::removeCurrentLocation(void)
03992 {
03993 QMutexLocker locker(&locationLock);
03994
03995 if (currentLocation.isEmpty())
03996 return QString("UNKNOWN");
03997
03998 QString result = currentLocation.last();
03999 currentLocation.pop_back();
04000 return result;
04001 }
04002
04003 QString MythContext::getCurrentLocation(void)
04004 {
04005 QMutexLocker locker(&locationLock);
04006
04007 if (currentLocation.isEmpty())
04008 return QString("UNKNOWN");
04009
04010 return currentLocation.last();
04011 }
04012
04013 bool MythContext::GetScreenIsAsleep(void)
04014 {
04015 if (!d->screensaver)
04016 return false;
04017 return d->screensaver->Asleep();
04018 }
04019
04022 void MythContext::SetX11Display(const QString &display)
04023 {
04024 x11_display = QDeepCopy<QString>(display);
04025 }
04026
04027 QString MythContext::GetX11Display(void)
04028 {
04029 return QDeepCopy<QString>(x11_display);
04030 }
04031
04032 void MythContext::dispatch(MythEvent &event)
04033 {
04034 if (print_verbose_messages & VB_NETWORK)
04035 VERBOSE(VB_NETWORK, QString("MythEvent: %1").arg(event.Message()));
04036
04037 MythObservable::dispatch(event);
04038 }
04039
04040 void MythContext::dispatchNow(MythEvent &event)
04041 {
04042 if (print_verbose_messages & VB_NETWORK)
04043 VERBOSE(VB_NETWORK, QString("MythEvent: %1").arg(event.Message()));
04044
04045 MythObservable::dispatchNow(event);
04046 }
04047
04048 void MythContext::sendPlaybackStart(void)
04049 {
04050 MythEvent me(QString("PLAYBACK_START %1").arg(GetHostName()));
04051 dispatchNow(me);
04052 }
04053
04054 void MythContext::sendPlaybackEnd(void)
04055 {
04056 MythEvent me(QString("PLAYBACK_END %1").arg(GetHostName()));
04057 dispatchNow(me);
04058 }
04059
04060