Tracker: Create a symlink of a file in the same directory again

... without regression from #19403. Fixes #19769.

Drop version of BPoseView::MoveSelectionTo() signature changed.
Pass in uint32 moveMode to pass on create link or relative link.

This fixes the double create link on:
- Context-click => Create link/relative link => Current folder.

Use synchronous Go() in ShowDropContextMenu() to get the
return value. Asynchronous Go() always returns NULL so didn't
work right. Also don't send message since we're handling this
through the return value. Send MoveTo/CopyTo/LinkTo shortcuts
messages to window instead of the view.

This fixes the double create link on:
- Context-click drag and drop => Create link/relative link here.

Update loop control status better. Set stat size and perms.

Fixup MoveTo/CopyTo/LinkTo pose locations.

Change-Id: Ibff9f16d7c1c921a46a425de2f280318c40f4c69
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10715
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: John Scipione <[email protected]>
This commit is contained in:
John Scipione
2026-04-26 20:50:15 +00:00
parent 1e58b7a03e
commit 54a3883e26
7 changed files with 120 additions and 58 deletions
+2 -1
View File
@@ -2433,7 +2433,8 @@ BContainerWindow::ShowDropContextMenu(BPoint where, BPoseView* source)
} }
} }
item = fDropContextMenu->Go(global, true, true); // Don't send message, sticky menu, synchronous
item = fDropContextMenu->Go(global, false, true, false);
if (item != NULL) if (item != NULL)
return item->Command(); return item->Command();
+18 -16
View File
@@ -980,6 +980,7 @@ InitCopy(CopyLoopControl* loopControl, uint32 moveMode,
} }
case kCreateLink: case kCreateLink:
case kCreateRelativeLink:
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
// moved, links created // moved, links created
@@ -1317,7 +1318,7 @@ CopyFile(BEntry* srcFile, StatStruct* srcStat, BDirectory* destDir,
throw (status_t)err; throw (status_t)err;
} else { } else {
// user selected continue in spite of error, update status bar // user selected continue in spite of error, update status bar
loopControl->UpdateStatus(NULL, ref, (int32)srcStat->st_size); loopControl->UpdateStatus(NULL, ref, srcStat->st_size);
} }
} }
} }
@@ -1752,25 +1753,18 @@ MoveItem(BEntry* entry, BDirectory* destDir, BPoint* loc, uint32 moveMode,
MoveError::FailOnError(destDir->GetNodeRef(&destNode)); MoveError::FailOnError(destDir->GetNodeRef(&destNode));
if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) {
PoseInfo poseInfo;
char name[B_FILE_NAME_LENGTH]; char name[B_FILE_NAME_LENGTH];
strlcpy(name, ref.name, B_FILE_NAME_LENGTH); strlcpy(name, ref.name, B_FILE_NAME_LENGTH);
loopControl->UpdateStatus(ref.name, ref, statbuf.st_size, true);
BSymLink link; BSymLink link;
BString suffix(" "); BString suffix(" ");
suffix << B_TRANSLATE_COMMENT("link", "filename link"); suffix << B_TRANSLATE_COMMENT("link", "filename link");
FSMakeOriginalName(name, destDir, suffix); FSMakeOriginalName(name, destDir, suffix);
undo.UpdateEntry(entry, name); undo.UpdateEntry(entry, name);
BPath path; BPath path(entry);
entry->GetPath(&path);
if (loc != NULL && loc != (BPoint*)-1) {
poseInfo.fInvisible = false;
poseInfo.fInitedDirectory = destNode.node;
poseInfo.fLocation = *loc;
}
status_t err = B_ERROR; status_t err = B_ERROR;
if (moveMode == kCreateRelativeLink) { if (moveMode == kCreateRelativeLink) {
@@ -1849,12 +1843,22 @@ MoveItem(BEntry* entry, BDirectory* destDir, BPoint* loc, uint32 moveMode,
B_TRANSLATE("Error creating link to \"%name\"."), B_TRANSLATE("Error creating link to \"%name\"."),
ref.name); ref.name);
if (loc != NULL && loc != (BPoint*)-1) BFile srcFile(entry, B_READ_ONLY);
link.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, sizeof(PoseInfo)); struct stat srcStat;
srcFile.GetStat(&srcStat);
// copy or write new pose location
SetupPoseLocation(ref.directory, destNode.node, &srcFile, &link, loc);
BNodeInfo nodeInfo(&link); BNodeInfo nodeInfo(&link);
nodeInfo.SetType(B_LINK_MIMETYPE); nodeInfo.SetType(B_LINK_MIMETYPE);
link.SetPermissions(srcStat.st_mode);
link.SetOwner(srcStat.st_uid);
link.SetGroup(srcStat.st_gid);
link.SetModificationTime(srcStat.st_mtime);
link.SetCreationTime(srcStat.st_crtime);
return B_OK; return B_OK;
} }
@@ -2242,9 +2246,7 @@ CheckName(uint32 moveMode, const BEntry* sourceEntry,
if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) {
// if we are creating link in the same directory, the conflict will // if we are creating link in the same directory, the conflict will
// be handled later by giving the link a unique name // be handled later by giving the link a unique name
sourceEntry->GetParent(&srcDirectory); if (sourceEntry->GetParent(&srcDirectory) == B_OK && srcDirectory == *destDir)
if (srcDirectory == *destDir)
return B_OK; return B_OK;
} }
+1 -1
View File
@@ -919,7 +919,7 @@ OpenWithPoseView::MoveSelectionToTrash(bool)
void void
OpenWithPoseView::MoveSelectionTo(BPoint, BPoint, BContainerWindow*) OpenWithPoseView::MoveSelectionTo(Model*, BPoint, BContainerWindow*, BPoint, uint32)
{ {
} }
+1 -1
View File
@@ -241,7 +241,7 @@ protected:
virtual void RestoreState(const BMessage&); virtual void RestoreState(const BMessage&);
virtual void SavePoseLocations(BRect* = NULL); virtual void SavePoseLocations(BRect* = NULL);
virtual void MoveSelectionToTrash(bool selectNext = true); virtual void MoveSelectionToTrash(bool selectNext = true);
virtual void MoveSelectionTo(BPoint, BPoint, BContainerWindow*); virtual void MoveSelectionTo(Model*, BPoint, BContainerWindow*, BPoint, uint32);
virtual void MoveSelectionInto(Model* destFolder, virtual void MoveSelectionInto(Model* destFolder,
BContainerWindow* srcWindow, bool forceCopy, BContainerWindow* srcWindow, bool forceCopy,
bool create_link = false); bool create_link = false);
+92 -33
View File
@@ -4514,6 +4514,7 @@ BPoseView::HandleDropCommon(BMessage* message, Model* targetModel, BPose* target
BView* view, BPoint dropPoint) BView* view, BPoint dropPoint)
{ {
uint32 buttons = (uint32)message->FindInt32("buttons"); uint32 buttons = (uint32)message->FindInt32("buttons");
uint32 moveMode = 0;
BContainerWindow* window = NULL; BContainerWindow* window = NULL;
BPoseView* poseView = dynamic_cast<BPoseView*>(view); BPoseView* poseView = dynamic_cast<BPoseView*>(view);
@@ -4698,7 +4699,6 @@ BPoseView::HandleDropCommon(BMessage* message, Model* targetModel, BPose* target
canCopy = true; canCopy = true;
} }
uint32 moveMode;
if (canCopy) if (canCopy)
moveMode = kCopySelectionTo; moveMode = kCopySelectionTo;
else if (canMove) else if (canMove)
@@ -4899,11 +4899,11 @@ BPoseView::HandleDropCommon(BMessage* message, Model* targetModel, BPose* target
} }
} }
if (poseView != NULL && !wasHandled) { if (!wasHandled && poseView != NULL) {
BPoint where = message->FindPoint("click_pt"); BPoint where = message->GetPoint("click_pt", B_ORIGIN);
// TODO: removed check for root here need to do that, possibly at a // TODO: removed check for root here need to do that, possibly at a
// different level // different level
poseView->MoveSelectionTo(dropPoint, where, srcWindow); poseView->MoveSelectionTo(targetModel, dropPoint, srcWindow, where, moveMode);
} }
if (poseView != NULL && poseView->fEnsurePosesVisible) if (poseView != NULL && poseView->fEnsurePosesVisible)
@@ -4971,22 +4971,82 @@ BPoseView::DragSelectionContains(const BPose* target,
} }
/* MoveTo/CopyTo/LinkTo menu version of MoveSelectionInto()
*
* The mouse cursor location is not helpful here since we're navigating
* menus potentially on a different view. Find the next available slot
* to move/copy/link the poses to.
*/
void void
BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow, BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
bool forceCopy, bool forceMove, bool createLink, bool relativeLink) bool forceCopy, bool forceMove, bool createLink, bool relativeLink)
{ {
BPoint dropPoint; BPoint where;
uint32 buttons; uint32 buttons;
GetMouse(&dropPoint, &buttons); GetMouse(&where, &buttons);
MoveSelectionInto(destFolder, srcWindow, dynamic_cast<BContainerWindow*>(Window()),
buttons, dropPoint, forceCopy, forceMove, createLink, relativeLink); int32 end = 0;
BPoint dropPoint(where);
// drop point is valid but not very helpful right now
// get destination view drop point in view coordinates if we can
BContainerWindow* destWindow = NULL;
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL) {
destWindow = tracker->FindContainerWindow(destFolder->NodeRef());
if (destWindow != NULL && destWindow->LockLooper()) {
BPoseView* destPoseView = destWindow->PoseView();
if (destPoseView != NULL && destPoseView->ViewMode() != kListMode) {
BRect destViewBounds(destPoseView->Bounds());
PoseList* poseList = destPoseView->CurrentPoseList();
// find an empty slot to put the new pose
if (poseList != NULL && poseList->ItemAt(0) != NULL) {
end = poseList->CountItems();
BRect slotRect(poseList->ItemAt(0)->CalcRect(destPoseView));
for (int32 i = 0; i < end && SlotOccupied(slotRect, destViewBounds); i++)
NextSlot(poseList->ItemAt(i), slotRect, destViewBounds);
// set the drop point to the next empty slot
dropPoint = slotRect.LeftTop();
} else {
// empty folder, set drop point to top left of view offset by a bit
dropPoint = destViewBounds.OffsetByCopy(20, 20).LeftTop();
}
// translate coordinates from dest view to view
dropPoint = ConvertFromScreen(destPoseView->ConvertToScreen(dropPoint));
}
destWindow->UnlockLooper();
}
}
MoveSelectionInto(destFolder, srcWindow, destWindow, buttons, dropPoint,
forceCopy, forceMove, createLink, relativeLink, where - dropPoint);
// auto-place new poses
if (destWindow != NULL && destWindow->LockLooper()) {
BPoseView* destPoseView = destWindow->PoseView();
if (destPoseView != NULL && destPoseView->ViewMode() != kListMode) {
int32 start = end;
PoseList* poseList = destPoseView->CurrentPoseList();
if (poseList != NULL) {
end = poseList->CountItems();
for (int32 i = start; i < end; i++) {
BPose* pose = poseList->ItemAt(i);
if (pose != NULL)
pose->SetAutoPlaced(true);
}
}
if (end > start)
destPoseView->CheckAutoPlacedPoses();
}
destWindow->UnlockLooper();
}
} }
void void
BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow, BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
BContainerWindow* destWindow, uint32 buttons, BPoint dropPoint, bool forceCopy, BContainerWindow* destWindow, uint32 buttons, BPoint dropPoint, bool forceCopy,
bool forceMove, bool createLink, bool relativeLink, BPoint where, bool pinToGrid) bool forceMove, bool createLink, bool createRelativeLink, BPoint dragStart, bool pinToGrid)
{ {
AutoLock<BWindow> lock(srcWindow); AutoLock<BWindow> lock(srcWindow);
if (!lock) if (!lock)
@@ -4997,7 +5057,6 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
if (srcWindow->PoseView()->CountSelected() == 0) if (srcWindow->PoseView()->CountSelected() == 0)
return; return;
bool createRelativeLink = relativeLink;
if (destWindow != NULL && SecondaryMouseButtonDown(modifiers(), buttons)) { if (destWindow != NULL && SecondaryMouseButtonDown(modifiers(), buttons)) {
BPoseView* poseView = (srcWindow != NULL ? srcWindow->PoseView() : NULL); BPoseView* poseView = (srcWindow != NULL ? srcWindow->PoseView() : NULL);
switch (destWindow->ShowDropContextMenu(dropPoint, poseView)) { switch (destWindow->ShowDropContextMenu(dropPoint, poseView)) {
@@ -5025,15 +5084,11 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
} }
// make sure source and destination folders are different // make sure source and destination folders are different
if (*srcWindow->PoseView()->TargetModel()->NodeRef() == *destFolder->NodeRef()) { if (*srcWindow->PoseView()->TargetModel()->NodeRef() == *destFolder->NodeRef()
&& !(createLink || createRelativeLink)) {
BPoseView* targetView = srcWindow->PoseView(); BPoseView* targetView = srcWindow->PoseView();
if (forceCopy) { if (forceCopy) {
targetView->DuplicateSelection(&where, &dropPoint); targetView->DuplicateSelection(&dragStart, &dropPoint);
return;
}
if (createLink || createRelativeLink) {
// cannot create link or relative link in the same folder
return; return;
} }
@@ -5042,7 +5097,7 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
return; return;
} }
BPoint delta = dropPoint - where; BPoint delta = dropPoint - dragStart;
int32 selectCount = targetView->CountSelected(); int32 selectCount = targetView->CountSelected();
for (int32 index = 0; index < selectCount; index++) { for (int32 index = 0; index < selectCount; index++) {
BPose* pose = targetView->SelectionList()->ItemAt(index); BPose* pose = targetView->SelectionList()->ItemAt(index);
@@ -5052,16 +5107,14 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
// need to do this because bsearch uses top of pose // need to do this because bsearch uses top of pose
// to locate pose to remove // to locate pose to remove
targetView->RemoveFromVSList(pose); targetView->RemoveFromVSList(pose);
BPoint location(pose->Location(targetView) + delta); BPoint loc(pose->Location(targetView) + delta);
BRect oldBounds(pose->CalcRect(targetView)); BRect oldBounds(pose->CalcRect(targetView));
if (pinToGrid) { if (pinToGrid)
location = targetView->PinToGrid(location, targetView->fGrid, loc = targetView->PinToGrid(loc, targetView->fGrid, targetView->fOffset);
targetView->fOffset);
}
// TODO: don't drop poses under desktop elements // TODO: don't drop poses under desktop elements
// ie: replicants, deskbar // ie: replicants, deskbar
pose->MoveTo(location, targetView); pose->MoveTo(loc, targetView);
targetView->RemoveFromExtent(oldBounds); targetView->RemoveFromExtent(oldBounds);
targetView->AddToExtent(pose->CalcRect(targetView)); targetView->AddToExtent(pose->CalcRect(targetView));
@@ -5126,9 +5179,9 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
if (okToMove) { if (okToMove) {
PoseList* selectionList = srcWindow->PoseView()->SelectionList(); PoseList* selectionList = srcWindow->PoseView()->SelectionList();
BList* pointList = destWindow->PoseView()->GetDropPointList(where, dropPoint, BList* pointList = destWindow->PoseView()->GetDropPointList(dragStart, dropPoint,
selectionList, srcWindow->PoseView()->ViewMode() == kListMode, pinToGrid); selectionList, srcWindow->PoseView()->ViewMode() == kListMode, pinToGrid);
int32 selectionSize = selectionList->CountItems(); int32 selectionSize = srcWindow->PoseView()->CountSelected();
BObjectList<entry_ref, true>* srcList = new BObjectList<entry_ref, true>(selectionSize); BObjectList<entry_ref, true>* srcList = new BObjectList<entry_ref, true>(selectionSize);
if (srcWindow->TargetModel()->IsVirtualDirectory()) { if (srcWindow->TargetModel()->IsVirtualDirectory()) {
@@ -5166,7 +5219,8 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
void void
BPoseView::MoveSelectionTo(BPoint dropPoint, BPoint where, BContainerWindow* srcWindow) BPoseView::MoveSelectionTo(Model* model, BPoint dropPoint, BContainerWindow* srcWindow,
BPoint dragStart, uint32 moveMode)
{ {
// Moves selection from srcWindow into this window, copying if necessary. // Moves selection from srcWindow into this window, copying if necessary.
@@ -5174,17 +5228,22 @@ BPoseView::MoveSelectionTo(BPoint dropPoint, BPoint where, BContainerWindow* src
if (window == NULL) if (window == NULL)
return; return;
ASSERT(window->PoseView() != NULL); if (model == NULL)
ASSERT(TargetModel() != NULL); model = TargetModel();
ASSERT(model != NULL);
// make sure this window is a legal drop target // make sure this window is a legal drop target
if (srcWindow != window && !TargetModel()->IsDropTarget()) if (srcWindow != window && !model->IsDropTarget())
return; return;
uint32 buttons = (uint32)window->CurrentMessage()->FindInt32("buttons"); ASSERT(window->CurrentMessage() != NULL);
uint32 buttons = (uint32)window->CurrentMessage()->GetInt32("buttons", 0);
bool pinToGrid = (modifiers() & B_COMMAND_KEY) != 0; bool pinToGrid = (modifiers() & B_COMMAND_KEY) != 0;
MoveSelectionInto(TargetModel(), srcWindow, window, buttons, dropPoint,
false, false, false, false, where, pinToGrid); MoveSelectionInto(model, srcWindow, window, buttons, dropPoint, false, false,
moveMode == kCreateLink, moveMode == kCreateRelativeLink, dragStart, pinToGrid);
} }
+3 -3
View File
@@ -286,7 +286,7 @@ public:
virtual void OpenSelection(BPose* clicked_pose = NULL, int32* index = NULL); virtual void OpenSelection(BPose* clicked_pose = NULL, int32* index = NULL);
void OpenSelectionUsing(BPose* clicked_pose = NULL, int32* index = NULL); void OpenSelectionUsing(BPose* clicked_pose = NULL, int32* index = NULL);
// launches the open with window // launches the open with window
virtual void MoveSelectionTo(BPoint, BPoint, BContainerWindow*); virtual void MoveSelectionTo(Model*, BPoint, BContainerWindow*, BPoint, uint32);
void DuplicateSelection(BPoint* dropStart = NULL,BPoint* dropEnd = NULL); void DuplicateSelection(BPoint* dropStart = NULL,BPoint* dropEnd = NULL);
// Move to trash calls try to select the next pose in the view // Move to trash calls try to select the next pose in the view
@@ -370,9 +370,9 @@ public:
void MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow, bool forceCopy, void MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow, bool forceCopy,
bool forceMove = false, bool createLink = false, bool relativeLink = false); bool forceMove = false, bool createLink = false, bool relativeLink = false);
static void MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow, static void MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
BContainerWindow* destWindow, uint32 buttons, BPoint loc, bool forceCopy, BContainerWindow* destWindow, uint32 buttons, BPoint dropPoint, bool forceCopy,
bool forceMove = false, bool createLink = false, bool relativeLink = false, bool forceMove = false, bool createLink = false, bool relativeLink = false,
BPoint where = B_ORIGIN, bool pinToGrid = false); BPoint dragStart = B_ORIGIN, bool pinToGrid = false);
bool UpdateDropTarget(BPoint, const BMessage*, bool trackingContextMenu); bool UpdateDropTarget(BPoint, const BMessage*, bool trackingContextMenu);
// return true if drop target changed // return true if drop target changed
+3 -3
View File
@@ -1007,7 +1007,7 @@ TShortcuts::UpdateCreateLinkItem(BMenuItem* item)
if (fInWindow) { if (fInWindow) {
item->SetEnabled(HasSelection()); item->SetEnabled(HasSelection());
item->SetTarget(PoseView()); item->SetTarget(fContainerWindow);
} }
} }
@@ -1023,7 +1023,7 @@ TShortcuts::UpdateCreateLinkHereItem(BMenuItem* item)
if (fInWindow) { if (fInWindow) {
item->SetEnabled(HasSelection()); item->SetEnabled(HasSelection());
item->SetTarget(PoseView()); item->SetTarget(fContainerWindow);
} }
} }
@@ -1199,7 +1199,7 @@ TShortcuts::UpdateMoveToItem(BMenuItem* item)
if (fInWindow) { if (fInWindow) {
item->SetEnabled(HasSelection() && !SelectionIsReadOnly()); item->SetEnabled(HasSelection() && !SelectionIsReadOnly());
item->SetTarget(PoseView()); item->SetTarget(fContainerWindow);
} }
} }