00001 #include <QCoreApplication>
00002 #include <QFileInfo>
00003 #include <QImageReader>
00004 #include <QString>
00005 #include <QStringList>
00006 #include <QTimer>
00007 #include <QUrl>
00008
00009 #include "mythlogging.h"
00010
00011 #include "mythdialogbox.h"
00012 #include "mythmainwindow.h"
00013 #include "mythfontproperties.h"
00014 #include "mythuiutils.h"
00015 #include "mythuitext.h"
00016 #include "mythuiimage.h"
00017 #include "mythuibuttonlist.h"
00018 #include "mythuibutton.h"
00019 #include "mythuistatetype.h"
00020 #include "mythuifilebrowser.h"
00021 #include "mythcorecontext.h"
00022
00024 MFileInfo::MFileInfo(QString fileName, QString sgDir, bool isDir, qint64 size)
00025 {
00026 init(fileName, sgDir, isDir, size);
00027 }
00028
00029 MFileInfo::~MFileInfo()
00030 {
00031 }
00032
00033 void MFileInfo::init(QString fileName, QString sgDir, bool isDir,
00034 qint64 size)
00035 {
00036 m_fileName = fileName;
00037 m_isRemote = false;
00038 m_isParentDir = false;
00039
00040 if (fileName.startsWith("myth://"))
00041 {
00042 QUrl qurl(fileName);
00043 m_hostName = qurl.host();
00044 m_storageGroup = qurl.userName();
00045 m_storageGroupDir = sgDir;
00046 m_subDir = qurl.path();
00047
00048 if (!qurl.fragment().isEmpty())
00049 m_subDir += "#" + qurl.fragment();
00050
00051 if (m_subDir.startsWith("/"))
00052 m_subDir.remove(0, 1);
00053
00054 m_isRemote = true;
00055
00056 m_isDir = isDir;
00057 m_isFile = !isDir;
00058 m_size = size;
00059 }
00060
00061 if (!fileName.isEmpty())
00062 QFileInfo::setFile(fileName);
00063 }
00064
00065 MFileInfo &MFileInfo::operator=(const MFileInfo &other)
00066 {
00067 QString sgDir = other.storageGroupDir();
00068 bool isDir = other.isDir();
00069 qint64 size = other.size();
00070 init(other.fileName(), sgDir, isDir, size);
00071
00072 return *this;
00073 }
00074
00075 QString MFileInfo::fileName(void) const
00076 {
00077 if (m_isRemote)
00078 return m_fileName;
00079 else
00080 return QFileInfo::fileName();
00081 }
00082
00083 QString MFileInfo::filePath(void) const
00084 {
00085 if (m_isRemote)
00086 return m_fileName;
00087 else
00088 return QFileInfo::filePath();
00089 }
00090
00091 bool MFileInfo::isDir(void) const
00092 {
00093 if (m_isRemote)
00094 return m_isDir;
00095 else
00096 return QFileInfo::isDir();
00097 }
00098
00099 bool MFileInfo::isFile(void) const
00100 {
00101 if (m_isRemote)
00102 return m_isFile;
00103 else
00104 return QFileInfo::isFile();
00105 }
00106
00107 bool MFileInfo::isParentDir(void) const
00108 {
00109 if (m_isRemote)
00110 return m_isParentDir;
00111 else
00112 return (QFileInfo::fileName() == "..");
00113 }
00114
00115 bool MFileInfo::isExecutable(void) const
00116 {
00117 if (m_isRemote)
00118 return false;
00119 else
00120 return QFileInfo::isExecutable();
00121 }
00122
00123 QString MFileInfo::absoluteFilePath(void) const
00124 {
00125 if (m_isRemote)
00126 return m_fileName;
00127 else
00128 return QFileInfo::absoluteFilePath();
00129 }
00130
00131 qint64 MFileInfo::size(void) const
00132 {
00133 if (m_isRemote)
00134 return m_size;
00135 else
00136 return QFileInfo::size();
00137 }
00138
00140
00148 MythUIFileBrowser::MythUIFileBrowser(MythScreenStack *parent,
00149 const QString &startPath)
00150 : MythScreenType(parent, "mythuifilebrowser")
00151 {
00152 m_retObject = NULL;
00153
00154 Init(startPath);
00155
00156 m_typeFilter = (QDir::AllDirs | QDir::Drives | QDir::Files |
00157 QDir::Readable | QDir::Writable | QDir::Executable);
00158 m_nameFilter.clear();
00159 m_nameFilter << "*";
00160
00161 m_previewTimer = new QTimer(this);
00162 m_previewTimer->setSingleShot(true);
00163 connect(m_previewTimer, SIGNAL(timeout()), SLOT(LoadPreview()));
00164 }
00165
00166 MythUIFileBrowser::~MythUIFileBrowser()
00167 {
00168 }
00169
00170 void MythUIFileBrowser::Init(const QString &startPath)
00171 {
00172 if (startPath.startsWith("myth://"))
00173 {
00174 m_isRemote = true;
00175
00176 QUrl qurl(startPath);
00177
00178 if (!qurl.path().isEmpty())
00179 {
00180
00181 m_baseDirectory = gCoreContext->GenMythURL(qurl.host(),
00182 0,
00183 "",
00184 qurl.userName());
00185
00186 }
00187 else
00188 {
00189 m_baseDirectory = startPath;
00190
00191 if (m_baseDirectory.endsWith("/"))
00192 m_baseDirectory.remove(m_baseDirectory.length() - 1, 1);
00193 }
00194
00195 m_subDirectory = "";
00196 m_storageGroupDir = "";
00197 }
00198 else
00199 {
00200 m_isRemote = false;
00201 m_baseDirectory = "";
00202 m_subDirectory = startPath;
00203 }
00204 }
00205
00206 bool MythUIFileBrowser::Create()
00207 {
00208 if (!CopyWindowFromBase("MythFileBrowser", this))
00209 return false;
00210
00211 m_fileList = dynamic_cast<MythUIButtonList *>(GetChild("filelist"));
00212 m_locationEdit = dynamic_cast<MythUITextEdit *>(GetChild("location"));
00213 m_okButton = dynamic_cast<MythUIButton *>(GetChild("ok"));
00214 m_cancelButton = dynamic_cast<MythUIButton *>(GetChild("cancel"));
00215 m_backButton = dynamic_cast<MythUIButton *>(GetChild("back"));
00216 m_homeButton = dynamic_cast<MythUIButton *>(GetChild("home"));
00217 m_previewImage = dynamic_cast<MythUIImage *>(GetChild("preview"));
00218 m_infoText = dynamic_cast<MythUIText *>(GetChild("info"));
00219 m_filenameText = dynamic_cast<MythUIText *>(GetChild("filename"));
00220 m_fullpathText = dynamic_cast<MythUIText *>(GetChild("fullpath"));
00221
00222 if (!m_fileList || !m_locationEdit || !m_okButton || !m_cancelButton)
00223 {
00224 LOG(VB_GENERAL, LOG_ERR, "MythUIFileBrowser: Your theme is missing"
00225 " some UI elements! Bailing out.");
00226 return false;
00227 }
00228
00229 connect(m_fileList, SIGNAL(itemClicked(MythUIButtonListItem *)),
00230 SLOT(PathClicked(MythUIButtonListItem *)));
00231 connect(m_fileList, SIGNAL(itemSelected(MythUIButtonListItem *)),
00232 SLOT(PathSelected(MythUIButtonListItem *)));
00233 connect(m_locationEdit, SIGNAL(LosingFocus()), SLOT(editLostFocus()));
00234 connect(m_okButton, SIGNAL(Clicked()), SLOT(OKPressed()));
00235 connect(m_cancelButton, SIGNAL(Clicked()), SLOT(cancelPressed()));
00236
00237 if (m_backButton)
00238 connect(m_backButton, SIGNAL(Clicked()), SLOT(backPressed()));
00239
00240 if (m_homeButton)
00241 connect(m_homeButton, SIGNAL(Clicked()), SLOT(homePressed()));
00242
00243 BuildFocusList();
00244 updateFileList();
00245
00246 return true;
00247 }
00248
00249 void MythUIFileBrowser::SetReturnEvent(QObject *retobject,
00250 const QString &resultid)
00251 {
00252 m_retObject = retobject;
00253 m_id = resultid;
00254 }
00255
00256 void MythUIFileBrowser::LoadPreview()
00257 {
00258 if (m_previewImage)
00259 m_previewImage->Load();
00260 }
00261
00262 void MythUIFileBrowser::PathSelected(MythUIButtonListItem *item)
00263 {
00264 if (!item)
00265 return;
00266
00267 if (m_previewImage)
00268 m_previewImage->Reset();
00269
00270 MFileInfo finfo = qVariantValue<MFileInfo>(item->GetData());
00271
00272 if (finfo.isParentDir())
00273 {
00274 if (m_infoText)
00275 m_infoText->Reset();
00276
00277 if (m_filenameText)
00278 m_filenameText->Reset();
00279
00280 if (m_fullpathText)
00281 m_fullpathText->Reset();
00282 }
00283 else
00284 {
00285 if (IsImage(finfo.suffix()) && m_previewImage)
00286 {
00287 m_previewImage->SetFilename(finfo.absoluteFilePath());
00288 m_previewTimer->start(250);
00289 }
00290
00291 if (m_infoText)
00292 m_infoText->SetText(FormatSize(finfo.size()));
00293
00294 if (m_filenameText)
00295 m_filenameText->SetText(finfo.fileName());
00296
00297 if (m_fullpathText)
00298 m_fullpathText->SetText(finfo.absoluteFilePath());
00299 }
00300 }
00301
00302 void MythUIFileBrowser::PathClicked(MythUIButtonListItem *item)
00303 {
00304 if (!item)
00305 return;
00306
00307 MFileInfo finfo = qVariantValue<MFileInfo>(item->GetData());
00308
00309 if (finfo.isFile())
00310 {
00311 if (m_retObject)
00312 {
00313 DialogCompletionEvent *dce =
00314 new DialogCompletionEvent(m_id, 0, finfo.filePath(),
00315 item->GetData());
00316 QCoreApplication::postEvent(m_retObject, dce);
00317 }
00318
00319 Close();
00320 return;
00321 }
00322
00323 if (!finfo.isDir())
00324 return;
00325
00326 if (finfo.isParentDir())
00327 {
00328 backPressed();
00329 }
00330 else
00331 {
00332 if (finfo.isRemote())
00333 {
00334 m_subDirectory = finfo.subDir();
00335 m_storageGroupDir = finfo.storageGroupDir();
00336 }
00337 else
00338 {
00339 m_subDirectory = finfo.filePath();
00340 m_storageGroupDir = "";
00341 }
00342 }
00343
00344 updateFileList();
00345 }
00346
00347 bool MythUIFileBrowser::IsImage(QString extension)
00348 {
00349 if (extension.isEmpty())
00350 return false;
00351
00352 extension = extension.toLower();
00353
00354 QList<QByteArray> formats = QImageReader::supportedImageFormats();
00355
00356 if (formats.contains(extension.toAscii()))
00357 return true;
00358
00359 return false;
00360 }
00361
00362 void MythUIFileBrowser::editLostFocus()
00363 {
00364 QString newPath = m_locationEdit->GetText();
00365
00366 Init(newPath);
00367
00368 updateFileList();
00369 }
00370
00371 void MythUIFileBrowser::backPressed()
00372 {
00373 if (m_isRemote)
00374 {
00375 m_subDirectory = m_parentDir;
00376
00377 if (m_subDirectory.startsWith(m_baseDirectory))
00378 {
00379 m_subDirectory.remove(0, m_baseDirectory.length());
00380
00381 if (m_subDirectory.startsWith("/"))
00382 m_subDirectory.remove(0, 1);
00383 }
00384
00385 m_storageGroupDir = m_parentSGDir;
00386 }
00387 else
00388 {
00389
00390 int pos = m_subDirectory.lastIndexOf('/');
00391
00392 if (pos > 0)
00393 m_subDirectory = m_subDirectory.left(pos);
00394 else
00395 m_subDirectory = "/";
00396 }
00397
00398 updateFileList();
00399 }
00400
00401 void MythUIFileBrowser::homePressed()
00402 {
00403 if (m_isRemote)
00404 {
00405 m_subDirectory = "";
00406 m_storageGroupDir = "";
00407 }
00408 else
00409 {
00410 char *home = getenv("HOME");
00411 m_subDirectory = home;
00412 }
00413
00414 updateFileList();
00415 }
00416
00417 void MythUIFileBrowser::OKPressed()
00418 {
00419 MythUIButtonListItem *item = m_fileList->GetItemCurrent();
00420 MFileInfo finfo = qVariantValue<MFileInfo>(item->GetData());
00421
00422 if (m_retObject)
00423 {
00424 QString selectedPath = m_locationEdit->GetText();
00425 DialogCompletionEvent *dce = new DialogCompletionEvent(m_id, 0,
00426 selectedPath,
00427 item->GetData());
00428 QCoreApplication::postEvent(m_retObject, dce);
00429 }
00430
00431 Close();
00432 }
00433
00434 void MythUIFileBrowser::cancelPressed()
00435 {
00436 Close();
00437 }
00438
00439 void MythUIFileBrowser::updateFileList()
00440 {
00441 m_fileList->Reset();
00442
00443 if (m_isRemote)
00444 updateRemoteFileList();
00445 else
00446 updateLocalFileList();
00447 }
00448
00449 void MythUIFileBrowser::updateRemoteFileList()
00450 {
00451 QStringList sgdirlist;
00452 QString sgdir;
00453 QStringList slist;
00454
00455 if (!m_baseDirectory.endsWith("/"))
00456 m_baseDirectory.append("/");
00457
00458 QString dirURL = QString("%1%2").arg(m_baseDirectory)
00459 .arg(m_subDirectory);
00460
00461 if (!GetRemoteFileList(m_baseDirectory, sgdir, sgdirlist))
00462 {
00463 LOG(VB_GENERAL, LOG_ERR, "GetRemoteFileList failed to get "
00464 "Storage Group dirs");
00465 return;
00466 }
00467
00468 if ((sgdirlist.size() == 1) &&
00469 (sgdirlist[0].startsWith("sgdir::")))
00470 {
00471 QStringList tokens = sgdirlist[0].split("::");
00472
00473 m_storageGroupDir = tokens[1];
00474 }
00475
00476 if (!GetRemoteFileList(dirURL, m_storageGroupDir, slist))
00477 {
00478 LOG(VB_GENERAL, LOG_ERR,
00479 QString("GetRemoteFileList failed for '%1' in '%2' SG dir")
00480 .arg(dirURL).arg(m_storageGroupDir));
00481 return;
00482 }
00483
00484 m_locationEdit->SetText(dirURL);
00485
00486 QString displayName;
00487 QString dataName;
00488 QString type;
00489
00490 if ((sgdirlist.size() > 1 && !m_storageGroupDir.isEmpty()) ||
00491 (!m_subDirectory.isEmpty()))
00492 {
00493 displayName = tr("Parent");
00494 type = "upfolder";
00495
00496 m_parentDir = m_baseDirectory;
00497
00498 if (!m_subDirectory.isEmpty())
00499 {
00500 m_parentDir += "/" + m_subDirectory;
00501
00502 int pos = m_parentDir.lastIndexOf('/');
00503
00504 if (pos > 0)
00505 m_parentDir = m_parentDir.left(pos);
00506 }
00507
00508
00509 MFileInfo finfo(m_parentDir, m_storageGroupDir, true);
00510 m_parentSGDir = m_storageGroupDir;
00511
00512 if (m_subDirectory.isEmpty() && m_parentDir == m_baseDirectory)
00513 {
00514 finfo.setSGDir("");
00515 m_parentSGDir = "";
00516 }
00517
00518 MythUIButtonListItem *item = new MythUIButtonListItem(
00519 m_fileList, displayName,
00520 qVariantFromValue(finfo));
00521
00522 item->SetText(QString("0"), "filesize");
00523 item->SetText(m_parentDir, "fullpath");
00524 item->DisplayState(type, "nodetype");
00525
00526 if (m_backButton)
00527 m_backButton->SetEnabled(true);
00528 }
00529 else
00530 {
00531 if (m_backButton)
00532 m_backButton->SetEnabled(false);
00533 }
00534
00535 QStringList::const_iterator it = slist.begin();
00536
00537 while (it != slist.end())
00538 {
00539 QStringList tokens = (*it).split("::");
00540
00541 if (tokens.size() < 2)
00542 {
00543 LOG(VB_GENERAL, LOG_ERR, QString("failed to parse '%1'.").arg(*it));
00544 ++it;
00545 continue;
00546 }
00547
00548 displayName = tokens[1];
00549
00550 if (tokens[0] == "sgdir")
00551 dataName = m_baseDirectory;
00552 else if (m_subDirectory.isEmpty())
00553 dataName = QString("%1%2").arg(m_baseDirectory)
00554 .arg(displayName);
00555 else
00556 dataName = QString("%1%2/%3").arg(m_baseDirectory)
00557 .arg(m_subDirectory).arg(displayName);
00558
00559 MFileInfo finfo(dataName, m_storageGroupDir);
00560
00561 if ((tokens[0] == "dir") &&
00562 (m_typeFilter & (QDir::Dirs | QDir::AllDirs)))
00563 {
00564 type = "folder";
00565 finfo.setIsDir(true);
00566 finfo.setSGDir(m_storageGroupDir);
00567 finfo.setSize(0);
00568 }
00569 else if ((tokens[0] == "sgdir") &&
00570 (m_typeFilter & (QDir::Dirs | QDir::AllDirs)))
00571 {
00572 type = "folder";
00573 finfo.setIsDir(true);
00574 finfo.setSGDir(displayName);
00575 finfo.setSize(0);
00576 }
00577 else if ((tokens[0] == "file") &&
00578 (m_typeFilter & QDir::Files))
00579 {
00580 finfo.setIsDir(false);
00581 finfo.setSize(tokens[2].toInt());
00582
00583 if (IsImage(finfo.suffix()))
00584 type = "image";
00585 else
00586 type = "file";
00587 }
00588 else
00589 {
00590
00591 ++it;
00592 continue;
00593 }
00594
00595 MythUIButtonListItem *item =
00596 new MythUIButtonListItem(m_fileList, displayName,
00597 qVariantFromValue(finfo));
00598
00599 if (finfo.size())
00600 item->SetText(FormatSize(finfo.size()), "filesize");
00601
00602 if (type == "image")
00603 item->SetImage(dataName);
00604
00605 item->SetText(dataName, "fullpath");
00606 item->DisplayState(type, "nodetype");
00607
00608 ++it;
00609 }
00610 }
00611
00612 void MythUIFileBrowser::updateLocalFileList()
00613 {
00614 QDir d;
00615
00616 d.setPath(m_subDirectory);
00617 d.setNameFilters(m_nameFilter);
00618 d.setFilter(m_typeFilter);
00619 d.setSorting(QDir::Name | QDir::DirsFirst | QDir::IgnoreCase);
00620
00621 if (!d.exists())
00622 {
00623 LOG(VB_GENERAL, LOG_ERR,
00624 "MythUIFileBrowser: current directory does not exist!");
00625 m_locationEdit->SetText("/");
00626 m_subDirectory = "/";
00627 d.setPath("/");
00628 }
00629
00630 QFileInfoList list = d.entryInfoList();
00631 bool showBackButton = false;
00632
00633 if (list.isEmpty())
00634 {
00635 MythUIButtonListItem *item = new MythUIButtonListItem(m_fileList,
00636 tr("Parent Directory"));
00637 item->DisplayState("upfolder", "nodetype");
00638 }
00639 else
00640 {
00641 QFileInfoList::const_iterator it = list.begin();
00642 const QFileInfo *fi;
00643
00644 while (it != list.end())
00645 {
00646 fi = &(*it);
00647 MFileInfo finfo(fi->filePath());
00648
00649 if (finfo.fileName() == ".")
00650 {
00651 ++it;
00652 continue;
00653 }
00654
00655 QString displayName = finfo.fileName();
00656 QString type;
00657
00658 if (displayName == "..")
00659 {
00660 if (m_subDirectory.endsWith("/"))
00661 {
00662 ++it;
00663 continue;
00664 }
00665
00666 displayName = tr("Parent");
00667 type = "upfolder";
00668 showBackButton = true;
00669 }
00670 else if (finfo.isDir())
00671 {
00672 type = "folder";
00673 }
00674 else if (finfo.isExecutable())
00675 {
00676 type = "executable";
00677 }
00678 else if (finfo.isFile())
00679 {
00680 type = "file";
00681 }
00682
00683 MythUIButtonListItem *item =
00684 new MythUIButtonListItem(m_fileList, displayName,
00685 qVariantFromValue(finfo));
00686
00687 if (IsImage(finfo.suffix()))
00688 {
00689 item->SetImage(finfo.absoluteFilePath());
00690 type = "image";
00691 }
00692
00693 item->SetText(FormatSize(finfo.size()), "filesize");
00694 item->SetText(finfo.absoluteFilePath(), "fullpath");
00695 item->DisplayState(type, "nodetype");
00696
00697 ++it;
00698 }
00699 }
00700
00701 if (m_backButton)
00702 m_backButton->SetEnabled(showBackButton);
00703
00704 m_locationEdit->SetText(m_subDirectory);
00705 }
00706
00707 QString MythUIFileBrowser::FormatSize(int size)
00708 {
00709 QString filesize("%L1 %2");
00710
00711 if (size < 1000000)
00712 filesize = filesize.arg((double)(size / 1000)).arg("KB");
00713 else if (size < 1000000000)
00714 filesize = filesize.arg((double)(size / 1000000)).arg("MB");
00715 else
00716 filesize = filesize.arg((double)(size / 1000000000)).arg("GB");
00717
00718 return filesize;
00719 }
00720
00721 bool MythUIFileBrowser::GetRemoteFileList(const QString &url,
00722 const QString &sgDir,
00723 QStringList &list)
00724 {
00725 QUrl qurl(url);
00726 QString storageGroup = qurl.userName();
00727
00728 list.clear();
00729
00730 if (storageGroup.isEmpty())
00731 storageGroup = "Default";
00732
00733 list << "QUERY_SG_GETFILELIST";
00734 list << qurl.host();
00735 list << storageGroup;
00736
00737 QString path = sgDir + qurl.path();
00738
00739 if (!qurl.fragment().isEmpty())
00740 path += "#" + qurl.fragment();
00741
00742 list << path;
00743 list << "0";
00744
00745 bool ok = gCoreContext->SendReceiveStringList(list);
00746
00747 if ((list.size() == 1) && (list[0] == "EMPTY LIST"))
00748 list.clear();
00749
00750 return ok;
00751
00752 }
00753
00754