code cleanup
[case] / src / fileoperator.cpp
1 // case - file manager for N900
2 // Copyright (C) 2010 Lukas Hrazky <lukkash@email.cz>
3 // 
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 // 
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 // 
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17
18 #include "fileoperator.h"
19
20 #include <QtGui>
21 #include <QMessageBox>
22 #include <QHBoxLayout>
23
24 #include "dialog.h"
25 #include "utils.h"
26
27
28 FileOperator::FileOperator(QWidget *parent) : QWidget(parent) {
29     QHBoxLayout *layout = new QHBoxLayout;
30     layout->setContentsMargins(0, 0, 0, 0);
31     layout->setSpacing(1);
32     setLayout(layout);
33
34     qRegisterMetaType<QFileInfo>("QFileInfo");
35     qRegisterMetaType<time_t>("time_t");
36
37     loadOperationIcons(palette(), "delete_small", deleteIcon, inverseDeleteIcon);
38     loadOperationIcons(palette(), "copy_small", copyIcon, inverseCopyIcon);
39     loadOperationIcons(palette(), "move_small", moveIcon, inverseMoveIcon);
40 }
41
42
43 void FileOperator::deleteFiles(const QFileInfoList &files) {
44     QString title, desc;
45     if (files.size() == 1) {
46         title = tr("Delete file");
47         desc = tr("Are you sure you want to delete %1?")
48             .arg(shortenPath(files[0].absoluteFilePath()));
49     } else {
50         title = tr("Delete files");
51         desc = tr("You are about to delete %1 files. Are you sure you want to continue?").arg(files.size());
52     }
53
54     int confirm = QMessageBox::warning(
55         0,
56         title,
57         desc,
58         QMessageBox::Yes,
59         QMessageBox::No
60     );
61
62     if(confirm == QMessageBox::Yes) {
63         ProgressBar *bar = new ProgressBar(deleteIcon, inverseDeleteIcon);
64         initOperation(new DeleteThread(files), bar);
65     }
66 }
67
68
69 void FileOperator::copyFiles(const QFileInfoList &files, QDir &destination) {
70     QString title, desc;
71     if (files.size() == 1) {
72         title = tr("Copy file");
73         desc = tr("Are you sure you want to copy %1 to %2?")
74             .arg(shortenPath(files[0].absoluteFilePath()))
75             .arg(shortenPath(destination.absolutePath()));
76     } else {
77         title = tr("Copy files");
78         desc = tr("You are about to copy %1 files to %2. Are you sure you want to continue?")
79             .arg(files.size()).arg(shortenPath(destination.absolutePath()));
80     }
81
82     int confirm = QMessageBox::warning(
83         0,
84         title,
85         desc,
86         QMessageBox::Yes,
87         QMessageBox::No
88     );
89
90     if(confirm == QMessageBox::Yes) {
91         ProgressBar *bar = new ProgressBar(copyIcon, inverseCopyIcon);
92         bar->setBottomTexts(shortenPath(files[0].absolutePath()), shortenPath(destination.absolutePath()));
93         initOperation(new CopyThread(files, destination), bar);
94     }
95 }
96
97
98 void FileOperator::moveFiles(const QFileInfoList &files, QDir &destination) {
99     // for move we don't wanna move to the same dir
100     if (files[0].absolutePath() == destination.absolutePath()) return;
101
102     QString title, desc;
103     if (files.size() == 1) {
104         title = tr("Move file");
105         desc = tr("Are you sure you want to move %1 to %2?")
106             .arg(shortenPath(files[0].absoluteFilePath()))
107             .arg(shortenPath(destination.absolutePath()));
108     } else {
109         title = tr("Move files");
110         desc = tr("You are about to move %1 files to %2. Are you sure you want to continue?")
111             .arg(files.size()).arg(shortenPath(destination.absolutePath()));
112     }
113
114     int confirm = QMessageBox::warning(
115         0,
116         title,
117         desc,
118         QMessageBox::Yes,
119         QMessageBox::No
120     );
121
122     if(confirm == QMessageBox::Yes) {
123         ProgressBar *bar = new ProgressBar(moveIcon, inverseMoveIcon);
124         bar->setBottomTexts(shortenPath(files[0].absolutePath()), shortenPath(destination.absolutePath()));
125         initOperation(new MoveThread(files, destination), bar);
126     }
127 }
128
129
130 void FileOperator::showErrorPrompt(OperationThread* op,
131     const QString &message,
132     const QString &fileName,
133     const int err)
134 {
135     QMessageBox msgBox;
136     QAbstractButton *cancelButton = msgBox.addButton(QMessageBox::Cancel);
137     QAbstractButton *abortButton = msgBox.addButton(tr("Abort"), QMessageBox::DestructiveRole);
138     QAbstractButton *retryButton = msgBox.addButton(QMessageBox::Retry);
139     QAbstractButton *ignoreButton = msgBox.addButton(QMessageBox::Ignore);
140     QAbstractButton *ignoreAllButton = msgBox.addButton(tr("Ignore All"), QMessageBox::AcceptRole);
141     msgBox.setText(message.arg(shortenPath(fileName)));
142
143     msgBox.exec();
144
145     if (msgBox.clickedButton() == cancelButton) {
146         op->pause = true;
147         op->setResponse(OperationThread::RETRY);
148     } else if (msgBox.clickedButton() == abortButton) {
149         op->setResponse(OperationThread::ABORT);
150     } else if (msgBox.clickedButton() == retryButton) {
151         op->setResponse(OperationThread::RETRY);
152     } else if (msgBox.clickedButton() == ignoreButton) {
153         op->setResponse(OperationThread::IGNORE);
154     } else if (msgBox.clickedButton() == ignoreAllButton) {
155         op->setResponse(OperationThread::IGNORE, true, err);
156     }
157 }
158
159
160 void FileOperator::showOverwritePrompt(
161     OperationThread* op,
162     const QString &fileName,
163     const bool dirOverDir)
164 {
165     Dialog msgBox;
166     QAbstractButton *yesButton = msgBox.addButtonFirst(QDialogButtonBox::Yes);
167     QAbstractButton *yesToAllButton = msgBox.addButtonFirst(QDialogButtonBox::YesToAll);
168     QAbstractButton *noButton = msgBox.addButtonSecond(QDialogButtonBox::No);
169     QAbstractButton *noToAllButton = msgBox.addButtonSecond(QDialogButtonBox::NoToAll);
170     QAbstractButton *abortButton = msgBox.addButtonSecond(tr("Abort"), QDialogButtonBox::DestructiveRole);
171     QAbstractButton *newNameButton = msgBox.addButtonFirst(tr("New Name"), QDialogButtonBox::AcceptRole);
172     QAbstractButton *askButton = 0;
173     QAbstractButton *skipDirButton = 0;
174
175     if (dirOverDir) {
176         msgBox.setText(tr("Directory %1 already exists. Overwrite the files inside?")
177             .arg(shortenPath(fileName)));
178         askButton = msgBox.addButtonFirst(tr("Ask"), QDialogButtonBox::AcceptRole);
179         skipDirButton = msgBox.addButtonSecond(tr("Skip"), QDialogButtonBox::NoRole);
180     } else {
181         msgBox.setText(tr("File %1 already exists. Overwrite?").arg(shortenPath(fileName)));
182     }
183
184     msgBox.exec();
185
186     if (msgBox.clickedButton == 0) {
187         op->pause = true;
188         op->setResponse(OperationThread::NONE);
189     } else if (msgBox.clickedButton == abortButton) {
190         op->setResponse(OperationThread::ABORT);
191     } else if (msgBox.clickedButton == yesButton) {
192         op->setResponse(OperationThread::OVERWRITE);
193     } else if (msgBox.clickedButton == yesToAllButton) {
194         op->setResponse(OperationThread::OVERWRITE, true);
195     } else if (msgBox.clickedButton == noButton) {
196         op->setResponse(OperationThread::KEEP);
197     } else if (msgBox.clickedButton == noToAllButton) {
198         op->setResponse(OperationThread::KEEP, true);
199     } else if (msgBox.clickedButton == askButton) {
200         op->setResponse(OperationThread::ASK);
201     } else if (msgBox.clickedButton == newNameButton) {
202         op->setResponse(OperationThread::NONE);
203     } else if (msgBox.clickedButton == skipDirButton) {
204         op->setResponse(OperationThread::SKIP_DIR);
205     }
206 }
207
208
209 void FileOperator::showInputFilenamePrompt(OperationThread* op,
210     const QFileInfo &file,
211     const bool dir)
212 {
213     bool ok;
214     QString prompt, error;
215
216     if (dir) {
217         prompt = tr("Enter the new directory name.");
218     } else {
219         prompt = tr("Enter the new file name.");
220     }
221
222     op->newNameFromDialog = "";
223     QString text = file.fileName();
224
225     while (true) {
226         text = QInputDialog::getText(this, QString(), prompt + error, QLineEdit::Normal, text, &ok);
227
228         if (!ok) break;
229
230         error = "";
231         if (text.contains(QRegExp("[\"*/:<>?\\\\|]"))) {
232             error = "<small><br/><font color = 'red'>" +
233                 tr("The name cannot contain any of the following characters: ") +
234                 "\"*/:&lt;&gt;?\\|</font></small>";
235         } else if (ok && !text.isEmpty()) {
236             QFileInfo info(file.path() + "/" + text);
237             op->newNameFromDialog = info.absoluteFilePath();
238             break;
239         }
240     }
241
242     op->wake();
243 }
244
245
246 void FileOperator::remove(OperationThread* op) {
247     op->wait();
248     ProgressBar *bar = get(op);
249     layout()->removeWidget(bar);
250     opList.removeAll(qMakePair(op, bar));
251     delete op;
252     delete bar;
253 }
254
255
256 void FileOperator::togglePauseOperation(ProgressBar* bar) {
257     OperationThread *op = get(bar);
258
259     if (op->pause) {
260         op->wake();
261     } else {
262         op->pause = true;
263     }
264 }
265
266
267 void FileOperator::abortOperation(ProgressBar* bar) {
268     OperationThread *op = get(bar);
269
270     int confirm = QMessageBox::warning(
271         0,
272         tr("Abort operation"),
273         tr("Are you sure you want to abort the operation?"),
274         QMessageBox::Yes,
275         QMessageBox::No
276     );
277
278     if(confirm == QMessageBox::Yes) {
279         op->abort = true;
280         op->pause = false;
281         op->wake();
282     }
283 }
284
285
286 void FileOperator::initOperation(OperationThread *thread, ProgressBar *bar) {
287     opList.append(qMakePair(thread, bar));
288
289     connect(thread, SIGNAL(showErrorPrompt(OperationThread*, const QString&, const QString&, const int)),
290         this, SLOT(showErrorPrompt(OperationThread*, const QString&, const QString&, const int)));
291     connect(thread, SIGNAL(showOverwritePrompt(OperationThread*, const QString&, bool)),
292         this, SLOT(showOverwritePrompt(OperationThread*, const QString&, bool)));
293     connect(thread, SIGNAL(showInputFilenamePrompt(OperationThread*, const QFileInfo&, bool)),
294         this, SLOT(showInputFilenamePrompt(OperationThread*, const QFileInfo&, bool)));
295     connect(thread, SIGNAL(finished(OperationThread*)),
296         this, SLOT(remove(OperationThread*)));
297
298     connect(thread, SIGNAL(totalSizeChanged(int)), bar, SLOT(setMaximum(int)));
299     connect(thread, SIGNAL(progressUpdate(int)), bar, SLOT(updateProgress(int)));
300     connect(thread, SIGNAL(fileNameUpdated(QString)), bar, SLOT(updateMainText(QString)));
301     connect(thread, SIGNAL(operationStarted(time_t)), bar, SLOT(setStartTime(time_t)));
302     connect(thread, SIGNAL(operationPaused()), bar, SLOT(pause()));
303     connect(thread, SIGNAL(operationResumed(time_t)), bar, SLOT(resume(time_t)));
304     connect(thread, SIGNAL(removeAfterCopy()), bar, SLOT(showRemoveNotice()));
305
306     connect(bar, SIGNAL(togglePauseOperation(ProgressBar*)), this, SLOT(togglePauseOperation(ProgressBar*)));
307     connect(bar, SIGNAL(abortOperation(ProgressBar*)), this, SLOT(abortOperation(ProgressBar*)));
308
309     layout()->addWidget(bar);
310     thread->start(QThread::LowestPriority);
311 }
312
313
314 ProgressBar *FileOperator::get(OperationThread *op) const {
315     for (OperationList::const_iterator it = opList.begin(); it != opList.end(); ++it) {
316         if (it->first == op) return it->second;
317     }
318     return 0;
319 }
320
321
322 OperationThread *FileOperator::get(ProgressBar *bar) const {
323     for (OperationList::const_iterator it = opList.begin(); it != opList.end(); ++it) {
324         if (it->second == bar) return it->first;
325     }
326     return 0;
327 }