00001 #include <unistd.h>
00002
00003 #include <stdlib.h>
00004 #include <unistd.h>
00005 #include <iostream>
00006 #include <cstdlib>
00007
00008
00009 #include <qdir.h>
00010 #include <qapplication.h>
00011
00012
00013 #include <mythtv/mythcontext.h>
00014 #include <mythtv/mythwidgets.h>
00015 #include <mythtv/libmythtv/remoteutil.h>
00016 #include <mythtv/libmythtv/programinfo.h>
00017
00018
00019 #include "exportnativewizard.h"
00020 #include "fileselector.h"
00021 #include "archiveutil.h"
00022 #include "recordingselector.h"
00023 #include "videoselector.h"
00024
00025
00026 const int LAST_PAGE = 2;
00027
00028 ExportNativeWizard::ExportNativeWizard(MythMainWindow *parent, QString window_name,
00029 QString theme_filename, const char *name)
00030 : MythThemedDialog(parent, window_name, theme_filename, name, true)
00031 {
00032 archiveList = NULL;
00033 popupMenu = NULL;
00034 setContext(1);
00035 wireUpTheme();
00036 assignFirstFocus();
00037 updateForeground();
00038
00039 bCreateISO = false;
00040 bDoBurn = false;
00041 bEraseDvdRw = false;
00042 saveFilename = "";
00043
00044 loadConfiguration();
00045 }
00046
00047 ExportNativeWizard::~ExportNativeWizard(void)
00048 {
00049 saveConfiguration();
00050
00051 if (archiveList)
00052 delete archiveList;
00053 }
00054
00055 void ExportNativeWizard::keyPressEvent(QKeyEvent *e)
00056 {
00057 bool handled = false;
00058 QStringList actions;
00059 gContext->GetMainWindow()->TranslateKeyPress("Archive", e, actions);
00060
00061 for (unsigned int i = 0; i < actions.size() && !handled; i++)
00062 {
00063 QString action = actions[i];
00064 handled = true;
00065
00066 if (action == "ESCAPE")
00067 {
00068 done(Rejected);
00069 }
00070 else if (action == "DOWN")
00071 {
00072 if (getCurrentFocusWidget() == archive_list)
00073 {
00074 archive_list->MoveDown(UIListBtnType::MoveItem);
00075 archive_list->refresh();
00076 }
00077 else
00078 nextPrevWidgetFocus(true);
00079 }
00080 else if (action == "UP")
00081 {
00082 if (getCurrentFocusWidget() == archive_list)
00083 {
00084 archive_list->MoveUp(UIListBtnType::MoveItem);
00085 archive_list->refresh();
00086 }
00087 else
00088 nextPrevWidgetFocus(false);
00089 }
00090 else if (action == "PAGEDOWN")
00091 {
00092 if (getCurrentFocusWidget() == archive_list)
00093 {
00094 archive_list->MoveDown(UIListBtnType::MovePage);
00095 archive_list->refresh();
00096 }
00097 }
00098 else if (action == "PAGEUP")
00099 {
00100 if (getCurrentFocusWidget() == archive_list)
00101 {
00102 archive_list->MoveUp(UIListBtnType::MovePage);
00103 archive_list->refresh();
00104 }
00105 }
00106 else if (action == "LEFT")
00107 {
00108 if (getCurrentFocusWidget() == destination_selector)
00109 destination_selector->push(false);
00110 else
00111 nextPrevWidgetFocus(false);
00112 }
00113 else if (action == "RIGHT")
00114 {
00115 if (getCurrentFocusWidget() == destination_selector)
00116 destination_selector->push(true);
00117 else
00118 nextPrevWidgetFocus(true);
00119 }
00120 else if (action == "SELECT")
00121 {
00122 activateCurrent();
00123 }
00124 else if (action == "MENU")
00125 {
00126 showMenu();
00127 }
00128 else
00129 handled = false;
00130 }
00131
00132 if (!handled)
00133 MythThemedDialog::keyPressEvent(e);
00134 }
00135
00136 void ExportNativeWizard::updateSizeBar()
00137 {
00138 long long size = 0;
00139 NativeItem *a;
00140
00141 vector<NativeItem *>::iterator i = archiveList->begin();
00142 for ( ; i != archiveList->end(); i++)
00143 {
00144 a = *i;
00145 size += a->size;
00146 }
00147
00148 usedSpace = size / 1024 / 1024;
00149
00150 UITextType *item;
00151 QString tmpSize;
00152
00153 if (size_bar)
00154 {
00155 size_bar->SetTotal(freeSpace);
00156 size_bar->SetUsed(usedSpace);
00157 }
00158
00159 tmpSize.sprintf("%0d Mb", freeSpace);
00160
00161 item = getUITextType("maxsize");
00162 if (item)
00163 item->SetText(tr(tmpSize));
00164
00165 item = getUITextType("minsize");
00166 if (item)
00167 item->SetText(tr("0 Mb"));
00168
00169 tmpSize.sprintf("%0d Mb", usedSpace);
00170
00171 if (usedSpace > freeSpace)
00172 {
00173 item = getUITextType("currentsize");
00174 if (item)
00175 item->hide();
00176
00177 item = getUITextType("currentsize_error");
00178 if (item)
00179 {
00180 item->show();
00181 item->SetText(tmpSize);
00182 }
00183 }
00184 else
00185 {
00186 item = getUITextType("currentsize_error");
00187 if (item)
00188 item->hide();
00189
00190 item = getUITextType("currentsize");
00191 if (item)
00192 {
00193 item->show();
00194 item->SetText(tmpSize);
00195 }
00196 }
00197
00198 size_bar->refresh();
00199 }
00200
00201 void ExportNativeWizard::wireUpTheme()
00202 {
00203
00204 createISO_check = getUICheckBoxType("makeisoimage_check");
00205 if (createISO_check)
00206 {
00207 connect(createISO_check, SIGNAL(pushed(bool)),
00208 this, SLOT(toggleCreateISO(bool)));
00209 }
00210
00211
00212 doBurn_check = getUICheckBoxType("burntodvdr_check");
00213 if (doBurn_check)
00214 {
00215 connect(doBurn_check, SIGNAL(pushed(bool)),
00216 this, SLOT(toggleDoBurn(bool)));
00217 }
00218 doBurn_text = getUITextType("burntodvdr_text");
00219
00220
00221 eraseDvdRw_check = getUICheckBoxType("erasedvdrw_check");
00222 if (eraseDvdRw_check)
00223 {
00224 connect(eraseDvdRw_check, SIGNAL(pushed(bool)),
00225 this, SLOT(toggleEraseDvdRw(bool)));
00226 }
00227 eraseDvdRw_text = getUITextType("erasedvdrw_text");
00228
00229
00230 next_button = getUITextButtonType("next_button");
00231 if (next_button)
00232 {
00233 next_button->setText(tr("Next"));
00234 connect(next_button, SIGNAL(pushed()), this, SLOT(handleNextPage()));
00235 }
00236
00237
00238 prev_button = getUITextButtonType("prev_button");
00239 if (prev_button)
00240 {
00241 prev_button->setText(tr("Previous"));
00242 connect(prev_button, SIGNAL(pushed()), this, SLOT(handlePrevPage()));
00243 }
00244
00245
00246 cancel_button = getUITextButtonType("cancel_button");
00247 if (cancel_button)
00248 {
00249 cancel_button->setText(tr("Cancel"));
00250 connect(cancel_button, SIGNAL(pushed()), this, SLOT(handleCancel()));
00251 }
00252
00253
00254 destination_selector = getUISelectorType("destination_selector");
00255 if (destination_selector)
00256 {
00257 connect(destination_selector, SIGNAL(pushed(int)),
00258 this, SLOT(setDestination(int)));
00259
00260 for (int x = 0; x < ArchiveDestinationsCount; x++)
00261 destination_selector->addItem(ArchiveDestinations[x].type,
00262 ArchiveDestinations[x].name);
00263 }
00264
00265 destination_text = getUITextType("destination_text");
00266
00267
00268
00269
00270 find_button = getUITextButtonType("find_button");
00271 if (find_button)
00272 {
00273 find_button->setText(tr("Choose File..."));
00274 connect(find_button, SIGNAL(pushed()), this, SLOT(handleFind()));
00275 }
00276
00277 filename_edit = getUIRemoteEditType("filename_edit");
00278 if (filename_edit)
00279 {
00280 filename_edit->createEdit(this);
00281 connect(filename_edit, SIGNAL(loosingFocus()), this,
00282 SLOT(filenameEditLostFocus()));
00283 }
00284
00285 freespace_text = getUITextType("freespace_text");
00286
00287 setDestination(0);
00288
00289 title_text = getUITextType("progtitle");
00290 datetime_text = getUITextType("progdatetime");
00291 description_text = getUITextType("progdescription");
00292 filesize_text = getUITextType("filesize");
00293 nofiles_text = getUITextType("nofiles");
00294
00295 size_bar = getUIStatusBarType("size_bar");
00296
00297 archive_list = getUIListBtnType("archivelist");
00298 if (archive_list)
00299 {
00300 getArchiveList();
00301 connect(archive_list, SIGNAL(itemSelected(UIListBtnTypeItem *)),
00302 this, SLOT(titleChanged(UIListBtnTypeItem *)));
00303 }
00304
00305
00306 addrecording_button = getUITextButtonType("addrecording_button");
00307 if (next_button)
00308 {
00309 addrecording_button->setText(tr("Add Recording"));
00310 connect(addrecording_button, SIGNAL(pushed()), this, SLOT(handleAddRecording()));
00311 }
00312
00313
00314 addvideo_button = getUITextButtonType("addvideo_button");
00315 if (addvideo_button)
00316 {
00317 addvideo_button->setText(tr("Add Video"));
00318 connect(addvideo_button, SIGNAL(pushed()), this, SLOT(handleAddVideo()));
00319 }
00320
00321 buildFocusList();
00322 }
00323
00324 void ExportNativeWizard::titleChanged(UIListBtnTypeItem *item)
00325 {
00326 NativeItem *a;
00327
00328 a = (NativeItem *) item->getData();
00329
00330 if (!a)
00331 return;
00332
00333 if (title_text)
00334 title_text->SetText(a->title);
00335
00336 if (datetime_text)
00337 datetime_text->SetText(a->startDate + " " + a->startTime);
00338
00339 if (description_text)
00340 description_text->SetText(
00341 (a->subtitle != "" ? a->subtitle + "\n" : "") + a->description);
00342
00343 if (filesize_text)
00344 {
00345 filesize_text->SetText(formatSize(a->size / 1024, 2));
00346 }
00347
00348 buildFocusList();
00349 }
00350
00351 void ExportNativeWizard::handleNextPage()
00352 {
00353 if (getContext() == LAST_PAGE && archiveList->size() == 0)
00354 {
00355 MythPopupBox::showOkPopup(gContext->GetMainWindow(), tr("Myth Archive"),
00356 tr("You need to add at least one item to archive!"));
00357 return;
00358 }
00359
00360 if (getContext() == LAST_PAGE)
00361 {
00362 runScript();
00363 done(Accepted);
00364 }
00365 else
00366 setContext(getContext() + 1);
00367
00368 if (next_button)
00369 {
00370 if (getContext() == LAST_PAGE)
00371 next_button->setText(tr("Finish"));
00372 else
00373 next_button->setText(tr("Next"));
00374 }
00375
00376 updateForeground();
00377 buildFocusList();
00378 }
00379
00380 void ExportNativeWizard::handlePrevPage()
00381 {
00382 if (getContext() == 1)
00383 done(Rejected);
00384
00385 if (getContext() > 1)
00386 setContext(getContext() - 1);
00387
00388 if (next_button)
00389 next_button->setText(tr("Next"));
00390
00391 updateForeground();
00392 buildFocusList();
00393 }
00394
00395 void ExportNativeWizard::handleCancel()
00396 {
00397 done(Rejected);
00398 }
00399
00400 void ExportNativeWizard::updateArchiveList(void)
00401 {
00402 archive_list->Reset();
00403
00404 if (archiveList->size() == 0)
00405 {
00406 if (title_text)
00407 title_text->SetText("");
00408
00409 if (datetime_text)
00410 datetime_text->SetText("");
00411
00412 if (description_text)
00413 description_text->SetText("");
00414
00415 if (filesize_text)
00416 filesize_text->SetText("");
00417
00418 if (nofiles_text)
00419 nofiles_text->show();
00420 }
00421 else
00422 {
00423 NativeItem *a;
00424 vector<NativeItem *>::iterator i = archiveList->begin();
00425 for ( ; i != archiveList->end(); i++)
00426 {
00427 a = *i;
00428
00429 UIListBtnTypeItem* item = new UIListBtnTypeItem(archive_list, a->title);
00430 item->setCheckable(false);
00431 item->setData(a);
00432 }
00433
00434 archive_list->SetItemCurrent(archive_list->GetItemFirst());
00435 titleChanged(archive_list->GetItemCurrent());
00436 if (nofiles_text)
00437 nofiles_text->hide();
00438 }
00439
00440 updateSizeBar();
00441 archive_list->refresh();
00442 }
00443
00444 void ExportNativeWizard::getArchiveListFromDB(void)
00445 {
00446 if (!archiveList)
00447 archiveList = new vector<NativeItem*>;
00448
00449 archiveList->clear();
00450
00451 MSqlQuery query(MSqlQuery::InitCon());
00452 query.prepare("SELECT intid, type, title, subtitle, description, size, "
00453 "startdate, starttime, filename, hascutlist "
00454 "FROM archiveitems WHERE type = 'Recording' OR type = 'Video' "
00455 "ORDER BY title, subtitle");
00456 query.exec();
00457 if (query.isActive() && query.numRowsAffected())
00458 {
00459 while (query.next())
00460 {
00461 NativeItem *item = new NativeItem;
00462
00463 item->id = query.value(0).toInt();
00464 item->type = QString::fromUtf8(query.value(1).toString());
00465 item->title = QString::fromUtf8(query.value(2).toString());
00466 item->subtitle = QString::fromUtf8(query.value(3).toString());
00467 item->description = QString::fromUtf8(query.value(4).toString());
00468 item->size = query.value(5).toLongLong();
00469 item->startDate = QString::fromUtf8(query.value(6).toString());
00470 item->startTime = QString::fromUtf8(query.value(7).toString());
00471 item->filename = QString::fromUtf8(query.value(8).toString());
00472 item->hasCutlist = (query.value(9).toInt() > 0);
00473 item->useCutlist = false;
00474 item->editedDetails = false;
00475
00476 archiveList->push_back(item);
00477 }
00478 }
00479 }
00480
00481 void ExportNativeWizard::getArchiveList(void)
00482 {
00483 getArchiveListFromDB();
00484 updateArchiveList();
00485 }
00486
00487 void ExportNativeWizard::loadConfiguration(void)
00488 {
00489 bCreateISO = (gContext->GetSetting("MythNativeCreateISO", "0") == "1");
00490 createISO_check->setState(bCreateISO);
00491
00492 bDoBurn = (gContext->GetSetting("MythNativeBurnDVDr", "1") == "1");
00493 doBurn_check->setState(bDoBurn);
00494
00495 bEraseDvdRw = (gContext->GetSetting("MythNativeEraseDvdRw", "0") == "1");
00496 eraseDvdRw_check->setState(bEraseDvdRw);
00497 }
00498
00499 void ExportNativeWizard::saveConfiguration(void)
00500 {
00501 gContext->SaveSetting("MythNativeCreateISO",
00502 (createISO_check->getState() ? "1" : "0"));
00503 gContext->SaveSetting("MythNativeBurnDVDr",
00504 (doBurn_check->getState() ? "1" : "0"));
00505 gContext->SaveSetting("MythNativeEraseDvdRw",
00506 (eraseDvdRw_check->getState() ? "1" : "0"));
00507 }
00508
00509 void ExportNativeWizard::showMenu()
00510 {
00511 if (popupMenu || getContext() != 2 || archiveList->size() == 0)
00512 return;
00513
00514 popupMenu = new MythPopupBox(gContext->GetMainWindow(),
00515 "popupMenu");
00516
00517 QButton *button;
00518
00519 button = popupMenu->addButton(tr("Remove Item"), this, SLOT(removeItem()));
00520 button->setFocus();
00521 popupMenu->addButton(tr("Cancel"), this, SLOT(closePopupMenu()));
00522
00523 popupMenu->ShowPopup(this, SLOT(closePopupMenu()));
00524 }
00525
00526 void ExportNativeWizard::closePopupMenu(void)
00527 {
00528 if (popupMenu)
00529 {
00530 popupMenu->deleteLater();
00531 popupMenu = NULL;
00532 }
00533 }
00534
00535 void ExportNativeWizard::removeItem()
00536 {
00537 if (!popupMenu)
00538 return;
00539
00540 UIListBtnTypeItem *item = archive_list->GetItemCurrent();
00541 NativeItem *curItem = (NativeItem *) item->getData();
00542
00543 if (!curItem)
00544 return;
00545
00546 MSqlQuery query(MSqlQuery::InitCon());
00547 query.prepare("DELETE FROM archiveitems WHERE filename = :FILENAME;");
00548 query.bindValue(":FILENAME", curItem->filename);
00549 query.exec();
00550 if (query.isActive() && query.numRowsAffected())
00551 {
00552 getArchiveList();
00553 }
00554
00555 closePopupMenu();
00556 }
00557
00558 void ExportNativeWizard::createConfigFile(const QString &filename)
00559 {
00560 QDomDocument doc("NATIVEARCHIVEJOB");
00561
00562 QDomElement root = doc.createElement("nativearchivejob");
00563 doc.appendChild(root);
00564
00565 QDomElement job = doc.createElement("job");
00566 root.appendChild(job);
00567
00568 QDomElement media = doc.createElement("media");
00569 job.appendChild(media);
00570
00571
00572 NativeItem *a;
00573
00574 vector<NativeItem *>::iterator i = archiveList->begin();
00575 for ( ; i != archiveList->end(); i++)
00576 {
00577 a = *i;
00578
00579 QDomElement file = doc.createElement("file");
00580 file.setAttribute("type", a->type.lower() );
00581 file.setAttribute("title", a->title);
00582 file.setAttribute("filename", a->filename);
00583 file.setAttribute("delete", "0");
00584 media.appendChild(file);
00585 }
00586
00587
00588 QDomElement options = doc.createElement("options");
00589 options.setAttribute("createiso", bCreateISO);
00590 options.setAttribute("doburn", bDoBurn);
00591 options.setAttribute("mediatype", archiveDestination.type);
00592 options.setAttribute("dvdrsize", freeSpace);
00593 options.setAttribute("erasedvdrw", bEraseDvdRw);
00594 options.setAttribute("savedirectory", saveFilename);
00595 job.appendChild(options);
00596
00597
00598 QFile f(filename);
00599 if (!f.open(IO_WriteOnly))
00600 {
00601 cout << "ExportNativeWizard::createConfigFile: Failed to open file for writing - "
00602 << filename << endl;
00603 return;
00604 }
00605
00606 QTextStream t(&f);
00607 t << doc.toString(4);
00608 f.close();
00609 }
00610
00611 void ExportNativeWizard::setDestination(int item)
00612 {
00613 if (item < 0 || item > ArchiveDestinationsCount - 1)
00614 item = 0;
00615
00616 destination_no = item;
00617 if (destination_text)
00618 {
00619 destination_text->SetText(ArchiveDestinations[item].description);
00620 }
00621
00622 archiveDestination = ArchiveDestinations[item];
00623
00624 switch(item)
00625 {
00626 case AD_DVD_SL:
00627 case AD_DVD_DL:
00628 filename_edit->hide();
00629 find_button->hide();
00630 eraseDvdRw_check->hide();
00631 eraseDvdRw_text->hide();
00632 doBurn_check->show();
00633 doBurn_text->show();
00634 break;
00635 case AD_DVD_RW:
00636 filename_edit->hide();
00637 find_button->hide();
00638 eraseDvdRw_check->show();
00639 eraseDvdRw_text->show();
00640 doBurn_check->show();
00641 doBurn_text->show();
00642 break;
00643 case AD_FILE:
00644 long long dummy;
00645 ArchiveDestinations[item].freeSpace =
00646 getDiskSpace(filename_edit->getText(), dummy, dummy);
00647
00648 filename_edit->show();
00649 find_button->show();
00650 eraseDvdRw_check->hide();
00651 eraseDvdRw_text->hide();
00652 doBurn_check->hide();
00653 doBurn_text->hide();
00654 break;
00655 }
00656
00657 filename_edit->refresh();
00658 eraseDvdRw_check->refresh();
00659 eraseDvdRw_text->refresh();
00660 find_button->refresh();
00661
00662
00663 if (ArchiveDestinations[item].freeSpace != -1)
00664 {
00665 freespace_text->SetText(formatSize(ArchiveDestinations[item].freeSpace, 2));
00666 freeSpace = ArchiveDestinations[item].freeSpace / 1024;
00667 }
00668 else
00669 {
00670 freespace_text->SetText("Unknown");
00671 freeSpace = 0;
00672 }
00673
00674 buildFocusList();
00675 }
00676
00677 void ExportNativeWizard::handleFind(void)
00678 {
00679 FileSelector selector(FSTYPE_FILE, "/", "*.*", gContext->GetMainWindow(),
00680 "file_selector", "mytharchive-", "file selector");
00681 qApp->unlock();
00682 bool res = (kDialogCodeRejected != selector.exec());
00683
00684 if (res)
00685 {
00686 filename_edit->setText(selector.getSelected());
00687 filenameEditLostFocus();
00688 }
00689 qApp->lock();
00690 }
00691
00692 void ExportNativeWizard::filenameEditLostFocus()
00693 {
00694 long long dummy;
00695 ArchiveDestinations[AD_FILE].freeSpace =
00696 getDiskSpace(filename_edit->getText(), dummy, dummy);
00697
00698 saveFilename = filename_edit->getText();
00699
00700
00701
00702 if (ArchiveDestinations[AD_FILE].freeSpace == -1)
00703 {
00704 QString dir = filename_edit->getText();
00705 int pos = dir.findRev('/');
00706 if (pos > 0)
00707 dir = dir.left(pos);
00708 else
00709 dir = "/";
00710
00711 ArchiveDestinations[AD_FILE].freeSpace = getDiskSpace(dir, dummy, dummy);
00712 }
00713
00714 if (ArchiveDestinations[AD_FILE].freeSpace != -1)
00715 {
00716 freespace_text->SetText(formatSize(ArchiveDestinations[AD_FILE].freeSpace, 2));
00717 freeSpace = ArchiveDestinations[AD_FILE].freeSpace / 1024;
00718 }
00719 else
00720 {
00721 freespace_text->SetText("Unknown");
00722 freeSpace = 0;
00723 }
00724 }
00725
00726 void ExportNativeWizard::runScript()
00727 {
00728 QString tempDir = getTempDirectory();
00729 QString logDir = tempDir + "logs";
00730 QString configDir = tempDir + "config";
00731 QString commandline;
00732
00733
00734 if (QFile::exists(logDir + "/progress.log"))
00735 QFile::remove(logDir + "/progress.log");
00736
00737
00738 if (QFile::exists(logDir + "/mythburncancel.lck"))
00739 QFile::remove(logDir + "/mythburncancel.lck");
00740
00741 createConfigFile(configDir + "/mydata.xml");
00742 commandline = "mytharchivehelper -n " + configDir + "/mydata.xml";
00743 commandline += " > " + logDir + "/progress.log 2>&1 &";
00744
00745 int state = system(commandline);
00746
00747 if (state != 0)
00748 {
00749 MythPopupBox::showOkPopup(gContext->GetMainWindow(), QObject::tr("Myth Archive"),
00750 QObject::tr("It was not possible to create the DVD. "
00751 " An error occured when running the scripts") );
00752 done(Rejected);
00753 return;
00754 }
00755
00756 done(Accepted);
00757 }
00758
00759 void ExportNativeWizard::handleAddRecording()
00760 {
00761 RecordingSelector selector(gContext->GetMainWindow(),
00762 "recording_selector", "mytharchive-", "recording selector");
00763 selector.exec();
00764
00765 getArchiveList();
00766 }
00767
00768 void ExportNativeWizard::handleAddVideo()
00769 {
00770 MSqlQuery query(MSqlQuery::InitCon());
00771 query.prepare("SELECT title FROM videometadata");
00772 query.exec();
00773 if (query.isActive() && query.numRowsAffected())
00774 {
00775 }
00776 else
00777 {
00778 MythPopupBox::showOkPopup(gContext->GetMainWindow(), QObject::tr("Video Selector"),
00779 QObject::tr("You don't have any videos!"));
00780 return;
00781 }
00782
00783 VideoSelector selector(gContext->GetMainWindow(),
00784 "video_selector", "mytharchive-", "video selector");
00785 selector.exec();
00786
00787 getArchiveList();
00788 }