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