From d324e6f466f2e9d0ae1bc6e589193eba37b3b749 Mon Sep 17 00:00:00 2001 From: Lukas Hrazky Date: Tue, 13 Jul 2010 23:42:57 +0200 Subject: [PATCH] the big bang Signed-off-by: Lukas Hrazky --- .gitignore | 3 + case.pro | 30 +++ src/addressbar.cpp | 37 +++ src/addressbar.h | 41 +++ src/button.cpp | 38 +++ src/button.h | 33 +++ src/case.cpp | 132 ++++++++++ src/case.h | 54 ++++ src/filelist.cpp | 101 ++++++++ src/filelist.h | 52 ++++ src/fileoperator.cpp | 705 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/fileoperator.h | 151 +++++++++++ src/main.cpp | 29 +++ src/pane.cpp | 108 ++++++++ src/pane.h | 58 +++++ 15 files changed, 1572 insertions(+) create mode 100644 .gitignore create mode 100644 case.pro create mode 100644 src/addressbar.cpp create mode 100644 src/addressbar.h create mode 100644 src/button.cpp create mode 100644 src/button.h create mode 100644 src/case.cpp create mode 100644 src/case.h create mode 100644 src/filelist.cpp create mode 100644 src/filelist.h create mode 100644 src/fileoperator.cpp create mode 100644 src/fileoperator.h create mode 100644 src/main.cpp create mode 100644 src/pane.cpp create mode 100644 src/pane.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8672cdf --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +case +Makefile +.build diff --git a/case.pro b/case.pro new file mode 100644 index 0000000..26bd047 --- /dev/null +++ b/case.pro @@ -0,0 +1,30 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Mon Jul 12 22:30:42 2010 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . src +INCLUDEPATH += . src + +# added manually! +CONFIG += link_pkgconfig +PKGCONFIG += dbus-1 gnome-vfs-2.0 +LIBS += -lhildonmime -ldbus-1 +OBJECTS_DIR=.build +MOC_DIR=.build + +# Input +HEADERS += src/addressbar.h \ + src/button.h \ + src/case.h \ + src/filelist.h \ + src/fileoperator.h \ + src/pane.h +SOURCES += src/addressbar.cpp \ + src/button.cpp \ + src/case.cpp \ + src/filelist.cpp \ + src/fileoperator.cpp \ + src/main.cpp \ + src/pane.cpp diff --git a/src/addressbar.cpp b/src/addressbar.cpp new file mode 100644 index 0000000..e0145a4 --- /dev/null +++ b/src/addressbar.cpp @@ -0,0 +1,37 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include "addressbar.h" + + +AddressBar::AddressBar(QWidget *parent) : QLineEdit(parent) { + setMaximumHeight(60); + setContentsMargins(-4, -4, -4, -4); + + connect(this, SIGNAL(returnPressed()), this, SLOT(returnPressed())); +} + + +void AddressBar::mousePressEvent(QMouseEvent *event) { + emit mousePressed(); + QLineEdit::mousePressEvent(event); +} + + +void AddressBar::returnPressed() { + emit pathEntered(text()); +} diff --git a/src/addressbar.h b/src/addressbar.h new file mode 100644 index 0000000..11fec38 --- /dev/null +++ b/src/addressbar.h @@ -0,0 +1,41 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#ifndef ADDRESSBAR_H +#define ADDRESSBAR_H + +#include + + +class AddressBar : public QLineEdit { + Q_OBJECT; + +signals: + void mousePressed(); + void pathEntered(QString path); + +public: + explicit AddressBar(QWidget *parent = 0); + +protected: + void mousePressEvent(QMouseEvent *event); + +protected slots: + void returnPressed(); +}; + +#endif // ADDRESSBAR_H diff --git a/src/button.cpp b/src/button.cpp new file mode 100644 index 0000000..19e78a3 --- /dev/null +++ b/src/button.cpp @@ -0,0 +1,38 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include "button.h" + + +Button::Button(QWidget *parent, const int maxWidth, const int maxHeight) : QPushButton(parent) { + setMaximumWidth(maxWidth); + setMaximumHeight(maxHeight); +} + +Button::Button(const QString &text, QWidget *parent, const int maxWidth, const int maxHeight) : + QPushButton(text, parent) +{ + setMaximumWidth(maxWidth); + setMaximumHeight(maxHeight); +} + +Button::Button(const QIcon &icon, const QString &text, QWidget *parent, const int maxWidth, const int maxHeight) : + QPushButton(icon, text, parent) +{ + setMaximumWidth(maxWidth); + setMaximumHeight(maxHeight); +} diff --git a/src/button.h b/src/button.h new file mode 100644 index 0000000..1379c1f --- /dev/null +++ b/src/button.h @@ -0,0 +1,33 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#ifndef BUTTON_H +#define BUTTON_H + +#include + + +class Button : public QPushButton { + Q_OBJECT + +public: + explicit Button(QWidget *parent = 0, int maxWidth = 70, int maxHeight = 70); + explicit Button(const QString &text, QWidget *parent = 0, const int maxWidth = 70, const int maxHeight = 70); + explicit Button(const QIcon &icon, const QString &text, QWidget *parent = 0, const int maxWidth = 70, const int maxHeight = 70); +}; + +#endif // BUTTON_H diff --git a/src/case.cpp b/src/case.cpp new file mode 100644 index 0000000..b3163ba --- /dev/null +++ b/src/case.cpp @@ -0,0 +1,132 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include "case.h" + +#include +#include + + +Case::Case(QWidget *parent) : + QMainWindow(parent), + leftPane(new Pane(this)), + rightPane(new Pane(this)), + activePane(leftPane), + inactivePane(rightPane), + cloneBtn(new Button(">>", 0, 60, 70)), + moveBtn(new Button("mv>", 0, 60, 70)), + copyBtn(new Button("cp>", 0, 60, 70)), + delBtn(new Button("rm", 0, 60, 70)), + swapBtn(new Button("<>", 0, 60, 70)), + fileOperator(new FileOperator(this)) +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + QHBoxLayout *paneLayout = new QHBoxLayout; + paneLayout->setContentsMargins(0, 0, 0, 0); + paneLayout->setSpacing(1); + + QWidget *central = new QWidget; + setCentralWidget(central); + central->setLayout(layout); + layout->addLayout(paneLayout); + + paneLayout->addWidget(leftPane); + leftPane->toggleActive(); + + QVBoxLayout *middleButtons = new QVBoxLayout; + paneLayout->addLayout(middleButtons); + middleButtons->setSpacing(10); + middleButtons->setContentsMargins(0, 0, 0, 0); + + cloneBtn->setContentsMargins(0, 0, 0, 0); + + middleButtons->addWidget(cloneBtn); + middleButtons->addWidget(moveBtn); + middleButtons->addWidget(copyBtn); + middleButtons->addWidget(delBtn); + middleButtons->addWidget(swapBtn); + + paneLayout->addWidget(rightPane); + + layout->addWidget(fileOperator); + + connect(this, SIGNAL(activePaneSwitched()), leftPane, SLOT(toggleActive())); + connect(this, SIGNAL(activePaneSwitched()), rightPane, SLOT(toggleActive())); + + connect(cloneBtn, SIGNAL(pressed()), this, SLOT(clonePane())); + connect(delBtn, SIGNAL(pressed()), this, SLOT(deleteFiles())); + connect(copyBtn, SIGNAL(pressed()), this, SLOT(copyFiles())); + connect(moveBtn, SIGNAL(pressed()), this, SLOT(moveFiles())); + connect(swapBtn, SIGNAL(pressed()), this, SLOT(swapPanes())); +} + + +void Case::switchActivePane() { + Pane *tmpPane = activePane; + activePane = inactivePane; + inactivePane = tmpPane; + + if (leftPane == activePane) { + cloneBtn->setText(">>"); + moveBtn->setText("mv>"); + copyBtn->setText("cp>"); + } else { + cloneBtn->setText("<<"); + moveBtn->setText("setText("selection().size()) { + fileOperator->deleteFiles(activePane->selection()); + } +} + + +void Case::copyFiles() { + if (activePane->selection().size()) { + QDir dest = inactivePane->path(); + fileOperator->copyFiles(activePane->selection(), dest); + } +} + + +void Case::moveFiles() { + if (activePane->selection().size()) { + QDir dest = inactivePane->path(); + fileOperator->moveFiles(activePane->selection(), dest); + } +} + + +void Case::clonePane() { + inactivePane->changePath(activePane->path()); +} + + +void Case::swapPanes() { + QString tmpPath = activePane->path(); + activePane->changePath(inactivePane->path()); + inactivePane->changePath(tmpPath); +} diff --git a/src/case.h b/src/case.h new file mode 100644 index 0000000..aee7cbb --- /dev/null +++ b/src/case.h @@ -0,0 +1,54 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#ifndef CASE_H +#define CASE_H + +#include "pane.h" +#include "fileoperator.h" +#include "button.h" + +#include +#include + + +class Case : public QMainWindow { + Q_OBJECT + +signals: + void activePaneSwitched(); + +public: + Case(QWidget *parent = 0); + +public slots: + void switchActivePane(); + +private: + Pane *leftPane, *rightPane, *activePane, *inactivePane; + Button *cloneBtn, *moveBtn, *copyBtn, *delBtn, *swapBtn; + FileOperator *fileOperator; + +private slots: + void deleteFiles(); + void copyFiles(); + void moveFiles(); + void clonePane(); + void swapPanes(); +}; + +#endif // CASE_H diff --git a/src/filelist.cpp b/src/filelist.cpp new file mode 100644 index 0000000..8bba30e --- /dev/null +++ b/src/filelist.cpp @@ -0,0 +1,101 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include "filelist.h" + +#include +#include + +#include +#include + + +FileList::FileList(QWidget *parent) : + QListView(parent), + fileSystemModel(new QFileSystemModel) +{ + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + //setSelectionMode(QAbstractItemView::SingleSelection); + setSelectionMode(QAbstractItemView::MultiSelection); + + setModel(fileSystemModel); + + fileSystemModel->setFilter(fileSystemModel->filter() | QDir::System); + setRootIndex(fileSystemModel->setRootPath(QDir::homePath())); + + connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(activateItem(QModelIndex))); +} + + +const QFileInfoList FileList::selection() const { + QFileInfoList files; + QModelIndexList l = selectionModel()->selectedIndexes(); + for (int i = 0; i < l.size(); ++i) { + files.append(fileSystemModel->fileInfo(l[i])); + } + return files; +} + + +const QString FileList::path() const { + return fileSystemModel->rootPath(); +} + + +bool FileList::changePath(QString path) { + QDir dir(fileSystemModel->rootPath()); + if (dir.cd(path)) { + setRootIndex(fileSystemModel->setRootPath(dir.absolutePath())); + clearSelection(); + emit pathChanged(fileSystemModel->rootPath()); + return true; + } + + return false; +} + + +bool FileList::goUp() { + return changePath(".."); +} + + +void FileList::activateItem(QModelIndex index) { + const QFileInfo &file = fileSystemModel->fileInfo(index); + + if(file.isDir()) { + changePath(file.absoluteFilePath()); + } else if(file.isExecutable()) { + QProcess::startDetached(file.absoluteFilePath()); + } else { + // TODO: find better solution for this, maybe get fixed in Qt + DBusConnection* conn; + conn = dbus_bus_get(DBUS_BUS_SESSION, 0); + hildon_mime_open_file(conn, QUrl::fromLocalFile(file.absoluteFilePath()).toEncoded().constData()); + + // Not working with maemo5. Uses hildon_uri_open function from + // libhildonmime which should work, but all files opened in browser. + //QDesktopServices::openUrl(QUrl::fromLocalFile(file.absoluteFilePath())); + } +} + + +void FileList::mousePressEvent(QMouseEvent *event) { + emit mousePressed(); + QListView::mousePressEvent(event); +} diff --git a/src/filelist.h b/src/filelist.h new file mode 100644 index 0000000..8e5a5c4 --- /dev/null +++ b/src/filelist.h @@ -0,0 +1,52 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#ifndef FILELIST_H +#define FILELIST_H + +#include +#include +#include + + +class FileList : public QListView { + Q_OBJECT; + +signals: + void mousePressed(); + void pathChanged(QString path); + +public: + explicit FileList(QWidget *parent = 0); + + const QFileInfoList selection() const; + const QString path() const; + +public slots: + bool changePath(QString path); + bool goUp(); + +protected: + QFileSystemModel *fileSystemModel; + + void mousePressEvent(QMouseEvent *event); + +private slots: + void activateItem(QModelIndex index); +}; + +#endif // FILELIST_H diff --git a/src/fileoperator.cpp b/src/fileoperator.cpp new file mode 100644 index 0000000..2aa8265 --- /dev/null +++ b/src/fileoperator.cpp @@ -0,0 +1,705 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include "fileoperator.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + + +#define SHOW_ERROR_PROMPT(promptString) \ + response = FileOperator::NONE; \ + if (ignoreAll[errno]) { \ + response = FileOperator::IGNORE; \ + } else { \ + char buf[255]; \ + char *realBuf = strerror_r(errno, buf, 255); \ + emit showErrorPrompt(this, promptString + ". " + realBuf + ".", errno); \ + waitCond.wait(&mutex); \ + } + + +#define ERROR_PROMPT(operation, promptString) \ +{ \ + response = FileOperator::NONE; \ + while (!abort && operation) { \ + SHOW_ERROR_PROMPT(promptString) \ + if (response == FileOperator::IGNORE) { \ + break; \ + } \ + } \ +} + + +#define ERROR_PROMPT_XP(operation, promptString, onIgnore, quitCmd) \ +{ \ + ERROR_PROMPT(operation, promptString) \ + if (abort || response == FileOperator::IGNORE) { \ + if (!abort) onIgnore; \ + quitCmd; \ + } \ +} + + +#define OVERWRITE_PROMPT(file, newFile) \ +{ \ + response = FileOperator::NONE; \ + \ + if (newFile.exists()) { \ + if (overwriteAll != FileOperator::NONE) { \ + response = overwriteAll; \ + } else { \ + bool dirOverDir = false; \ + if (newFile.isDir() && file.isDir()) dirOverDir = true; \ + emit showOverwritePrompt(this, newFile.absoluteFilePath(), dirOverDir); \ + waitCond.wait(&mutex); \ + } \ + } \ +} + + +FileOperator::FileOperator(QWidget *parent) : QWidget(parent) { + QHBoxLayout *layout = new QHBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + setLayout(layout); +} + + +void FileOperator::deleteFiles(const QFileInfoList &files) { + QString title, desc; + if (files.size() == 1) { + title = tr("Delete file"); + desc = tr("Are you sure you want to delete %1?").arg(files[0].absoluteFilePath()); + } else { + title = tr("Delete files"); + desc = tr("You are about to delete %1 files. Are you sure you want to continue?").arg(files.size()); + } + + int confirm = QMessageBox::warning( + 0, + title, + desc, + QMessageBox::Yes, + QMessageBox::No + ); + + if(confirm == QMessageBox::Yes) { + caterNewThread(new DeleteThread(files)); + } +} + + +void FileOperator::copyFiles(const QFileInfoList &files, QDir &destination) { + QString title, desc; + if (files.size() == 1) { + title = tr("Copy file"); + desc = tr("Are you sure you want to copy %1 to %2?").arg(files[0].absoluteFilePath()) + .arg(destination.absolutePath()); + } else { + title = tr("Copy files"); + desc = tr("You are about to copy %1 files to %2. Are you sure you want to continue?") + .arg(files.size()).arg(destination.absolutePath()); + } + + int confirm = QMessageBox::warning( + 0, + title, + desc, + QMessageBox::Yes, + QMessageBox::No + ); + + if(confirm == QMessageBox::Yes) { + caterNewThread(new CopyThread(files, destination)); + } +} + + +void FileOperator::moveFiles(const QFileInfoList &files, QDir &destination) { + // for move we don't wanna move to the same dir + if (files[0].absolutePath() == destination.absolutePath()) return; + + QString title, desc; + if (files.size() == 1) { + title = tr("Move file"); + desc = tr("Are you sure you want to move %1 to %2?").arg(files[0].absoluteFilePath()) + .arg(destination.absolutePath()); + } else { + title = tr("Move files"); + desc = tr("You are about to move %1 files to %2. Are you sure you want to continue?") + .arg(files.size()).arg(destination.absolutePath()); + } + + int confirm = QMessageBox::warning( + 0, + title, + desc, + QMessageBox::Yes, + QMessageBox::No + ); + + if(confirm == QMessageBox::Yes) { + caterNewThread(new MoveThread(files, destination)); + } +} + + +void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator, const QString &message, const int err) { + QMessageBox msgBox; + msgBox.addButton(QMessageBox::Cancel); + QAbstractButton *abortButton = msgBox.addButton(tr("Abort"), QMessageBox::DestructiveRole); + QAbstractButton *retryButton = msgBox.addButton(QMessageBox::Retry); + QAbstractButton *ignoreButton = msgBox.addButton(QMessageBox::Ignore); + QAbstractButton *ignoreAllButton = msgBox.addButton(tr("Ignore All"), QMessageBox::AcceptRole); + msgBox.setText(message); + + msgBox.exec(); + + if (msgBox.clickedButton() == abortButton) { + manipulator->setResponse(ABORT); + } else if (msgBox.clickedButton() == retryButton) { + manipulator->setResponse(RETRY); + } else if (msgBox.clickedButton() == ignoreButton) { + manipulator->setResponse(IGNORE); + } else if (msgBox.clickedButton() == ignoreAllButton) { + manipulator->setResponse(IGNORE, true, err); + } +} + + +void FileOperator::showOverwritePrompt( + FileManipulatorThread* manipulator, + const QString &fileName, + const bool dirOverDir) +{ + QMessageBox msgBox; + msgBox.addButton(QMessageBox::Cancel); + QAbstractButton *yesButton = msgBox.addButton(QMessageBox::Yes); + QAbstractButton *yesToAllButton = msgBox.addButton(QMessageBox::YesToAll); + QAbstractButton *noButton = msgBox.addButton(QMessageBox::No); + QAbstractButton *noToAllButton = msgBox.addButton(QMessageBox::NoToAll); + QAbstractButton *abortButton = msgBox.addButton(tr("Abort"), QMessageBox::DestructiveRole); + QAbstractButton *askButton = 0; + + if (dirOverDir) { + msgBox.setText(tr("Directory %1 already exists. Overwrite the files inside?").arg(fileName)); + askButton = msgBox.addButton(tr("Ask"), QMessageBox::AcceptRole); + } else { + msgBox.setText(tr("File %1 already exists. Overwrite?").arg(fileName)); + } + + msgBox.exec(); + + if (msgBox.clickedButton() == abortButton) { + manipulator->setResponse(ABORT); + } else if (msgBox.clickedButton() == yesButton) { + manipulator->setResponse(OVERWRITE); + } else if (msgBox.clickedButton() == yesToAllButton) { + manipulator->setResponse(OVERWRITE, true); + } else if (msgBox.clickedButton() == noButton) { + manipulator->setResponse(KEEP); + } else if (msgBox.clickedButton() == noToAllButton) { + manipulator->setResponse(KEEP, true); + } else if (msgBox.clickedButton() == askButton) { + manipulator->setResponse(NONE, true); + } +} + + +void FileOperator::remove(FileManipulatorThread* manipulator) { + layout()->removeWidget(manipulator->widget); + manipulatorList.removeAll(manipulator); + delete manipulator; +} + + +void FileOperator::setBarSize(FileManipulatorThread* manipulator, unsigned int size) { + manipulator->widget->setMinimum(0); + manipulator->widget->setMaximum(size); +} + + +void FileOperator::updateProgress(FileManipulatorThread* manipulator, int value) { + if (manipulator->widget->value() + value > manipulator->widget->maximum()) { + std::cout << "WARNING, EXCEEDING MAXIMUM BY " << value << std::endl; + } + manipulator->widget->setValue(manipulator->widget->value() + value); +} + + +void FileOperator::caterNewThread(FileManipulatorThread *thread) { + manipulatorList.append(thread); + + connect(thread, SIGNAL(showErrorPrompt(FileManipulatorThread*, const QString&, const int)), + this, SLOT(showErrorPrompt(FileManipulatorThread*, const QString&, const int))); + connect(thread, SIGNAL(showOverwritePrompt(FileManipulatorThread*, const QString&, bool)), + this, SLOT(showOverwritePrompt(FileManipulatorThread*, const QString&, bool))); + connect(thread, SIGNAL(finished(FileManipulatorThread*)), + this, SLOT(remove(FileManipulatorThread*))); + connect(thread, SIGNAL(setBarSize(FileManipulatorThread*, unsigned int)), + this, SLOT(setBarSize(FileManipulatorThread*, unsigned int))); + connect(thread, SIGNAL(updateProgress(FileManipulatorThread*, int)), + this, SLOT(updateProgress(FileManipulatorThread*, int))); + + thread->widget->setValue(0); + + layout()->addWidget(thread->widget); + thread->start(); +} + + +FileManipulatorThread::FileManipulatorThread(const QFileInfoList files, QDir dest) : + widget(new QProgressBar()), + files(files), + dest(dest), + response(FileOperator::NONE), + overwriteAll(FileOperator::NONE), + abort(false), + barSize(0), + barValue(0), + fileSize(0), + fileValue(0) +{ + memset(ignoreAll, false, sizeof(ignoreAll)); + //widget->setStyle(new QPlastiqueStyle); +} + + +void FileManipulatorThread::setResponse( + const FileOperator::Response response, + const bool applyToAll, + const int err) +{ + mutex.lock(); + + this->response = response; + + if (applyToAll) { + if (response == FileOperator::KEEP + || response == FileOperator::OVERWRITE + || response == FileOperator::NONE) + { + overwriteAll = response; + } + + if (response == FileOperator::IGNORE) { + ignoreAll[err] = true; + } + } + + if (response == FileOperator::ABORT) abort = true; + + mutex.unlock(); + waitCond.wakeAll(); +} + + +void FileManipulatorThread::processFiles(const QFileInfoList &files) { + for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) { + perform(*it); + if (abort) break; + } +} + + +bool FileManipulatorThread::remove(QString &fileName, const bool ignoreDirNotEmpty) { + return remove(QFileInfo(fileName), ignoreDirNotEmpty); +} + + +bool FileManipulatorThread::remove(const QFileInfoList &files, const bool ignoreDirNotEmpty) { + bool res = true; + for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) { + if (!remove(*it, ignoreDirNotEmpty)) res = false; + if (abort) break; + } + return res; +} + + +bool FileManipulatorThread::remove(const QFileInfo &file, const bool ignoreDirNotEmpty) { + QString path = file.absoluteFilePath(); + QFSFileEngine engine(path); + + if (file.isDir()) { + QFileInfoList list = listDirFiles(path); + + if (ignoreDirNotEmpty && list.size()) return true; + + if (!remove(list, ignoreDirNotEmpty)) return false; + + ERROR_PROMPT(!engine.rmdir(path, false), + tr("Error deleting directory %1.").arg(path)) + } else { + ERROR_PROMPT(!engine.remove(), + tr("Error deleting file %1.").arg(path)) + } + + if (abort || response == FileOperator::IGNORE) return false; + return true; +} + + +void FileManipulatorThread::copy(const QFileInfo &file, const bool removeAfterCopy) { + std::cout << (removeAfterCopy ? "MOVING " : "COPYING ") << file.absoluteFilePath().toStdString() + << " to " << dest.absolutePath().toStdString() << std::endl; + + QString path(file.absoluteFilePath()); + QString newPath(dest.absolutePath() + "/" + file.fileName()); + QFSFileEngine engine(path); + QFSFileEngine newEngine(newPath); + QFileInfo newFile(newPath); + + updateFile(path); + + // hack to prevent asking about the same file if we already asked in the rename(...) function + if (overwriteAll == FileOperator::DONT_ASK_ONCE) { + overwriteAll = FileOperator::NONE; + } else { + OVERWRITE_PROMPT(file, newFile) + } + + if (abort) return; + + // this loop is here only to allow easily breaking out to the end (and remove the source file/dir) + while (1) { + if (response == FileOperator::KEEP) { + updateProgress(fileSizeMap[path]); + break; + } + + FileOperator::Response overwriteResponse = response; + + if (file.isDir()) { + if (newFile.exists() && !newFile.isDir()) { + if(!remove(newPath)) { + updateProgress(fileSizeMap[path]); + break; + } + newFile = QFileInfo(newPath); + } + + if (!newFile.exists()) { + ERROR_PROMPT_XP(!engine.mkdir(newPath, false), + tr("Error creating directory %1.").arg(newPath), + updateProgress(fileSizeMap[path]), + break) + } + + updateProgress(1); + + QDir destBackup = dest; + dest = newPath; + + FileOperator::Response tmpResp = overwriteAll; + overwriteAll = overwriteResponse; + + processFiles(listDirFiles(path)); + + overwriteAll = tmpResp; + + ERROR_PROMPT(!newEngine.setPermissions(file.permissions()), + tr("Error setting permissions for directory %1.").arg(newPath)) + + if (abort) return; + + dest = destBackup; + } else { + ERROR_PROMPT_XP(engine.isSequential(), + tr("Cannot copy sequential file %1.").arg(path), + updateProgress(fileSizeMap[path]), + break) + + if (newFile.exists() && newFile.isDir()) { + ERROR_PROMPT_XP(!remove(newPath), + tr("Cannot replace directory %1 due to previous errors.").arg(newPath), + updateProgress(fileSizeMap[path]), + break) + } + + ERROR_PROMPT_XP(!engine.open(QIODevice::ReadOnly), + tr("Error reading file %1.").arg(path), + updateProgress(fileSizeMap[path]), + break) + + bool ignore = false; + while (!abort && !ignore) { + engine.seek(0); + + ERROR_PROMPT(!newEngine.open(QIODevice::WriteOnly | QIODevice::Truncate), + tr("Error writing file %1.").arg(newPath)) + + if (abort || response == FileOperator::IGNORE) { + if (response == FileOperator::IGNORE) { + updateProgress(fileSizeMap[path] - fileValue); + ignore = true; + } + break; + } + + bool error = false; + char block[4096]; + qint64 bytes; + while ((bytes = engine.read(block, sizeof(block))) > 0) { + if (bytes == -1 || bytes != newEngine.write(block, bytes)) { + if (bytes == -1) { + SHOW_ERROR_PROMPT(tr("Error while reading from file %1.").arg(path)); + } else { + SHOW_ERROR_PROMPT(tr("Error while writing to file %1.").arg(newPath)); + } + + if (!abort) { + if (response == FileOperator::IGNORE) { + updateProgress(fileSizeMap[path] - fileValue); + ignore = true; + } else { + updateProgress(-fileValue); + } + } + error = true; + break; + } + + updateProgress(1); + } + + if (!error) break; + } + + engine.close(); + newEngine.close(); + + if (abort || ignore) { + newEngine.remove(); + } else { + ERROR_PROMPT(!newEngine.setPermissions(file.permissions()), + tr("Error setting permissions for file %1.").arg(newPath)) + } + } + + break; + } + + if (removeAfterCopy && !abort) remove(path, true); +} + + +unsigned int FileManipulatorThread::countFiles(const QFileInfoList &files) { + unsigned int res = 0; + + for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) { + unsigned int size = 1; + + if (it->isDir()) { + size += countFiles(listDirFiles(it->absoluteFilePath())); + } + + res += size; + fileSizeMap[it->absoluteFilePath()] = size; + } + + return res; +} + + +unsigned int FileManipulatorThread::calculateFileSize(const QFileInfoList &files) { + unsigned int res = 0; + + for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) { + unsigned int size = 1; + + if (it->isDir()) { + size += calculateFileSize(listDirFiles(it->absoluteFilePath())); + } else { + size = ceil(static_cast(it->size()) / 4096); + } + + res += size; + fileSizeMap[it->absoluteFilePath()] = size; + } + + return res; +} + + +QFileInfoList FileManipulatorThread::listDirFiles(const QString &dirPath) { + QDir dir = dirPath; + return dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden); +} + + +void FileManipulatorThread::setBarSize(unsigned int size) { + barSize = size; + emit setBarSize(this, size); +} + + +void FileManipulatorThread::updateProgress(int value) { + barValue += value; + fileValue += value; + emit updateProgress(this, value); +} + + +void FileManipulatorThread::updateFile(const QString &fileName) { + fileValue = 0; + emit updateFile(this, fileName); +} + + +void DeleteThread::run() { + mutex.lock(); + + setBarSize(countFiles(files)); + + processFiles(files); + + sleep(0.5); + emit finished(this); +} + + +void DeleteThread::perform(const QFileInfo &file) { + std::cout << "DELETING " << file.absoluteFilePath().toStdString() << std::endl; + + QString path = file.absoluteFilePath(); + QFSFileEngine engine(path); + + if (file.isDir()) { + processFiles(listDirFiles(path)); + + if (!listDirFiles(path).size()) { + ERROR_PROMPT(!engine.rmdir(path, false), + tr("Error deleting directory %1.").arg(path)) + } + } else { + ERROR_PROMPT(!engine.remove(), + tr("Error deleting file %1.").arg(path)) + } + + if (!abort) updateProgress(1); +} + + +void CopyThread::run() { + mutex.lock(); + + setBarSize(calculateFileSize(files)); + + processFiles(files); + + sleep(0.5); + emit finished(this); +} + + +void CopyThread::perform(const QFileInfo &file) { + copy(file, false); +} + + +void MoveThread::run() { + mutex.lock(); + + rename(files, dest); + + sleep(0.5); + emit finished(this); +} + + +void MoveThread::rename(const QFileInfoList &files, const QDir &dest) { + setBarSize(barSize + files.size()); + + for (int i = 0; i < files.size(); ++i) { + QString path = files[i].absoluteFilePath(); + QFSFileEngine engine(path); + QString newPath = dest.absolutePath() + "/" + files[i].fileName(); + + updateFile(path); + + OVERWRITE_PROMPT(files[i], QFileInfo(newPath)) + + if (response == FileOperator::KEEP) { + remove(path); + if (abort) break; + updateProgress(1); + continue; + } + + while (!abort && !engine.rename(newPath)) { + // source and target are on different partitions + // this should happen on the first file, unless some are skipped by overwrite prompt + // we calculate the actual file sizes, because from now on copy & remove takes over + if (errno == EXDEV) { + setBarSize(barValue + calculateFileSize(files)); + + FileOperator::Response tmpResp = overwriteAll; + overwriteAll = response; + // hack: we already checked the first file we are sending to processFiles(...) + // so we don't want to ask about this one again + if (overwriteAll == FileOperator::NONE) overwriteAll = FileOperator::DONT_ASK_ONCE; + + processFiles(files.mid(i)); + + overwriteAll = tmpResp; + + // just to quit the loops, we are done + abort = true; + // the target is nonempty dir. lets call this recursively and rename the contents one by one + } else if (errno == ENOTEMPTY || errno == EEXIST) { + FileOperator::Response tmpResp = overwriteAll; + overwriteAll = response; + + rename(listDirFiles(path), QDir(newPath)); + if (abort) break; + + overwriteAll = tmpResp; + + ERROR_PROMPT(!engine.rmdir(path, false), tr("Error deleting directory %1.").arg(path)) + + break; + // source and target are nonmatching types(file and dir) + // remove the target and let it loop once again + } else if (errno == ENOTDIR || errno == EISDIR) { + if (!remove(newPath)) break; + } else { + SHOW_ERROR_PROMPT(tr("Error moving %1.").arg(path)) + + if (response == FileOperator::IGNORE) { + break; + } + } + } + + if (abort) break; + updateProgress(1); + } +} + + +void MoveThread::perform(const QFileInfo &file) { + copy(file, true); +} diff --git a/src/fileoperator.h b/src/fileoperator.h new file mode 100644 index 0000000..7d53bfb --- /dev/null +++ b/src/fileoperator.h @@ -0,0 +1,151 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#ifndef FILEOPERATOR_H +#define FILEOPERATOR_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +class FileManipulatorThread; + + +class FileOperator : public QWidget { + Q_OBJECT + +public: + // DONT_ASK_ONCE is a hackish way to avoid asking twice to overwrite the same directory when moving + enum Response{NONE, ABORT, RETRY, IGNORE, KEEP, OVERWRITE, DONT_ASK_ONCE}; + + FileOperator(QWidget *parent = 0); + + void deleteFiles(const QFileInfoList &files); + void copyFiles(const QFileInfoList &files, QDir &destination); + void moveFiles(const QFileInfoList &files, QDir &destination); + +public slots: + void showErrorPrompt(FileManipulatorThread* manipulator, const QString &message, const int err); + void showOverwritePrompt(FileManipulatorThread* manipulator, const QString &fileName, const bool dirOverDir); + void remove(FileManipulatorThread* manipulator); + void setBarSize(FileManipulatorThread* manipulator, unsigned int size); + void updateProgress(FileManipulatorThread* manipulator, int value); + +protected: + void caterNewThread(FileManipulatorThread *thread); + + QList manipulatorList; +}; + + +class FileManipulatorThread : public QThread { + Q_OBJECT + +public: + explicit FileManipulatorThread(const QFileInfoList files, QDir dest = QDir()); + void setResponse(const FileOperator::Response response, const bool appyToAll = false, const int err = 0); + + QProgressBar *widget; + +protected: + void processFiles(const QFileInfoList &files); + virtual void perform(const QFileInfo &file) = 0; + + bool remove(QString &filename, const bool ignoreDirNotEmpty = false); + bool remove(const QFileInfoList &files, const bool ignoreDirNotEmpty = false); + bool remove(const QFileInfo &file, const bool ignoreDirNotEmpty = false); + + void copy(const QFileInfo &file, const bool removeAfterCopy); + + unsigned int countFiles(const QFileInfoList &files); + unsigned int calculateFileSize(const QFileInfoList &files); + + QFileInfoList listDirFiles(const QString &dirPath); + + void setBarSize(unsigned int size); + void updateProgress(int value); + void updateFile(const QString &fileName); + + const QFileInfoList files; + QDir dest; + + FileOperator::Response response; + FileOperator::Response overwriteAll; + bool abort; + bool ignoreAll[256]; + + QMap fileSizeMap; + + QMutex mutex; + QWaitCondition waitCond; + + unsigned int barSize, barValue, fileSize, fileValue; + +signals: + void showErrorPrompt(FileManipulatorThread*, const QString&, const int); + void showOverwritePrompt(FileManipulatorThread*, const QString&, const bool); + void finished(FileManipulatorThread*); + void setBarSize(FileManipulatorThread*, unsigned int); + void updateProgress(FileManipulatorThread*, int); + void updateFile(FileManipulatorThread*, const QString&); +}; + + +class DeleteThread : public FileManipulatorThread { + Q_OBJECT + +public: + explicit DeleteThread(const QFileInfoList &files) : FileManipulatorThread(files) {} + +protected: + void run(); + virtual void perform(const QFileInfo &file); +}; + + +class CopyThread : public FileManipulatorThread { + Q_OBJECT + +public: + explicit CopyThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {} + +protected: + void run(); + virtual void perform(const QFileInfo &file); +}; + + +class MoveThread : public FileManipulatorThread { + Q_OBJECT + +public: + explicit MoveThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {} + +protected: + void run(); + virtual void perform(const QFileInfo &file); + void rename(const QFileInfoList &files, const QDir &dest); +}; + + +#endif // FILEOPERATOR_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..8a7ba40 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,29 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include + +#include "case.h" + + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + Case theCase; + + theCase.show(); + return app.exec(); +} diff --git a/src/pane.cpp b/src/pane.cpp new file mode 100644 index 0000000..68b556f --- /dev/null +++ b/src/pane.cpp @@ -0,0 +1,108 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#include "pane.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +Pane::Pane(QWidget *theCase, QWidget *parent) : + QWidget(parent), + theCase(theCase), + active(false), + location(new AddressBar), + up(new Button("^", 0, 70, 60)), + fileList(new FileList) +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(3, 3, 3, 3); + layout->setSpacing(0); + setLayout(layout); + + QHBoxLayout *topLine = new QHBoxLayout; + + location->setText(fileList->path()); + layout->setSpacing(0); + + topLine->addWidget(location); + topLine->addWidget(up); + layout->addLayout(topLine); + layout->addWidget(fileList); + + connect(location, SIGNAL(pathEntered(QString)), fileList, SLOT(changePath(QString))); + connect(up, SIGNAL(pressed()), fileList, SLOT(goUp())); + connect(fileList, SIGNAL(pathChanged(QString)), location, SLOT(setText(QString))); + + activationConnect(); +} + + +void Pane::paintEvent(QPaintEvent *) { + if (active) { + QPainter painter(this); + painter.setPen(palette().color(QPalette::Highlight)); + QRect g = this->geometry(); + g.moveTo(1, 1); + g.setWidth(g.width() - 3); + g.setHeight(g.height() - 3); + painter.drawRect(g); + } +} + + +void Pane::activationConnect() { + connect(up, SIGNAL(clicked()), theCase, SLOT(switchActivePane())); + connect(location, SIGNAL(mousePressed()), theCase, SLOT(switchActivePane())); + connect(fileList, SIGNAL(mousePressed()), theCase, SLOT(switchActivePane())); +} + + +void Pane::activationDisconnect() { + disconnect(up, SIGNAL(clicked()), theCase, SLOT(switchActivePane())); + disconnect(location, SIGNAL(mousePressed()), theCase, SLOT(switchActivePane())); + disconnect(fileList, SIGNAL(mousePressed()), theCase, SLOT(switchActivePane())); +} + + +void Pane::toggleActive() { + active = !active; + if (active) activationDisconnect(); else activationConnect(); + update(); +} + + +const QFileInfoList Pane::selection() const { + return fileList->selection(); +} + + +bool Pane::changePath(QString path) { + location->setText(path); + return fileList->changePath(path); +} + + +const QString Pane::path() const { + return fileList->path(); +} diff --git a/src/pane.h b/src/pane.h new file mode 100644 index 0000000..7489468 --- /dev/null +++ b/src/pane.h @@ -0,0 +1,58 @@ +// case - file manager for N900 +// Copyright (C) 2010 Lukas Hrazky +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#ifndef PANE_H +#define PANE_H + +#include +#include + +#include "button.h" +#include "filelist.h" +#include "addressbar.h" + + +class Pane : public QWidget { + Q_OBJECT + +public: + explicit Pane(QWidget *theCase, QWidget *parent = 0); + + const QString path() const; + const QFileInfoList selection() const; + +public slots: + bool changePath(QString path); + void toggleActive(); + +protected: + void paintEvent(QPaintEvent *); + + void activationConnect(); + void activationDisconnect(); + +private: + QWidget *theCase; + + bool active; + + AddressBar *location; + Button *up; + FileList *fileList; +}; + +#endif // PANE_H -- 1.7.9.5