Resolve several bugs that are probably as old as Tracker itself:

1) Move To now works as advertised in all cases - previously it
silently converted a move into a copy if the destination volume
was different, as its move code was not geared towards handling
that case properly. This also has the side effect that drag and
drop is now always a move as well unless overridden via right click
DnD. This may or may not be reverted back to the original behavior
later depending on the consensus.
2) Copying a folder onto another folder of the same name now merges
the contents instead of destroying the original. This fixes ticket
#1420. Still to do: interactive prompting to allow fine grained file
replacement and skipping in this and other multi-file cases, but 
I'll open a new enhancement ticket for that.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28731 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Rene Gollent
2008-11-26 03:40:41 +00:00
parent 9141571856
commit 87f41e61ea
+81 -50
View File
@@ -112,7 +112,7 @@ status_t CalcItemsAndSize(BObjectList<entry_ref> *refList, int32 *totalCount, of
status_t MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, status_t MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc,
uint32 moveMode, const char *newName, Undo &undo); uint32 moveMode, const char *newName, Undo &undo);
ConflictCheckResult PreFlightNameCheck(BObjectList<entry_ref> *srcList, const BDirectory *destDir, ConflictCheckResult PreFlightNameCheck(BObjectList<entry_ref> *srcList, const BDirectory *destDir,
int32 *collisionCount); int32 *collisionCount, uint32 moveMode);
status_t CheckName(uint32 moveMode, const BEntry *srcEntry, const BDirectory *destDir, status_t CheckName(uint32 moveMode, const BEntry *srcEntry, const BDirectory *destDir,
bool multipleCollisions, ConflictCheckResult &); bool multipleCollisions, ConflictCheckResult &);
void CopyAttributes(CopyLoopControl *control, BNode *srcNode, BNode* destNode, void *buffer, void CopyAttributes(CopyLoopControl *control, BNode *srcNode, BNode* destNode, void *buffer,
@@ -141,8 +141,8 @@ const char *kReplaceStr = "You are trying to replace the item:\n"
"\t%s%s\n\n" "\t%s%s\n\n"
"Would you like to replace it with the one you are %s?"; "Would you like to replace it with the one you are %s?";
const char *kDirectoryReplaceStr = "An item named \"%s\" already exists in this folder. " const char *kDirectoryReplaceStr = "An item named \"%s\" already exists in this folder, and may contain\n"
"Would you like to replace it with the one you are %s?"; "items with the same names. Would you like to replace them with those contained in the folder you are %s?";
const char *kSymLinkReplaceStr = "An item named \"%s\" already exists in this folder. " const char *kSymLinkReplaceStr = "An item named \"%s\" already exists in this folder. "
"Would you like to replace it with the symbolic link you are creating?"; "Would you like to replace it with the symbolic link you are creating?";
@@ -579,7 +579,7 @@ InitCopy(uint32 moveMode, BObjectList<entry_ref> *srcList, thread_id thread,
*preflightResult = kPrompt; *preflightResult = kPrompt;
*collisionCount = 0; *collisionCount = 0;
*preflightResult = PreFlightNameCheck(srcList, destDir, collisionCount); *preflightResult = PreFlightNameCheck(srcList, destDir, collisionCount, moveMode);
if (*preflightResult == kCanceled) // user canceled if (*preflightResult == kCanceled) // user canceled
return B_ERROR; return B_ERROR;
} }
@@ -588,6 +588,7 @@ InitCopy(uint32 moveMode, BObjectList<entry_ref> *srcList, thread_id thread,
switch (moveMode) { switch (moveMode) {
case kCopySelectionTo: case kCopySelectionTo:
case kDuplicateSelection: case kDuplicateSelection:
case kMoveSelectionTo:
{ {
if (gStatusWindow) if (gStatusWindow)
gStatusWindow->CreateStatusItem(thread, kCopyState); gStatusWindow->CreateStatusItem(thread, kCopyState);
@@ -610,7 +611,6 @@ InitCopy(uint32 moveMode, BObjectList<entry_ref> *srcList, thread_id thread,
break; break;
} }
case kMoveSelectionTo:
case kCreateLink: case kCreateLink:
if (numItems > 10) { if (numItems > 10) {
// this will be fast, only put up status if lots of items // this will be fast, only put up status if lots of items
@@ -703,9 +703,6 @@ MoveTask(BObjectList<entry_ref> *srcList, BEntry *destEntry, BList *pointList, u
} }
// change the move mode if needed // change the move mode if needed
if (moveMode == kMoveSelectionTo && srcVolumeDevice != destVolumeDevice)
// move across volumes - copy instead
moveMode = kCopySelectionTo;
if (moveMode == kCopySelectionTo && destIsTrash) if (moveMode == kCopySelectionTo && destIsTrash)
// cannot copy to trash // cannot copy to trash
moveMode = kMoveSelectionTo; moveMode = kMoveSelectionTo;
@@ -908,13 +905,11 @@ CopyFile(BEntry *srcFile, StatStruct *srcStat, BDirectory *destDir,
return; return;
case TrackerCopyLoopControl::kReplace: case TrackerCopyLoopControl::kReplace:
if (conflictingEntry.IsDirectory()) if (!conflictingEntry.IsDirectory()) {
// remove existing folder recursively
ThrowOnError(FSDeleteFolder(&conflictingEntry, loopControl, false));
else
ThrowOnError(conflictingEntry.Remove()); ThrowOnError(conflictingEntry.Remove());
break; break;
}
// fall through if not a directory
case TrackerCopyLoopControl::kMerge: case TrackerCopyLoopControl::kMerge:
// This flag implies that the attributes should be kept // This flag implies that the attributes should be kept
// on the file. Just ignore it. // on the file. Just ignore it.
@@ -1174,7 +1169,7 @@ CopyAttributes(CopyLoopControl *control, BNode *srcNode, BNode *destNode, void *
static void static void
CopyFolder(BEntry *srcEntry, BDirectory *destDir, CopyLoopControl *loopControl, CopyFolder(BEntry *srcEntry, BDirectory *destDir, CopyLoopControl *loopControl,
BPoint *loc, bool makeOriginalName, Undo &undo) BPoint *loc, bool makeOriginalName, Undo &undo, bool removeSource = false)
{ {
BDirectory newDir; BDirectory newDir;
BEntry entry; BEntry entry;
@@ -1209,16 +1204,14 @@ CopyFolder(BEntry *srcEntry, BDirectory *destDir, CopyLoopControl *loopControl,
// we are about to ignore this entire directory // we are about to ignore this entire directory
return; return;
case TrackerCopyLoopControl::kReplace: case TrackerCopyLoopControl::kReplace:
if (isDirectory) if (!isDirectory) {
// remove existing folder recursively
ThrowOnError(FSDeleteFolder(&existingEntry, loopControl, false));
else
// conflicting with a file or symbolic link, remove entry // conflicting with a file or symbolic link, remove entry
ThrowOnError(existingEntry.Remove()); ThrowOnError(existingEntry.Remove());
break; break;
}
// fall through if directory, do not replace.
case TrackerCopyLoopControl::kMerge: case TrackerCopyLoopControl::kMerge:
ASSERT(isDirectory); ASSERT(isDirectory);
// do not create a new directory, use the current one // do not create a new directory, use the current one
@@ -1231,7 +1224,6 @@ CopyFolder(BEntry *srcEntry, BDirectory *destDir, CopyLoopControl *loopControl,
// loop through everything in src folder and copy it to new folder // loop through everything in src folder and copy it to new folder
BDirectory srcDir(srcEntry); BDirectory srcDir(srcEntry);
srcDir.Rewind(); srcDir.Rewind();
srcEntry->Unset();
// create a new folder inside of destination folder // create a new folder inside of destination folder
if (createDirectory) { if (createDirectory) {
@@ -1284,13 +1276,57 @@ CopyFolder(BEntry *srcEntry, BDirectory *destDir, CopyLoopControl *loopControl,
continue; continue;
} }
CopyFolder(&entry, &newDir, loopControl, 0, false, undo); CopyFolder(&entry, &newDir, loopControl, 0, false, undo, removeSource);
} else if (removeSource)
FSDeleteFolder(&entry, loopControl, true, true, false);
} else {
CopyFile(&entry, &statbuf, &newDir, loopControl, 0, false, undo); CopyFile(&entry, &statbuf, &newDir, loopControl, 0, false, undo);
if (removeSource)
entry.Remove();
}
} }
if (removeSource)
srcEntry->Remove();
else
srcEntry->Unset();
} }
status_t
RecursiveMove(BEntry *entry, BDirectory *destDir)
{
TrackerCopyLoopControl loopControl(find_thread(NULL));
char name[B_FILE_NAME_LENGTH];
if (entry->GetName(name) == B_OK) {
if (destDir->Contains(name)) {
BPath path (destDir, name);
BDirectory subDir (path.Path());
entry_ref ref;
entry->GetRef(&ref);
BDirectory source(&ref);
if (source.InitCheck() == B_OK) {
source.Rewind();
BEntry current;
while (source.GetNextEntry(&current) == B_OK) {
if (current.IsDirectory()) {
RecursiveMove(&current, &subDir);
current.Remove();
} else {
current.GetName(name);
if (loopControl.OverwriteOnConflict(&current, name, &subDir, true, false) != TrackerCopyLoopControl::kSkip)
MoveError::FailOnError(current.MoveTo(&subDir, NULL, true));
}
}
}
entry->Remove();
} else {
MoveError::FailOnError(entry->MoveTo(destDir));
}
}
return B_OK;
}
status_t status_t
MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, uint32 moveMode, MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, uint32 moveMode,
const char *newName, Undo &undo) const char *newName, Undo &undo)
@@ -1299,7 +1335,6 @@ MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, uint32 moveMode,
try { try {
node_ref destNode; node_ref destNode;
StatStruct statbuf; StatStruct statbuf;
MoveError::FailOnError(entry->GetStat(&statbuf)); MoveError::FailOnError(entry->GetStat(&statbuf));
MoveError::FailOnError(entry->GetRef(&ref)); MoveError::FailOnError(entry->GetRef(&ref));
MoveError::FailOnError(destDir->GetNodeRef(&destNode)); MoveError::FailOnError(destDir->GetNodeRef(&destNode));
@@ -1412,16 +1447,19 @@ MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, uint32 moveMode,
thread_id thread = find_thread(NULL); thread_id thread = find_thread(NULL);
if (gStatusWindow && gStatusWindow->HasStatus(thread)) if (gStatusWindow && gStatusWindow->HasStatus(thread))
gStatusWindow->UpdateStatus(thread, ref.name, 1); gStatusWindow->UpdateStatus(thread, ref.name, 1);
if (entry->IsDirectory())
return RecursiveMove(entry, destDir);
MoveError::FailOnError(entry->MoveTo(destDir, newName)); MoveError::FailOnError(entry->MoveTo(destDir, newName));
} else { } else {
TrackerCopyLoopControl loopControl(find_thread(NULL)); TrackerCopyLoopControl loopControl(find_thread(NULL));
bool makeOriginalName = (moveMode == kDuplicateSelection); bool makeOriginalName = (moveMode == kDuplicateSelection);
if (S_ISDIR(statbuf.st_mode)) if (S_ISDIR(statbuf.st_mode)) {
CopyFolder(entry, destDir, &loopControl, loc, makeOriginalName, undo); CopyFolder(entry, destDir, &loopControl, loc, makeOriginalName, undo, moveMode == kMoveSelectionTo);
else } else {
CopyFile(entry, &statbuf, destDir, &loopControl, loc, makeOriginalName, undo); CopyFile(entry, &statbuf, destDir, &loopControl, loc, makeOriginalName, undo);
if (moveMode == kMoveSelectionTo)
entry->Remove();
}
} }
} catch (status_t error) { } catch (status_t error) {
// no alert, was already taken care of before // no alert, was already taken care of before
@@ -1648,7 +1686,7 @@ MoveEntryToTrash(BEntry *entry, BPoint *loc, Undo &undo)
ConflictCheckResult ConflictCheckResult
PreFlightNameCheck(BObjectList<entry_ref> *srcList, const BDirectory *destDir, PreFlightNameCheck(BObjectList<entry_ref> *srcList, const BDirectory *destDir,
int32 *collisionCount) int32 *collisionCount, uint32 moveMode)
{ {
// count the number of name collisions in dest folder // count the number of name collisions in dest folder
@@ -1662,20 +1700,15 @@ PreFlightNameCheck(BObjectList<entry_ref> *srcList, const BDirectory *destDir,
entry.GetParent(&parent); entry.GetParent(&parent);
if (parent != *destDir) { if (parent != *destDir) {
if (destDir->Contains(srcRef->name)) if (destDir->Contains(srcRef->name))
(*collisionCount)++; (*collisionCount)++;
} }
} }
// prompt user only if there is more than one collision, otherwise the // prompt user only if there is more than one collision, otherwise the
// single collision case will be handled as a "Prompt" case by CheckName // single collision case will be handled as a "Prompt" case by CheckName
if (*collisionCount > 1) { if (*collisionCount > 0) {
entry_ref *srcRef = (entry_ref*)srcList->FirstItem(); const char *verb = (moveMode == kMoveSelectionTo) ? "moving" : "copying";
StatStruct statbuf;
destDir->GetStat(&statbuf);
const char *verb = (srcRef->device == statbuf.st_dev) ? "moving" : "copying";
char replaceMsg[256]; char replaceMsg[256];
sprintf(replaceMsg, kReplaceManyStr, verb, verb); sprintf(replaceMsg, kReplaceManyStr, verb, verb);
@@ -1812,9 +1845,8 @@ CheckName(uint32 moveMode, const BEntry *sourceEntry, const BDirectory *destDir,
// special case single collision (don't need Replace All shortcut) // special case single collision (don't need Replace All shortcut)
BAlert *alert; BAlert *alert;
if (multipleCollisions) if (multipleCollisions || sourceIsDirectory)
alert = new BAlert("", replaceMsg, "Skip", "Replace All", alert = new BAlert("", replaceMsg, "Skip", "Replace All");
"Replace");
else else
alert = new BAlert("", replaceMsg, "Cancel", "Replace"); alert = new BAlert("", replaceMsg, "Cancel", "Replace");
@@ -1822,7 +1854,7 @@ CheckName(uint32 moveMode, const BEntry *sourceEntry, const BDirectory *destDir,
case 0: // user selected "Cancel" or "Skip" case 0: // user selected "Cancel" or "Skip"
replaceAll = kCanceled; replaceAll = kCanceled;
return B_ERROR; return B_ERROR;
case 1: // user selected "Replace" or "Replace All" case 1: // user selected "Replace" or "Replace All"
replaceAll = kReplaceAll; replaceAll = kReplaceAll;
// doesn't matter which since a single // doesn't matter which since a single
@@ -1833,11 +1865,11 @@ CheckName(uint32 moveMode, const BEntry *sourceEntry, const BDirectory *destDir,
} }
// delete destination item // delete destination item
if (destIsDir) { if (!destIsDir) {
TrackerCopyLoopControl loopControl(find_thread(NULL)); TrackerCopyLoopControl loopControl(find_thread(NULL));
err = FSDeleteFolder(&entry, &loopControl, false);
} else
err = entry.Remove(); err = entry.Remove();
} else
return B_OK;
if (err != B_OK) { if (err != B_OK) {
BString error; BString error;
@@ -1862,7 +1894,6 @@ FSDeleteFolder(BEntry *dir_entry, CopyLoopControl *loopControl, bool update_stat
dir.SetTo(dir_entry); dir.SetTo(dir_entry);
dir.Rewind(); dir.Rewind();
// loop through everything in folder and delete it, skipping trouble files // loop through everything in folder and delete it, skipping trouble files
for (;;) { for (;;) {
if (dir.GetNextEntry(&entry) != B_OK) if (dir.GetNextEntry(&entry) != B_OK)
@@ -1898,7 +1929,7 @@ FSDeleteFolder(BEntry *dir_entry, CopyLoopControl *loopControl, bool update_stat
if (update_status && delete_top_dir) if (update_status && delete_top_dir)
loopControl->UpdateStatus(NULL, ref, 1); loopControl->UpdateStatus(NULL, ref, 1);
if (delete_top_dir) if (delete_top_dir)
return dir_entry->Remove(); return dir_entry->Remove();
else else
return B_OK; return B_OK;