1
/**************************************************************************
3
* Copyright 2013 Canonical Ltd.
4
* Copyright 2013 Carlos J Mazieri <carlos.mazieri@gmail.com>
6
* You may use this file under the terms of the BSD license as follows:
8
* "Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions are
11
* * Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* * Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in
15
* the documentation and/or other materials provided with the
17
* * Neither the name of Nemo Mobile nor the names of its contributors
18
* may be used to endorse or promote products derived from this
19
* software without specific prior written permission.
21
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
33
* File: filesystemaction.cpp
37
#include "filesystemaction.h"
38
#include "clipboard.h"
40
#if defined(Q_OS_UNIX)
41
#include <sys/statvfs.h>
46
#include <QDirIterator>
52
#include <QTemporaryFile>
55
* number of the files to work on a step, when this number is reached a signal is emitted
60
* buffer size to to single read/write operation
62
#define COPY_BUFFER_SIZE 4096
65
* Auxiliar Actions do not emit progress() signal
66
* \sa moveDirToTempAndRemoveItLater()
68
#define SHOULD_EMIT_PROGRESS_SIGNAL(action) (!action->isAux)
70
#define COMMON_SIZE_ITEM 120
75
void FileSystemAction::CopyFile::clear()
78
if (source) delete source;
79
if (target) delete target;
86
//===============================================================================================
88
* \brief FileSystemAction::FileSystemAction
91
FileSystemAction::FileSystemAction(QObject *parent) :
94
, m_cancelCurrentAction(false)
96
, m_clipboardChanged(false)
101
//===============================================================================================
103
* \brief FileSystemAction::~FileSystemAction
105
FileSystemAction::~FileSystemAction()
110
//===============================================================================================
112
* \brief FileSystemAction::remove
115
void FileSystemAction::remove(const QStringList &paths)
117
createAndProcessAction(ActionRemove, paths);
120
//===============================================================================================
122
* \brief FileSystemAction::createAction
127
FileSystemAction::Action* FileSystemAction::createAction(ActionType type, int origBase)
129
Action * action = new Action();
131
action->baseOrigSize = origBase;
132
action->targetPath = m_path;
133
action->totalItems = 0;
134
action->currItem = 0;
135
action->currEntryIndex = 0;
136
action->totalBytes = 0;
137
action->bytesWritten = 0;
138
action->done = false;
139
action->auxAction = 0;
140
action->isAux = false;
141
action->currEntry = 0;
147
//===============================================================================================
149
* \brief FileSystemAction::addEntry
153
void FileSystemAction::addEntry(Action* action, const QString& pathname)
156
qDebug() << Q_FUNC_INFO << pathname;
158
DirItemInfo info(pathname);
159
if (!info.isAbsolute())
161
info.setFile(action->targetPath, pathname);
165
emit error(QObject::tr("File or Directory does not exist"),
166
pathname + QObject::tr(" does not exist")
170
ActionEntry * entry = new ActionEntry();
171
//this is the item being handled
172
entry->reversedOrder.append(info);
173
// verify if the destination item already exists
174
if (action->type == ActionCopy ||
175
action->type == ActionMove ||
176
action->type == ActionHardMoveCopy)
178
DirItemInfo destination(targetFom(info.absoluteFilePath(), action));
179
entry->alreadyExists = destination.exists();
181
//ActionMove will perform a rename, so no Directory expanding is necessary
182
if (action->type != ActionMove && info.isDir() && !info.isSymLink())
184
QDirIterator it(info.absoluteFilePath(),
185
QDir::AllEntries | QDir::System |
186
QDir::NoDotAndDotDot | QDir::Hidden,
187
QDirIterator::Subdirectories);
188
while (it.hasNext() && !it.next().isEmpty())
190
entry->reversedOrder.prepend(it.fileInfo());
193
//set steps and total bytes considering all items in the Entry
194
int counter = entry->reversedOrder.count();
197
int bufferSize = (COPY_BUFFER_SIZE * STEP_FILES);
200
const DirItemInfo & item = entry->reversedOrder.at(counter);
201
size = (item.isFile() && !item.isDir() && !item.isSymLink()) ?
202
item.size() : COMMON_SIZE_ITEM;
203
action->totalBytes += size;
204
if (action->type == ActionCopy || action->type == ActionHardMoveCopy)
206
if ( (sizeSteps = size / bufferSize) )
208
if ( !(size % bufferSize) )
212
action->steps += sizeSteps ;
216
//set final steps for the Entry based on Items number
217
int entrySteps = entry->reversedOrder.count() / STEP_FILES;
218
if ( entry->reversedOrder.count() % STEP_FILES) entrySteps++;
219
action->steps += entrySteps;
220
action->totalItems += entry->reversedOrder.count();
222
qDebug() << "entrySteps" << entrySteps << "from entry counter" << entry->reversedOrder.count()
223
<< "total steps" << action->steps;
225
//now put the Entry in the Action
226
action->entries.append(entry);
229
//===============================================================================================
231
* \brief FileSystemAction::processAction
233
void FileSystemAction::processAction()
237
//it will be ActionHardMoveRemove only when switched from ActionHardMoveCopy
238
//in this case the move is done in two steps COPY and REMOVE
239
if (m_curAction->type != ActionHardMoveCopy)
245
if (!m_curAction && m_queuedActions.count())
247
m_curAction = m_queuedActions.at(0);
248
m_curAction->currEntry = static_cast<ActionEntry*>
249
( m_curAction->entries.at(0));
250
m_queuedActions.remove(0,1);
255
qDebug() << Q_FUNC_INFO << "performing action type" << m_curAction->type;
258
m_cancelCurrentAction = false;
260
m_errorTitle.clear();
261
scheduleSlot(SLOT(processActionEntry()));
262
if (SHOULD_EMIT_PROGRESS_SIGNAL(m_curAction))
264
emit progress(0,m_curAction->totalItems, 0);
274
//===============================================================================================
276
* \brief FileSystemAction::processActionEntry
278
void FileSystemAction::processActionEntry()
281
qDebug() << Q_FUNC_INFO;
284
ActionEntry * curEntry = m_curAction->currEntry;
286
#if defined(SIMULATE_LONG_ACTION)
288
unsigned int delay = SIMULATE_LONG_ACTION;
291
delay = 100; //each (10 * STEP_FILES) files will waits a second
292
QThread::currentThread()->wait(delay);
296
if (!m_cancelCurrentAction)
298
switch(m_curAction->type)
301
case ActionHardMoveRemove:
302
removeEntry(curEntry);
306
case ActionHardMoveCopy:
307
processCopyEntry(); // specially: this is a slot
317
//===============================================================================================
319
* \brief FileSystemAction::endActionEntry
321
void FileSystemAction::endActionEntry()
324
qDebug() << Q_FUNC_INFO;
326
ActionEntry * curEntry = m_curAction->currEntry;
328
// first of all check for any error or a cancel issued by the user
329
if (m_cancelCurrentAction)
331
if (!m_errorTitle.isEmpty())
333
emit error(m_errorTitle, m_errorMsg);
335
//it may have other actions to do
336
scheduleSlot(SLOT(processAction()));
339
// check if the current entry has finished
340
// if so Views need to receive the notification about that
341
if (curEntry->currItem == curEntry->reversedOrder.count())
343
const DirItemInfo & mainItem = curEntry->reversedOrder.at(curEntry->currItem -1);
344
m_curAction->currEntryIndex++;
345
switch(m_curAction->type)
348
emit removed(mainItem);
350
case ActionHardMoveRemove: // nothing to do
352
case ActionHardMoveCopy:
353
//check if is doing a hard move and the copy part has finished
354
//if so switch the action to remove
355
if (m_curAction->currEntryIndex == m_curAction->entries.count())
357
m_curAction->type = ActionHardMoveRemove;
358
m_curAction->currEntryIndex = 0;
359
int entryCounter = m_curAction->entries.count();
361
while (entryCounter--)
363
entry = m_curAction->entries.at(entryCounter);
368
case ActionCopy: // ActionHardMoveCopy is also checked here
371
QString addedItem = targetFom(mainItem.absoluteFilePath(), m_curAction);
372
if (!curEntry->added && !curEntry->alreadyExists)
374
emit added(addedItem);
375
curEntry->added = true;
379
emit changed(DirItemInfo(addedItem));
385
}//end if (curEntry->currItem == curEntry->reversedOrder.count())
387
if (curEntry->currStep == STEP_FILES)
389
curEntry->currStep = 0;
392
int percent = notifyProgress();
393
//Check if the current action has finished or cancelled
394
if (m_cancelCurrentAction ||
395
m_curAction->currEntryIndex == m_curAction->entries.count())
397
if (!m_cancelCurrentAction)
405
//it may have other actions to do
406
scheduleSlot(SLOT(processAction()));
410
m_curAction->currEntry = static_cast<ActionEntry*>
411
( m_curAction->entries.at(m_curAction->currEntryIndex) );
412
//keep working on current Action maybe more entries
413
scheduleSlot(SLOT(processActionEntry()));
417
//===============================================================================================
419
* \brief FileSystemAction::cancel
421
void FileSystemAction::cancel()
423
m_cancelCurrentAction = true;
426
//===============================================================================================
428
* \brief FileSystemAction::removeEntry
431
void FileSystemAction::removeEntry(ActionEntry *entry)
434
//do one step at least
435
for(; !m_cancelCurrentAction &&
436
entry->currStep < STEP_FILES &&
437
m_curAction->currItem < m_curAction->totalItems &&
438
entry->currItem < entry->reversedOrder.count()
439
; entry->currStep++, m_curAction->currItem++, entry->currItem++
443
const DirItemInfo &fi = entry->reversedOrder.at(entry->currItem);
444
if (fi.isDir() && !fi.isSymLink())
446
m_cancelCurrentAction = !dir.rmdir(fi.absoluteFilePath());
450
m_cancelCurrentAction = !QFile::remove(fi.absoluteFilePath());
453
qDebug() << Q_FUNC_INFO << "remove ret=" << !m_cancelCurrentAction << fi.absoluteFilePath();
455
if (m_cancelCurrentAction)
457
m_errorTitle = QObject::tr("Could not remove the item ") +
458
fi.absoluteFilePath();
459
m_errorMsg = ::strerror(errno);
465
//===============================================================================================
467
* \brief FileSystemAction::copyEntry
470
void FileSystemAction::processCopyEntry()
472
ActionEntry * entry = m_curAction->currEntry;
475
qDebug() << Q_FUNC_INFO << "processing"
476
<< entry->reversedOrder.at(entry->reversedOrder.count() -1).absoluteFilePath();
479
* This flag will be true when processCopySingleFile() has put any slot in the execution queue
480
* it will work to stop the loop.
481
* Later processCopyEntry() will be called again to continue working
483
bool scheduleAnySlot = false;
485
//first item from an Entry,
486
if (entry->currItem == 0 && entry->alreadyExists && entry->newName == 0)
488
//making backup only if the targetpath == origPath, otherwise the item is overwritten
489
if (m_curAction->targetPath == m_curAction->origPath)
491
//it will check again if the target exists
492
//if so, sets the entry->newName
493
//then targetFom() will use entry->newName for
494
// sub items in the Entry if the Entry is a directory
495
if (!makeBackupNameForCurrentItem(m_curAction) )
497
m_cancelCurrentAction = true;
498
m_errorTitle = QObject::tr("Could not find a suitable name to backup");
499
m_errorMsg = entry->reversedOrder.at(
500
entry->reversedOrder.count() -1
501
).absoluteFilePath();
507
qDebug() << entry->reversedOrder.at(entry->reversedOrder.count() -1).absoluteFilePath()
508
<< " already exists and will be overwritten";
513
for(; !m_cancelCurrentAction && !scheduleAnySlot &&
514
entry->currStep < STEP_FILES &&
515
m_curAction->currItem < m_curAction->totalItems &&
516
entry->currItem < entry->reversedOrder.count()
517
; entry->currStep++, entry->currItem++
521
const DirItemInfo &fi = entry->reversedOrder.at(entry->currItem);
522
QString orig = fi.absoluteFilePath();
523
QString target = targetFom(orig, m_curAction);
524
QString path(target);
525
// do this here to allow progress send right item number, copySingleFile will emit progress()
526
m_curAction->currItem++;
528
if (fi.isFile() || fi.isSymLink())
530
DirItemInfo t(target);
533
//check if the main item in the entry is a directory
534
//if so it needs to appear on any attached view
535
if ( m_curAction->currItem == 1
536
&& entry->reversedOrder.last().isDir()
537
&& !entry->reversedOrder.last().isSymLink()
540
QString entryDir = targetFom(entry->reversedOrder.last().absoluteFilePath(), m_curAction);
541
QDir entryDirObj(entryDir);
542
if (!entryDirObj.exists() && entryDirObj.mkpath(entryDir))
544
emit added(entryDir);
549
if (!d.exists() && !d.mkpath(path))
551
m_cancelCurrentAction = true;
552
m_errorTitle = QObject::tr("Could not create the directory");
558
m_cancelCurrentAction = ! copySymLink(target,fi.diskFileInfo());
559
if (m_cancelCurrentAction)
561
m_errorTitle = QObject::tr("Could not create link to");
564
m_curAction->bytesWritten += COMMON_SIZE_ITEM;
569
m_cancelCurrentAction = !
570
QFile(target).setPermissions(fi.permissions());
571
if (m_cancelCurrentAction)
573
m_errorTitle = QObject::tr("Could not set permissions to dir");
576
m_curAction->bytesWritten += COMMON_SIZE_ITEM;
581
qint64 needsSize = 0;
582
m_curAction->copyFile.clear();
583
m_curAction->copyFile.source = new QFile(orig);
584
m_cancelCurrentAction = !m_curAction->copyFile.source->open(QFile::ReadOnly);
585
if (m_cancelCurrentAction)
587
m_errorTitle = QObject::tr("Could not open file");
592
needsSize = m_curAction->copyFile.source->size();
594
m_curAction->copyFile.target = new QFile(target);
595
m_curAction->copyFile.targetName = target;
596
//first open it read-only to get its size if exists
597
if (m_curAction->copyFile.target->open(QFile::ReadOnly))
599
needsSize -= m_curAction->copyFile.target->size();
600
m_curAction->copyFile.target->close();
602
//check if there is disk space to copy source to target
603
if (needsSize > 0 && !isThereDiskSpace( needsSize ))
605
m_cancelCurrentAction = true;
606
m_errorTitle = QObject::tr("There is no space on disk to copy");
607
m_errorMsg = m_curAction->copyFile.target->fileName();
610
if (!m_cancelCurrentAction)
612
m_cancelCurrentAction =
613
!m_curAction->copyFile.target->open(QFile::WriteOnly | QFile::Truncate);
614
if (m_cancelCurrentAction)
616
m_errorTitle = QObject::tr("Could not create file");
617
m_errorMsg = m_curAction->copyFile.target->fileName();
620
if (!m_cancelCurrentAction)
622
m_curAction->copyFile.isEntryItem = entry->currItem == (entry->reversedOrder.count() -1);
623
scheduleAnySlot = processCopySingleFile();
624
//main item from the entry. notify views new item inserted,
625
//depending on the file size it may take longer, the view needs to be informed
626
if (m_curAction->copyFile.isEntryItem && !m_cancelCurrentAction)
628
if (!entry->alreadyExists)
635
emit changed(DirItemInfo(target));
643
if (!scheduleAnySlot)
650
//===============================================================================================
652
* \brief FileSystemAction::moveEntry
655
void FileSystemAction::moveEntry(ActionEntry *entry)
659
for(; !m_cancelCurrentAction &&
660
entry->currStep < STEP_FILES &&
661
m_curAction->currItem < m_curAction->totalItems &&
662
entry->currItem < entry->reversedOrder.count()
663
; entry->currStep++, m_curAction->currItem++, entry->currItem++
667
const DirItemInfo &fi = entry->reversedOrder.at(entry->currItem);
668
file.setFileName(fi.absoluteFilePath());
669
QString target(targetFom(fi.absoluteFilePath(), m_curAction));
670
DirItemInfo targetInfo(target);
672
if (targetInfo.exists())
674
//will not emit removed() neither added()
676
if (targetInfo.isFile() || targetInfo.isSymLink())
678
if (!QFile::remove(target))
680
m_cancelCurrentAction = true;
681
m_errorTitle = QObject::tr("Could remove the directory/file ") + target;
682
m_errorMsg = ::strerror(errno);
686
if (targetInfo.isDir())
688
//move target to /tmp and remove it later by creating an Remove action
689
//this will emit removed()
690
moveDirToTempAndRemoveItLater(target);
693
if (!m_cancelCurrentAction && !file.rename(target))
695
m_cancelCurrentAction = true;
696
m_errorTitle = QObject::tr("Could not move the directory/file ") + target;
697
m_errorMsg = ::strerror(errno);
702
//===============================================================================================
704
* \brief FileSystemAction::pathChanged
707
void FileSystemAction::pathChanged(const QString &path)
714
void FileSystemAction::copyIntoCurrentPath(const QStringList& items)
717
qDebug() << Q_FUNC_INFO << items;
719
m_clipboardChanged = false;
722
DirItemInfo destination(m_path);
723
if (destination.isWritable())
725
createAndProcessAction(ActionCopy, items);
729
emit error(tr("Cannot copy items"),
730
tr("no write permission on folder ") + destination.absoluteFilePath() );
737
void FileSystemAction::moveIntoCurrentPath(const QStringList& items)
740
qDebug() << Q_FUNC_INFO << items;
742
m_clipboardChanged = false;
745
DirItemInfo destination(m_path);
746
DirItemInfo origin(DirItemInfo(items.at(0)).absolutePath());
747
ActionType actionType = ActionMove;
748
static QString titleError = tr("Cannot move items");
749
static QString noWriteError = tr("no write permission on folder ");
750
//we allow Copy to backup items, but Cut must Fail
751
if (destination.absoluteFilePath() == origin.absoluteFilePath())
753
emit error(titleError,
754
tr("origin and destination folders are the same"));
757
// cut needs write permission on origin
758
if (!origin.isWritable())
760
emit error(titleError, noWriteError + origin.absoluteFilePath());
763
//check if it is possible to move items
764
if ( !moveUsingSameFileSystem(items.at(0)) )
766
actionType = ActionHardMoveCopy; // first step
768
if (!destination.isWritable())
770
emit error(titleError, noWriteError + destination.absoluteFilePath());
773
createAndProcessAction(actionType, items);
778
//===============================================================================================
780
* \brief FileSystemAction::createAndProcessAction
785
void FileSystemAction::createAndProcessAction(ActionType actionType, const QStringList& paths)
788
qDebug() << Q_FUNC_INFO << paths;
790
Action *myAction = 0;
792
myAction = createAction(actionType, origPathLen);
793
myAction->origPath = DirItemInfo(paths.at(0)).absolutePath();
794
myAction->baseOrigSize = myAction->origPath.length();
795
for (int counter=0; counter < paths.count(); counter++)
797
addEntry(myAction, paths.at(counter));
799
if (myAction->totalItems > 0)
801
if (actionType == ActionHardMoveCopy)
803
myAction->totalItems *= 2; //duplicate this
806
if (actionType == ActionHardMoveCopy || actionType == ActionCopy)
808
//if a file size is less than (COPY_BUFFER_SIZE * STEP_FILES) a single step does that
809
//and it is already computed
810
myAction->steps += myAction->totalBytes / (COPY_BUFFER_SIZE * STEP_FILES);
813
m_queuedActions.append(myAction);
820
{ // no items were added into the Action, maybe items were removed
821
//addEntry() emits error() signal when items do not exist
824
qDebug() << Q_FUNC_INFO << "Action is empty, no work to do";
830
//===============================================================================================
832
* \brief FileSystemAction::targetFom() makes a destination full pathname from \a origItem
833
* \param origItem full pathname from a item intended to be copied or moved into current path
834
* \return full pathname of target
836
QString FileSystemAction::targetFom(const QString& origItem, const Action* const action)
838
QString destinationUnderTarget(origItem.mid(action->baseOrigSize));
839
if (action->currEntry && action->currEntry->newName)
841
int len = destinationUnderTarget.indexOf(QDir::separator(), 1);
843
len = destinationUnderTarget.size();
845
destinationUnderTarget.replace(1, len -1, *action->currEntry->newName);
847
QString target(action->targetPath + destinationUnderTarget);
850
qDebug() << Q_FUNC_INFO << "orig" << origItem
851
<< "target" << target;
857
//===============================================================================================
859
* \brief FileSystemAction::moveUsingSameFileSystem() Checks if the item being moved to
860
* current m_path belongs to the same File System
862
* It is used to set ActionHardMoveCopy or ActionMove for cut operations.
864
* \param itemToMovePathname first item being moved from a paste operation
866
* \return true if the item being moved to the current m_path belongs to the same file system as m_path
868
bool FileSystemAction::moveUsingSameFileSystem(const QString& itemToMovePathname)
870
unsigned long targetFsId = 0xffff;
871
unsigned long originFsId = 0xfffe;
872
#if defined(Q_OS_UNIX)
874
if ( ::statvfs( QFile::encodeName(m_path).constData(), &vfs) == 0 )
876
targetFsId = vfs.f_fsid;
878
if ( ::statvfs(QFile::encodeName(itemToMovePathname).constData(), &vfs) == 0)
880
originFsId = vfs.f_fsid;
883
Q_UNUSED(itemToMovePathname);
885
return targetFsId == originFsId;
889
//================================================================================
891
* \brief FileSystemAction::endCurrentAction() finishes an Action
893
* If a Paste was made from a Cut operation, items pasted become avaialable in the clipboard
894
* as from Copy source operation, so items can be now Pasted again, but with no source removal
896
* It checks for \a m_clipboardChanged that idenftifies if the clipboard was modified during the
897
* operation maybe by another application.
899
void FileSystemAction::endCurrentAction()
902
if ( !m_clipboardChanged &&
903
m_curAction->origPath != m_curAction->targetPath &&
904
(m_curAction->type == ActionMove || m_curAction->type == ActionHardMoveRemove)
908
const ActionEntry *entry;
910
for(int e = 0; e < m_curAction->entries.count(); e++)
912
entry = m_curAction->entries.at(e);
913
last = entry->reversedOrder.count() -1;
914
QString item(targetFom(entry->reversedOrder.at(last).absoluteFilePath(), m_curAction));
919
QString targetPath = m_curAction->targetPath;
920
//it is not necessary to handle own clipboard here
921
emit recopy(items, targetPath);
926
//================================================================================
928
* \brief FileSystemAction::copySingleFile() do a single file copy
930
* Several write operations are required to copy big files, each operation writes (STEP_FILES * 4k) bytes.
931
* After a write operation if more operations are required to copy the whole file,
932
* a progress() signal is emitted and a new write operation is scheduled to happen in the next loop interaction.
934
* \return true if scheduled to another slot either processCopyEntry() or itself; false if not.
936
bool FileSystemAction::processCopySingleFile()
939
qDebug() << Q_FUNC_INFO;
941
char block[COPY_BUFFER_SIZE];
943
bool copySingleFileDone = false;
944
bool scheduleAnySlot = true;
945
int startBytes = m_curAction->copyFile.bytesWritten;
947
while( m_curAction->copyFile.source &&
948
!m_curAction->copyFile.source->atEnd() &&
949
!m_cancelCurrentAction &&
950
m_curAction->copyFile.bytesWritten < m_curAction->copyFile.source->size() &&
954
qint64 in = m_curAction->copyFile.source->read(block, sizeof(block));
957
if(in != m_curAction->copyFile.target->write(block, in))
959
m_curAction->copyFile.source->close();
960
m_curAction->copyFile.target->close();
961
m_cancelCurrentAction = true;
962
m_errorTitle = QObject::tr("Write error in ")
963
+ m_curAction->copyFile.targetName,
964
m_errorMsg = ::strerror(errno);
967
m_curAction->bytesWritten += in;
968
m_curAction->copyFile.bytesWritten += in;
969
if (m_curAction->copyFile.isEntryItem)
971
m_curAction->copyFile.amountSavedToRefresh -= in;
977
m_cancelCurrentAction = true;
978
m_errorTitle = QObject::tr("Read error in ")
979
+ m_curAction->copyFile.source->fileName();
980
m_errorMsg = ::strerror(errno);
985
// write loop finished, the copy might be finished
986
if (!m_cancelCurrentAction
987
&& m_curAction->copyFile.source
988
&& m_curAction->copyFile.bytesWritten == m_curAction->copyFile.source->size()
989
&& m_curAction->copyFile.source->isOpen()
992
copySingleFileDone = endCopySingleFile();
995
if (m_cancelCurrentAction)
997
if (m_curAction->copyFile.target)
999
if (m_curAction->copyFile.target->isOpen())
1001
m_curAction->copyFile.target->close();
1003
if (m_curAction->copyFile.target->remove())
1005
emit removed(m_curAction->copyFile.targetName);
1008
m_curAction->copyFile.clear();
1013
if (copySingleFileDone)
1015
m_curAction->copyFile.clear();
1016
//whem the whole copy could be done just in one call
1017
//do not schedule to call copyEntry()
1020
//the whole took more than one call to copySingleFile()
1021
scheduleSlot(SLOT(processCopyEntry()));
1024
{ //return normally to entry loop
1025
scheduleAnySlot = false;
1031
if (m_curAction->copyFile.isEntryItem && m_curAction->copyFile.amountSavedToRefresh <= 0)
1033
m_curAction->copyFile.amountSavedToRefresh = AMOUNT_COPIED_TO_REFRESH_ITEM_INFO;
1034
emit changed(DirItemInfo(m_curAction->copyFile.targetName));
1036
scheduleSlot(SLOT(processCopySingleFile()));
1040
return scheduleAnySlot;
1044
//================================================================================
1046
* \brief FileSystemAction::percentWorkDone() Compute the percent of work done
1048
* Copy operations are based on bytes written while remove/move operations are based on items number
1050
* \return the percent of work done
1052
int FileSystemAction::percentWorkDone()
1056
//copying empty files will have totalBytes==0
1057
if ( m_curAction->totalBytes > 0 &&
1058
(m_curAction->type == ActionCopy || m_curAction->type == ActionHardMoveCopy)
1061
percent = (m_curAction->bytesWritten * 100) / m_curAction->totalBytes ;
1064
{ //percentage based on number of items performed
1065
percent = (m_curAction->currItem * 100) / m_curAction->totalItems;
1076
//================================================================================
1078
* \brief FileSystemAction::notifyProgress() Notify the progress signal
1080
* \return the percent of work done
1082
int FileSystemAction::notifyProgress(int forcePercent)
1084
int percent = forcePercent > 0 ? forcePercent : percentWorkDone();
1089
if (SHOULD_EMIT_PROGRESS_SIGNAL(m_curAction) && !m_curAction->done)
1091
if (m_curAction->type == ActionHardMoveCopy ||
1092
m_curAction->type ==ActionHardMoveRemove)
1094
emit progress(m_curAction->currItem/2, m_curAction->totalItems/2, percent);
1098
emit progress(m_curAction->currItem, m_curAction->totalItems, percent);
1100
if (percent == 100 && m_curAction->currItem == m_curAction->totalItems)
1102
m_curAction->done = true;
1108
//================================================================================
1110
* \brief FileSystemAction::copySymLink() creates the \a target as a link according to \a orig
1111
* \param target full pathname of the file to be created
1112
* \param orig original file, it carries the link that \a target will point to
1113
* \return true if it could create, else if not
1115
bool FileSystemAction::copySymLink(const QString &target, const QFileInfo &orig)
1117
QString link(orig.symLinkTarget());
1118
QFileInfo linkFile(link);
1119
if (linkFile.isAbsolute() && linkFile.absolutePath() == orig.absolutePath())
1121
link = linkFile.fileName();
1123
#if QT_VERSION <= 0x040704
1124
QString current = QDir::currentPath();
1125
QDir::setCurrent(linkFile.absolutePath());
1126
bool ret = QFile::link(link, target);
1127
QDir::setCurrent(current);
1129
bool ret = QFile::link(link, target);
1132
qDebug() << Q_FUNC_INFO << ret << target << link;
1137
//================================================================================
1138
void FileSystemAction::scheduleSlot(const char *slot)
1141
qDebug() << Q_FUNC_INFO << slot;
1143
QTimer::singleShot(0, this, slot);
1148
//================================================================================
1150
* \brief FileSystemAction::moveDirToTempAndRemoveItLater() moves a directory to temp and shedules it for be removed later
1152
* When pasting from cut actions, directories will be totally replaced, when they already exist, they need to be removed
1153
* before moving the new content, so the solution is to move them to temp directory and create another action to remove
1154
* them later, after that the content is moved to a target that does not exist any more.
1156
* \param dir directory name which is the target for paste operation and needs get removed first
1158
void FileSystemAction::moveDirToTempAndRemoveItLater(const QString& dir)
1162
//create this temporary file just to get a unique name
1164
d.setAutoRemove(true);
1167
tempDir = d.fileName();
1169
#if defined(DEBUG_MESSAGES) || defined(REGRESSION_TEST_FOLDERLISTMODEL)
1170
qDebug() << Q_FUNC_INFO << dir << "being moved to" << tempDir;
1172
if (QFile::rename(dir, tempDir))
1174
if (!m_curAction->auxAction)
1175
{ // this new action as Remove will remove all dirs
1176
m_curAction->auxAction = createAction(ActionRemove);
1177
m_curAction->auxAction->isAux = true;
1178
m_queuedActions.append(m_curAction->auxAction);
1180
addEntry(m_curAction->auxAction, tempDir);
1184
//================================================================================
1186
* \brief FileSystemAction::isBusy() just inform if there is any Action going on
1187
* \return true when there is any Action going on
1189
bool FileSystemAction::isBusy() const
1194
//==================================================================
1196
* \brief FileSystemAction::makeBackupNameForCurrentItem() creates a new name suitable for backup an item
1198
* The item can be a folder or a single file, but it is an Entry that means it is under the path were Copy happened
1199
* The newName field from current entry will be set to a suitable name
1202
bool FileSystemAction::makeBackupNameForCurrentItem(Action *action)
1205
if (action->currEntry->alreadyExists)
1207
const DirItemInfo& fi =
1208
action->currEntry->reversedOrder.at(action->currEntry->reversedOrder.count() -1);
1209
DirItemInfo backuped;
1214
QString copy(QObject::tr(" Copy"));
1217
copy += QLatin1Char('(') +
1218
QString::number(counter) +
1221
name = fi.fileName();
1222
int pos = name.size();
1225
int dot = name.lastIndexOf(QChar('.'));
1231
name.insert(pos,copy);
1232
backuped.setFile(fi.absolutePath(), name);
1233
} while (backuped.exists() && counter < 100);
1236
action->currEntry->newName = new QString(backuped.fileName());
1243
//==================================================================
1245
* \brief FileSystemAction::getProgressCounter
1246
* \return number of progress notification from current Action
1248
int FileSystemAction::getProgressCounter() const
1253
steps = m_curAction->steps;
1259
//==================================================================
1260
bool FileSystemAction::endCopySingleFile()
1263
m_curAction->copyFile.source->close();
1264
m_curAction->copyFile.target->close();
1265
m_cancelCurrentAction = !m_curAction->copyFile.target->setPermissions(
1266
m_curAction->copyFile.source->permissions());
1267
if (m_cancelCurrentAction)
1269
m_errorTitle = QObject::tr("Set permissions error in ")
1270
+ m_curAction->copyFile.targetName,
1271
m_errorMsg = ::strerror(errno);
1277
//==================================================================
1278
bool FileSystemAction::isThereDiskSpace(qint64 requiredSize)
1281
#if defined(Q_OS_UNIX)
1283
if ( ::statvfs( QFile::encodeName(m_path).constData(), &vfs) == 0 )
1285
qint64 free = vfs.f_bsize * vfs.f_bfree;
1286
ret = free > requiredSize;
1293
//==================================================================
1295
* \brief FileSystemAction::onClipboardChanged()
1297
* sets \ref m_clipboardChanged indicating the fhe Clipboard was changed.
1299
void FileSystemAction::onClipboardChanged()
1301
m_clipboardChanged = true;