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