00001 #include <unistd.h>
00002 #include <fcntl.h>
00003
00004 #include <iostream>
00005 #include <memory>
00006
00007 #include <QString>
00008 #include <QDir>
00009 #include <QMap>
00010 #include <QApplication>
00011 #include <QFileInfo>
00012
00013 #include "mythconfig.h"
00014 #include "mythcontext.h"
00015 #include "mythdbcon.h"
00016 #include "mythlogging.h"
00017 #include "mythversion.h"
00018 #include "langsettings.h"
00019 #include "mythtranslation.h"
00020 #include "exitcodes.h"
00021 #include "exitprompt.h"
00022 #include "storagegroupeditor.h"
00023 #include "myththemedmenu.h"
00024 #include "mythuihelper.h"
00025 #include "mythdirs.h"
00026 #include "channelscanner_cli.h"
00027 #include "scanwizardconfig.h"
00028 #include "scaninfo.h"
00029 #include "channelimporter.h"
00030 #include "cardutil.h"
00031 #include "dbcheck.h"
00032 #include "videosource.h"
00033 #include "channeleditor.h"
00034 #include "remoteutil.h"
00035 #include "backendsettings.h"
00036 #include "checksetup.h"
00037 #include "startprompt.h"
00038 #include "mythsystemevent.h"
00039 #include "expertsettingseditor.h"
00040 #include "commandlineparser.h"
00041 #include "profilegroup.h"
00042
00043 using namespace std;
00044
00045 ExitPrompter *exitPrompt = NULL;
00046 StartPrompter *startPrompt = NULL;
00047
00048 static MythThemedMenu *menu;
00049 static QString logfile;
00050
00051 class CleanupGuard
00052 {
00053 public:
00054 typedef void (*CleanupFunc)();
00055
00056 public:
00057 CleanupGuard(CleanupFunc cleanFunction) :
00058 m_cleanFunction(cleanFunction) {}
00059
00060 ~CleanupGuard()
00061 {
00062 m_cleanFunction();
00063 }
00064
00065 private:
00066 CleanupFunc m_cleanFunction;
00067 };
00068
00069 static void cleanup()
00070 {
00071 DestroyMythMainWindow();
00072
00073 delete gContext;
00074 gContext = NULL;
00075
00076 delete qApp;
00077 }
00078
00079 static void SetupMenuCallback(void* data, QString& selection)
00080 {
00081 (void)data;
00082
00083 QString sel = selection.toLower();
00084
00085 if (sel == "general")
00086 {
00087 BackendSettings be;
00088 be.exec();
00089 }
00090 else if (sel == "capture cards")
00091 {
00092 CaptureCardEditor cce;
00093 cce.exec();
00094 }
00095 else if (sel == "video sources")
00096 {
00097 VideoSourceEditor vse;
00098 vse.exec();
00099 }
00100 else if (sel == "card inputs")
00101 {
00102 CardInputEditor cie;
00103 cie.exec();
00104 }
00105 else if (sel == "recording profile")
00106 {
00107 ProfileGroupEditor editor;
00108 editor.exec();
00109 }
00110 else if (sel == "channel editor")
00111 {
00112 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00113
00114 ChannelEditor *chanedit = new ChannelEditor(mainStack);
00115
00116 if (chanedit->Create())
00117 mainStack->AddScreen(chanedit);
00118 else
00119 delete chanedit;
00120 }
00121 else if (sel == "storage groups")
00122 {
00123 StorageGroupListEditor sge;
00124 sge.exec();
00125 }
00126 else if (sel == "systemeventeditor")
00127 {
00128 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00129
00130 MythSystemEventEditor *msee = new MythSystemEventEditor(
00131 mainStack, "System Event Editor");
00132
00133 if (msee->Create())
00134 mainStack->AddScreen(msee);
00135 else
00136 delete msee;
00137 }
00138 else if (sel.startsWith("exiting_app"))
00139 {
00140 if (!exitPrompt)
00141 exitPrompt = new ExitPrompter();
00142 exitPrompt->handleExit();
00143 }
00144 else
00145 LOG(VB_GENERAL, LOG_ERR, "Unknown menu action: " + selection);
00146 }
00147
00148 static bool RunMenu(QString themedir, QString themename)
00149 {
00150 QByteArray tmp = themedir.toLocal8Bit();
00151 menu = new MythThemedMenu(
00152 QString(tmp.constData()), "setup.xml",
00153 GetMythMainWindow()->GetMainStack(), "mainmenu", false);
00154
00155 if (menu->foundTheme())
00156 {
00157 menu->setCallback(SetupMenuCallback, gContext);
00158 GetMythMainWindow()->GetMainStack()->AddScreen(menu);
00159 return true;
00160 }
00161
00162 LOG(VB_GENERAL, LOG_ERR, QString("Couldn't use theme '%1'").arg(themename));
00163 delete menu;
00164 menu = NULL;
00165
00166 return false;
00167 }
00168
00169
00170
00171 static bool resetTheme(QString themedir, const QString badtheme)
00172 {
00173 QString themename = DEFAULT_UI_THEME;
00174
00175 if (badtheme == DEFAULT_UI_THEME)
00176 themename = FALLBACK_UI_THEME;
00177
00178 LOG(VB_GENERAL, LOG_ERR,
00179 QString("Overriding broken theme '%1' with '%2'")
00180 .arg(badtheme).arg(themename));
00181
00182 gCoreContext->OverrideSettingForSession("Theme", themename);
00183 themedir = GetMythUI()->FindThemeDir(themename);
00184
00185 MythTranslation::reload();
00186 GetMythUI()->LoadQtConfig();
00187 GetMythMainWindow()->Init();
00188
00189 GetMythMainWindow()->ReinitDone();
00190
00191 return RunMenu(themedir, themename);
00192 }
00193
00194 static int reloadTheme(void)
00195 {
00196 QString themename = gCoreContext->GetSetting("Theme", DEFAULT_UI_THEME);
00197 QString themedir = GetMythUI()->FindThemeDir(themename);
00198 if (themedir.isEmpty())
00199 {
00200 LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find theme '%1'")
00201 .arg(themename));
00202 return GENERIC_EXIT_NO_THEME;
00203 }
00204
00205 MythTranslation::reload();
00206
00207 GetMythMainWindow()->SetEffectsEnabled(false);
00208
00209 GetMythUI()->LoadQtConfig();
00210
00211 menu->Close();
00212 GetMythMainWindow()->Init();
00213
00214 GetMythMainWindow()->ReinitDone();
00215
00216 GetMythMainWindow()->SetEffectsEnabled(true);
00217
00218 if (!RunMenu(themedir, themename) && !resetTheme(themedir, themename))
00219 return GENERIC_EXIT_NO_THEME;
00220
00221 return 0;
00222 }
00223
00224 int main(int argc, char *argv[])
00225 {
00226 QString geometry = QString::null;
00227 QString display = QString::null;
00228 bool doScan = false;
00229 bool doScanList = false;
00230 bool doScanSaveOnly = false;
00231 bool scanInteractive = true;
00232 bool expertMode = false;
00233 uint scanImport = 0;
00234 bool scanFTAOnly = false;
00235 ServiceRequirements scanServiceRequirements = kRequireAV;
00236 uint scanCardId = 0;
00237 QString scanTableName = "atsc-vsb8-us";
00238 QString scanInputName = "";
00239
00240 MythTVSetupCommandLineParser cmdline;
00241 if (!cmdline.Parse(argc, argv))
00242 {
00243 cmdline.PrintHelp();
00244 return GENERIC_EXIT_INVALID_CMDLINE;
00245 }
00246
00247 if (cmdline.toBool("showhelp"))
00248 {
00249 cmdline.PrintHelp();
00250 return GENERIC_EXIT_OK;
00251 }
00252
00253 if (cmdline.toBool("showversion"))
00254 {
00255 cmdline.PrintVersion();
00256 return GENERIC_EXIT_OK;
00257 }
00258
00259 bool quiet = false, use_display = true;
00260 if (cmdline.toBool("scan"))
00261 {
00262 quiet = true;
00263 use_display = false;
00264 }
00265
00266 CleanupGuard callCleanup(cleanup);
00267
00268 #ifdef Q_WS_MACX
00269
00270
00271 QApplication::setDesktopSettingsAware(FALSE);
00272 #endif
00273 new QApplication(argc, argv, use_display);
00274 QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHTV_SETUP);
00275
00276 if (cmdline.toBool("display"))
00277 display = cmdline.toString("display");
00278 if (cmdline.toBool("geometry"))
00279 geometry = cmdline.toString("geometry");
00280
00281 int retval;
00282 QString mask("general");
00283 if ((retval = cmdline.ConfigureLogging(mask, quiet)) != GENERIC_EXIT_OK)
00284 return retval;
00285
00286 if (cmdline.toBool("expert"))
00287 expertMode = true;
00288 if (cmdline.toBool("scanlist"))
00289 doScanList = true;
00290 if (cmdline.toBool("savescan"))
00291 doScanSaveOnly = true;
00292 if (cmdline.toBool("scannoninteractive"))
00293 scanInteractive = false;
00294
00295 if (cmdline.toBool("importscan"))
00296 scanImport = cmdline.toUInt("importscan");
00297 if (cmdline.toBool("ftaonly"))
00298 scanFTAOnly = true;
00299 if (cmdline.toBool("servicetype"))
00300 {
00301 scanServiceRequirements = kRequireNothing;
00302 if (cmdline.toString("servicetype").contains("radio"))
00303 scanServiceRequirements = kRequireAudio;
00304 if (cmdline.toString("servicetype").contains("tv"))
00305 scanServiceRequirements = kRequireAV;
00306 if (cmdline.toString("servicetype").contains("tv+radio") ||
00307 cmdline.toString("servicetype").contains("radio+tv"))
00308 scanServiceRequirements = kRequireAudio;
00309 if (cmdline.toString("servicetype").contains("all"))
00310 scanServiceRequirements = kRequireNothing;
00311 }
00312
00313 if (cmdline.toBool("scan"))
00314 {
00315 scanCardId = cmdline.toUInt("scan");
00316 doScan = true;
00317 }
00318 if (cmdline.toBool("freqtable"))
00319 scanTableName = cmdline.toString("freqtable");
00320 if (cmdline.toBool("inputname"))
00321 scanInputName = cmdline.toString("inputname");
00322
00323 if (!display.isEmpty())
00324 {
00325 MythUIHelper::SetX11Display(display);
00326 }
00327
00328 if (!geometry.isEmpty())
00329 {
00330 MythUIHelper::ParseGeometryOverride(geometry);
00331 }
00332
00333 gContext = new MythContext(MYTH_BINARY_VERSION);
00334
00335 if (!gContext->Init(use_display))
00336 {
00337 LOG(VB_GENERAL, LOG_ERR, "Failed to init MythContext, exiting.");
00338 return GENERIC_EXIT_NO_MYTHCONTEXT;
00339 }
00340
00341 cmdline.ApplySettingsOverride();
00342
00343 if (!GetMythDB()->HaveSchema())
00344 {
00345 if (!InitializeMythSchema())
00346 return GENERIC_EXIT_DB_ERROR;
00347 }
00348
00349 if (use_display)
00350 {
00351 gCoreContext->OverrideSettingForSession("Theme", DEFAULT_UI_THEME);
00352 GetMythUI()->LoadQtConfig();
00353
00354 QString fileprefix = GetConfDir();
00355
00356 QDir dir(fileprefix);
00357 if (!dir.exists())
00358 dir.mkdir(fileprefix);
00359 }
00360
00361 if (doScan)
00362 {
00363 bool okCardID = scanCardId;
00364
00365 QStringList inputnames = CardUtil::GetInputNames(scanCardId);
00366 okCardID &= !inputnames.empty();
00367
00368 if (scanInputName.isEmpty())
00369 scanInputName = CardUtil::GetStartInput(scanCardId);
00370
00371 bool okInputName = inputnames.contains(scanInputName);
00372
00373 doScan = (okCardID && okInputName);
00374
00375 if (!okCardID)
00376 {
00377 cerr << "You must enter a valid cardid to scan." << endl;
00378 vector<uint> cardids = CardUtil::GetCardIDs();
00379 if (cardids.empty())
00380 {
00381 cerr << "But no cards have been defined on this host"
00382 << endl;
00383 return GENERIC_EXIT_INVALID_CMDLINE;
00384 }
00385 cerr << "Valid cards: " << endl;
00386 for (uint i = 0; i < cardids.size(); i++)
00387 {
00388 fprintf(stderr, "%5i: %s %s\n",
00389 cardids[i],
00390 CardUtil::GetRawCardType(cardids[i])
00391 .toAscii().constData(),
00392 CardUtil::GetVideoDevice(cardids[i])
00393 .toAscii().constData());
00394 }
00395 return GENERIC_EXIT_INVALID_CMDLINE;
00396 }
00397
00398 if (!okInputName)
00399 {
00400 cerr << "You must enter a valid input to scan this card."
00401 << endl;
00402 cerr << "Valid inputs: " << endl;
00403 for (int i = 0; i < inputnames.size(); i++)
00404 {
00405 cerr << inputnames[i].toAscii().constData() << endl;
00406 }
00407 return GENERIC_EXIT_INVALID_CMDLINE;
00408 }
00409 }
00410
00411 if (doScan)
00412 {
00413 int ret = 0;
00414 int firstBreak = scanTableName.indexOf("-");
00415 int secondBreak = scanTableName.lastIndexOf("-");
00416 if (!firstBreak || !secondBreak || firstBreak == secondBreak)
00417 {
00418 cerr << "Failed to parse the frequence table parameter "
00419 << scanTableName.toLocal8Bit().constData() << endl
00420 << "Please make sure it is in the format freq_std-"
00421 "modulation-country." << endl;
00422 return GENERIC_EXIT_INVALID_CMDLINE;
00423 }
00424 QString freq_std = scanTableName.mid(0, firstBreak).toLower();
00425 QString mod = scanTableName.mid(
00426 firstBreak+1, secondBreak-firstBreak-1).toLower();
00427 QString tbl = scanTableName.mid(secondBreak+1).toLower();
00428 uint inputid = CardUtil::GetInputID(scanCardId, scanInputName);
00429 uint sourceid = CardUtil::GetSourceID(inputid);
00430 QMap<QString,QString> startChan;
00431 {
00432 ChannelScannerCLI scanner(doScanSaveOnly, scanInteractive);
00433 scanner.Scan(
00434 (freq_std=="atsc") ?
00435 ScanTypeSetting::FullScan_ATSC :
00436 ((freq_std=="dvbt") ?
00437 ScanTypeSetting::FullScan_DVBT :
00438 ScanTypeSetting::FullScan_ATSC),
00439 scanCardId,
00440 scanInputName,
00441 sourceid,
00442 false,
00443 true,
00444 true,
00445 scanFTAOnly,
00446 scanServiceRequirements,
00447
00448 0,
00449 startChan, freq_std, mod, tbl);
00450 ret = qApp->exec();
00451 }
00452 return (ret) ? GENERIC_EXIT_NOT_OK : GENERIC_EXIT_OK;
00453 }
00454
00455 if (doScanList)
00456 {
00457 vector<ScanInfo> scans = LoadScanList();
00458
00459 cout<<" scanid cardid sourceid processed date"<<endl;
00460 for (uint i = 0; i < scans.size(); i++)
00461 {
00462 printf("%5i %6i %8i %8s %20s\n",
00463 scans[i].scanid, scans[i].cardid,
00464 scans[i].sourceid, (scans[i].processed) ? "yes" : "no",
00465 scans[i].scandate.toString().toAscii().constData());
00466 }
00467 cout<<endl;
00468
00469 return GENERIC_EXIT_OK;
00470 }
00471
00472 if (scanImport)
00473 {
00474 vector<ScanInfo> scans = LoadScanList();
00475 cout<<"*** SCAN IMPORT START ***"<<endl;
00476 {
00477 ScanDTVTransportList list = LoadScan(scanImport);
00478 ChannelImporter ci(false, true, true, true, false,
00479 scanFTAOnly, scanServiceRequirements);
00480 ci.Process(list);
00481 }
00482 cout<<"*** SCAN IMPORT END ***"<<endl;
00483 return GENERIC_EXIT_OK;
00484 }
00485
00486 MythTranslation::load("mythfrontend");
00487
00488 QString themename = gCoreContext->GetSetting("Theme", DEFAULT_UI_THEME);
00489 QString themedir = GetMythUI()->FindThemeDir(themename);
00490 if (themedir.isEmpty())
00491 {
00492 LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find theme '%1'")
00493 .arg(themename));
00494 return GENERIC_EXIT_NO_THEME;
00495 }
00496
00497 MythMainWindow *mainWindow = GetMythMainWindow();
00498 mainWindow->Init();
00499 mainWindow->setWindowTitle(QObject::tr("MythTV Setup"));
00500
00501
00502
00503
00504 if (LanguageSelection::prompt())
00505 {
00506 if (!reloadTheme())
00507 return GENERIC_EXIT_NO_THEME;
00508 }
00509
00510 if (!UpgradeTVDatabaseSchema(true))
00511 {
00512 LOG(VB_GENERAL, LOG_ERR, "Couldn't upgrade database to new schema.");
00513 return GENERIC_EXIT_DB_OUTOFDATE;
00514 }
00515
00516
00517
00518 mainWindow->ReloadKeys();
00519
00520 if (!startPrompt)
00521 startPrompt = new StartPrompter();
00522 startPrompt->handleStart();
00523
00524
00525 if (!RunMenu(themedir, themename) && !resetTheme(themedir, themename))
00526 return GENERIC_EXIT_NO_THEME;
00527
00528 ExpertSettingsEditor *expertEditor = NULL;
00529 if (expertMode)
00530 {
00531 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00532 expertEditor =
00533 new ExpertSettingsEditor(mainStack, "Expert Settings Editor");
00534 if (expertEditor->Create())
00535 mainStack->AddScreen(expertEditor);
00536 else
00537 {
00538 delete expertEditor;
00539 expertEditor = NULL;
00540 LOG(VB_GENERAL, LOG_ERR,
00541 "Unable to create expert settings editor window");
00542 return GENERIC_EXIT_OK;
00543 }
00544 }
00545
00546 qApp->exec();
00547 }
00548
00549