6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software..
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
36 #include "FSClipboard.h"
38 #include <Clipboard.h>
42 #include <NodeMonitor.h>
50 static void MakeNodeFromName(node_ref
* node
, char* name
);
51 static inline void MakeRefName(char* refName
, const node_ref
* node
);
52 static inline void MakeModeName(char* modeName
, const node_ref
* node
);
53 static inline void MakeModeNameFromRefName(char* modeName
, char* refName
);
54 static inline bool CompareModeAndRefName(const char* modeName
,
59 FSClipboardCheckIntegrity()
66 MakeNodeFromName(node_ref
* node
, char* name
)
68 char* nodeString
= strchr(name
, '_');
69 if (nodeString
!= NULL
) {
70 node
->node
= strtoll(nodeString
+ 1, (char**)NULL
, 10);
71 node
->device
= atoi(name
+ 1);
77 MakeRefName(char* refName
, const node_ref
* node
)
79 sprintf(refName
, "r%" B_PRIdDEV
"_%" B_PRIdINO
, node
->device
, node
->node
);
84 MakeModeName(char* modeName
, const node_ref
* node
)
86 sprintf(modeName
, "m%" B_PRIdDEV
"_%" B_PRIdINO
, node
->device
, node
->node
);
91 MakeModeName(char* name
)
98 MakeModeNameFromRefName(char* modeName
, char* refName
)
100 strcpy(modeName
, refName
);
106 CompareModeAndRefName(const char* modeName
, const char* refName
)
108 return !strcmp(refName
+ 1, modeName
+ 1);
112 // #pragma mark - FSClipBoard
115 #undef B_TRANSLATION_CONTEXT
116 #define B_TRANSLATION_CONTEXT "FSClipBoard"
124 if (be_clipboard
->Lock()) {
125 BMessage
* clip
= be_clipboard
->Data();
127 #ifdef B_BEOS_VERSION_DANO
131 #ifdef B_BEOS_VERSION_DANO
137 if (clip
->GetInfo(B_REF_TYPE
, 0, &refName
, &type
, &count
) == B_OK
138 && clip
->GetInfo(B_INT32_TYPE
, 0, &modeName
, &type
, &count
)
140 result
= CompareModeAndRefName(modeName
, refName
);
143 be_clipboard
->Unlock();
150 FSClipboardStartWatch(BMessenger target
)
152 TTracker
* tracker
= dynamic_cast<TTracker
*>(be_app
);
153 if (tracker
!= NULL
&& tracker
->ClipboardRefsWatcher() != NULL
)
154 tracker
->ClipboardRefsWatcher()->AddToNotifyList(target
);
156 // this code is used by external apps using objects using FSClipboard
157 // functions, i.e. applications using FilePanel
158 BMessenger
messenger(kTrackerSignature
);
159 if (messenger
.IsValid()) {
160 BMessage
message(kStartWatchClipboardRefs
);
161 message
.AddMessenger("target", target
);
162 messenger
.SendMessage(&message
);
169 FSClipboardStopWatch(BMessenger target
)
171 TTracker
* tracker
= dynamic_cast<TTracker
*>(be_app
);
172 if (tracker
!= NULL
&& tracker
->ClipboardRefsWatcher() != NULL
)
173 tracker
->ClipboardRefsWatcher()->AddToNotifyList(target
);
175 // this code is used by external apps using objects using FSClipboard
176 // functions, i.e. applications using FilePanel
177 BMessenger
messenger(kTrackerSignature
);
178 if (messenger
.IsValid()) {
179 BMessage
message(kStopWatchClipboardRefs
);
180 message
.AddMessenger("target", target
);
181 messenger
.SendMessage(&message
);
190 if (!be_clipboard
->Lock())
193 be_clipboard
->Clear();
194 be_clipboard
->Commit();
195 be_clipboard
->Unlock();
199 /** This function adds the given poses list to the clipboard, for both copy
200 * and cut. All poses in the list must have "directory" as parent.
201 * "moveMode" is either kMoveSelection or kCopySelection.
202 * It will check if the entries are already present, so that there can only
203 * be one reference to them in the clipboard.
206 FSClipboardAddPoses(const node_ref
* directory
, PoseList
* list
,
207 uint32 moveMode
, bool clearClipboard
)
209 uint32 refsAdded
= 0;
210 int32 listCount
= list
->CountItems();
212 if (listCount
== 0 || !be_clipboard
->Lock())
215 // update message to be send to all listeners
216 BMessage
updateMessage(kFSClipboardChanges
);
217 updateMessage
.AddInt32("device", directory
->device
);
218 updateMessage
.AddInt64("directory", directory
->node
);
219 updateMessage
.AddBool("clearClipboard", clearClipboard
);
221 TClipboardNodeRef clipNode
;
222 clipNode
.moveMode
= moveMode
;
225 be_clipboard
->Clear();
227 BMessage
* clip
= be_clipboard
->Data();
229 for (int32 index
= 0; index
< listCount
; index
++) {
230 char refName
[64], modeName
[64];
231 BPose
* pose
= (BPose
*)list
->ItemAt(index
);
232 Model
* model
= pose
->TargetModel();
233 const node_ref
* node
= model
->NodeRef();
236 model
->GetEntry(&entry
);
237 if (model
->IsVolume()
240 || model
->IsDesktop())
243 MakeRefName(refName
, node
);
244 MakeModeNameFromRefName(modeName
, refName
);
246 if (clearClipboard
) {
247 if (clip
->AddInt32(modeName
, (int32
)moveMode
) == B_OK
) {
248 if (clip
->AddRef(refName
, model
->EntryRef()) == B_OK
) {
249 pose
->SetClipboardMode(moveMode
);
251 clipNode
.node
= *node
;
252 updateMessage
.AddData("tcnode", T_CLIPBOARD_NODE
,
253 &clipNode
, sizeof(TClipboardNodeRef
), true,
258 clip
->RemoveName(modeName
);
261 if (clip
->ReplaceInt32(modeName
, (int32
)moveMode
) == B_OK
) {
262 // replace old mode if entry already exists in clipboard
263 if (clip
->ReplaceRef(refName
, model
->EntryRef()) == B_OK
) {
264 pose
->SetClipboardMode(moveMode
);
266 clipNode
.node
= *node
;
267 updateMessage
.AddData("tcnode", T_CLIPBOARD_NODE
,
268 &clipNode
, sizeof(TClipboardNodeRef
), true,
273 clip
->RemoveName(modeName
);
275 clipNode
.node
= *node
;
276 clipNode
.moveMode
= kDelete
; // note removing node
277 updateMessage
.AddData("tcnode", T_CLIPBOARD_NODE
,
278 &clipNode
, sizeof(TClipboardNodeRef
), true,
280 clipNode
.moveMode
= moveMode
;
281 // set it back to current value
284 // add it if it doesn't exist
285 if (clip
->AddRef(refName
, model
->EntryRef()) == B_OK
286 && clip
->AddInt32(modeName
, (int32
)moveMode
) == B_OK
) {
287 pose
->SetClipboardMode(moveMode
);
289 clipNode
.node
= *node
;
290 updateMessage
.AddData("tcnode", T_CLIPBOARD_NODE
,
291 &clipNode
, sizeof(TClipboardNodeRef
), true,
296 clip
->RemoveName(modeName
);
297 clip
->RemoveName(refName
);
298 // here notifying delete isn't needed as node didn't
299 // exist in clipboard
304 be_clipboard
->Commit();
306 be_clipboard
->Unlock();
308 BMessenger(kTrackerSignature
).SendMessage(&updateMessage
);
309 // Tracker will notify all listeners
316 FSClipboardRemovePoses(const node_ref
* directory
, PoseList
* list
)
318 if (!be_clipboard
->Lock())
321 // update message to be send to all listeners
322 BMessage
updateMessage(kFSClipboardChanges
);
323 updateMessage
.AddInt32("device", directory
->device
);
324 updateMessage
.AddInt64("directory", directory
->node
);
325 updateMessage
.AddBool("clearClipboard", false);
327 TClipboardNodeRef clipNode
;
328 clipNode
.moveMode
= kDelete
;
330 uint32 refsRemoved
= 0;
332 BMessage
* clip
= be_clipboard
->Data();
334 int32 listCount
= list
->CountItems();
336 for (int32 index
= 0; index
< listCount
; index
++) {
337 char refName
[64], modeName
[64];
338 BPose
* pose
= (BPose
*)list
->ItemAt(index
);
340 clipNode
.node
= *pose
->TargetModel()->NodeRef();
341 MakeRefName(refName
, &clipNode
.node
);
342 MakeModeName(modeName
);
344 if (clip
->RemoveName(refName
) == B_OK
345 && clip
->RemoveName(modeName
)) {
346 updateMessage
.AddData("tcnode", T_CLIPBOARD_NODE
, &clipNode
,
347 sizeof(TClipboardNodeRef
), true, listCount
);
351 be_clipboard
->Commit();
353 be_clipboard
->Unlock();
355 BMessenger(kTrackerSignature
).SendMessage(&updateMessage
);
356 // Tracker will notify all listeners
362 /** Pastes entries from the clipboard to the target model's directory.
363 * Updates moveModes and notifies listeners if necessary.
366 FSClipboardPaste(Model
* model
, uint32 linksMode
)
368 if (!FSClipboardHasRefs())
371 BMessenger
tracker(kTrackerSignature
);
373 node_ref
* destNodeRef
= (node_ref
*)model
->NodeRef();
375 // these will be passed to the asynchronous copy/move process
376 BObjectList
<entry_ref
>* moveList
= new BObjectList
<entry_ref
>(0, true);
377 BObjectList
<entry_ref
>* copyList
= new BObjectList
<entry_ref
>(0, true);
379 if ((be_clipboard
->Lock())) {
380 BMessage
* clip
= be_clipboard
->Data();
385 BMessage
* updateMessage
= NULL
;
386 node_ref updateNodeRef
;
387 updateNodeRef
.device
= -1;
392 for (int32 index
= 0; clip
->GetInfo(B_REF_TYPE
, index
,
393 #ifdef B_BEOS_VERSION_DANO
396 &refName
, &type
, &count
) == B_OK
; index
++) {
398 if (clip
->FindRef(refName
, &ref
) != B_OK
)
401 // If the entry_ref's directory has changed, send previous notification
402 // (if any), and start new one for the new directory
403 if (updateNodeRef
.device
!= ref
.device
404 || updateNodeRef
.node
!= ref
.directory
) {
405 if (updateMessage
!= NULL
) {
406 tracker
.SendMessage(updateMessage
);
407 delete updateMessage
;
410 updateNodeRef
.device
= ref
.device
;
411 updateNodeRef
.node
= ref
.directory
;
413 updateMessage
= new BMessage(kFSClipboardChanges
);
414 updateMessage
->AddInt32("device", updateNodeRef
.device
);
415 updateMessage
->AddInt64("directory", updateNodeRef
.node
);
418 // we need this data later on
419 MakeModeNameFromRefName(modeName
, refName
);
420 if (!linksMode
&& clip
->FindInt32(modeName
, (int32
*)&moveMode
)
427 uint32 newMoveMode
= 0;
428 bool sameDirectory
= destNodeRef
->device
== ref
.device
429 && destNodeRef
->node
== ref
.directory
;
431 if (!entry
.Exists()) {
432 // The entry doesn't exist anymore, so we'll remove
433 // that entry from the clipboard as well
434 clip
->RemoveName(refName
);
435 clip
->RemoveName(modeName
);
437 newMoveMode
= kDelete
;
439 // the entry does exist, so lets see what we will
441 if (!sameDirectory
) {
442 if (linksMode
|| moveMode
== kMoveSelectionTo
) {
443 // the linksMode uses the moveList as well
444 moveList
->AddItem(new entry_ref(ref
));
445 } else if (moveMode
== kCopySelectionTo
)
446 copyList
->AddItem(new entry_ref(ref
));
449 // if the entry should have been removed from its
450 // directory, we want to copy that entry next time, no
451 // matter if the items don't have to be moved at all
452 // (source == target)
453 if (moveMode
== kMoveSelectionTo
)
454 newMoveMode
= kCopySelectionTo
;
457 // add the change to the update message (if necessary)
459 clip
->ReplaceInt32(modeName
, kCopySelectionTo
);
461 TClipboardNodeRef clipNode
;
462 MakeNodeFromName(&clipNode
.node
, modeName
);
463 clipNode
.moveMode
= kDelete
;
464 updateMessage
->AddData("tcnode", T_CLIPBOARD_NODE
,
465 &clipNode
, sizeof(TClipboardNodeRef
), true);
468 be_clipboard
->Commit();
470 // send notification for the last directory
471 if (updateMessage
!= NULL
) {
472 tracker
.SendMessage(updateMessage
);
473 delete updateMessage
;
476 be_clipboard
->Unlock();
479 bool okToMove
= true;
481 // can't copy/paste to root('/') directory
482 if (model
->IsRoot()) {
483 BAlert
* alert
= new BAlert("",
484 B_TRANSLATE("You must drop items on one of the disk icons "
485 "in the \"Disks\" window."), B_TRANSLATE("Cancel"), NULL
, NULL
,
486 B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
487 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
493 model
->GetEntry(&entry
);
495 // can't copy items into the trash
496 if (copyList
->CountItems() > 0 && model
->IsTrash()) {
497 BAlert
* alert
= new BAlert("",
498 B_TRANSLATE("Sorry, you can't copy items to the Trash."),
499 B_TRANSLATE("Cancel"), NULL
, NULL
, B_WIDTH_AS_USUAL
,
501 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
507 // there was some problem with our target, so we bail out here
513 // asynchronous calls take over ownership of the objects passed to it
514 if (moveList
->CountItems() > 0) {
515 FSMoveToFolder(moveList
, new BEntry(entry
),
516 linksMode
? linksMode
: kMoveSelectionTo
);
520 if (copyList
->CountItems() > 0)
521 FSMoveToFolder(copyList
, new BEntry(entry
), kCopySelectionTo
);
529 // Seek node in clipboard, if found return it's moveMode
532 FSClipboardFindNodeMode(Model
* model
, bool autoLock
, bool updateRefIfNeeded
)
536 if (!be_clipboard
->Lock())
542 BMessage
* clip
= be_clipboard
->Data();
544 const node_ref
* node
= model
->NodeRef();
546 MakeModeName(modeName
, node
);
547 if ((clip
->FindInt32(modeName
, &moveMode
) == B_OK
)) {
548 const entry_ref
* ref
= model
->EntryRef();
551 MakeRefName(refName
, node
);
552 if ((clip
->FindRef(refName
, &clipref
) == B_OK
)) {
553 if (clipref
!= *ref
) {
554 if (updateRefIfNeeded
) {
555 clip
->ReplaceRef(refName
, ref
);
558 clip
->RemoveName(refName
);
559 clip
->RemoveName(modeName
);
566 clip
->RemoveName(modeName
);
574 be_clipboard
->Commit();
577 be_clipboard
->Unlock();
580 FSClipboardRemove(model
);
582 return (uint32
)moveMode
;
587 FSClipboardRemove(Model
* model
)
589 BMessenger
messenger(kTrackerSignature
);
590 if (messenger
.IsValid()) {
591 BMessage
* report
= new BMessage(kFSClipboardChanges
);
592 TClipboardNodeRef tcnode
;
593 tcnode
.node
= *model
->NodeRef();
594 tcnode
.moveMode
= kDelete
;
595 const entry_ref
* ref
= model
->EntryRef();
596 report
->AddInt32("device", ref
->device
);
597 report
->AddInt64("directory", ref
->directory
);
598 report
->AddBool("clearClipboard", false);
599 report
->AddData("tcnode", T_CLIPBOARD_NODE
, &tcnode
, sizeof(tcnode
),
601 messenger
.SendMessage(report
);
610 BClipboardRefsWatcher::BClipboardRefsWatcher()
611 : BLooper("ClipboardRefsWatcher", B_LOW_PRIORITY
, 4096),
612 fNotifyList(10, false)
614 watch_node(NULL
, B_WATCH_MOUNT
, this);
615 fRefsInClipboard
= FSClipboardHasRefs();
616 be_clipboard
->StartWatching(this);
620 BClipboardRefsWatcher::~BClipboardRefsWatcher()
623 be_clipboard
->StopWatching(this);
628 BClipboardRefsWatcher::AddToNotifyList(BMessenger target
)
631 // add the messenger if it's not already in the list
632 // ToDo: why do we have to care about that?
633 BMessenger
* messenger
;
636 for (int32 index
= 0; (messenger
= fNotifyList
.ItemAt(index
)) != NULL
;
638 if (*messenger
== target
) {
644 fNotifyList
.AddItem(new BMessenger(target
));
652 BClipboardRefsWatcher::RemoveFromNotifyList(BMessenger target
)
655 BMessenger
* messenger
;
657 for (int32 index
= 0; (messenger
= fNotifyList
.ItemAt(index
)) != NULL
;
659 if (*messenger
== target
) {
660 delete fNotifyList
.RemoveItemAt(index
);
670 BClipboardRefsWatcher::AddNode(const node_ref
* node
)
672 TTracker::WatchNode(node
, B_WATCH_NAME
, this);
673 fRefsInClipboard
= true;
678 BClipboardRefsWatcher::RemoveNode(node_ref
* node
, bool removeFromClipboard
)
680 watch_node(node
, B_STOP_WATCHING
, this);
682 if (!removeFromClipboard
)
685 if (be_clipboard
->Lock()) {
686 BMessage
* clip
= be_clipboard
->Data();
689 MakeRefName(name
, node
);
690 clip
->RemoveName(name
);
692 clip
->RemoveName(name
);
694 be_clipboard
->Commit();
696 be_clipboard
->Unlock();
702 BClipboardRefsWatcher::RemoveNodesByDevice(dev_t device
)
704 if (!be_clipboard
->Lock())
707 BMessage
* clip
= be_clipboard
->Data();
710 sprintf(deviceName
, "r%" B_PRIdDEV
"_", device
);
716 while (clip
->GetInfo(B_REF_TYPE
, index
,
717 #ifdef B_BEOS_VERSION_DANO
720 &refName
, &type
, &count
) == B_OK
) {
721 if (!strncmp(deviceName
, refName
, strlen(deviceName
))) {
722 clip
->RemoveName(refName
);
723 MakeModeName(refName
);
724 clip
->RemoveName(refName
);
727 MakeNodeFromName(&node
, refName
);
728 watch_node(&node
, B_STOP_WATCHING
, this);
732 be_clipboard
->Commit();
734 be_clipboard
->Unlock();
739 BClipboardRefsWatcher::UpdateNode(node_ref
* node
, entry_ref
* ref
)
741 if (!be_clipboard
->Lock())
744 BMessage
* clip
= be_clipboard
->Data();
747 MakeRefName(name
, node
);
748 if ((clip
->ReplaceRef(name
, ref
)) != B_OK
) {
749 clip
->RemoveName(name
);
751 clip
->RemoveName(name
);
755 be_clipboard
->Commit();
757 be_clipboard
->Unlock();
762 BClipboardRefsWatcher::Clear()
765 watch_node(NULL
, B_WATCH_MOUNT
, this);
767 BMessage
message(kFSClipboardChanges
);
768 message
.AddBool("clearClipboard", true);
770 int32 items
= fNotifyList
.CountItems();
771 for (int32 i
= 0;i
< items
;i
++) {
772 fNotifyList
.ItemAt(i
)->SendMessage(&message
);
780 //BClipboardRefsWatcher::UpdatePoseViews(bool clearClipboard,
781 // const node_ref* node)
783 // BMessage message(kFSClipboardChanges);
784 // message.AddInt32("device", node->device);
785 // message.AddInt64("directory", node->node);
786 // message.AddBool("clearClipboard", clearClipboard);
789 // int32 items = fNotifyList.CountItems();
790 // for (int32 i = 0;i < items;i++) {
791 // fNotifyList.ItemAt(i)->SendMessage(&message);
799 BClipboardRefsWatcher::UpdatePoseViews(BMessage
* reportMessage
)
802 // check if it was cleared, if so clear watching
803 bool clearClipboard
= false;
804 if (reportMessage
->FindBool("clearClipboard", &clearClipboard
) == B_OK
807 watch_node(NULL
, B_WATCH_MOUNT
, this);
810 // loop through reported node_ref's movemodes:
811 // move or copy: start watching node_ref
812 // remove: stop watching node_ref
814 TClipboardNodeRef
* tcnode
= NULL
;
816 while (reportMessage
->FindData("tcnode", T_CLIPBOARD_NODE
, index
,
817 (const void**)&tcnode
, &size
) == B_OK
) {
818 if (tcnode
->moveMode
== kDelete
) {
819 watch_node(&tcnode
->node
, B_STOP_WATCHING
, this);
821 watch_node(&tcnode
->node
, B_STOP_WATCHING
, this);
822 TTracker::WatchNode(&tcnode
->node
, B_WATCH_NAME
, this);
823 fRefsInClipboard
= true;
829 int32 items
= fNotifyList
.CountItems();
830 for (int32 i
= 0;i
< items
;i
++) {
831 fNotifyList
.ItemAt(i
)->SendMessage(reportMessage
);
839 BClipboardRefsWatcher::MessageReceived(BMessage
* message
)
841 if (message
->what
== B_CLIPBOARD_CHANGED
&& fRefsInClipboard
) {
842 if (!(fRefsInClipboard
= FSClipboardHasRefs()))
845 } else if (message
->what
!= B_NODE_MONITOR
) {
846 _inherited::MessageReceived(message
);
850 switch (message
->FindInt32("opcode")) {
856 const char* name
= NULL
;
857 message
->FindInt64("from directory", &fromDir
);
858 message
->FindInt64("to directory", &toDir
);
859 message
->FindInt64("node", &node
.node
);
860 message
->FindInt32("device", &node
.device
);
861 message
->FindString("name", &name
);
862 entry_ref
ref(node
.device
, toDir
, name
);
863 UpdateNode(&node
, &ref
);
867 case B_DEVICE_UNMOUNTED
:
870 message
->FindInt32("device", &device
);
871 RemoveNodesByDevice(device
);
875 case B_ENTRY_REMOVED
:
878 message
->FindInt64("node", &node
.node
);
879 message
->FindInt32("device", &node
.device
);
880 RemoveNode(&node
, true);