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