/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version
2 of the License, or (at your option) any later version.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "archivebrowser.h"
#include "archivebrowsermodel.h"
#include "copyworker.h"
#include "smtreeview.h"
#include "smglobals.h"
#include "pictureviewer2.h"
#include "delegates.h"
#include "helper.h"
ArchiveBrowser::ArchiveBrowser(QWidget *parent) : QWidget(parent), mSelectedSize(0), mSelectedItems(0){
//prep
mModel = static_cast(SmGlobals::instance()->model("BrowserModel"));
mProxy = new ArchiveBrowserModelProxy;
mProxy->setSourceModel(mModel);
mTree = new SmTreeView("ui/archivebrowserheaders");
mTree->setModel(mProxy);
mTree->setColumnHidden(ArchiveBrowserModel::GenericId, true);
mTree->setColumnHidden(ArchiveBrowserModel::NodeType, true);
mTree->setItemDelegateForColumn(ArchiveBrowserModel::TotalSize, new SizeDelegate(this));
mTree->setItemDelegateForColumn(ArchiveBrowserModel::FileType, new FileTypeDelegate(this));
mTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
QToolBar *toolBar = new QToolBar;
QWidget *spacer1 = new QWidget;
toolBar->addWidget(spacer1);
spacer1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
QLabel *qualityL = new QLabel(tr("Quality"));
toolBar->addWidget(qualityL);
mQualityFilter = new QComboBox;
toolBar->addWidget(mQualityFilter);
setupQualityFilter();
connect(mQualityFilter, QOverload::of(&QComboBox::currentIndexChanged), mProxy, &ArchiveBrowserModelProxy::setQualityFilter);
toolBar->addSeparator();
mSizeFilter = new QCheckBox(tr("Filter by size"));
connect(mSizeFilter, &QCheckBox::stateChanged, mProxy, &ArchiveBrowserModelProxy::setSizeFilter);
toolBar->addWidget(mSizeFilter);
toolBar->addSeparator();
QAction *refreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh"), this);
connect(refreshA, &QAction::triggered, this, &ArchiveBrowser::refresh);
toolBar->addAction(refreshA);
mTree->addAction(refreshA);
toolBar->addSeparator();
mTree->addAction(Helper::createSeparator(this));
QAction *playSelectedA = new QAction(QIcon(":/dildo.png"), tr("Play selected..."), this);
connect(playSelectedA, &QAction::triggered, this, &ArchiveBrowser::playSelected);
toolBar->addAction(playSelectedA);
mTree->addAction(playSelectedA);
QAction *moveToUSBA = new QAction(QIcon(":/clean_tampon.png"), tr("Move to USB..."), this);
connect(moveToUSBA, &QAction::triggered, this, &ArchiveBrowser::moveToUSB);
toolBar->addAction(moveToUSBA);
mTree->addAction(moveToUSBA);
QAction *moveToBurnA = new QAction(QIcon(":/shackles.png"), tr("Move to burn..."), this);
connect(moveToBurnA, &QAction::triggered, this, &ArchiveBrowser::moveToBurn);
toolBar->addAction(moveToBurnA);
mTree->addAction(moveToBurnA);
toolBar->addSeparator();
mTree->addAction(Helper::createSeparator(this));
QIcon downArrowIcon = Helper::icon(QColor(255,85,255), Qt::white, QChar(0x2b07), true, true);
QAction *expandAllA = new QAction(downArrowIcon, tr("Expand all"), this);
connect(expandAllA, &QAction::triggered, archiveTree(), &SmTreeView::expandAll);
toolBar->addAction(expandAllA);
mTree->addAction(expandAllA);
QIcon upArrowIcon = Helper::icon(QColor(255,85,255), Qt::white, QChar(0x2b06), true, true);
QAction *collapseAllA = new QAction(upArrowIcon, tr("Collapse all"), this);
connect(collapseAllA, &QAction::triggered, archiveTree(), &SmTreeView::collapseAll);
toolBar->addAction(collapseAllA);
mTree->addAction(collapseAllA);
toolBar->addSeparator();
mTree->readHeaderConfig();
QHash hData = mModel->headerData();
QStringList hDataSorted = hData.keys();
std::sort(hDataSorted.begin(), hDataSorted.end());
QActionGroup *hDataAG = new QActionGroup(this);
hDataAG->setExclusive(false);
for(const QString &h : hDataSorted){
QAction *a = new QAction(h, this);
a->setCheckable(true);
a->setData(hData.value(h));
hDataAG->addAction(a);
if(!mTree->header()->isSectionHidden(hData.value(h))){
a->setChecked(true);
}
connect(a, &QAction::triggered, [=] { mTree->toggleHeader(a); });
}
QIcon headerIcon = Helper::icon(QColor(255,85,255), Qt::white, 'H', true, true);
QAction *headerA = new QAction(headerIcon, tr("Show headers"), this);
QMenu *headerMenu = new QMenu;
headerMenu->addActions(hDataAG->actions());
headerA->setMenu(headerMenu);
toolBar->addAction(headerA);
toolBar->addSeparator();
toolBar->addAction(SmGlobals::instance()->globalAction());
QWidget *spacer2 = new QWidget;
spacer2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
toolBar->addWidget(spacer2);
//connect
connect(mTree->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ArchiveBrowser::browserSelectionChanged);
connect(mTree, &SmTreeView::doubleClicked, this, &ArchiveBrowser::itemDoubleClicked);
connect(mModel, &ArchiveBrowserModel::populated, this, &ArchiveBrowser::resetAll);
//make widget
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(toolBar);
mainLayout->addWidget(mTree);
setLayout(mainLayout);
mTree->setSortingEnabled(true);
//copyworker
mUSBProgress = 0;
mCopyWorker = new CopyWorker(this);
connect(mCopyWorker, &CopyWorker::error, this, &ArchiveBrowser::copyError);
connect(mCopyWorker, &CopyWorker::success, this, &ArchiveBrowser::copySuccess);
connect(mCopyWorker, &CopyWorker::file, this, &ArchiveBrowser::setCopyFile);
}
void ArchiveBrowser::browserSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
QModelIndexList selectedIdx = selectedRows(selected);
QModelIndexList deselectedIdx = selectedRows(deselected);
for(const QModelIndex &sel : selectedIdx){
mSelectedSize += sel.data(ArchiveBrowserModel::TotalSizeRole).toDouble();
++mSelectedItems;
mModel->setData(sel, true, ArchiveBrowserModel::SelectedRole);
}
for(const QModelIndex &desel : deselectedIdx){
mSelectedSize -= desel.data(ArchiveBrowserModel::TotalSizeRole).toDouble();
--mSelectedItems;
mModel->setData(desel, false, ArchiveBrowserModel::SelectedRole);
}
emit sizeChanged(mSelectedSize);
emit itemCountChanged(mSelectedItems);
qint64 remaining = DVDSIZE - mSelectedSize;
mProxy->setBytesRemaining(remaining);
}
void ArchiveBrowser::readConfig(){
QSettings s;
QString qualFilter = s.value("ui/browserquality", tr("(none)")).toString();
mQualityFilter->setCurrentText(qualFilter);
}
void ArchiveBrowser::writeSettings(){
mTree->writeHeaderConfig();
QSettings s;
s.setValue("ui/browserquality", mQualityFilter->currentText());
}
void ArchiveBrowser::moveToBurn() {
QModelIndexList sel = mTree->selectionModel()->selectedRows();
if(sel.isEmpty()){
return;
}
QSettings s;
QString destDirS = s.value("paths/burn").toString();
QDir burnDir(destDirS);
if(!burnDir.exists()){
QString msg = QString(tr("Destination directory %1 does not exist!\nBailing out!")).arg(destDirS);
QMessageBox::critical(this, tr("Error"), msg);
return;
}
QString msg = QString(tr("This will do the following:
- Move %1 file(s) to %2
- Update the DVD no. for %1 files
Continue?
")).arg(sel.size()).arg(destDirS);
int retval = QMessageBox::question(this, tr("Question"), msg, QMessageBox::Yes | QMessageBox::No);
if(retval == QMessageBox::Yes){
QList filesToUpdate;
for(const QModelIndex &idx : sel){
QString dirName = idx.data(ArchiveBrowserModel::NameRole).toString();
dirName.replace(' ', '.');
burnDir.mkdir(dirName);
QString burnDirS = QString("%1/%2").arg(destDirS).arg(dirName);
QModelIndex real = mProxy->mapToSource(idx);
QModelIndexList children = mModel->children(real);
for(const QModelIndex &child : children){
QFileInfo current(child.data(ArchiveBrowserModel::FullPathRole).toString());
int type = child.data(ArchiveBrowserModel::FileTypeRole).toInt();
QString destination = QString("%1/%2").arg(burnDirS).arg(current.fileName());
if(type == FT_MOVIE){
QFile::rename(current.absoluteFilePath(), destination);
filesToUpdate << child.data(ArchiveBrowserModel::GenericIdRole).toInt();
}else{
QFile::copy(current.absoluteFilePath(), destination);
}
}
}
mModel->updateDVDNo(filesToUpdate);
mModel->refresh();
mProxy->setBytesRemaining(0);
}
}
void ArchiveBrowser::moveToUSB(){
QModelIndexList sel = mTree->selectionModel()->selectedRows();
if(sel.isEmpty()){
return;
}
QSettings s;
QString destDirS = s.value("paths/usb").toString();
QFileInfo destFi(destDirS);
if(!destFi.exists() || !destFi.isDir()){
QString msg = QString(tr("%1 does not exist or isn't a directory!")).arg(destFi.absoluteFilePath());
QMessageBox::critical(this, tr("Error"), msg);
return;
}
int nextDVDNo = mModel->nextDVDNo();
QString dvdDirS = QString("DVD_%1").arg(QString::number(nextDVDNo));
QDir dest(destDirS);
if(dest.exists(dvdDirS)){
QString msg = QString(tr("Something fishy is going on: %1 already exists in %2!")).arg(dvdDirS).arg(destDirS);
QMessageBox::critical(this, tr("Error"), msg);
return;
}
bool mkdir = dest.mkdir(dvdDirS);
if(!mkdir){
QString msg = QString(tr("Failed to create %1 in %2!")).arg(dvdDirS).arg(destDirS);
QMessageBox::critical(this, tr("Error"), msg);
return;
}
// this one is .../DVD_123
QString finalDir = QString("%1/%2").arg(destDirS).arg(dvdDirS);
QString msg = QString(tr("This will do the following:
- Move %1 file(s) to %2
- Update the DVD no. for %1 files
Continue?
")).arg(sel.size()).arg(finalDir);
int retval = QMessageBox::question(this, tr("Question"), msg, QMessageBox::Yes | QMessageBox::No);
if(retval == QMessageBox::Yes){
mCopyWorker->clear();
QDir finalD(finalDir);
for(const QModelIndex &idx : sel){
QString dirName = idx.data(ArchiveBrowserModel::NameRole).toString();
dirName.replace(' ', '.');
// this one .../DVD_123/series_name
// don't check for success, it may already exist!
finalD.mkdir(dirName);
QModelIndex real = mProxy->mapToSource(idx);
QModelIndexList children = mModel->children(real);
for(const QModelIndex &child : children){
QString source = child.data(ArchiveBrowserModel::FullPathRole).toString();
QFileInfo sFi(source);
QString destination = QString("%1/%2/%3").arg(finalDir).arg(dirName).arg(sFi.fileName());
mCopyWorker->enqueue(source, destination);
mCopyWorker->appendData(source, child.data(ArchiveBrowserModel::FileTypeRole));
mCopyWorker->appendData(source, child.data(ArchiveBrowserModel::GenericIdRole));
}
}
mUSBProgress = new QProgressDialog(this);
connect(mCopyWorker, &CopyWorker::bytesRead, mUSBProgress, &QProgressDialog::setValue);
mUSBProgress->setLabelText(tr("Copying files..."));
mUSBProgress->setMinimum(0);
mUSBProgress->setMaximum(mCopyWorker->max());
mUSBProgress->show();
mCopyWorker->start();
}
}
void ArchiveBrowser::refresh() {
mModel->refresh();
}
void ArchiveBrowser::playSelected(){
QPair pgdata = Helper::programData("movieviewer");
QModelIndexList sel = mTree->selectionModel()->selectedRows();
QStringList movieFiles;
for(const QModelIndex &idx : sel){
QModelIndex real = mProxy->mapToSource(idx);
QModelIndexList children = mModel->children(real);
for(const QModelIndex &ci : children){
if(ci.data(ArchiveBrowserModel::FileTypeRole).toInt() == 1){
movieFiles << ci.data(ArchiveBrowserModel::FullPathRole).toString();
}
}
}
if(movieFiles.isEmpty()){
return;
}
QString prg = pgdata.first;
QStringList args = pgdata.second;
args.append(movieFiles);
QProcess::startDetached(prg, args);
}
void ArchiveBrowser::itemDoubleClicked(QModelIndex cur){
if(cur.data(ArchiveBrowserModel::NodeTypeRole).toInt() == ArchiveBrowserModel::SeriesPartNode){
return;
}
if(cur.data(ArchiveBrowserModel::NodeTypeRole).toInt() == ArchiveBrowserModel::FileNode){
int fileType = cur.data(ArchiveBrowserModel::FileTypeRole).toInt();
PictureViewer2 *picView = SmGlobals::instance()->pictureViewer();
if(fileType == FT_MOVIE){
QPixmap pm = Helper::preview(cur.data(ArchiveBrowserModel::FullPathRole).toString());
picView->setPixmap(pm);
}else{
picView->setFile(cur.data(ArchiveBrowserModel::FullPathRole).toString());
}
picView->show();
}
}
void ArchiveBrowser::setupQualityFilter(){
mQualityFilter->clear();
QList qualities = mModel->availableQualities();
std::sort(qualities.begin(), qualities.end());
QStringList qualityList = QStringList() << tr("(none)");
for(int q : qualities){
qualityList << QString::number(q);
}
mQualityFilter->addItems(qualityList);
}
void ArchiveBrowser::resetAll() {
mTree->selectionModel()->clear();
mSelectedItems = 0;
mSelectedSize = 0;
emit sizeChanged(0);
emit itemCountChanged(0);
}
void ArchiveBrowser::copyError(QString error){
mUSBProgress->hide();
QMessageBox::critical(this, tr("Copy Error"), error);
mCopyWorker->disconnect(mUSBProgress);
QProgressDialog *old = mUSBProgress;
old->deleteLater();
mUSBProgress = 0;
}
void ArchiveBrowser::copySuccess(QString success){
mUSBProgress->hide();
QMessageBox::information(this, tr("Copy Success"), success);
mCopyWorker->disconnect(mUSBProgress);
QProgressDialog *old = mUSBProgress;
old->deleteLater();
mUSBProgress = 0;
QString msg = QString(tr("Delete source files and update database?"));
int q = QMessageBox::question(this, tr("Question"), msg);
if(q == QMessageBox::Yes){
QHash > data = mCopyWorker->data();
QList filesToUpdate;
for(const QString &source : data.keys()){
int ft = data.value(source).at(0).toInt();
if(ft == FT_MOVIE){
QFile::remove(source);
filesToUpdate << data.value(source).at(1).toInt();
}
}
mModel->updateDVDNo(filesToUpdate);
mModel->refresh();
mProxy->setBytesRemaining(0);
emit needFSFreeUpdate();
mSizeFilter->setChecked(false);
}
}
void ArchiveBrowser::setCopyFile(QString file){
QString msg = QString(tr("Copying %1")).arg(file);
mUSBProgress->setLabelText(msg);
}
QModelIndexList ArchiveBrowser::selectedRows(const QItemSelection &sel){
QModelIndexList retval;
QModelIndexList selIdx = sel.indexes();
for(const QModelIndex &idx : selIdx){
QModelIndex real = mProxy->mapToSource(idx);
if(real.column() == 0){
retval << real;
}
}
return retval;
}