68ca019256fd7719fe7bc6c2d5258a9ab9d8ea7c
[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 <QDir>
22 #include <QMessageBox>
23 #include <QHBoxLayout>
24 #include <QChar>
25
26 #include <math.h>
27 #include <errno.h>
28 #include <iostream>
29
30
31 #define SHOW_ERROR_PROMPT(promptString, fileName)                                           \
32     response = FileOperator::NONE;                                                          \
33     if (ignoreAll[errno]) {                                                                 \
34         response = FileOperator::IGNORE;                                                    \
35     } else {                                                                                \
36         char buf[255];                                                                      \
37         char *realBuf = strerror_r(errno, buf, 255);                                        \
38         emit showErrorPrompt(this, promptString + " " + realBuf + ".", fileName, errno);    \
39         waitCond.wait(&mutex);                                                              \
40     }
41
42
43 #define ERROR_PROMPT(operation, promptString, fileName)                                     \
44 {                                                                                           \
45     response = FileOperator::NONE;                                                          \
46     while (!abort && operation) {                                                           \
47         SHOW_ERROR_PROMPT(promptString, fileName)                                           \
48         if (response == FileOperator::IGNORE) {                                             \
49             break;                                                                          \
50         }                                                                                   \
51     }                                                                                       \
52 }
53
54
55 #define ERROR_PROMPT_XP(operation, promptString, fileName, onIgnore, quitCmd)               \
56 {                                                                                           \
57     ERROR_PROMPT(operation, promptString, fileName)                                         \
58     if (abort || response == FileOperator::IGNORE) {                                        \
59         if (!abort) onIgnore;                                                               \
60         quitCmd;                                                                            \
61     }                                                                                       \
62 }
63
64
65 #define OVERWRITE_PROMPT(file, newFile)                                                     \
66 {                                                                                           \
67     response = FileOperator::NONE;                                                          \
68                                                                                             \
69     if (newFile.exists()) {                                                                 \
70         if (overwriteAll != FileOperator::NONE) {                                           \
71             response = overwriteAll;                                                        \
72         } else {                                                                            \
73             bool dirOverDir = false;                                                        \
74             if (newFile.isDir() && file.isDir()) dirOverDir = true;                         \
75             emit showOverwritePrompt(this, newFile.absoluteFilePath(), dirOverDir);         \
76             waitCond.wait(&mutex);                                                          \
77         }                                                                                   \
78     }                                                                                       \
79 }
80
81
82 FileOperator::FileOperator(QWidget *parent) : QWidget(parent) {
83     QHBoxLayout *layout = new QHBoxLayout;
84     layout->setContentsMargins(0, 0, 0, 0);
85     layout->setSpacing(0);
86     setLayout(layout);
87 }
88
89
90 QString FileOperator::shortenPath(const QString &path) {
91     QString homePath = QFSFileEngine::homePath();
92     QString result = path;
93     if (path.indexOf(homePath, 0) == 0) {
94         result.replace(0, homePath.size(), "~");
95     }
96
97     return result;
98 }
99
100
101 void FileOperator::deleteFiles(const QFileInfoList &files) {
102     QString title, desc;
103     if (files.size() == 1) {
104         title = tr("Delete file");
105         desc = tr("Are you sure you want to delete %1?")
106             .arg(FileOperator::shortenPath(files[0].absoluteFilePath()));
107     } else {
108         title = tr("Delete files");
109         desc = tr("You are about to delete %1 files. Are you sure you want to continue?").arg(files.size());
110     }
111
112     int confirm = QMessageBox::warning(
113         0,
114         title,
115         desc,
116         QMessageBox::Yes,
117         QMessageBox::No
118     );
119
120     if(confirm == QMessageBox::Yes) {
121         caterNewThread(new DeleteThread(files));
122     }
123 }
124
125
126 void FileOperator::copyFiles(const QFileInfoList &files, QDir &destination) {
127     QString title, desc;
128     if (files.size() == 1) {
129         title = tr("Copy file");
130         desc = tr("Are you sure you want to copy %1 to %2?")
131             .arg(FileOperator::shortenPath(files[0].absoluteFilePath()))
132             .arg(FileOperator::shortenPath(destination.absolutePath()));
133     } else {
134         title = tr("Copy files");
135         desc = tr("You are about to copy %1 files to %2. Are you sure you want to continue?")
136             .arg(files.size()).arg(FileOperator::shortenPath(destination.absolutePath()));
137     }
138
139     int confirm = QMessageBox::warning(
140         0,
141         title,
142         desc,
143         QMessageBox::Yes,
144         QMessageBox::No
145     );
146
147     if(confirm == QMessageBox::Yes) {
148         caterNewThread(new CopyThread(files, destination));
149     }
150 }
151
152
153 void FileOperator::moveFiles(const QFileInfoList &files, QDir &destination) {
154     // for move we don't wanna move to the same dir
155     if (files[0].absolutePath() == destination.absolutePath()) return;
156
157     QString title, desc;
158     if (files.size() == 1) {
159         title = tr("Move file");
160         desc = tr("Are you sure you want to move %1 to %2?")
161             .arg(FileOperator::shortenPath(files[0].absoluteFilePath()))
162             .arg(FileOperator::shortenPath(destination.absolutePath()));
163     } else {
164         title = tr("Move files");
165         desc = tr("You are about to move %1 files to %2. Are you sure you want to continue?")
166             .arg(files.size()).arg(FileOperator::shortenPath(destination.absolutePath()));
167     }
168
169     int confirm = QMessageBox::warning(
170         0,
171         title,
172         desc,
173         QMessageBox::Yes,
174         QMessageBox::No
175     );
176
177     if(confirm == QMessageBox::Yes) {
178         caterNewThread(new MoveThread(files, destination));
179     }
180 }
181
182
183 void FileOperator::showErrorPrompt(FileManipulatorThread* manipulator,
184     const QString &message,
185     const QString &fileName,
186     const int err)
187 {
188     QMessageBox msgBox;
189     msgBox.addButton(QMessageBox::Cancel);
190     QAbstractButton *abortButton = msgBox.addButton(tr("Abort"), QMessageBox::DestructiveRole);
191     QAbstractButton *retryButton = msgBox.addButton(QMessageBox::Retry);
192     QAbstractButton *ignoreButton = msgBox.addButton(QMessageBox::Ignore);
193     QAbstractButton *ignoreAllButton = msgBox.addButton(tr("Ignore All"), QMessageBox::AcceptRole);
194     msgBox.setText(message.arg(FileOperator::shortenPath(fileName)));
195
196     msgBox.exec();
197
198     if (msgBox.clickedButton() == abortButton) {
199         manipulator->setResponse(ABORT);
200     } else if (msgBox.clickedButton() == retryButton) {
201         manipulator->setResponse(RETRY);
202     } else if (msgBox.clickedButton() == ignoreButton) {
203         manipulator->setResponse(IGNORE);
204     } else if (msgBox.clickedButton() == ignoreAllButton) {
205         manipulator->setResponse(IGNORE, true, err);
206     }
207 }
208
209
210 void FileOperator::showOverwritePrompt(
211     FileManipulatorThread* manipulator,
212     const QString &fileName,
213     const bool dirOverDir)
214 {
215     QMessageBox msgBox;
216     msgBox.addButton(QMessageBox::Cancel);
217     QAbstractButton *yesButton = msgBox.addButton(QMessageBox::Yes);
218     QAbstractButton *yesToAllButton = msgBox.addButton(QMessageBox::YesToAll);
219     QAbstractButton *noButton = msgBox.addButton(QMessageBox::No);
220     QAbstractButton *noToAllButton = msgBox.addButton(QMessageBox::NoToAll);
221     QAbstractButton *abortButton = msgBox.addButton(tr("Abort"), QMessageBox::DestructiveRole);
222     QAbstractButton *askButton = 0;
223
224     if (dirOverDir) {
225         msgBox.setText(tr("Directory %1 already exists. Overwrite the files inside?")
226             .arg(FileOperator::shortenPath(fileName)));
227         askButton = msgBox.addButton(tr("Ask"), QMessageBox::AcceptRole);
228     } else {
229         msgBox.setText(tr("File %1 already exists. Overwrite?").arg(FileOperator::shortenPath(fileName)));
230     }
231
232     msgBox.exec();
233
234     if (msgBox.clickedButton() == abortButton) {
235         manipulator->setResponse(ABORT);
236     } else if (msgBox.clickedButton() == yesButton) {
237         manipulator->setResponse(OVERWRITE);
238     } else if (msgBox.clickedButton() == yesToAllButton) {
239         manipulator->setResponse(OVERWRITE, true);
240     } else if (msgBox.clickedButton() == noButton) {
241         manipulator->setResponse(KEEP);
242     } else if (msgBox.clickedButton() == noToAllButton) {
243         manipulator->setResponse(KEEP, true);
244     } else if (msgBox.clickedButton() == askButton) {
245         manipulator->setResponse(NONE, true);
246     }
247 }
248
249
250 void FileOperator::remove(FileManipulatorThread* manipulator) {
251     manipulator->wait();
252     layout()->removeWidget(manipulator->progressBar);
253     manipulatorList.removeAll(manipulator);
254     delete manipulator;
255 }
256
257
258 void FileOperator::setBarSize(FileManipulatorThread* manipulator, unsigned int size) {
259     if (!manipulator->progressBar->maximum()) {
260         manipulator->startTime = time(0);
261     }
262     manipulator->progressBar->setMinimum(0);
263     manipulator->progressBar->setMaximum(size);
264 }
265
266
267 void FileOperator::updateProgress(FileManipulatorThread* manipulator, int value) {
268     manipulator->setText(value);
269 }
270
271
272 void FileOperator::caterNewThread(FileManipulatorThread *thread) {
273     manipulatorList.append(thread);
274
275     connect(thread, SIGNAL(showErrorPrompt(FileManipulatorThread*, const QString&, const QString&, const int)),
276         this, SLOT(showErrorPrompt(FileManipulatorThread*, const QString&, const QString&, const int)));
277     connect(thread, SIGNAL(showOverwritePrompt(FileManipulatorThread*, const QString&, bool)),
278         this, SLOT(showOverwritePrompt(FileManipulatorThread*, const QString&, bool)));
279     connect(thread, SIGNAL(finished(FileManipulatorThread*)),
280         this, SLOT(remove(FileManipulatorThread*)));
281     connect(thread, SIGNAL(setBarSize(FileManipulatorThread*, unsigned int)),
282         this, SLOT(setBarSize(FileManipulatorThread*, unsigned int)));
283     connect(thread, SIGNAL(updateProgress(FileManipulatorThread*, int)),
284         this, SLOT(updateProgress(FileManipulatorThread*, int)));
285
286     thread->progressBar->setValue(0);
287
288     layout()->addWidget(thread->progressBar);
289     thread->start(QThread::LowestPriority);
290 }
291
292
293 FileManipulatorThread::FileManipulatorThread(const QFileInfoList files, QDir dest) :
294     progressBar(new QProgressBar()),
295     startTime(0),
296     files(files),
297     dest(dest),
298     response(FileOperator::NONE),
299     overwriteAll(FileOperator::NONE),
300     abort(false),
301     lastTimeUpdate(0),
302     barSize(0),
303     barValue(0),
304     fileSize(0),
305     fileValue(0)
306 {
307     memset(ignoreAll, false, sizeof(ignoreAll));
308     progressBar->setMaximum(0);
309     QFont barFont = progressBar->font();
310     barFont.setPointSize(12);
311     progressBar->setFont(barFont);
312     progressBar->setFormat(tr("Gathering information..."));
313     progressBar->setMinimumHeight(44);
314     progressBar->setStyle(new QPlastiqueStyle);
315     //progressBar->setStyle(new QMotifStyle);
316 }
317
318
319 FileManipulatorThread::~FileManipulatorThread() {
320     delete progressBar;
321 }
322
323
324 void FileManipulatorThread::setResponse(
325     const FileOperator::Response response,
326     const bool applyToAll,
327     const int err)
328 {
329     mutex.lock();
330
331     this->response = response;
332
333     if (applyToAll) {
334         if (response == FileOperator::KEEP
335             || response == FileOperator::OVERWRITE
336             || response == FileOperator::NONE)
337         {
338             overwriteAll = response;
339         }
340
341         if (response == FileOperator::IGNORE) {
342             ignoreAll[err] = true;
343         }
344     }
345
346     if (response == FileOperator::ABORT) abort = true;
347
348     mutex.unlock();
349     waitCond.wakeAll();
350 }
351
352
353 void FileManipulatorThread::processFiles(const QFileInfoList &files) {
354     for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
355         perform(*it);
356         if (abort) break;
357     }
358 }
359
360
361 bool FileManipulatorThread::remove(QString &fileName, const bool ignoreDirNotEmpty) {
362     return remove(QFileInfo(fileName), ignoreDirNotEmpty);
363 }
364
365
366 bool FileManipulatorThread::remove(const QFileInfoList &files, const bool ignoreDirNotEmpty) {
367     bool res = true;
368     for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
369         if (!remove(*it, ignoreDirNotEmpty)) res = false;
370         if (abort) break;
371     }
372     return res;
373 }
374
375
376 bool FileManipulatorThread::remove(const QFileInfo &file, const bool ignoreDirNotEmpty) {
377     QString path = file.absoluteFilePath();
378     QFSFileEngine engine(path);
379
380     if (file.isDir()) {
381         QFileInfoList list = listDirFiles(path);
382
383         if (ignoreDirNotEmpty && list.size()) return true;
384
385         if (!remove(list, ignoreDirNotEmpty)) return false;
386
387         ERROR_PROMPT(!engine.rmdir(path, false),
388             tr("Error deleting directory %1."), path)
389     } else {
390         ERROR_PROMPT(!engine.remove(),
391             tr("Error deleting file %1."), path)
392     }
393
394     if (abort || response == FileOperator::IGNORE) return false;
395     return true;
396 }
397
398
399 void FileManipulatorThread::copy(const QFileInfo &file, const bool removeAfterCopy) {
400     std::cout << (removeAfterCopy ? "MOVING " : "COPYING ") << file.absoluteFilePath().toStdString()
401         << " to " << dest.absolutePath().toStdString() << std::endl;
402
403     QString path(file.absoluteFilePath());
404     QString newPath(dest.absolutePath() + "/" + file.fileName());
405     QFSFileEngine engine(path);
406     QFSFileEngine newEngine(newPath);
407     QFileInfo newFile(newPath);
408
409     updateFile(path);
410
411     // hack to prevent asking about the same file if we already asked in the rename(...) function
412     if (overwriteAll == FileOperator::DONT_ASK_ONCE) {
413         overwriteAll = FileOperator::NONE;
414     } else {
415         OVERWRITE_PROMPT(file, newFile)
416     }
417
418     if (abort) return;
419
420     // this loop is here only to allow easily breaking out to the end (and remove the source file/dir)
421     while (1) {
422         if (response == FileOperator::KEEP) {
423             updateProgress(fileSizeMap[path]);
424             break;
425         }
426
427         FileOperator::Response overwriteResponse = response;
428
429         if (file.isDir()) {
430             if (newFile.exists() && !newFile.isDir()) {
431                 if(!remove(newPath)) {
432                     updateProgress(fileSizeMap[path]);
433                     break;
434                 }
435                 newFile = QFileInfo(newPath);
436             }
437
438             if (!newFile.exists()) {
439                 ERROR_PROMPT_XP(!engine.mkdir(newPath, false),
440                     tr("Error creating directory %1."), newPath,
441                     updateProgress(fileSizeMap[path]),
442                     break)
443             }
444
445             updateProgress(1);
446             
447             QDir destBackup = dest;
448             dest = newPath;
449
450             FileOperator::Response tmpResp = overwriteAll;
451             overwriteAll = overwriteResponse;
452
453             processFiles(listDirFiles(path));
454
455             overwriteAll = tmpResp;
456
457             ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
458                 tr("Error setting permissions for directory %1."), newPath)
459
460             if (abort) return;
461
462             dest = destBackup;
463         } else {
464             ERROR_PROMPT_XP(engine.isSequential(),
465                 tr("Cannot copy sequential file %1."), path,
466                 updateProgress(fileSizeMap[path]),
467                 break)
468
469             if (newFile.exists() && newFile.isDir()) {
470                 ERROR_PROMPT_XP(!remove(newPath),
471                     tr("Cannot replace directory %1 due to previous errors."), newPath,
472                     updateProgress(fileSizeMap[path]),
473                     break)
474             }
475
476             ERROR_PROMPT_XP(!engine.open(QIODevice::ReadOnly),
477                 tr("Error reading file %1."), path,
478                 updateProgress(fileSizeMap[path]),
479                 break)
480
481             bool ignore = false;
482             while (!abort && !ignore) {
483                 engine.seek(0);
484
485                 ERROR_PROMPT(!newEngine.open(QIODevice::WriteOnly | QIODevice::Truncate),
486                     tr("Error writing file %1."), newPath)
487
488                 if (abort || response == FileOperator::IGNORE) {
489                     if (response == FileOperator::IGNORE) {
490                         updateProgress(fileSizeMap[path] - fileValue);
491                         ignore = true;
492                     }
493                     break;
494                 }
495
496                 bool error = false;
497                 char block[524288];
498                 qint64 bytes;
499                 while ((bytes = engine.read(block, sizeof(block))) > 0) {
500                     if (bytes == -1 || bytes != newEngine.write(block, bytes)) {
501                         if (bytes == -1) {
502                             SHOW_ERROR_PROMPT(tr("Error while reading from file %1."), path);
503                         } else {
504                             SHOW_ERROR_PROMPT(tr("Error while writing to file %1."), newPath);
505                         }
506
507                         if (!abort) {
508                             if (response == FileOperator::IGNORE) {
509                                 updateProgress(fileSizeMap[path] - fileValue);
510                                 ignore = true;
511                             } else {
512                                 updateProgress(-fileValue);
513                             }
514                         }
515                         error = true;
516                         break;
517                     }
518
519                     updateProgress(1);
520                 }
521
522                 if (!error) break;
523             }
524
525             engine.close();
526             newEngine.close();
527
528             if (abort || ignore) {
529                 newEngine.remove();
530             } else {
531                 ERROR_PROMPT(!newEngine.setPermissions(file.permissions()),
532                     tr("Error setting permissions for file %1."), newPath)
533             }
534         }
535
536         break;
537     }
538
539     if (removeAfterCopy && !abort) remove(path, true);
540 }
541
542
543 unsigned int FileManipulatorThread::countFiles(const QFileInfoList &files) {
544     unsigned int res = 0;
545
546     for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
547         unsigned int size = 1;
548
549         if (it->isDir()) {
550             size += countFiles(listDirFiles(it->absoluteFilePath()));
551         }
552
553         res += size;
554         fileSizeMap[it->absoluteFilePath()] = size;
555     }
556
557     return res;
558 }
559
560
561 unsigned int FileManipulatorThread::calculateFileSize(const QFileInfoList &files) {
562     unsigned int res = 0;
563
564     for (QFileInfoList::const_iterator it = files.begin(); it != files.end(); ++it) {
565         unsigned int size = 1;
566
567         if (it->isDir()) {
568             size += calculateFileSize(listDirFiles(it->absoluteFilePath()));
569         } else {
570             size = ceil(static_cast<float>(it->size()) / 524288);
571         }
572
573         res += size;
574         fileSizeMap[it->absoluteFilePath()] = size;
575     }
576
577     return res;
578 }
579
580
581 QFileInfoList FileManipulatorThread::listDirFiles(const QString &dirPath) {
582     QDir dir = dirPath;
583     return dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden);
584 }
585
586
587 void FileManipulatorThread::setBarSize(unsigned int size) {
588     barSize = size;
589     emit setBarSize(this, size);
590 }
591
592
593 void FileManipulatorThread::updateProgress(int value) {
594     barValue += value;
595     fileValue += value;
596     emit updateProgress(this, value);
597 }
598
599
600 void FileManipulatorThread::updateFile(const QString &name) {
601     fileValue = 0;
602     fileName = FileOperator::shortenPath(name);
603     emit updateProgress(this, 0);
604 }
605
606
607 void FileManipulatorThread::setText(int value) {
608     if (progressBar->value() + value > progressBar->maximum()) {
609         std::cout << "WARNING, EXCEEDING MAXIMUM BY " << value << std::endl;
610     }
611  
612     time_t now = time(0);
613     if (lastTimeUpdate < now) {
614         lastTimeUpdate = now;
615
616         time_t elapsed = now - startTime;
617         time_t remaining = (time_t) ((float) elapsed / barValue * (barSize - barValue));
618         struct tm *ts = gmtime(&remaining);
619         
620         if (remaining < 60) {
621             strftime(timeBuf, sizeof(timeBuf), "%Ss", ts);
622         } else if (remaining < 3600) {
623             strftime(timeBuf, sizeof(timeBuf), "%M:%S", ts);
624         } else {
625             strftime(timeBuf, sizeof(timeBuf), "%H:%M:%S", ts);
626         }
627     }
628
629     progressBar->setFormat(barText.arg(fileName) + "\n%p%   ETA " + timeBuf);
630     progressBar->setValue(progressBar->value() + value);
631 }
632
633
634 DeleteThread::DeleteThread(const QFileInfoList &files) : FileManipulatorThread(files) {
635     barText = tr("deleting %1");
636 }
637
638
639 void DeleteThread::run() {
640     mutex.lock();
641
642     setBarSize(countFiles(files));
643
644     processFiles(files);
645
646     sleep(0.5);
647     emit finished(this);
648 }
649
650
651 void DeleteThread::perform(const QFileInfo &file) {
652     std::cout << "DELETING " << file.absoluteFilePath().toStdString() << std::endl;
653
654     QString path = file.absoluteFilePath();
655     QFSFileEngine engine(path);
656
657     updateFile(path);
658
659     if (file.isDir()) {
660         processFiles(listDirFiles(path));
661
662         if (!listDirFiles(path).size()) {
663             ERROR_PROMPT(!engine.rmdir(path, false),
664                 tr("Error deleting directory %1."), path)
665         }
666     } else {
667         ERROR_PROMPT(!engine.remove(),
668             tr("Error deleting file %1."), path)
669     }
670
671     if (!abort) updateProgress(1);
672 }
673
674
675 CopyThread::CopyThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {
676     barText = tr("copying %1");
677 }
678
679
680 void CopyThread::run() {
681     mutex.lock();
682
683     setBarSize(calculateFileSize(files));
684
685     processFiles(files);
686
687     sleep(0.5);
688     emit finished(this);
689 }
690
691
692 void CopyThread::perform(const QFileInfo &file) {
693     copy(file, false);
694 }
695
696
697 MoveThread::MoveThread(const QFileInfoList &files, QDir &dest) : FileManipulatorThread(files, dest) {
698     barText = tr("moving %1");
699 }
700
701
702 void MoveThread::run() {
703     mutex.lock();
704
705     rename(files, dest);
706
707     sleep(0.5);
708     emit finished(this);
709 }
710
711
712 void MoveThread::rename(const QFileInfoList &files, const QDir &dest) {
713     setBarSize(barSize + files.size());
714
715     for (int i = 0; i < files.size(); ++i) {
716         QString path = files[i].absoluteFilePath();
717         QFSFileEngine engine(path);
718         QString newPath = dest.absolutePath() + "/" + files[i].fileName();
719
720         updateFile(path);
721
722         OVERWRITE_PROMPT(files[i], QFileInfo(newPath))
723
724         if (response == FileOperator::KEEP) {
725             remove(path);
726             if (abort) break;
727             updateProgress(1);
728             continue;
729         }
730
731         while (!abort && !engine.rename(newPath)) {
732             // source and target are on different partitions
733             // this should happen on the first file, unless some are skipped by overwrite prompt
734             // we calculate the actual file sizes, because from now on copy & remove takes over
735             if (errno == EXDEV) {
736                 setBarSize(barValue + calculateFileSize(files));
737
738                 FileOperator::Response tmpResp = overwriteAll;
739                 overwriteAll = response;
740                 // hack: we already checked the first file we are sending to processFiles(...)
741                 // so we don't want to ask about this one again
742                 if (overwriteAll == FileOperator::NONE) overwriteAll = FileOperator::DONT_ASK_ONCE;
743
744                 processFiles(files.mid(i));
745
746                 overwriteAll = tmpResp;
747
748                 // just to quit the loops, we are done
749                 abort = true;
750             // the target is nonempty dir. lets call this recursively and rename the contents one by one
751             } else if (errno == ENOTEMPTY || errno == EEXIST) {
752                 FileOperator::Response tmpResp = overwriteAll;
753                 overwriteAll = response;
754
755                 rename(listDirFiles(path), QDir(newPath));
756                 if (abort) break;
757
758                 overwriteAll = tmpResp;
759
760                 ERROR_PROMPT(!engine.rmdir(path, false), tr("Error deleting directory %1."), path)
761
762                 break;
763             // source and target are nonmatching types(file and dir)
764             // remove the target and let it loop once again
765             } else if (errno == ENOTDIR || errno == EISDIR) {
766                 if (!remove(newPath)) break;
767             } else {
768                 SHOW_ERROR_PROMPT(tr("Error moving %1."), path)
769
770                 if (response == FileOperator::IGNORE) {
771                     break;
772                 }
773             }
774         }
775             
776         if (abort) break;
777         updateProgress(1);
778     }
779 }
780
781
782 void MoveThread::perform(const QFileInfo &file) {
783     copy(file, true);
784 }