xref: /haiku/src/kits/tracker/FSClipboard.cpp (revision 788e359072485f0439a9bed8278168aed8fe023b)
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 
379 	if ((be_clipboard->Lock())) {
380 		BMessage* clip = be_clipboard->Data();
381 		if (clip != NULL) {
382 			char modeName[64];
383 			uint32 moveMode = 0;
384 
385 			BMessage* updateMessage = NULL;
386 			node_ref updateNodeRef;
387 			updateNodeRef.device = -1;
388 
389 			char* refName;
390 			type_code type;
391 			int32 count;
392 			for (int32 index = 0; clip->GetInfo(B_REF_TYPE, index,
393 #ifdef B_BEOS_VERSION_DANO
394 				(const char**)
395 #endif
396 				&refName, &type, &count) == B_OK; index++) {
397 				entry_ref ref;
398 				if (clip->FindRef(refName, &ref) != B_OK)
399 					continue;
400 
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;
408 					}
409 
410 					updateNodeRef.device = ref.device;
411 					updateNodeRef.node = ref.directory;
412 
413 					updateMessage = new BMessage(kFSClipboardChanges);
414 					updateMessage->AddInt32("device", updateNodeRef.device);
415 					updateMessage->AddInt64("directory", updateNodeRef.node);
416 				}
417 
418 				// we need this data later on
419 				MakeModeNameFromRefName(modeName, refName);
420 				if (!linksMode && clip->FindInt32(modeName, (int32*)&moveMode)
421 					!= B_OK) {
422 					continue;
423 				}
424 
425 				BEntry entry(&ref);
426 
427 				uint32 newMoveMode = 0;
428 				bool sameDirectory = destNodeRef->device == ref.device
429 					&& destNodeRef->node == ref.directory;
430 
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);
436 
437 					newMoveMode = kDelete;
438 				} else {
439 					// the entry does exist, so lets see what we will
440 					// do with it
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));
447 					}
448 
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 				}
456 
457 				// add the change to the update message (if necessary)
458 				if (newMoveMode) {
459 					clip->ReplaceInt32(modeName, kCopySelectionTo);
460 
461 					TClipboardNodeRef clipNode;
462 					MakeNodeFromName(&clipNode.node, modeName);
463 					clipNode.moveMode = kDelete;
464 					updateMessage->AddData("tcnode", T_CLIPBOARD_NODE,
465 						&clipNode, sizeof(TClipboardNodeRef), true);
466 				}
467 			}
468 			be_clipboard->Commit();
469 
470 			// send notification for the last directory
471 			if (updateMessage != NULL) {
472 				tracker.SendMessage(updateMessage);
473 				delete updateMessage;
474 			}
475 		}
476 		be_clipboard->Unlock();
477 	}
478 
479 	bool okToMove = true;
480 
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);
488 		alert->Go();
489 		okToMove = false;
490 	}
491 
492 	BEntry entry;
493 	model->GetEntry(&entry);
494 
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,
500 			B_WARNING_ALERT);
501 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
502 		alert->Go();
503 		okToMove = false;
504 	}
505 
506 	if (!okToMove) {
507 		// there was some problem with our target, so we bail out here
508 		delete moveList;
509 		delete copyList;
510 		return false;
511 	}
512 
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);
517 	} else
518 		delete moveList;
519 
520 	if (copyList->CountItems() > 0)
521 		FSMoveToFolder(copyList, new BEntry(entry), kCopySelectionTo);
522 	else
523 		delete copyList;
524 
525 	return true;
526 }
527 
528 
529 // Seek node in clipboard, if found return it's moveMode
530 // else return 0
531 uint32
532 FSClipboardFindNodeMode(Model* model, bool autoLock, bool updateRefIfNeeded)
533 {
534 	int32 moveMode = 0;
535 	if (autoLock) {
536 		if (!be_clipboard->Lock())
537 			return 0;
538 	}
539 	bool remove = false;
540 	bool change = false;
541 
542 	BMessage* clip = be_clipboard->Data();
543 	if (clip != NULL) {
544 		const node_ref* node = model->NodeRef();
545 		char modeName[64];
546 		MakeModeName(modeName, node);
547 		if ((clip->FindInt32(modeName, &moveMode) == B_OK)) {
548 			const entry_ref* ref = model->EntryRef();
549 			entry_ref clipref;
550 			char refName[64];
551 			MakeRefName(refName, node);
552 			if ((clip->FindRef(refName, &clipref) == B_OK)) {
553 				if (clipref != *ref) {
554 					if (updateRefIfNeeded) {
555 						clip->ReplaceRef(refName, ref);
556 						change = true;
557 					} else {
558 						clip->RemoveName(refName);
559 						clip->RemoveName(modeName);
560 						change = true;
561 						remove = true;
562 						moveMode = 0;
563 					}
564 				}
565 			} else {
566 				clip->RemoveName(modeName);
567 				change = true;
568 				remove = true;
569 				moveMode = 0;
570 			}
571 		}
572 	}
573 	if (change)
574 		be_clipboard->Commit();
575 
576 	if (autoLock)
577 		be_clipboard->Unlock();
578 
579 	if (remove)
580 		FSClipboardRemove(model);
581 
582 	return (uint32)moveMode;
583 }
584 
585 
586 void
587 FSClipboardRemove(Model* model)
588 {
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),
600 			true);
601 		messenger.SendMessage(report);
602 		delete report;
603 	}
604 }
605 
606 
607 //	#pragma mark -
608 
609 
610 BClipboardRefsWatcher::BClipboardRefsWatcher()
611 	:	BLooper("ClipboardRefsWatcher", B_LOW_PRIORITY, 4096),
612 	fNotifyList(10, false)
613 {
614 	watch_node(NULL, B_WATCH_MOUNT, this);
615 	fRefsInClipboard = FSClipboardHasRefs();
616 	be_clipboard->StartWatching(this);
617 }
618 
619 
620 BClipboardRefsWatcher::~BClipboardRefsWatcher()
621 {
622 	stop_watching(this);
623 	be_clipboard->StopWatching(this);
624 }
625 
626 
627 void
628 BClipboardRefsWatcher::AddToNotifyList(BMessenger target)
629 {
630 	if (Lock()) {
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;
634 		bool found = false;
635 
636 		for (int32 index = 0; (messenger = fNotifyList.ItemAt(index)) != NULL;
637 				index++) {
638 			if (*messenger == target) {
639 				found = true;
640 				break;
641 			}
642 		}
643 		if (!found)
644 			fNotifyList.AddItem(new BMessenger(target));
645 
646 		Unlock();
647 	}
648 }
649 
650 
651 void
652 BClipboardRefsWatcher::RemoveFromNotifyList(BMessenger target)
653 {
654 	if (Lock()) {
655 		BMessenger* messenger;
656 
657 		for (int32 index = 0; (messenger = fNotifyList.ItemAt(index)) != NULL;
658 				index++) {
659 			if (*messenger == target) {
660 				delete fNotifyList.RemoveItemAt(index);
661 				break;
662 			}
663 		}
664 		Unlock();
665 	}
666 }
667 
668 
669 void
670 BClipboardRefsWatcher::AddNode(const node_ref* node)
671 {
672 	TTracker::WatchNode(node, B_WATCH_NAME, this);
673 	fRefsInClipboard = true;
674 }
675 
676 
677 void
678 BClipboardRefsWatcher::RemoveNode(node_ref* node, bool removeFromClipboard)
679 {
680 	watch_node(node, B_STOP_WATCHING, this);
681 
682 	if (!removeFromClipboard)
683 		return;
684 
685 	if (be_clipboard->Lock()) {
686 		BMessage* clip = be_clipboard->Data();
687 		if (clip != NULL) {
688 			char name[64];
689 			MakeRefName(name, node);
690 			clip->RemoveName(name);
691 			MakeModeName(name);
692 			clip->RemoveName(name);
693 
694 			be_clipboard->Commit();
695 		}
696 		be_clipboard->Unlock();
697 	}
698 }
699 
700 
701 void
702 BClipboardRefsWatcher::RemoveNodesByDevice(dev_t device)
703 {
704 	if (!be_clipboard->Lock())
705 		return;
706 
707 	BMessage* clip = be_clipboard->Data();
708 	if (clip != NULL) {
709 		char deviceName[6];
710 		sprintf(deviceName, "r%" B_PRIdDEV "_", device);
711 
712 		int32 index = 0;
713 		char* refName;
714 		type_code type;
715 		int32 count;
716 		while (clip->GetInfo(B_REF_TYPE, index,
717 #ifdef B_BEOS_VERSION_DANO
718 			(const char**)
719 #endif
720 			&refName, &type, &count) == B_OK) {
721 			if (!strncmp(deviceName, refName, strlen(deviceName))) {
722 				clip->RemoveName(refName);
723 				MakeModeName(refName);
724 				clip->RemoveName(refName);
725 
726 				node_ref node;
727 				MakeNodeFromName(&node, refName);
728 				watch_node(&node, B_STOP_WATCHING, this);
729 			}
730 			index++;
731 		}
732 		be_clipboard->Commit();
733 	}
734 	be_clipboard->Unlock();
735 }
736 
737 
738 void
739 BClipboardRefsWatcher::UpdateNode(node_ref* node, entry_ref* ref)
740 {
741 	if (!be_clipboard->Lock())
742 		return;
743 
744 	BMessage* clip = be_clipboard->Data();
745 	if (clip != NULL) {
746 		char name[64];
747 		MakeRefName(name, node);
748 		if ((clip->ReplaceRef(name, ref)) != B_OK) {
749 			clip->RemoveName(name);
750 			MakeModeName(name);
751 			clip->RemoveName(name);
752 
753 			RemoveNode(node);
754 		}
755 		be_clipboard->Commit();
756 	}
757 	be_clipboard->Unlock();
758 }
759 
760 
761 void
762 BClipboardRefsWatcher::Clear()
763 {
764 	stop_watching(this);
765 	watch_node(NULL, B_WATCH_MOUNT, this);
766 
767 	BMessage message(kFSClipboardChanges);
768 	message.AddBool("clearClipboard", true);
769 	if (Lock()) {
770 		int32 items = fNotifyList.CountItems();
771 		for (int32 i = 0;i < items;i++) {
772 			fNotifyList.ItemAt(i)->SendMessage(&message);
773 		}
774 		Unlock();
775 	}
776 }
777 
778 
779 //void
780 //BClipboardRefsWatcher::UpdatePoseViews(bool clearClipboard,
781 //	const node_ref* node)
782 //{
783 //	BMessage message(kFSClipboardChanges);
784 //	message.AddInt32("device", node->device);
785 //	message.AddInt64("directory", node->node);
786 //	message.AddBool("clearClipboard", clearClipboard);
787 //
788 //	if (Lock()) {
789 //		int32 items = fNotifyList.CountItems();
790 //		for (int32 i = 0;i < items;i++) {
791 //			fNotifyList.ItemAt(i)->SendMessage(&message);
792 //		}
793 //		Unlock();
794 //	}
795 //}
796 
797 
798 void
799 BClipboardRefsWatcher::UpdatePoseViews(BMessage* reportMessage)
800 {
801 	if (Lock()) {
802 		// check if it was cleared, if so clear watching
803 		bool clearClipboard = false;
804 		if (reportMessage->FindBool("clearClipboard", &clearClipboard) == B_OK
805 			&& clearClipboard) {
806 			stop_watching(this);
807 			watch_node(NULL, B_WATCH_MOUNT, this);
808 		}
809 
810 		// loop through reported node_ref's movemodes:
811 		// move or copy: start watching node_ref
812 		// remove: stop watching node_ref
813 		int32 index = 0;
814 		TClipboardNodeRef* tcnode = NULL;
815 		ssize_t size;
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);
820 			} else {
821 				watch_node(&tcnode->node, B_STOP_WATCHING, this);
822 				TTracker::WatchNode(&tcnode->node, B_WATCH_NAME, this);
823 				fRefsInClipboard = true;
824 			}
825 			index++;
826 		}
827 
828 		// send report
829 		int32 items = fNotifyList.CountItems();
830 		for (int32 i = 0;i < items;i++) {
831 			fNotifyList.ItemAt(i)->SendMessage(reportMessage);
832 		}
833 		Unlock();
834 	}
835 }
836 
837 
838 void
839 BClipboardRefsWatcher::MessageReceived(BMessage* message)
840 {
841 	if (message->what == B_CLIPBOARD_CHANGED && fRefsInClipboard) {
842 		if (!(fRefsInClipboard = FSClipboardHasRefs()))
843 			Clear();
844 		return;
845 	} else if (message->what != B_NODE_MONITOR) {
846 		_inherited::MessageReceived(message);
847 		return;
848 	}
849 
850 	switch (message->FindInt32("opcode")) {
851 		case B_ENTRY_MOVED:
852 		{
853 			ino_t toDir;
854 			ino_t fromDir;
855 			node_ref node;
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);
864 			break;
865 		}
866 
867 		case B_DEVICE_UNMOUNTED:
868 		{
869 			dev_t device;
870 			message->FindInt32("device", &device);
871 			RemoveNodesByDevice(device);
872 			break;
873 		}
874 
875 		case B_ENTRY_REMOVED:
876 		{
877 			node_ref node;
878 			message->FindInt64("node", &node.node);
879 			message->FindInt32("device", &node.device);
880 			RemoveNode(&node, true);
881 			break;
882 		}
883 	}
884 }
885