/* 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 #include "filesystemwidget.h" #include "filesystemdirproxy.h" #include "fileview.h" #include "shemoviconprovider.h" #include "filesystemfileproxy.h" #include "helper.h" #include "messagedialog.h" #include "extractordialog.h" FilesystemWidget::FilesystemWidget(QWidget *parent) : QWidget(parent) { mModel = new QDirModel; mModel->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); mModel->setSorting(QDir::DirsFirst | QDir::IgnoreCase); 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); 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::MultiSelection); 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(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())); QSettings s; QStringList expandPaths = s.value("ui/expandpaths").toStringList(); QString last; foreach(QString p, expandPaths){ QModelIndex idx = mModel->index(p); last = p; if(idx.isValid()){ QModelIndex proxy = mDirProxy->mapFromSource(idx); mDirView->setExpanded(proxy, true); expandParents(proxy); } } QString select = s.value("ui/selectstartup").toString(); windowTitle(select); setWindowTitle(select); QModelIndex sel = mModel->index(select); mDirView->setCurrentIndex(mDirProxy->mapFromSource(sel)); 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::directoryChanged(const QModelIndex &selected, const QModelIndex &deselected){ QModelIndex real = mDirProxy->mapToSource(selected); if(!real.isValid()){ return; } mDirEdit->setText(mModel->filePath(real)); setWindowTitle(mModel->filePath(real)); QModelIndex oldSelected = mDirProxy->mapToSource(deselected); mFileView->selectionModel()->setCurrentIndex(mFileProxy->mapFromSource(oldSelected), QItemSelectionModel::NoUpdate); 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)){ mDirView->setCurrentIndex(mDirProxy->mapFromSource(real)); return; } QString path = mModel->filePath(real); QString mt = Helper::mimeType(path); QSettings s; QStringList programArgs; QString program; if(mt.toLower().startsWith("video")){ program = s.value("paths/movieviewer").toString(); programArgs = s.value("paths/movieviewerargs").toStringList(); } if(mt.toLower().startsWith("image")){ program = s.value("paths/pictureviewer").toString(); programArgs = s.value("paths/pictureviewerargs").toStringList(); } programArgs << path; QProcess::execute(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(); QAction *refresh = action(mFileView, "RE"); if(refresh){ // a refresh would invalidate selection refresh->setEnabled(false); } 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(); mModel->refresh(QModelIndex()); if(refresh){ refresh->setEnabled(true); } }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)); mModel->refresh(real.parent()); } } 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){ QAction *refresh = action(mFileView, "RE"); if(refresh){ refresh->setEnabled(false); } 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(); mModel->refresh(rootIndex); if(refresh){ refresh->setEnabled(true); } } } 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){ QAction *refresh = action(mFileView, "RE"); if(refresh){ refresh->setEnabled(false); } 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); if(refresh){ refresh->setEnabled(true); } mFileView->selectionModel()->clearSelection(); mModel->refresh(QModelIndex()); } } 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); mModel->refresh(real.parent()); }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::extract(const QString &destDir){ QModelIndex idx = mFileView->currentIndex(); if(idx.column() != 0){ idx = mFileView->model()->index(idx.row(), 0); } QSortFilterProxyModel *proxy = static_cast(mFileView->model()); QModelIndex real = proxy->mapToSource(idx); QFileInfo info = mModel->fileInfo(real); if(info.isDir()){ return; } ExtractorDialog dlg(info.absoluteFilePath(), destDir, this); dlg.exec(); } 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()); } } 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); mModel->refresh(real.parent()); } QFileInfo n(newName); mTemplate = n.completeBaseName(); emit newTemplate(mTemplate); } void FilesystemWidget::expandParents(const QModelIndex &idx){ if(idx.isValid()){ QModelIndex parent = idx.parent(); do{ mDirView->setExpanded(parent, true); parent = parent.parent(); }while(parent.isValid()); } } QAction * FilesystemWidget::action(QWidget *widget, const QVariant &data) const{ QAction *retval = 0; foreach(QAction *a, widget->actions()){ if(a->data() == data){ retval = a; break; } } return retval; }