#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "filewidget.h" #include "filesorter.h" #include "filedisplay.h" #include "helper.h" #include "globals.h" FileWidget::FileWidget(QWidget *parent) : QWidget(parent) { setupGui(); } FileWidget::~FileWidget(){ writeHeaderData(); writeSettings(); } void FileWidget::setupGui(){ mDir = new QLineEdit; QCompleter *fsCompleter = new QCompleter(this); QDirModel *dirModel = new QDirModel(this); fsCompleter->setModel(dirModel); fsCompleter->setCompletionMode(QCompleter::PopupCompletion); mDir->setCompleter(fsCompleter); connect(mDir, SIGNAL(returnPressed()), this, SLOT(gatherData())); QPushButton *selDirBtn = new QPushButton(tr("&Browse...")); connect(selDirBtn, SIGNAL(clicked()), this, SLOT(selectDir())); QGroupBox *dirGB = new QGroupBox(tr("Directory")); QHBoxLayout *dirGBL = new QHBoxLayout; dirGBL->addWidget(mDir); dirGBL->addWidget(selDirBtn); dirGB->setLayout(dirGBL); mFilter = new QLineEdit; connect(mFilter, SIGNAL(returnPressed()), this, SLOT(filter())); QPushButton *searchBtn = new QPushButton(tr("Filter")); connect(searchBtn, SIGNAL(clicked()), this, SLOT(filter())); QPushButton *clearBtn = new QPushButton(tr("&Clear")); connect(clearBtn, SIGNAL(clicked()), this, SLOT(clearFilter())); QGroupBox *filterGB = new QGroupBox(tr("Filter")); QHBoxLayout *filterGBL = new QHBoxLayout; filterGBL->addWidget(mFilter); filterGBL->addWidget(clearBtn); filterGBL->addWidget(searchBtn); filterGB->setLayout(filterGBL); mFileView = new QTreeView; mFileView->setSortingEnabled(true); mFileView->setSelectionBehavior(QAbstractItemView::SelectRows); mFileView->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(mFileView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(properties(QModelIndex))); QGroupBox *filesGB = new QGroupBox(tr("Files")); QHBoxLayout *filesGBL = new QHBoxLayout; filesGBL->addWidget(mFileView); filesGB->setLayout(filesGBL); mModel = new QStandardItemModel; mProxy = new VideoSorter; mProxy->setSourceModel(mModel); mFileView->setModel(mProxy); readSettings(); QSqlDatabase mDb = QSqlDatabase::database("shemovdb"); if(!mDb.isOpen()){ mDb.open(); } mQOrigin = QSqlQuery(mDb); mQOrigin.prepare("SELECT COUNT(*) FROM files_origin WHERE cmd5sum = :md5"); mQFiles = QSqlQuery(mDb); mQFiles.prepare("SELECT COUNT(*) FROM files WHERE cmd5sum = :md5"); mFileDisplay = new FileDisplay(this); mMenuBar = new QMenuBar; mToolBar = new QToolBar; mToolBar->setIconSize(QSize(16, 16)); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(mMenuBar); mainLayout->addWidget(mToolBar); mainLayout->addWidget(dirGB); mainLayout->addWidget(filterGB); mainLayout->addWidget(filesGB); setLayout(mainLayout); connect(mFileView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(fileSelectionChanged(QItemSelection,QItemSelection))); createActions(); mMimeData = new QMimeData; mFileView->setFocus(); } void FileWidget::createActions(){ mSelDirA = new QAction(QIcon(":/folder.png"), tr("Select Folder..."), this); mSelDirA->setShortcut(tr("CTRL+O")); connect(mSelDirA, SIGNAL(triggered()), this, SLOT(selectDir())); mRefreshA = new QAction(QIcon(":/refresh.png"), tr("Refresh"), this); mRefreshA->setShortcut(tr("CTRL+R")); connect(mRefreshA, SIGNAL(triggered()), this, SLOT(gatherData())); mDirUpA = new QAction(QIcon(":/up_dick.png"), tr("Dir up"), this); mDirUpA->setShortcut(Qt::Key_Backspace); connect(mDirUpA, SIGNAL(triggered()), this, SLOT(cdUp())); mDirBackA = new QAction(QIcon(":/back_dick.png"), tr("Back"), this); mDirBackA->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Left)); connect(mDirBackA, SIGNAL(triggered()), this, SLOT(cdLast())); mCopyA = new QAction(QIcon(":/edit-copy.png"), tr("Copy"), this); mCopyA->setShortcut(tr("CTRL+C")); connect(mCopyA, SIGNAL(triggered()), this, SLOT(copy())); mCutA = new QAction(QIcon(":/edit-cut.png"), tr("Cut"), this); mCutA->setShortcut(tr("CTRL+X")); connect(mCutA, SIGNAL(triggered()), this, SLOT(cut())); mPasteA = new QAction(QIcon(":/edit-paste.png"), tr("Paste"), this); mPasteA->setShortcut(tr("CTRL+V")); connect(mPasteA, SIGNAL(triggered()), this, SLOT(paste())); mDeleteA = new QAction(QIcon(":/delete.png"), tr("Delete..."), this); mDeleteA->setShortcut(Qt::Key_Delete); connect(mDeleteA, SIGNAL(triggered()), this, SLOT(deleteFiles())); mPropertiesA = new QAction(QIcon(":/diaper.png"), tr("Properties..."), this); mPropertiesA->setShortcut(Qt::Key_Return); connect(mPropertiesA, SIGNAL(triggered()), this, SLOT(properties())); QMenu *fileMenu = new QMenu(tr("&File")); fileMenu->addAction(mSelDirA); fileMenu->addAction(mRefreshA); fileMenu->addSeparator(); fileMenu->addAction(Globals::instance()->action(Globals::QuitAction)); QMenu *editMenu = new QMenu(tr("&Edit")); editMenu->addAction(mDirUpA); editMenu->addAction(mDirBackA); editMenu->addSeparator(); editMenu->addAction(mCopyA); editMenu->addAction(mCutA); editMenu->addAction(mPasteA); editMenu->addAction(mDeleteA); editMenu->addSeparator(); editMenu->addAction(mPropertiesA); editMenu->addSeparator(); editMenu->addAction(Globals::instance()->action(Globals::ConfigAction)); mMenuBar->addMenu(fileMenu); mMenuBar->addMenu(editMenu); mToolBar->addAction(mRefreshA); mToolBar->addAction(mSelDirA); mToolBar->addAction(createSeparator()); mToolBar->addAction(mDirUpA); mToolBar->addAction(mDirBackA); mToolBar->addAction(createSeparator()); mToolBar->addAction(mCopyA); mToolBar->addAction(mCutA); mToolBar->addAction(mPasteA); mToolBar->addAction(mDeleteA); mToolBar->addAction(createSeparator()); mToolBar->addAction(mPropertiesA); mToolBar->addAction(createSeparator()); mToolBar->addAction(Globals::instance()->action(Globals::ConfigAction)); addActions(QList() << mRefreshA << mSelDirA << createSeparator() << mDirUpA << mDirBackA << createSeparator() << mCopyA << mCutA << mPasteA << mDeleteA); } void FileWidget::fileData(const QString &fullPath, const QString &md5){ mFileDisplay->setFileData(fullPath, md5); mFileDisplay->show(); } void FileWidget::selectDir(){ QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory"), QDir::homePath()); if(!dir.isEmpty()){ mDir->setText(QDir::toNativeSeparators(dir)); gatherData(); } } void FileWidget::gatherData(){ QDir d(mDir->text()); QFileInfoList fl = d.entryInfoList(QStringList() << "*", QDir::Files | QDir::Dirs | QDir::NoDot, QDir::Name | QDir::DirsFirst); mModel->clear(); QStandardItem *root = mModel->invisibleRootItem(); mModel->setHorizontalHeaderLabels(QStringList() << QChar(0x26A7) << tr("Name") << tr("MIME") << tr("Duration") << tr("MD5")); QBrush redBrush(Qt::red); QBrush greenBrush(Qt::darkGreen); QMimeDatabase db; qApp->setOverrideCursor(Qt::BusyCursor); QString gatherMsg(tr("Gathering data: %1")); mProgressBar->reset(); mProgressBar->setMinimum(0); mProgressBar->setMaximum(fl.count() - 1); int count = 0; qApp->processEvents(); foreach(QFileInfo fi, fl){ emit statusMessage(gatherMsg.arg(fi.fileName())); QMimeType mime = db.mimeTypeForFile(fi); QList fData; FileAttrs attr = NotPresent; Helper::Duration dur; for(int i = 0; i < ColumnCount; ++i){ QStandardItem *item = new QStandardItem; item->setEditable(false); if(i == Md5Column){ item->setFont(QFont("courier new")); } fData << item; } fData[NameColumn]->setText(fi.fileName()); QString md5(tr("n/a")); QString duration(tr("n/a")); qint64 seconds = 0; if(mime.name().startsWith("video")){ md5 = Helper::md5Sum(fi.absoluteFilePath()); fData[Md5Column]->setText(md5); int haveFile = md5Count(md5); if(haveFile){ fData[IconColumn]->setIcon(QIcon(":/huge_bra.png")); fData[NameColumn]->setForeground(greenBrush); attr = Present; }else{ fData[IconColumn]->setIcon(QIcon(":/chastity_belt.png")); fData[NameColumn]->setForeground(redBrush); attr = NotPresent; } QJsonDocument jDoc = Helper::ffpmegData(fi.absoluteFilePath()); QJsonObject jObj = jDoc.object(); QJsonValue durationV = jObj["format"].toObject()["duration"]; seconds = durationV.toVariant().toDouble(); dur = Helper::Duration(seconds); duration = dur.toString(); }else if(fi.isDir()){ if(fi.fileName() == ".."){ fData[IconColumn]->setIcon(QIcon(":/up_dick.png")); attr = ParentDir; }else{ fData[IconColumn]->setIcon(QIcon(":/folder.png")); attr = Directory; } }else{ fData[IconColumn]->setIcon(QIcon(":/gaping_ass.png")); attr = NoVideo; } fData[DurationColumn]->setText(duration); fData[DurationColumn]->setData(seconds, SecondsRole); fData[Md5Column]->setText(md5); fData[MimeColumn]->setText(mime.name()); if(mime.name() == "inode/directory"){ fData[MimeColumn]->setText("directory"); } for(int i = 0; i < ColumnCount; ++i){ fData[i]->setData(attr, AttrsRole); fData[i]->setData(fi.absoluteFilePath(), FullPathRole); fData[i]->setData(md5, MD5SumRole); } root->appendRow(fData); ++count; mProgressBar->setValue(count); } readHeaderData(); qApp->restoreOverrideCursor(); fileSelectionChanged(QItemSelection(), QItemSelection()); emit statusMessage(QString(tr("Gathering data... DONE!"))); } int FileWidget::md5Count(const QString &md5) { mQOrigin.bindValue(":md5", md5); mQOrigin.exec(); int retval = 0; while(mQOrigin.next()){ retval = mQOrigin.value(0).toInt(); } if(!retval){ mQFiles.bindValue(":md5", md5); mQFiles.exec(); while(mQFiles.next()){ retval = mQFiles.value(0).toInt(); } } return retval; } void FileWidget::readHeaderData(){ QSettings s; QByteArray headers = s.value("video/fileheaders").toByteArray(); if(!headers.isEmpty()){ mFileView->header()->restoreState(headers); } } void FileWidget::writeHeaderData(){ QSettings s; s.setValue("video/fileheaders", mFileView->header()->saveState()); } void FileWidget::readSettings(){ QSettings s; QString dir = s.value("video/searchdir", QDir::toNativeSeparators(QDir::homePath())).toString(); mDir->setText(dir); } void FileWidget::writeSettings(){ QSettings s; s.setValue("video/searchdir", mDir->text()); } void FileWidget::deleteRecursive(const QFileInfo &start){ QString msg = QString(tr("Removed %1")); if(start.isDir()){ QDir curDir(start.absoluteFilePath()); foreach(QFileInfo fi, curDir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)){ if(fi.isDir()){ deleteRecursive(fi); }else{ QFile::remove(fi.absoluteFilePath()); emit statusMessage(msg.arg(fi.absoluteFilePath())); } } QDir dir = start.absoluteDir(); dir.rmdir(start.fileName()); emit statusMessage(msg.arg(start.absoluteFilePath())); }else{ QFile::remove(start.absoluteFilePath()); emit statusMessage(start.absoluteFilePath()); } } QAction *FileWidget::createSeparator(){ QAction *retval = new QAction(this); retval->setSeparator(true); return retval; } void FileWidget::filter(){ mProxy->setFilterRegExp(mFilter->text()); mProxy->setFilterKeyColumn(NameColumn); mProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); } void FileWidget::clearFilter(){ mFilter->clear(); filter(); } void FileWidget::copy(){ mFileAction = Copy; cutOrCopy(); } void FileWidget::cut(){ mFileAction = Cut; cutOrCopy(); } void FileWidget::cutOrCopy(){ mMimeData->clear(); QModelIndexList idxL = mFileView->selectionModel()->selectedRows(NameColumn); QList files; foreach(QModelIndex i, idxL){ QUrl url = QUrl::fromLocalFile(i.data(FullPathRole).toString()); files << url; } mMimeData->setUrls(files); QClipboard *clip = qApp->clipboard(); clip->setMimeData(mMimeData); } void FileWidget::paste(){ QClipboard *clip = qApp->clipboard(); const QMimeData *md = clip->mimeData(); if(md->hasUrls()){ qApp->setOverrideCursor(Qt::WaitCursor); QList urls = md->urls(); int count = 0; mProgressBar->reset(); mProgressBar->setMaximum(urls.count() - 1); foreach (QUrl u, urls){ QString path = u.toLocalFile(); QString newDir = mDir->text(); QFileInfo fi(path); QString newFile = QString("%1/%2").arg(newDir).arg(fi.fileName()); mProgressBar->setValue(++count); if(mFileAction == Copy){ QString msg = QString(tr("Copying %1").arg(path)); emit statusMessage(msg); qApp->processEvents(); QFile::copy(path, newFile); }else if(mFileAction == Cut){ QString msg = QString(tr("Moving %1").arg(path)); emit statusMessage(msg); qApp->processEvents(); QFile::rename(path, newFile); } emit statusMessage("Finished Paste!"); } gatherData(); qApp->restoreOverrideCursor(); } } void FileWidget::fileSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected){ Q_UNUSED(selected); Q_UNUSED(deselected); int count = mFileView->selectionModel()->selectedRows().count(); int total = mModel->rowCount(); QString msg = QString("%1/%2").arg(QString::number(count)).arg(QString::number(total)); emit selectionCountChanged(msg); QModelIndexList idxl = mFileView->selectionModel()->selectedRows(DurationColumn); qint64 seconds = 0; foreach(QModelIndex idx, idxl){ seconds += idx.data(SecondsRole).toLongLong(); } Helper::Duration dur(seconds); emit durationChanged(dur.toString()); } void FileWidget::properties(){ QModelIndexList idxs = mFileView->selectionModel()->selectedRows(); if(!idxs.isEmpty()){ QModelIndex cur = idxs.first(); int attr = cur.data(AttrsRole).toInt(); if(attr != ParentDir && attr != Directory){ mFileView->selectionModel()->select(cur, QItemSelectionModel::Rows); properties(cur); } } } void FileWidget::properties(const QModelIndex &idx){ QModelIndex name = idx; QString mime = idx.sibling(idx.row(), MimeColumn).data().toString(); if(name.column() != NameColumn){ name = idx.sibling(idx.row(), NameColumn); } int attr = name.data(AttrsRole).toInt(); if(attr == ParentDir || attr == Directory){ cd(name.data().toString()); return; } if(mime.startsWith("video")){ fileData(name.data(FullPathRole).toString(), name.data(MD5SumRole).toString()); } } void FileWidget::cd(const QString &to){ QString ld = mDir->text(); QDir cur(mDir->text()); if(cur.cd(to)){ QString newDir = cur.absolutePath(); mDir->setText(QDir::toNativeSeparators(newDir)); mLastDir = ld; gatherData(); }else{ QString msg = QString(tr("Could not cd to %1")).arg(to); emit statusMessage(msg); } } void FileWidget::cdUp(){ cd(".."); } void FileWidget::cdLast(){ cd(mLastDir); } void FileWidget::deleteFiles(){ QModelIndexList selFiles = mFileView->selectionModel()->selectedRows(NameColumn); if(selFiles.isEmpty()){ return; } const QString msg = QString(tr("Really delete %1 file(s)?")).arg(QString::number(selFiles.count())); int retval = QMessageBox::question(this, tr("Delete files..."), msg); if(retval == QMessageBox::Yes){ foreach(QModelIndex idx, selFiles){ QFileInfo fi(idx.data(FullPathRole).toString()); deleteRecursive(fi); } gatherData(); } } void FileWidget::keyPressEvent(QKeyEvent *e){ if(e->key() == Qt::Key_S && (e->modifiers() & Qt::ControlModifier)){ mFilter->setFocus(); mFilter->selectAll(); goto exit; } if(e->key() == Qt::Key_D && (e->modifiers() & Qt::ControlModifier)){ mDir->setFocus(); mDir->selectAll(); goto exit; } if(e->key() == Qt::Key_F && (e->modifiers() & Qt::ControlModifier)){ mFileView->setFocus(); goto exit; } if(mFileView->hasFocus()){ if(e->key() == Qt::Key_Return){ QModelIndex idx = mFileView->selectionModel()->selectedRows(NameColumn).at(0); properties(idx); goto exit; } if(e->key() == Qt::Key_Delete){ deleteFiles(); goto exit; } if(e->key() == Qt::Key_Backspace){ cdUp(); goto exit; } } QWidget::keyPressEvent(e); exit: e->accept(); } void FileWidget::contextMenuEvent(QContextMenuEvent *e){ QMenu contextMenu(this); contextMenu.addActions(actions()); contextMenu.exec(e->globalPos()); }