/* 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 "filesystemwidget.h" #include "filesystemdirproxy.h" #include "fileview.h" #include "shemoviconprovider.h" #include "filesystemfileproxy.h" #include "helper.h" #include "messagedialog.h" #include "archiveeditdialog.h" FilesystemWidget::FilesystemWidget(QWidget *parent) : QWidget(parent) { mModel = new QFileSystemModel; mModel->setRootPath("/"); mModel->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); mModel->setReadOnly(false); SheMovIconProvider *p = new SheMovIconProvider; mModel->setIconProvider(p); mDirProxy = new FilesystemDirProxy; mDirProxy->setSourceModel(mModel); mDirView = new QTreeView; mDirView->setModel(mDirProxy); mDirView->setColumnHidden(1, true); mDirView->setColumnHidden(2, true); mDirView->setColumnHidden(3, true); mDirView->setRootIsDecorated(false); mDirView->setSelectionMode(QAbstractItemView::SingleSelection); mDirView->setEditTriggers(QAbstractItemView::NoEditTriggers); mDirView->setSortingEnabled(true); mDirView->sortByColumn(0, Qt::AscendingOrder); mFileView = new FileView; mFileProxy = new FilesystemFileProxy; mFileProxy->setSourceModel(mModel); mFileView->setModel(mFileProxy); mFileView->setSortingEnabled(true); mFileView->sortByColumn(0, Qt::AscendingOrder); mFileView->setItemsExpandable(false); mFileView->setSelectionMode(QAbstractItemView::ExtendedSelection); mFileView->setEditTriggers(QAbstractItemView::NoEditTriggers); QString title = QString(tr("%1 - Rename file")).arg(qApp->applicationName()); mRenameDialog = new MessageDialog(tr("Rename dummy to:"), title, this); connect(mRenameDialog, SIGNAL(accepted()), this, SLOT(doRenameFile())); QWidget *fileWidget = new QWidget; QHBoxLayout *directoryEdit = new QHBoxLayout; QLabel *dirLabel = new QLabel(tr("&Directory")); mDirEdit = new QLineEdit; QCompleter *completer = new QCompleter(this); completer->setModel(mModel); completer->setCompletionMode(QCompleter::PopupCompletion); mDirEdit->setCompleter(completer); dirLabel->setBuddy(mDirEdit); directoryEdit->addWidget(dirLabel); directoryEdit->addWidget(mDirEdit); QVBoxLayout *fwLayout = new QVBoxLayout; fwLayout->addLayout(directoryEdit); fwLayout->addWidget(mFileView); fileWidget->setLayout(fwLayout); connect(mDirView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(directoryChanged(const QModelIndex &, const QModelIndex &))); connect(mDirView, SIGNAL(expanded(QModelIndex)), this, SLOT(dirExpanded(QModelIndex))); connect(mDirView, SIGNAL(collapsed(QModelIndex)), this, SLOT(dirCollapsed(QModelIndex))); connect(mFileView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(fileViewActivated(const QModelIndex &))); connect(mFileView, SIGNAL(enterPressed(const QModelIndex &)), this, SLOT(fileViewActivated(const QModelIndex &))); connect(mFileView, SIGNAL(upDir()), this, SLOT(parentDir())); connect(mDirEdit, SIGNAL(returnPressed()), this, SLOT(directoryEdited())); connect(mFileView, SIGNAL(delFiles()), this, SLOT(deleteFiles())); mFileView->resizeColumnToContents(0); QVBoxLayout *mainLayout = new QVBoxLayout; QSplitter *splitter = new QSplitter; splitter->addWidget(mDirView); splitter->addWidget(fileWidget); splitter->setStretchFactor(0, 1); splitter->setStretchFactor(1, 2); mainLayout->addWidget(splitter); setLayout(mainLayout); } void FilesystemWidget::setArchiveDialog(ArchiveEditDialog *dlg){ mAEDialog = dlg; } void FilesystemWidget::directoryChanged(const QModelIndex &selected, const QModelIndex & /* deselected */){ QModelIndex real = mDirProxy->mapToSource(selected); if(!real.isValid()){ return; } mModel->setRootPath(mModel->filePath(real)); mDirEdit->setText(mModel->filePath(real)); setWindowTitle(mModel->filePath(real)); mFileView->selectionModel()->clear(); mFileView->setRootIndex(mFileProxy->mapFromSource(real)); } void FilesystemWidget::directoryEdited(){ QString path = mDirEdit->text(); if(path.isEmpty()){ return; } QModelIndex index = mModel->index(path); if(index.isValid()){ mDirView->setCurrentIndex(mDirProxy->mapFromSource(index)); } mFileView->setFocus(Qt::ActiveWindowFocusReason); } void FilesystemWidget::fileViewActivated(const QModelIndex &idx){ QModelIndex real = mFileProxy->mapToSource(idx); if(mModel->isDir(real)){ fileView()->selectionModel()->select(idx, QItemSelectionModel::Deselect); mDirView->setCurrentIndex(mDirProxy->mapFromSource(real)); return; } QString path = mModel->filePath(real); QString mt = Helper::mimeType(path); QStringList programArgs; QString program; if(mt.toLower().startsWith("video")){ QPair data = programData("movieviewer", QString()); if(data.first.isEmpty()){ QMessageBox::critical(this, tr("Error"), tr("No viedeo viewer configured.")); return; } program = data.first; programArgs = data.second; } if(mt.toLower().startsWith("image")){ QPair data = programData("pictureviewer", QString()); if(data.first.isEmpty()){ QMessageBox::critical(this, tr("Error"), tr("No picture viewer configured.")); return; } program = data.first; programArgs = data.second; } programArgs << path; QProcess::startDetached(program, programArgs); } void FilesystemWidget::parentDir(){ QModelIndex idx = mDirView->currentIndex(); if(idx.parent().isValid()){ mDirView->setCurrentIndex(idx.parent()); } } void FilesystemWidget::deleteFiles(){ QSortFilterProxyModel *proxy = static_cast(mFileView->model()); QModelIndexList selected = mFileView->selectionModel()->selectedRows(); int count(0); if(!selected.isEmpty()){ count = selected.count(); QString message = QString(tr("Really delete %1 files?")).arg(QString::number(selected.count())); int retval = QMessageBox::question(this, tr("Question"), message, QMessageBox::Yes | QMessageBox::No); if(retval == QMessageBox::Yes){ foreach(QModelIndex idx, selected){ QModelIndex real = proxy->mapToSource(idx); deleteRecursive(mModel->fileInfo(real)); } } mFileView->selectionModel()->clearSelection(); }else{ count = 1; QModelIndex cur = mFileView->currentIndex(); QModelIndex real = proxy->mapToSource(cur); QString message = QString(tr("Really delete %1?")).arg(mModel->fileName(real)); int retval = QMessageBox::question(this, tr("Question"), message, QMessageBox::Yes | QMessageBox::No); if(retval == QMessageBox::Yes){ deleteRecursive(mModel->fileInfo(real)); } } QString message = QString(tr("Deleted %1 file(s)")).arg(count); emit statusbarMessage(message); } void FilesystemWidget::copyFiles(){ QModelIndexList selected = mFileView->selectionModel()->selectedRows(); if(selected.isEmpty()){ emit statusbarMessage(tr("No files selected!")); return; } QSortFilterProxyModel *proxy = static_cast(mFileView->model()); QModelIndex rootIndex = proxy->mapToSource(mFileView->rootIndex()); QFileInfo root = mModel->fileInfo(rootIndex); QString message = QString(tr("Really copy %1 files to %2?")).arg(selected.count()).arg(root.absoluteFilePath()); int retval = QMessageBox::question(this, tr("Question"), message, QMessageBox::Yes | QMessageBox::No); if(retval == QMessageBox::Yes){ int files(0), dirs(0), failed(0); foreach(QModelIndex idx, selected){ QModelIndex real = proxy->mapToSource(idx); QFileInfo info = mModel->fileInfo(real); if(info.isDir()){ ++dirs; copyRecursive(info, root.absoluteFilePath()); }else{ QString newFile = QString("%1/%2").arg(root.absoluteFilePath()).arg(info.fileName()); if(QFile::copy(info.absoluteFilePath(), newFile)){ ++files; }else{ ++failed; } } } QString message = QString(tr("Successfully copied %1 files and %2 directories, %3 errors")).arg(files).arg(dirs).arg(failed); statusbarMessage(message); mFileView->selectionModel()->clearSelection(); } } void FilesystemWidget::moveFiles(){ QModelIndexList selected = mFileView->selectionModel()->selectedRows(); if(selected.isEmpty()){ emit statusbarMessage(tr("No files selected!")); return; } QSortFilterProxyModel *proxy = static_cast(mFileView->model()); QModelIndex rootIndex = proxy->mapToSource(mFileView->rootIndex()); QFileInfo root = mModel->fileInfo(rootIndex); QString message = QString(tr("Really move %1 file(s) to %2?")).arg(selected.count()).arg(root.absoluteFilePath()); int retval = QMessageBox::question(this, tr("Question"), message, QMessageBox::Yes | QMessageBox::No); if(retval == QMessageBox::Yes){ int success(0), failed(0); foreach(QModelIndex cur, selected){ QModelIndex real = proxy->mapToSource(cur); QFileInfo info = mModel->fileInfo(real); QString dest = QString("%1/%2").arg(root.absoluteFilePath()).arg(info.fileName()); if(QFile::rename(info.absoluteFilePath(), dest)){ ++success; }else{ ++failed; } } QString message = QString(tr("Successfully moved %1 file(s), %2 errors")).arg(success).arg(failed); emit statusbarMessage(message); mFileView->selectionModel()->clearSelection(); } } void FilesystemWidget::renameFile(){ QModelIndex cur = mFileView->currentIndex(); if(!cur.isValid()){ emit statusbarMessage(tr("Nothing to rename!")); return; } QSortFilterProxyModel *proxy = static_cast(mFileView->model()); QModelIndex source = proxy->mapToSource(cur); QFileInfo info = mModel->fileInfo(source); QString text = QString(tr("Rename %1 to:")).arg(info.fileName()); mRenameDialog->setMessage(text); QString newFilename = info.fileName().toLower(); QRegExp re2("['#()&%!]+"); newFilename.replace(re2, " "); QRegExp re1("\\s{2,}"); newFilename.replace(re1, " "); newFilename.replace(' ', '.'); mRenameDialog->setText(newFilename); mRenameDialog->exec(); } void FilesystemWidget::renameCover(const QString &infix){ if(mTemplate.isEmpty()){ emit statusbarMessage(tr("No template set!")); return; } QModelIndex idx = mFileView->currentIndex(); QSortFilterProxyModel *proxy = static_cast(mFileView->model()); QModelIndex real = proxy->mapToSource(idx); QFileInfo info = mModel->fileInfo(real); QString newFilename = QString("%1/%2.%3.%4").arg(info.absolutePath()).arg(mTemplate).arg(infix).arg(info.suffix()); QFileInfo nf(newFilename); if(nf.exists()){ QString message = QString(tr("New file %1 already exists!")).arg(newFilename); statusbarMessage(message); return; } QString question = QString(tr("Really rename %1 to %2?")).arg(info.absoluteFilePath()).arg(nf.fileName()); int retval = QMessageBox::question(this, tr("Question"), question, QMessageBox::Yes | QMessageBox::No); if(retval == QMessageBox::Yes){ if(QFile::rename(info.absoluteFilePath(), newFilename)){ QString message = QString(tr("Successfully renamed %1 to %2")).arg(info.fileName()).arg(nf.fileName()); emit statusbarMessage(message); }else{ QString message = QString(tr("Failed to rename %1 to %2")).arg(info.fileName()).arg(nf.fileName()); emit statusbarMessage(message); } } } void FilesystemWidget::setTemplate(){ QModelIndex idx = mFileView->currentIndex(); if(idx.column() != 0){ idx = mFileView->model()->index(idx.row(), 0); } //since we only need the filename, no need to map it to anything! QFileInfo info(idx.data().toString()); mTemplate = info.completeBaseName().toLower(); emit newTemplate(mTemplate); } void FilesystemWidget::archiveFiles(){ QModelIndexList selected = fileView()->selectionModel()->selectedRows(); if(selected.isEmpty()){ return; } QSortFilterProxyModel *proxy = static_cast(fileView()->model()); QStringList files; foreach(QModelIndex idx, selected){ QModelIndex src = proxy->mapToSource(idx); files << mModel->filePath(src); } mAEDialog->setFiles(files); mAEDialog->show(); mAEDialog->raise(); mAEDialog->activateWindow(); } void FilesystemWidget::playSelected(const QString &player){ QStringList files = selectedFiles(); if(files.isEmpty()){ statusbarMessage(tr("Nothing selected.")); return; } QPair data = programData("movieviewer", player); if(data.first.isEmpty()){ data = programData("pictureviewer", player); if(data.first.isEmpty()){ QString message = QString(tr("Cannot find program %1.")).arg(player); QMessageBox::critical(this, tr("Error"), message); return; } } QString program = data.first; QStringList programArgs(data.second); programArgs << files; QProcess::startDetached(program, programArgs); } void FilesystemWidget::readSettings(){ QSettings s; QStringList expandedDirs = s.value("paths/expandeddirs").toStringList(); if(expandedDirs.isEmpty()){ expandedDirs << QDir::homePath(); } foreach(QString p, expandedDirs){ QModelIndex idx = mModel->index(p); if(idx.isValid()){ QModelIndex pidx = mDirProxy->mapFromSource(idx); if(pidx.isValid()){ mDirView->setExpanded(pidx, true); } } } QString selectedDir = s.value("paths/selecteddir").toString(); if(!selectedDir.isEmpty()){ QModelIndex diridx = mModel->index(selectedDir); if(diridx.isValid()){ QModelIndex pidx = mDirProxy->mapFromSource(diridx); mDirView->selectionModel()->setCurrentIndex(pidx, QItemSelectionModel::ClearAndSelect); } } } void FilesystemWidget::writeSettings(){ QSettings s; s.setValue("paths/expandeddirs", mExpandedDirs); QModelIndex currentDir = mDirView->selectionModel()->currentIndex(); if(currentDir.isValid()){ QModelIndex real = mDirProxy->mapToSource(currentDir); QString dir = mModel->filePath(real); s.setValue("paths/selecteddir", dir); } } void FilesystemWidget::setWindowTitle(const QString &dir){ mWindowTitle = QString("%1 - %2").arg(qApp->applicationName()).arg(dir); emit windowTitle(mWindowTitle); } void FilesystemWidget::deleteRecursive(const QFileInfo &start){ if(start.isDir()){ QDir curDir = QDir(start.absoluteFilePath());; foreach(QFileInfo info, curDir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)){ if(info.isDir()){ deleteRecursive(info); }else{ QFile::remove(info.absoluteFilePath()); } } QDir dir = start.absoluteDir(); dir.rmdir(start.fileName()); }else{ QFile::remove(start.absoluteFilePath()); } } QPair FilesystemWidget::programData(const QString &prefix, const QString &preferred){ QSettings s; QString section = QString("programs_%1").arg(prefix); QHash data = s.value(QString("%1/data").arg(section)).toHash(); if(data.isEmpty()){ return QPair(); } QHash programData; if(!preferred.isEmpty()){ if(data.keys().contains(preferred)){ programData = data.value(preferred).toHash(); return qMakePair(programData.value("path").toString(), programData.value("args").toStringList()); } return QPair(); } QString defaultProg = s.value(QString("%1/default").arg(section)).toString(); if(defaultProg.isEmpty()){ return QPair(); } programData = data.value(defaultProg).toHash(); return qMakePair(programData.value("path").toString(), programData.value("args").toStringList()); } void FilesystemWidget::copyRecursive(const QFileInfo &start, const QString &destdir){ if(!start.isDir()){ return; } QDir source(start.absoluteFilePath()); QDir dest = QDir(destdir); if(!dest.exists(source.dirName())){ dest.mkdir(source.dirName()); } QString d = QString("%1/%2").arg(destdir).arg(source.dirName()); foreach(QFileInfo cur, source.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)){ if(cur.isDir()){ copyRecursive(cur, d); }else{ QString destPath = QString("%1/%2").arg(d).arg(cur.fileName()); QFile::copy(cur.absoluteFilePath(), destPath); } } } void FilesystemWidget::doRenameFile(){ QString name = mRenameDialog->text(); QSortFilterProxyModel *proxy = static_cast(mFileView->model()); QModelIndex cur = mFileView->currentIndex(); QModelIndex real = proxy->mapToSource(cur); QFileInfo info = mModel->fileInfo(real); QString newName = QString("%1/%2").arg(info.absolutePath()).arg(name); if(QFile::rename(info.absoluteFilePath(), newName)){ QString message = QString(tr("Renamed %1 to %2")).arg(info.absoluteFilePath()).arg(newName); emit statusbarMessage(message); } QFileInfo n(newName); mTemplate = n.completeBaseName(); emit newTemplate(mTemplate); } void FilesystemWidget::dirExpanded(const QModelIndex &idx){ QModelIndex real = mDirProxy->mapToSource(idx); if(real.isValid()){ mExpandedDirs << mModel->filePath(real); } } void FilesystemWidget::dirCollapsed(const QModelIndex &idx){ QModelIndex real = mDirProxy->mapToSource(idx); if(real.isValid()){ QString path = mModel->filePath(real); if(mExpandedDirs.contains(path)){ mExpandedDirs.removeAll(path); } } } QStringList FilesystemWidget::selectedFiles(){ QStringList retval; QModelIndexList selected = fileView()->selectionModel()->selectedRows(); if(selected.isEmpty()){ return QStringList(); } QSortFilterProxyModel *proxy = static_cast(fileView()->model()); foreach(QModelIndex idx, selected){ QModelIndex src = proxy->mapToSource(idx); retval << mModel->filePath(src); } return retval; }