xref: /haiku/src/kits/tracker/FSClipboard.cpp (revision 1b80286772b529a3d6de3bbeb0720c62e6a32fed)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
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:
14 
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..
17 
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.
24 
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.
28 
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.
32 All rights reserved.
33 */
34 
35 #include "FSClipboard.h"
36 #include <Clipboard.h>
37 #include <Alert.h>
38 #include <NodeMonitor.h>
39 #include "Commands.h"
40 #include "FSUtils.h"
41 #include "Tracker.h"
42 
43 // prototypes
44 static void MakeNodeFromName(node_ref *node, char *name);
45 static inline void MakeRefName(char *refName, const node_ref *node);
46 static inline void MakeModeName(char *modeName, const node_ref *node);
47 static inline void MakeModeNameFromRefName(char *modeName, char *refName);
48 static inline bool CompareModeAndRefName(const char *modeName, const char *refName);
49 
50 
51 //these are from PoseView.cpp
52 extern const char *kNoCopyToTrashStr;
53 extern const char *kNoCopyToRootStr;
54 extern const char *kOkToMoveStr;
55 
56 /*
57 static bool
58 FSClipboardCheckIntegrity()
59 {
60 	return true;
61 }
62 */
63 
64 static void
65 MakeNodeFromName(node_ref *node, char *name)
66 {
67 	char *nodeString = strchr(name, '_');
68 	if (nodeString != NULL) {
69 		node->node = strtoll(nodeString + 1, (char **)NULL, 10);
70 		node->device = atoi(name + 1);
71 	}
72 }
73 
74 
75 static inline void
76 MakeRefName(char *refName, const node_ref *node)
77 {
78 	sprintf(refName, "r%ld_%Ld", node->device, node->node);
79 }
80 
81 
82 static inline void
83 MakeModeName(char *modeName, const node_ref *node)
84 {
85 	sprintf(modeName, "m%ld_%Ld", node->device, node->node);
86 }
87 
88 
89 static inline void
90 MakeModeName(char *name)
91 {
92 	name[0] = 'm';
93 }
94 
95 
96 static inline void
97 MakeModeNameFromRefName(char *modeName, char *refName)
98 {
99 	strcpy(modeName, refName);
100 	modeName[0] = 'm';
101 }
102 
103 
104 static inline bool
105 CompareModeAndRefName(const char *modeName, const char *refName)
106 {
107 	return !strcmp(refName + 1, modeName + 1);
108 }
109 
110 
111 //	#pragma mark -
112 
113 
114 bool
115 FSClipboardHasRefs()
116 {
117 	bool result = false;
118 
119 	if (be_clipboard->Lock()) {
120 		BMessage *clip = be_clipboard->Data();
121 		if (clip != NULL) {
122 #ifdef B_BEOS_VERSION_DANO
123 			const
124 #endif
125 			char *refName;
126 #ifdef B_BEOS_VERSION_DANO
127 			const
128 #endif
129 			char *modeName;
130 			uint32 type;
131 			int32 count;
132 			if (clip->GetInfo(B_REF_TYPE, 0, &refName, &type, &count) == B_OK
133 				&& clip->GetInfo(B_INT32_TYPE, 0, &modeName, &type, &count) == B_OK)
134 				result = CompareModeAndRefName(modeName, refName);
135 		}
136 		be_clipboard->Unlock();
137 	}
138 	return result;
139 }
140 
141 
142 void
143 FSClipboardStartWatch(BMessenger target)
144 {
145 	if (dynamic_cast<TTracker *>(be_app) != NULL)
146 		((TTracker *)be_app)->ClipboardRefsWatcher()->AddToNotifyList(target);
147 	else {
148 		// this code is used by external apps using objects using FSClipboard functions
149 		// i.e: applications using FilePanel
150 		BMessenger messenger(kTrackerSignature);
151 		if (messenger.IsValid()) {
152 			BMessage message(kStartWatchClipboardRefs);
153 			message.AddMessenger("target", target);
154 			messenger.SendMessage(&message);
155 		}
156 	}
157 }
158 
159 
160 void
161 FSClipboardStopWatch(BMessenger target)
162 {
163 	if (dynamic_cast<TTracker *>(be_app) != NULL)
164 		((TTracker *)be_app)->ClipboardRefsWatcher()->AddToNotifyList(target);
165 	else {
166 		// this code is used by external apps using objects using FSClipboard functions
167 		// i.e: applications using FilePanel
168 		BMessenger messenger(kTrackerSignature);
169 		if (messenger.IsValid()) {
170 			BMessage message(kStopWatchClipboardRefs);
171 			message.AddMessenger("target", target);
172 			messenger.SendMessage(&message);
173 		}
174 	}
175 }
176 
177 
178 void
179 FSClipboardClear()
180 {
181 	if (!be_clipboard->Lock())
182 		return;
183 
184 	be_clipboard->Clear();
185 	be_clipboard->Commit();
186 	be_clipboard->Unlock();
187 }
188 
189 
190 /** This function adds the given poses list to the clipboard, for both copy
191  *	and cut. All poses in the list must have "directory" as parent.
192  *	"moveMode" is either kMoveSelection or kCopySelection.
193  *	It will check if the entries are already present, so that there can only
194  *	be one reference to them in the clipboard.
195  */
196 
197 uint32
198 FSClipboardAddPoses(const node_ref *directory, PoseList *list, uint32 moveMode,
199 	bool clearClipboard)
200 {
201 	uint32 refsAdded = 0;
202 	int32 listCount = list->CountItems();
203 
204 	if (listCount == 0 || !be_clipboard->Lock())
205 		return 0;
206 
207 	// update message to be send to all listeners
208 	BMessage updateMessage(kFSClipboardChanges);
209 	updateMessage.AddInt32("device", directory->device);
210 	updateMessage.AddInt64("directory", directory->node);
211 	updateMessage.AddBool("clearClipboard", clearClipboard);
212 
213 	TClipboardNodeRef clipNode;
214 	clipNode.moveMode = moveMode;
215 
216 	if (clearClipboard)
217 		be_clipboard->Clear();
218 
219 	BMessage *clip = be_clipboard->Data();
220 	if (clip != NULL) {
221 		for (int32 index = 0; index < listCount; index++) {
222 			char refName[64], modeName[64];
223 			BPose *pose = (BPose *)list->ItemAt(index);
224 			Model *model = pose->TargetModel();
225 			const node_ref *node = model->NodeRef();
226 
227 			BEntry entry;
228 			model->GetEntry(&entry);
229 			if (model->IsVolume()
230 				|| model->IsRoot()
231 				|| FSIsTrashDir(&entry)
232 				|| FSIsDeskDir(&entry))
233 				continue;
234 
235 			MakeRefName(refName, node);
236 			MakeModeNameFromRefName(modeName, refName);
237 
238 			if (clearClipboard) {
239 				if (clip->AddInt32(modeName, (int32)moveMode) == B_OK)
240 					if (clip->AddRef(refName, model->EntryRef()) == B_OK) {
241 						pose->SetClipboardMode(moveMode);
242 
243 						clipNode.node = *node;
244 						updateMessage.AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
245 							sizeof(TClipboardNodeRef), true, listCount);
246 
247 						refsAdded++;
248 					} else
249 						clip->RemoveName(modeName);
250 			} else {
251 				if (clip->ReplaceInt32(modeName, (int32)moveMode) == B_OK) {
252 					// replace old mode if entry already exists in clipboard
253 					if (clip->ReplaceRef(refName, model->EntryRef()) == B_OK) {
254 						pose->SetClipboardMode(moveMode);
255 
256 						clipNode.node = *node;
257 						updateMessage.AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
258 							sizeof(TClipboardNodeRef), true, listCount);
259 
260 						refsAdded++;
261 					} else {
262 						clip->RemoveName(modeName);
263 
264 						clipNode.node = *node;
265 						clipNode.moveMode = kDelete;	// note removing node
266 						updateMessage.AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
267 							sizeof(TClipboardNodeRef), true, listCount);
268 						clipNode.moveMode = moveMode; // set it back to current value
269 					}
270 				} else {
271 					// add it if it doesn't exist
272 					if (clip->AddRef(refName, model->EntryRef()) == B_OK
273 						&& clip->AddInt32(modeName, (int32)moveMode) == B_OK) {
274 						pose->SetClipboardMode(moveMode);
275 
276 						clipNode.node = *node;
277 						updateMessage.AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
278 							sizeof(TClipboardNodeRef), true, listCount);
279 
280 						refsAdded++;
281 					} else {
282 						clip->RemoveName(modeName);
283 						clip->RemoveName(refName);
284 						// here notifying delete isn't needed as node didn't
285 						// exist in clipboard
286 					}
287 				}
288 			}
289 		}
290 		be_clipboard->Commit();
291 	}
292 	be_clipboard->Unlock();
293 
294 	BMessenger(kTrackerSignature).SendMessage(&updateMessage);
295 		// Tracker will notify all listeners
296 
297 	return refsAdded;
298 }
299 
300 
301 uint32
302 FSClipboardRemovePoses(const node_ref *directory, PoseList *list)
303 {
304 	if (!be_clipboard->Lock())
305 		return 0;
306 
307 	// update message to be send to all listeners
308 	BMessage updateMessage(kFSClipboardChanges);
309 	updateMessage.AddInt32("device", directory->device);
310 	updateMessage.AddInt64("directory", directory->node);
311 	updateMessage.AddBool("clearClipboard", false);
312 
313 	TClipboardNodeRef clipNode;
314 	clipNode.moveMode = kDelete;
315 
316 	uint32 refsRemoved = 0;
317 
318 	BMessage *clip = be_clipboard->Data();
319 	if (clip != NULL) {
320 		int32 listCount = list->CountItems();
321 
322 		for (int32 index = 0; index < listCount; index++) {
323 			char refName[64], modeName[64];
324 			BPose *pose = (BPose *)list->ItemAt(index);
325 
326 			clipNode.node = *pose->TargetModel()->NodeRef();
327 			MakeRefName(refName, &clipNode.node);
328 			MakeModeName(modeName);
329 
330 			if (clip->RemoveName(refName) == B_OK && clip->RemoveName(modeName)) {
331 				updateMessage.AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
332 					sizeof(TClipboardNodeRef), true, listCount);
333 				refsRemoved++;
334 			}
335 		}
336 		be_clipboard->Commit();
337 	}
338 	be_clipboard->Unlock();
339 
340 	BMessenger(kTrackerSignature).SendMessage(&updateMessage);
341 		// Tracker will notify all listeners
342 
343 	return refsRemoved;
344 }
345 
346 
347 /** Pastes entries from the clipboard to the target model's directory.
348  *	Updates moveModes and notifies listeners if necessary.
349  */
350 
351 bool
352 FSClipboardPaste(Model *model, uint32 linksMode)
353 {
354 	if (!FSClipboardHasRefs())
355 		return false;
356 
357 	BMessenger tracker(kTrackerSignature);
358 
359 	node_ref *destNodeRef = (node_ref *)model->NodeRef();
360 
361 	// these will be passed to the asynchronous copy/move process
362 	BObjectList<entry_ref> *moveList = new BObjectList<entry_ref>(0, true);
363 	BObjectList<entry_ref> *copyList = new BObjectList<entry_ref>(0, true);
364 
365 	if ((be_clipboard->Lock())) {
366 		BMessage *clip = be_clipboard->Data();
367 		if (clip != NULL) {
368 			char modeName[64];
369 			uint32 moveMode = 0;
370 
371 			BMessage *updateMessage = NULL;
372 			node_ref updateNodeRef;
373 			updateNodeRef.device = -1;
374 
375 			char *refName;
376 			type_code type;
377 			int32 count;
378 			for (int32 index = 0; clip->GetInfo(B_REF_TYPE, index,
379 #ifdef B_BEOS_VERSION_DANO
380 				(const char **)
381 #endif
382 				&refName, &type, &count) == B_OK; index++) {
383 				entry_ref ref;
384 				if (clip->FindRef(refName, &ref) != B_OK)
385 					continue;
386 
387 				// If the entry_ref's directory has changed, send previous notification
388 				// (if any), and start new one for the new directory
389 				if (updateNodeRef.device != ref.device
390 					|| updateNodeRef.node != ref.directory) {
391 					if (updateMessage != NULL) {
392 						tracker.SendMessage(updateMessage);
393 						delete updateMessage;
394 					}
395 
396 					updateNodeRef.device = ref.device;
397 					updateNodeRef.node = ref.directory;
398 
399 					updateMessage = new BMessage(kFSClipboardChanges);
400 					updateMessage->AddInt32("device", updateNodeRef.device);
401 					updateMessage->AddInt64("directory", updateNodeRef.node);
402 				}
403 
404 				// we need this data later on
405 				MakeModeNameFromRefName(modeName, refName);
406 				if (!linksMode && clip->FindInt32(modeName, (int32 *)&moveMode) != B_OK)
407 					continue;
408 
409 				BEntry entry(&ref);
410 
411 				uint32 newMoveMode = 0;
412 				bool sameDirectory = destNodeRef->device == ref.device && destNodeRef->node == ref.directory;
413 
414 				if (!entry.Exists()) {
415 					// The entry doesn't exist anymore, so we'll remove
416 					// that entry from the clipboard as well
417 					clip->RemoveName(refName);
418 					clip->RemoveName(modeName);
419 
420 					newMoveMode = kDelete;
421 				} else {
422 					// the entry does exist, so lets see what we will
423 					// do with it
424 					if (!sameDirectory) {
425 						if (linksMode || moveMode == kMoveSelectionTo) {
426 							// the linksMode uses the moveList as well
427 							moveList->AddItem(new entry_ref(ref));
428 						} else if (moveMode == kCopySelectionTo)
429 							copyList->AddItem(new entry_ref(ref));
430 					}
431 
432 					// if the entry should have been removed from its directory,
433 					// we want to copy that entry next time, no matter if the
434 					// items don't have to be moved at all (source == target)
435 					if (moveMode == kMoveSelectionTo)
436 						newMoveMode = kCopySelectionTo;
437 				}
438 
439 				// add the change to the update message (if necessary)
440 				if (newMoveMode) {
441 					clip->ReplaceInt32(modeName, kCopySelectionTo);
442 
443 					TClipboardNodeRef clipNode;
444 					MakeNodeFromName(&clipNode.node, modeName);
445 					clipNode.moveMode = kDelete;
446 					updateMessage->AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
447 						sizeof(TClipboardNodeRef), true);
448 				}
449 			}
450 			be_clipboard->Commit();
451 
452 			// send notification for the last directory
453 			if (updateMessage != NULL) {
454 				tracker.SendMessage(updateMessage);
455 				delete updateMessage;
456 			}
457 		}
458 		be_clipboard->Unlock();
459 	}
460 
461 	bool okToMove = true;
462 
463 	// can't copy/paste to root('/') directory
464 	if (model->IsRoot()) {
465 		BAlert *alert = new BAlert("", kNoCopyToRootStr, "Cancel",
466 			NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
467 		alert->SetShortcut(0, B_ESCAPE);
468 		alert->Go();
469 		okToMove = false;
470 	}
471 
472 	BEntry entry;
473 	model->GetEntry(&entry);
474 
475 	// can't copy items into the trash
476 	if (copyList->CountItems() > 0 && FSIsTrashDir(&entry)) {
477 		BAlert *alert = new BAlert("", kNoCopyToTrashStr, "Cancel",
478 			NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
479 		alert->SetShortcut(0, B_ESCAPE);
480 		alert->Go();
481 		okToMove = false;
482 	}
483 
484 	if (!okToMove) {
485 		// there was some problem with our target, so we bail out here
486 		delete moveList;
487 		delete copyList;
488 		return false;
489 	}
490 
491 	// asynchronous calls take over ownership of the objects passed to it
492 	if (moveList->CountItems() > 0)
493 		FSMoveToFolder(moveList, new BEntry(entry), linksMode ? linksMode : kMoveSelectionTo);
494 	else
495 		delete moveList;
496 
497 	if (copyList->CountItems() > 0)
498 		FSMoveToFolder(copyList, new BEntry(entry), kCopySelectionTo);
499 	else
500 		delete copyList;
501 
502 	return true;
503 }
504 
505 
506 /**	Seek node in clipboard, if found return it's moveMode
507  *	else return 0
508  */
509 
510 uint32
511 FSClipboardFindNodeMode(Model *model, bool autoLock, bool updateRefIfNeeded)
512 {
513 	int32 moveMode = 0;
514 	if (autoLock) {
515 		if (!be_clipboard->Lock())
516 			return 0;
517 	}
518 	bool remove = false;
519 	bool change = false;
520 
521 	BMessage *clip = be_clipboard->Data();
522 	if (clip != NULL) {
523 		const node_ref *node = model->NodeRef();
524 		char modeName[64];
525 		MakeModeName(modeName, node);
526 		if ((clip->FindInt32(modeName, &moveMode) == B_OK)) {
527 			const entry_ref *ref = model->EntryRef();
528 			entry_ref clipref;
529 			char refName[64];
530 			MakeRefName(refName, node);
531 			if ((clip->FindRef(refName, &clipref) == B_OK)) {
532 				if (clipref != *ref) {
533 					if (updateRefIfNeeded) {
534 						clip->ReplaceRef(refName, ref);
535 						change = true;
536 					} else {
537 						clip->RemoveName(refName);
538 						clip->RemoveName(modeName);
539 						change = true;
540 						remove = true;
541 						moveMode = 0;
542 					}
543 				}
544 			} else {
545 				clip->RemoveName(modeName);
546 				change = true;
547 				remove = true;
548 				moveMode = 0;
549 			}
550 		}
551 	}
552 	if (change)
553 		be_clipboard->Commit();
554 
555 	if (autoLock)
556 		be_clipboard->Unlock();
557 
558 	if (remove)
559 		FSClipboardRemove(model);
560 
561 	return (uint32)moveMode;
562 }
563 
564 
565 void
566 FSClipboardRemove(Model *model)
567 {
568 	BMessenger messenger(kTrackerSignature);
569 	if (messenger.IsValid()) {
570 		BMessage *report = new BMessage(kFSClipboardChanges);
571 		TClipboardNodeRef tcnode;
572 		tcnode.node = *model->NodeRef();
573 		tcnode.moveMode = kDelete;
574 		const entry_ref *ref = model->EntryRef();
575 		report->AddInt32("device", ref->device);
576 		report->AddInt64("directory", ref->directory);
577 		report->AddBool("clearClipboard", false);
578 		report->AddData("tcnode", T_CLIPBOARD_NODE, &tcnode, sizeof(tcnode), true);
579 		messenger.SendMessage(report);
580 		delete report;
581 	}
582 }
583 
584 
585 //	#pragma mark -
586 
587 
588 BClipboardRefsWatcher::BClipboardRefsWatcher()
589 	:	BLooper("ClipboardRefsWatcher", B_LOW_PRIORITY, 4096),
590 	fNotifyList(10, false)
591 {
592 	watch_node(NULL, B_WATCH_MOUNT, this);
593 	fRefsInClipboard = FSClipboardHasRefs();
594 	be_clipboard->StartWatching(this);
595 }
596 
597 
598 BClipboardRefsWatcher::~BClipboardRefsWatcher()
599 {
600 	stop_watching(this);
601 	be_clipboard->StopWatching(this);
602 }
603 
604 
605 void
606 BClipboardRefsWatcher::AddToNotifyList(BMessenger target)
607 {
608 	if (Lock()) {
609 		// add the messenger if it's not already in the list
610 		// ToDo: why do we have to care about that?
611 		BMessenger *messenger;
612 		bool found = false;
613 
614 		for (int32 index = 0;(messenger = fNotifyList.ItemAt(index)) != NULL; index++) {
615 			if (*messenger == target) {
616 				found = true;
617 				break;
618 			}
619 		}
620 		if (!found)
621 			fNotifyList.AddItem(new BMessenger(target));
622 
623 		Unlock();
624 	}
625 }
626 
627 
628 void
629 BClipboardRefsWatcher::RemoveFromNotifyList(BMessenger target)
630 {
631 	if (Lock()) {
632 		BMessenger *messenger;
633 
634 		for (int32 index = 0;(messenger = fNotifyList.ItemAt(index)) != NULL; index++) {
635 			if (*messenger == target) {
636 				delete fNotifyList.RemoveItemAt(index);
637 				break;
638 			}
639 		}
640 		Unlock();
641 	}
642 }
643 
644 
645 void
646 BClipboardRefsWatcher::AddNode(const node_ref *node)
647 {
648 	TTracker::WatchNode(node, B_WATCH_NAME, this);
649 	fRefsInClipboard = true;
650 }
651 
652 
653 void
654 BClipboardRefsWatcher::RemoveNode(node_ref *node, bool removeFromClipboard)
655 {
656 	watch_node(node, B_STOP_WATCHING, this);
657 
658 	if (!removeFromClipboard)
659 		return;
660 
661 	if (be_clipboard->Lock()) {
662 		BMessage *clip = be_clipboard->Data();
663 		if (clip != NULL) {
664 			char name[64];
665 			MakeRefName(name, node);
666 			clip->RemoveName(name);
667 			MakeModeName(name);
668 			clip->RemoveName(name);
669 
670 			be_clipboard->Commit();
671 		}
672 		be_clipboard->Unlock();
673 	}
674 }
675 
676 
677 void
678 BClipboardRefsWatcher::RemoveNodesByDevice(dev_t device)
679 {
680 	if (!be_clipboard->Lock())
681 		return;
682 
683 	BMessage *clip = be_clipboard->Data();
684 	if (clip != NULL) {
685 		char deviceName[6];
686 		sprintf(deviceName, "r%ld_", device);
687 
688 		int32 index = 0;
689 		char *refName;
690 		type_code type;
691 		int32 count;
692 		while (clip->GetInfo(B_REF_TYPE, index,
693 #ifdef B_BEOS_VERSION_DANO
694 			(const char **)
695 #endif
696 			&refName, &type, &count) == B_OK) {
697 			if (!strncmp(deviceName, refName, strlen(deviceName))) {
698 				clip->RemoveName(refName);
699 				MakeModeName(refName);
700 				clip->RemoveName(refName);
701 
702 				node_ref node;
703 				MakeNodeFromName(&node, refName);
704 				watch_node(&node, B_STOP_WATCHING, this);
705 			}
706 			index++;
707 		}
708 		be_clipboard->Commit();
709 	}
710 	be_clipboard->Unlock();
711 }
712 
713 
714 void
715 BClipboardRefsWatcher::UpdateNode(node_ref *node, entry_ref *ref)
716 {
717 	if (!be_clipboard->Lock())
718 		return;
719 
720 	BMessage *clip = be_clipboard->Data();
721 	if (clip != NULL) {
722 		char name[64];
723 		MakeRefName(name, node);
724 		if ((clip->ReplaceRef(name, ref)) != B_OK) {
725 			clip->RemoveName(name);
726 			MakeModeName(name);
727 			clip->RemoveName(name);
728 
729 			RemoveNode(node);
730 		}
731 		be_clipboard->Commit();
732 	}
733 	be_clipboard->Unlock();
734 }
735 
736 
737 void
738 BClipboardRefsWatcher::Clear()
739 {
740 	stop_watching(this);
741 	watch_node(NULL, B_WATCH_MOUNT, this);
742 
743 	BMessage message(kFSClipboardChanges);
744 	message.AddBool("clearClipboard", true);
745 	if (Lock()) {
746 		int32 items = fNotifyList.CountItems();
747 		for (int32 i = 0;i < items;i++) {
748 			fNotifyList.ItemAt(i)->SendMessage(&message);
749 		}
750 		Unlock();
751 	}
752 }
753 
754 /*
755 void
756 BClipboardRefsWatcher::UpdatePoseViews(bool clearClipboard, const node_ref *node)
757 {
758 	BMessage message(kFSClipboardChanges);
759 	message.AddInt32("device", node->device);
760 	message.AddInt64("directory", node->node);
761 	message.AddBool("clearClipboard", clearClipboard);
762 
763 	if (Lock()) {
764 		int32 items = fNotifyList.CountItems();
765 		for (int32 i = 0;i < items;i++) {
766 			fNotifyList.ItemAt(i)->SendMessage(&message);
767 		}
768 		Unlock();
769 	}
770 }
771 */
772 
773 void
774 BClipboardRefsWatcher::UpdatePoseViews(BMessage *reportMessage)
775 {
776 	if (Lock()) {
777 		// check if it was cleared, if so clear watching
778 		bool clearClipboard = false;
779 		if (reportMessage->FindBool("clearClipboard", &clearClipboard) == B_OK
780 			&& clearClipboard) {
781 			stop_watching(this);
782 			watch_node(NULL, B_WATCH_MOUNT, this);
783 		}
784 
785 		// loop through reported node_ref's movemodes:
786 		// move or copy: start watching node_ref
787 		// remove: stop watching node_ref
788 		int32 index = 0;
789 		TClipboardNodeRef *tcnode = NULL;
790 		ssize_t size;
791 		while (reportMessage->FindData("tcnode", T_CLIPBOARD_NODE, index, (const void**)&tcnode, &size) == B_OK) {
792 			if (tcnode->moveMode == kDelete) {
793 				watch_node(&tcnode->node, B_STOP_WATCHING, this);
794 			} else {
795 				watch_node(&tcnode->node, B_STOP_WATCHING, this);
796 				TTracker::WatchNode(&tcnode->node, B_WATCH_NAME, this);
797 				fRefsInClipboard = true;
798 			}
799 			index++;
800 		}
801 
802 		// send report
803 		int32 items = fNotifyList.CountItems();
804 		for (int32 i = 0;i < items;i++) {
805 			fNotifyList.ItemAt(i)->SendMessage(reportMessage);
806 		}
807 		Unlock();
808 	}
809 }
810 
811 
812 void
813 BClipboardRefsWatcher::MessageReceived(BMessage *message)
814 {
815 	if (message->what == B_CLIPBOARD_CHANGED && fRefsInClipboard) {
816 		if (!(fRefsInClipboard = FSClipboardHasRefs()))
817 			Clear();
818 		return;
819 	} else if (message->what != B_NODE_MONITOR) {
820 		_inherited::MessageReceived(message);
821 		return;
822 	}
823 
824 	switch (message->FindInt32("opcode")) {
825 		case B_ENTRY_MOVED:
826 		{
827 			ino_t toDir;
828 			ino_t fromDir;
829 			node_ref node;
830 			const char *name = NULL;
831 			message->FindInt64("from directory", &fromDir);
832 			message->FindInt64("to directory", &toDir);
833 			message->FindInt64("node", &node.node);
834 			message->FindInt32("device", &node.device);
835 			message->FindString("name", &name);
836 			entry_ref ref(node.device, toDir, name);
837 			UpdateNode(&node, &ref);
838 			break;
839 		}
840 
841 		case B_DEVICE_UNMOUNTED:
842 		{
843 			dev_t device;
844 			message->FindInt32("device", &device);
845 			RemoveNodesByDevice(device);
846 			break;
847 		}
848 
849 		case B_ENTRY_REMOVED:
850 		{
851 			node_ref node;
852 			message->FindInt64("node", &node.node);
853 			message->FindInt32("device", &node.device);
854 			RemoveNode(&node, true);
855 			break;
856 		}
857 	}
858 }
859