00001 #include <cstdlib>
00002
00003
00004 #include <QApplication>
00005 #include <QDir>
00006 #include <QFileInfo>
00007 #include <QDomDocument>
00008
00009
00010 #include <mythcontext.h>
00011 #include <mythdbcon.h>
00012 #include <mythuihelper.h>
00013 #include <mythmainwindow.h>
00014 #include <mythuitext.h>
00015 #include <mythuitextedit.h>
00016 #include <mythuibutton.h>
00017 #include <mythuibuttonlist.h>
00018 #include <mythdialogbox.h>
00019 #include <mythsystem.h>
00020 #include <exitcodes.h>
00021
00022
00023 #include "importnative.h"
00024 #include "archiveutil.h"
00025 #include "logviewer.h"
00026
00027
00029
00030 static bool loadDetailsFromXML(const QString &filename, FileDetails *details)
00031 {
00032 QDomDocument doc("mydocument");
00033 QFile file(filename);
00034 if (!file.open(QIODevice::ReadOnly))
00035 return false;
00036
00037 if (!doc.setContent(&file))
00038 {
00039 file.close();
00040 return false;
00041 }
00042 file.close();
00043
00044 QString docType = doc.doctype().name();
00045
00046 if (docType == "MYTHARCHIVEITEM")
00047 {
00048 QDomNodeList itemNodeList = doc.elementsByTagName("item");
00049 QString type, dbVersion;
00050
00051 if (itemNodeList.count() < 1)
00052 {
00053 LOG(VB_GENERAL, LOG_ERR,
00054 "Couldn't find an 'item' element in XML file");
00055 return false;
00056 }
00057
00058 QDomNode n = itemNodeList.item(0);
00059 QDomElement e = n.toElement();
00060 type = e.attribute("type");
00061 dbVersion = e.attribute("databaseversion");
00062 if (type == "recording")
00063 {
00064 QDomNodeList nodeList = e.elementsByTagName("recorded");
00065 if (nodeList.count() < 1)
00066 {
00067 LOG(VB_GENERAL, LOG_ERR,
00068 "Couldn't find a 'recorded' element in XML file");
00069 return false;
00070 }
00071
00072 n = nodeList.item(0);
00073 e = n.toElement();
00074 n = e.firstChild();
00075 while (!n.isNull())
00076 {
00077 e = n.toElement();
00078 if (!e.isNull())
00079 {
00080 if (e.tagName() == "title")
00081 details->title = e.text();
00082
00083 if (e.tagName() == "subtitle")
00084 details->subtitle = e.text();
00085
00086 if (e.tagName() == "starttime")
00087 details->startTime = QDateTime::fromString(e.text(), Qt::ISODate);
00088
00089 if (e.tagName() == "description")
00090 details->description = e.text();
00091 }
00092 n = n.nextSibling();
00093 }
00094
00095
00096 n = itemNodeList.item(0);
00097 e = n.toElement();
00098 nodeList = e.elementsByTagName("channel");
00099 if (nodeList.count() < 1)
00100 {
00101 LOG(VB_GENERAL, LOG_ERR,
00102 "Couldn't find a 'channel' element in XML file");
00103 details->chanID = "";
00104 details->chanNo = "";
00105 details->chanName = "";
00106 details->callsign = "";
00107 return false;
00108 }
00109
00110 n = nodeList.item(0);
00111 e = n.toElement();
00112 details->chanID = e.attribute("chanid");
00113 details->chanNo = e.attribute("channum");
00114 details->chanName = e.attribute("name");
00115 details->callsign = e.attribute("callsign");
00116 return true;
00117 }
00118 else if (type == "video")
00119 {
00120 QDomNodeList nodeList = e.elementsByTagName("videometadata");
00121 if (nodeList.count() < 1)
00122 {
00123 LOG(VB_GENERAL, LOG_ERR,
00124 "Couldn't find a 'videometadata' element in XML file");
00125 return false;
00126 }
00127
00128 n = nodeList.item(0);
00129 e = n.toElement();
00130 n = e.firstChild();
00131 while (!n.isNull())
00132 {
00133 e = n.toElement();
00134 if (!e.isNull())
00135 {
00136 if (e.tagName() == "title")
00137 {
00138 details->title = e.text();
00139 details->subtitle = "";
00140 details->startTime = QDateTime();
00141 }
00142
00143 if (e.tagName() == "plot")
00144 {
00145 details->description = e.text();
00146 }
00147 }
00148 n = n.nextSibling();
00149 }
00150
00151 details->chanID = "N/A";
00152 details->chanNo = "N/A";
00153 details->chanName = "N/A";
00154 details->callsign = "N/A";
00155
00156 return true;
00157 }
00158 }
00159
00160 return false;
00161 }
00162
00164
00165 ArchiveFileSelector::ArchiveFileSelector(MythScreenStack *parent) :
00166 FileSelector(parent, NULL, FSTYPE_FILE, "", "*.xml"),
00167 m_nextButton(NULL),
00168 m_prevButton(NULL),
00169 m_progTitle(NULL),
00170 m_progSubtitle(NULL),
00171 m_progStartTime(NULL)
00172 {
00173 m_curDirectory = gCoreContext->GetSetting("MythNativeLoadFilename", "/");
00174 }
00175
00176 ArchiveFileSelector::~ArchiveFileSelector(void)
00177 {
00178 gCoreContext->SaveSetting("MythNativeLoadFilename", m_curDirectory);
00179 }
00180
00181 bool ArchiveFileSelector::Create(void)
00182 {
00183 bool foundtheme = false;
00184
00185
00186 foundtheme = LoadWindowFromXML("mythnative-ui.xml", "archivefile_selector", this);
00187
00188 if (!foundtheme)
00189 return false;
00190
00191 bool err = false;
00192 UIUtilW::Assign(this, m_titleText, "title");
00193 UIUtilE::Assign(this, m_fileButtonList, "filelist", &err);
00194 UIUtilE::Assign(this, m_locationEdit, "location_edit", &err);
00195 UIUtilE::Assign(this, m_backButton, "back_button", &err);
00196 UIUtilE::Assign(this, m_homeButton, "home_button", &err);
00197 UIUtilE::Assign(this, m_nextButton, "next_button", &err);
00198 UIUtilE::Assign(this, m_prevButton, "prev_button", &err);
00199 UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
00200 UIUtilE::Assign(this, m_progTitle, "title_text", &err);
00201 UIUtilE::Assign(this, m_progSubtitle, "subtitle_text", &err);
00202 UIUtilE::Assign(this, m_progStartTime, "starttime_text", &err);
00203
00204 if (err)
00205 {
00206 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'archivefile_selector'");
00207 return false;
00208 }
00209
00210 if (m_titleText)
00211 m_titleText->SetText(tr("Find File To Import"));
00212
00213 connect(m_nextButton, SIGNAL(Clicked()), this, SLOT(nextPressed()));
00214 connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(cancelPressed()));
00215 connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(prevPressed()));
00216
00217 connect(m_locationEdit, SIGNAL(LosingFocus()),
00218 this, SLOT(locationEditLostFocus()));
00219 m_locationEdit->SetText(m_curDirectory);
00220
00221 connect(m_backButton, SIGNAL(Clicked()), this, SLOT(backPressed()));
00222 connect(m_homeButton, SIGNAL(Clicked()), this, SLOT(homePressed()));
00223
00224 connect(m_fileButtonList, SIGNAL(itemSelected(MythUIButtonListItem *)),
00225 this, SLOT(itemSelected(MythUIButtonListItem *)));
00226
00227 connect(m_fileButtonList, SIGNAL(itemClicked(MythUIButtonListItem *)),
00228 this, SLOT(itemClicked(MythUIButtonListItem *)));
00229
00230 BuildFocusList();
00231
00232 SetFocusWidget(m_fileButtonList);
00233
00234 updateSelectedList();
00235 updateFileList();
00236
00237 return true;
00238 }
00239
00240 void ArchiveFileSelector::itemSelected(MythUIButtonListItem *item)
00241 {
00242 m_xmlFile.clear();
00243
00244 if (!item)
00245 return;
00246
00247 FileData *fileData = qVariantValue<FileData*>(item->GetData());
00248 if (!fileData)
00249 return;
00250
00251 if (loadDetailsFromXML(m_curDirectory + "/" + fileData->filename, &m_details))
00252 {
00253 m_xmlFile = m_curDirectory + "/" + fileData->filename;
00254 m_progTitle->SetText(m_details.title);
00255 m_progSubtitle->SetText(m_details.subtitle);
00256 m_progStartTime->SetText(m_details.startTime.toString("dd MMM yy (hh:mm)"));
00257 }
00258 else
00259 {
00260 m_progTitle->Reset();
00261 m_progSubtitle->Reset();
00262 m_progStartTime->Reset();
00263 }
00264 }
00265
00266 void ArchiveFileSelector::nextPressed()
00267 {
00268 if (m_xmlFile == "")
00269 {
00270 ShowOkPopup(tr("The selected item is not a valid archive file!"));
00271 }
00272 else
00273 {
00274 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00275
00276 ImportNative *native = new ImportNative(mainStack, this, m_xmlFile, m_details);
00277
00278 if (native->Create())
00279 mainStack->AddScreen(native);
00280 }
00281 }
00282
00283 void ArchiveFileSelector::prevPressed()
00284 {
00285 Close();
00286 }
00287
00288 void ArchiveFileSelector::cancelPressed()
00289 {
00290 Close();
00291 }
00292
00294
00295 ImportNative::ImportNative(
00296 MythScreenStack *parent, MythScreenType *previousScreen,
00297 const QString &xmlFile, FileDetails details) :
00298 MythScreenType(parent, "ImportNative"),
00299 m_xmlFile(xmlFile),
00300 m_details(details),
00301 m_previousScreen(previousScreen),
00302 m_progTitle_text(NULL),
00303 m_progDateTime_text(NULL),
00304 m_progDescription_text(NULL),
00305 m_chanID_text(NULL),
00306 m_chanNo_text(NULL),
00307 m_chanName_text(NULL),
00308 m_callsign_text(NULL),
00309 m_localChanID_text(NULL),
00310 m_localChanNo_text(NULL),
00311 m_localChanName_text(NULL),
00312 m_localCallsign_text(NULL),
00313 m_searchChanID_button(NULL),
00314 m_searchChanNo_button(NULL),
00315 m_searchChanName_button(NULL),
00316 m_searchCallsign_button(NULL),
00317 m_finishButton(NULL),
00318 m_prevButton(NULL),
00319 m_cancelButton(NULL),
00320 m_isValidXMLSelected(false)
00321 {
00322 }
00323
00324 ImportNative::~ImportNative()
00325 {
00326 }
00327
00328 bool ImportNative::Create(void)
00329 {
00330 bool foundtheme = false;
00331
00332
00333 foundtheme = LoadWindowFromXML("mythnative-ui.xml", "importnative", this);
00334
00335 if (!foundtheme)
00336 return false;
00337
00338 bool err = false;
00339 UIUtilE::Assign(this, m_progTitle_text, "progtitle", &err);
00340 UIUtilE::Assign(this, m_progDateTime_text, "progdatetime", &err);
00341 UIUtilE::Assign(this, m_progDescription_text, "progdescription", &err);
00342
00343 UIUtilE::Assign(this, m_chanID_text, "chanid", &err);
00344 UIUtilE::Assign(this, m_chanNo_text, "channo", &err);
00345 UIUtilE::Assign(this, m_chanName_text, "name", &err);
00346 UIUtilE::Assign(this, m_callsign_text, "callsign", &err);
00347
00348 UIUtilE::Assign(this, m_localChanID_text, "local_chanid", &err);
00349 UIUtilE::Assign(this, m_localChanNo_text, "local_channo", &err);
00350 UIUtilE::Assign(this, m_localChanName_text, "local_name", &err);
00351 UIUtilE::Assign(this, m_localCallsign_text, "local_callsign", &err);
00352
00353 UIUtilE::Assign(this, m_searchChanID_button, "searchchanid_button", &err);
00354 UIUtilE::Assign(this, m_searchChanNo_button, "searchchanno_button", &err);
00355 UIUtilE::Assign(this, m_searchChanName_button, "searchname_button", &err);
00356 UIUtilE::Assign(this, m_searchCallsign_button ,"searchcallsign_button", &err);
00357
00358 UIUtilE::Assign(this, m_finishButton, "finish_button", &err);
00359 UIUtilE::Assign(this, m_prevButton, "prev_button", &err);
00360 UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
00361
00362 if (err)
00363 {
00364 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'importarchive'");
00365 return false;
00366 }
00367
00368 connect(m_finishButton, SIGNAL(Clicked()), this, SLOT(finishedPressed()));
00369 connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(prevPressed()));
00370 connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(cancelPressed()));
00371
00372 connect(m_searchChanID_button, SIGNAL(Clicked()), this, SLOT(searchChanID()));
00373 connect(m_searchChanNo_button, SIGNAL(Clicked()), this, SLOT(searchChanNo()));
00374 connect(m_searchChanName_button, SIGNAL(Clicked()), this, SLOT(searchName()));
00375 connect(m_searchCallsign_button, SIGNAL(Clicked()), this, SLOT(searchCallsign()));
00376
00377 m_progTitle_text->SetText(m_details.title);
00378
00379 m_progDateTime_text->SetText(m_details.startTime.toString("dd MMM yy (hh:mm)"));
00380 m_progDescription_text->SetText(
00381 (m_details.subtitle == "" ? m_details.subtitle + "\n" : "") + m_details.description);
00382
00383 m_chanID_text->SetText(m_details.chanID);
00384 m_chanNo_text->SetText(m_details.chanNo);
00385 m_chanName_text->SetText(m_details.chanName);
00386 m_callsign_text->SetText(m_details.callsign);
00387
00388 findChannelMatch(m_details.chanID, m_details.chanNo,
00389 m_details.chanName, m_details.callsign);
00390
00391 BuildFocusList();
00392
00393 SetFocusWidget(m_finishButton);
00394
00395 return true;
00396 }
00397
00398 bool ImportNative::keyPressEvent(QKeyEvent *event)
00399 {
00400 if (GetFocusWidget()->keyPressEvent(event))
00401 return true;
00402
00403 bool handled = false;
00404 QStringList actions;
00405 handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
00406
00407 for (int i = 0; i < actions.size() && !handled; i++)
00408 {
00409 QString action = actions[i];
00410 handled = true;
00411
00412 if (action == "MENU")
00413 {
00414 }
00415 else
00416 handled = false;
00417 }
00418
00419 if (!handled && MythScreenType::keyPressEvent(event))
00420 handled = true;
00421
00422 return handled;
00423 }
00424
00425 void ImportNative::finishedPressed()
00426
00427 {
00428 if (m_details.chanID != "N/A" && m_localChanID_text->GetText() == "")
00429 {
00430 ShowOkPopup(tr("You need to select a valid channel id!"));
00431 return;
00432 }
00433
00434 QString commandline;
00435 QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
00436 QString chanID = m_localChanID_text->GetText();
00437
00438 if (chanID == "")
00439 chanID = m_details.chanID;
00440
00441 if (tempDir == "")
00442 return;
00443
00444 if (!tempDir.endsWith("/"))
00445 tempDir += "/";
00446
00447 QString logDir = tempDir + "logs";
00448
00449
00450 if (QFile::exists(logDir + "/progress.log"))
00451 QFile::remove(logDir + "/progress.log");
00452
00453 commandline = "mytharchivehelper --importarchive --infile \"" + m_xmlFile +
00454 "\" --chanid " + chanID;
00455 commandline += logPropagateArgs;
00456 if (!logPropagateQuiet())
00457 commandline += " --quiet";
00458 commandline += " > " + logDir + "/progress.log 2>&1 &";
00459
00460 uint flags = kMSRunBackground | kMSDontBlockInputDevs |
00461 kMSDontDisableDrawing;
00462 uint retval = myth_system(commandline, flags);
00463 if (retval != GENERIC_EXIT_RUNNING && retval != GENERIC_EXIT_OK)
00464 {
00465 ShowOkPopup(tr("It was not possible to import the Archive. "
00466 " An error occured when running 'mytharchivehelper'") );
00467 return;
00468 }
00469
00470 showLogViewer();
00471
00472 m_previousScreen->Close();
00473 Close();
00474 }
00475
00476 void ImportNative::prevPressed()
00477 {
00478 Close();
00479 }
00480
00481 void ImportNative::cancelPressed()
00482 {
00483 m_previousScreen->Close();
00484 Close();
00485 }
00486
00487 void ImportNative::findChannelMatch(const QString &chanID, const QString &chanNo,
00488 const QString &name, const QString &callsign)
00489 {
00490
00491
00492
00493 MSqlQuery query(MSqlQuery::InitCon());
00494 query.prepare("SELECT chanid, channum, name, callsign FROM channel "
00495 "WHERE chanid = :CHANID AND channum = :CHANNUM AND name = :NAME "
00496 "AND callsign = :CALLSIGN;");
00497 query.bindValue(":CHANID", chanID);
00498 query.bindValue(":CHANNUM", chanNo);
00499 query.bindValue(":NAME", name);
00500 query.bindValue(":CALLSIGN", callsign);
00501
00502 if (query.exec() && query.next())
00503 {
00504
00505 m_localChanID_text->SetText(query.value(0).toString());
00506 m_localChanNo_text->SetText(query.value(1).toString());
00507 m_localChanName_text->SetText(query.value(2).toString());
00508 m_localCallsign_text->SetText(query.value(3).toString());
00509 return;
00510 }
00511
00512
00513 query.prepare("SELECT chanid, channum, name, callsign FROM channel "
00514 "WHERE callsign = :CALLSIGN;");
00515 query.bindValue(":CALLSIGN", callsign);
00516
00517 if (query.exec() && query.next())
00518 {
00519
00520 m_localChanID_text->SetText(query.value(0).toString());
00521 m_localChanNo_text->SetText(query.value(1).toString());
00522 m_localChanName_text->SetText(query.value(2).toString());
00523 m_localCallsign_text->SetText(query.value(3).toString());
00524 return;
00525 }
00526
00527
00528 query.prepare("SELECT chanid, channum, name, callsign FROM channel "
00529 "WHERE name = :NAME;");
00530 query.bindValue(":NAME", callsign);
00531
00532 if (query.exec() && query.next())
00533 {
00534
00535 m_localChanID_text->SetText(query.value(0).toString());
00536 m_localChanNo_text->SetText(query.value(1).toString());
00537 m_localChanName_text->SetText(query.value(2).toString());
00538 m_localCallsign_text->SetText(query.value(3).toString());
00539 return;
00540 }
00541
00542
00543 m_localChanID_text->Reset();
00544 m_localChanNo_text->Reset();
00545 m_localChanName_text->Reset();
00546 m_localCallsign_text->Reset();
00547 }
00548
00549 void ImportNative::showList(const QString &caption, QString &value,
00550 const char *slot)
00551 {
00552 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00553
00554 MythUISearchDialog *searchDialog = new
00555 MythUISearchDialog(popupStack, caption, m_searchList, true, value);
00556
00557 if (!searchDialog->Create())
00558 {
00559 delete searchDialog;
00560 searchDialog = NULL;
00561 return;
00562 }
00563
00564 connect(searchDialog, SIGNAL(haveResult(QString)), this, slot);
00565
00566 popupStack->AddScreen(searchDialog);
00567 }
00568
00569 void ImportNative::fillSearchList(const QString &field)
00570 {
00571
00572 m_searchList.clear();
00573
00574 QString querystr;
00575 querystr = QString("SELECT %1 FROM channel ORDER BY %2").arg(field).arg(field);
00576
00577 MSqlQuery query(MSqlQuery::InitCon());
00578
00579 if (query.exec(querystr))
00580 {
00581 while (query.next())
00582 {
00583 m_searchList << query.value(0).toString();
00584 }
00585 }
00586 }
00587
00588 void ImportNative::searchChanID()
00589 {
00590 QString s;
00591
00592 fillSearchList("chanid");
00593
00594 s = m_chanID_text->GetText();
00595 showList(tr("Select a channel id"), s, SLOT(gotChanID(QString)));
00596 }
00597
00598 void ImportNative::gotChanID(QString value)
00599 {
00600 MSqlQuery query(MSqlQuery::InitCon());
00601 query.prepare("SELECT chanid, channum, name, callsign "
00602 "FROM channel WHERE chanid = :CHANID;");
00603 query.bindValue(":CHANID", value);
00604
00605 if (query.exec() && query.next())
00606 {
00607 m_localChanID_text->SetText(query.value(0).toString());
00608 m_localChanNo_text->SetText(query.value(1).toString());
00609 m_localChanName_text->SetText(query.value(2).toString());
00610 m_localCallsign_text->SetText(query.value(3).toString());
00611 }
00612 }
00613
00614 void ImportNative::searchChanNo()
00615 {
00616 QString s;
00617
00618 fillSearchList("channum");
00619
00620 s = m_chanNo_text->GetText();
00621 showList(tr("Select a channel number"), s, SLOT(gotChanNo(QString)));
00622 }
00623
00624 void ImportNative::gotChanNo(QString value)
00625 {
00626 MSqlQuery query(MSqlQuery::InitCon());
00627 query.prepare("SELECT chanid, channum, name, callsign "
00628 "FROM channel WHERE channum = :CHANNUM;");
00629 query.bindValue(":CHANNUM", value);
00630
00631 if (query.exec() && query.next())
00632 {
00633 m_localChanID_text->SetText(query.value(0).toString());
00634 m_localChanNo_text->SetText(query.value(1).toString());
00635 m_localChanName_text->SetText(query.value(2).toString());
00636 m_localCallsign_text->SetText(query.value(3).toString());
00637 }
00638 }
00639
00640 void ImportNative::searchName()
00641 {
00642 QString s;
00643
00644 fillSearchList("name");
00645
00646 s = m_chanName_text->GetText();
00647 showList(tr("Select a channel name"), s, SLOT(gotName(QString)));
00648 }
00649
00650 void ImportNative::gotName(QString value)
00651 {
00652 MSqlQuery query(MSqlQuery::InitCon());
00653 query.prepare("SELECT chanid, channum, name, callsign "
00654 "FROM channel WHERE name = :NAME;");
00655 query.bindValue(":NAME", value);
00656
00657 if (query.exec() && query.next())
00658 {
00659 m_localChanID_text->SetText(query.value(0).toString());
00660 m_localChanNo_text->SetText(query.value(1).toString());
00661 m_localChanName_text->SetText(query.value(2).toString());
00662 m_localCallsign_text->SetText(query.value(3).toString());
00663 }
00664 }
00665
00666 void ImportNative::searchCallsign()
00667 {
00668 QString s;
00669
00670 fillSearchList("callsign");
00671
00672 s = m_callsign_text->GetText();
00673 showList(tr("Select a Callsign"), s, SLOT(gotCallsign(QString)));
00674 }
00675
00676 void ImportNative::gotCallsign(QString value)
00677 {
00678 MSqlQuery query(MSqlQuery::InitCon());
00679 query.prepare("SELECT chanid, channum, name, callsign "
00680 "FROM channel WHERE callsign = :CALLSIGN;");
00681 query.bindValue(":CALLSIGN", value);
00682
00683 if (query.exec() && query.next())
00684 {
00685 m_localChanID_text->SetText(query.value(0).toString());
00686 m_localChanNo_text->SetText(query.value(1).toString());
00687 m_localChanName_text->SetText(query.value(2).toString());
00688 m_localCallsign_text->SetText(query.value(3).toString());
00689 }
00690 }