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:
@@ -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:
|
|
||||||
if (isDirectory)
|
|
||||||
// remove existing folder recursively
|
|
||||||
ThrowOnError(FSDeleteFolder(&existingEntry, loopControl, false));
|
|
||||||
|
|
||||||
else
|
case TrackerCopyLoopControl::kReplace:
|
||||||
|
if (!isDirectory) {
|
||||||
// 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(¤t) == B_OK) {
|
||||||
|
if (current.IsDirectory()) {
|
||||||
|
RecursiveMove(¤t, &subDir);
|
||||||
|
current.Remove();
|
||||||
|
} else {
|
||||||
|
current.GetName(name);
|
||||||
|
if (loopControl.OverwriteOnConflict(¤t, 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
|
||||||
@@ -1669,13 +1707,8 @@ PreFlightNameCheck(BObjectList<entry_ref> *srcList, const BDirectory *destDir,
|
|||||||
|
|
||||||
// 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");
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user