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