xref: /haiku/src/kits/tracker/FSClipboard.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
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 <NodeMonitor.h>
39 #include "Commands.h"
40 #include "FSUtils.h"
41 #include "Tracker.h"
42 
43 // prototypes
44 static void MakeNodeFromName(node_ref *node, char *name);
45 static inline void MakeRefName(char *refName, const node_ref *node);
46 static inline void MakeModeName(char *modeName, const node_ref *node);
47 static inline void MakeModeNameFromRefName(char *modeName, char *refName);
48 static inline bool CompareModeAndRefName(const char *modeName, const char *refName);
49 
50 
51 //these are from PoseView.cpp
52 extern const char *kNoCopyToTrashStr;
53 extern const char *kNoCopyToRootStr;
54 extern const char *kOkToMoveStr;
55 
56 /*
57 static bool
58 FSClipboardCheckIntegrity()
59 {
60 	return true;
61 }
62 */
63 
64 static void
65 MakeNodeFromName(node_ref *node, char *name)
66 {
67 	char *nodeString = strchr(name, '_');
68 	if (nodeString != NULL) {
69 		node->node = strtoll(nodeString + 1, (char **)NULL, 10);
70 		node->device = atoi(name + 1);
71 	}
72 }
73 
74 
75 static inline void
76 MakeRefName(char *refName, const node_ref *node)
77 {
78 	sprintf(refName, "r%ld_%Ld", node->device, node->node);
79 }
80 
81 
82 static inline void
83 MakeModeName(char *modeName, const node_ref *node)
84 {
85 	sprintf(modeName, "m%ld_%Ld", node->device, node->node);
86 }
87 
88 
89 static inline void
90 MakeModeName(char *name)
91 {
92 	name[0] = 'm';
93 }
94 
95 
96 static inline void
97 MakeModeNameFromRefName(char *modeName, char *refName)
98 {
99 	strcpy(modeName, refName);
100 	modeName[0] = 'm';
101 }
102 
103 
104 static inline bool
105 CompareModeAndRefName(const char *modeName, const char *refName)
106 {
107 	return !strcmp(refName + 1, modeName + 1);
108 }
109 
110 
111 //	#pragma mark -
112 
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 				|| FSIsTrashDir(&entry)
232 				|| FSIsDeskDir(&entry))
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("", kNoCopyToRootStr, "Cancel",
472 			NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
473 		alert->SetShortcut(0, B_ESCAPE);
474 		alert->Go();
475 		okToMove = false;
476 	}
477 
478 	BEntry entry;
479 	model->GetEntry(&entry);
480 
481 	// can't copy items into the trash
482 	if (copyList->CountItems() > 0 && FSIsTrashDir(&entry)) {
483 		BAlert *alert = new BAlert("", kNoCopyToTrashStr, "Cancel",
484 			NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
485 		alert->SetShortcut(0, B_ESCAPE);
486 		alert->Go();
487 		okToMove = false;
488 	}
489 
490 	if (!okToMove) {
491 		// there was some problem with our target, so we bail out here
492 		delete moveList;
493 		delete copyList;
494 		return false;
495 	}
496 
497 	// asynchronous calls take over ownership of the objects passed to it
498 	if (moveList->CountItems() > 0)
499 		FSMoveToFolder(moveList, new BEntry(entry), linksMode ? linksMode : kMoveSelectionTo);
500 	else
501 		delete moveList;
502 
503 	if (copyList->CountItems() > 0)
504 		FSMoveToFolder(copyList, new BEntry(entry), kCopySelectionTo);
505 	else
506 		delete copyList;
507 
508 	return true;
509 }
510 
511 
512 /**	Seek node in clipboard, if found return it's moveMode
513  *	else return 0
514  */
515 
516 uint32
517 FSClipboardFindNodeMode(Model *model, bool autoLock, bool updateRefIfNeeded)
518 {
519 	int32 moveMode = 0;
520 	if (autoLock) {
521 		if (!be_clipboard->Lock())
522 			return 0;
523 	}
524 	bool remove = false;
525 	bool change = false;
526 
527 	BMessage *clip = be_clipboard->Data();
528 	if (clip != NULL) {
529 		const node_ref *node = model->NodeRef();
530 		char modeName[64];
531 		MakeModeName(modeName, node);
532 		if ((clip->FindInt32(modeName, &moveMode) == B_OK)) {
533 			const entry_ref *ref = model->EntryRef();
534 			entry_ref clipref;
535 			char refName[64];
536 			MakeRefName(refName, node);
537 			if ((clip->FindRef(refName, &clipref) == B_OK)) {
538 				if (clipref != *ref) {
539 					if (updateRefIfNeeded) {
540 						clip->ReplaceRef(refName, ref);
541 						change = true;
542 					} else {
543 						clip->RemoveName(refName);
544 						clip->RemoveName(modeName);
545 						change = true;
546 						remove = true;
547 						moveMode = 0;
548 					}
549 				}
550 			} else {
551 				clip->RemoveName(modeName);
552 				change = true;
553 				remove = true;
554 				moveMode = 0;
555 			}
556 		}
557 	}
558 	if (change)
559 		be_clipboard->Commit();
560 
561 	if (autoLock)
562 		be_clipboard->Unlock();
563 
564 	if (remove)
565 		FSClipboardRemove(model);
566 
567 	return (uint32)moveMode;
568 }
569 
570 
571 void
572 FSClipboardRemove(Model *model)
573 {
574 	BMessenger messenger(kTrackerSignature);
575 	if (messenger.IsValid()) {
576 		BMessage *report = new BMessage(kFSClipboardChanges);
577 		TClipboardNodeRef tcnode;
578 		tcnode.node = *model->NodeRef();
579 		tcnode.moveMode = kDelete;
580 		const entry_ref *ref = model->EntryRef();
581 		report->AddInt32("device", ref->device);
582 		report->AddInt64("directory", ref->directory);
583 		report->AddBool("clearClipboard", false);
584 		report->AddData("tcnode", T_CLIPBOARD_NODE, &tcnode, sizeof(tcnode), true);
585 		messenger.SendMessage(report);
586 		delete report;
587 	}
588 }
589 
590 
591 //	#pragma mark -
592 
593 
594 BClipboardRefsWatcher::BClipboardRefsWatcher()
595 	:	BLooper("ClipboardRefsWatcher", B_LOW_PRIORITY, 4096),
596 	fNotifyList(10, false)
597 {
598 	watch_node(NULL, B_WATCH_MOUNT, this);
599 	fRefsInClipboard = FSClipboardHasRefs();
600 	be_clipboard->StartWatching(this);
601 }
602 
603 
604 BClipboardRefsWatcher::~BClipboardRefsWatcher()
605 {
606 	stop_watching(this);
607 	be_clipboard->StopWatching(this);
608 }
609 
610 
611 void
612 BClipboardRefsWatcher::AddToNotifyList(BMessenger target)
613 {
614 	if (Lock()) {
615 		// add the messenger if it's not already in the list
616 		// ToDo: why do we have to care about that?
617 		BMessenger *messenger;
618 		bool found = false;
619 
620 		for (int32 index = 0;(messenger = fNotifyList.ItemAt(index)) != NULL; index++) {
621 			if (*messenger == target) {
622 				found = true;
623 				break;
624 			}
625 		}
626 		if (!found)
627 			fNotifyList.AddItem(new BMessenger(target));
628 
629 		Unlock();
630 	}
631 }
632 
633 
634 void
635 BClipboardRefsWatcher::RemoveFromNotifyList(BMessenger target)
636 {
637 	if (Lock()) {
638 		BMessenger *messenger;
639 
640 		for (int32 index = 0;(messenger = fNotifyList.ItemAt(index)) != NULL; index++) {
641 			if (*messenger == target) {
642 				delete fNotifyList.RemoveItemAt(index);
643 				break;
644 			}
645 		}
646 		Unlock();
647 	}
648 }
649 
650 
651 void
652 BClipboardRefsWatcher::AddNode(const node_ref *node)
653 {
654 	TTracker::WatchNode(node, B_WATCH_NAME, this);
655 	fRefsInClipboard = true;
656 }
657 
658 
659 void
660 BClipboardRefsWatcher::RemoveNode(node_ref *node, bool removeFromClipboard)
661 {
662 	watch_node(node, B_STOP_WATCHING, this);
663 
664 	if (!removeFromClipboard)
665 		return;
666 
667 	if (be_clipboard->Lock()) {
668 		BMessage *clip = be_clipboard->Data();
669 		if (clip != NULL) {
670 			char name[64];
671 			MakeRefName(name, node);
672 			clip->RemoveName(name);
673 			MakeModeName(name);
674 			clip->RemoveName(name);
675 
676 			be_clipboard->Commit();
677 		}
678 		be_clipboard->Unlock();
679 	}
680 }
681 
682 
683 void
684 BClipboardRefsWatcher::RemoveNodesByDevice(dev_t device)
685 {
686 	if (!be_clipboard->Lock())
687 		return;
688 
689 	BMessage *clip = be_clipboard->Data();
690 	if (clip != NULL) {
691 		char deviceName[6];
692 		sprintf(deviceName, "r%ld_", device);
693 
694 		int32 index = 0;
695 		char *refName;
696 		type_code type;
697 		int32 count;
698 		while (clip->GetInfo(B_REF_TYPE, index,
699 #ifdef B_BEOS_VERSION_DANO
700 			(const char **)
701 #endif
702 			&refName, &type, &count) == B_OK) {
703 			if (!strncmp(deviceName, refName, strlen(deviceName))) {
704 				clip->RemoveName(refName);
705 				MakeModeName(refName);
706 				clip->RemoveName(refName);
707 
708 				node_ref node;
709 				MakeNodeFromName(&node, refName);
710 				watch_node(&node, B_STOP_WATCHING, this);
711 			}
712 			index++;
713 		}
714 		be_clipboard->Commit();
715 	}
716 	be_clipboard->Unlock();
717 }
718 
719 
720 void
721 BClipboardRefsWatcher::UpdateNode(node_ref *node, entry_ref *ref)
722 {
723 	if (!be_clipboard->Lock())
724 		return;
725 
726 	BMessage *clip = be_clipboard->Data();
727 	if (clip != NULL) {
728 		char name[64];
729 		MakeRefName(name, node);
730 		if ((clip->ReplaceRef(name, ref)) != B_OK) {
731 			clip->RemoveName(name);
732 			MakeModeName(name);
733 			clip->RemoveName(name);
734 
735 			RemoveNode(node);
736 		}
737 		be_clipboard->Commit();
738 	}
739 	be_clipboard->Unlock();
740 }
741 
742 
743 void
744 BClipboardRefsWatcher::Clear()
745 {
746 	stop_watching(this);
747 	watch_node(NULL, B_WATCH_MOUNT, this);
748 
749 	BMessage message(kFSClipboardChanges);
750 	message.AddBool("clearClipboard", true);
751 	if (Lock()) {
752 		int32 items = fNotifyList.CountItems();
753 		for (int32 i = 0;i < items;i++) {
754 			fNotifyList.ItemAt(i)->SendMessage(&message);
755 		}
756 		Unlock();
757 	}
758 }
759 
760 /*
761 void
762 BClipboardRefsWatcher::UpdatePoseViews(bool clearClipboard, const node_ref *node)
763 {
764 	BMessage message(kFSClipboardChanges);
765 	message.AddInt32("device", node->device);
766 	message.AddInt64("directory", node->node);
767 	message.AddBool("clearClipboard", clearClipboard);
768 
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(BMessage *reportMessage)
781 {
782 	if (Lock()) {
783 		// check if it was cleared, if so clear watching
784 		bool clearClipboard = false;
785 		if (reportMessage->FindBool("clearClipboard", &clearClipboard) == B_OK
786 			&& clearClipboard) {
787 			stop_watching(this);
788 			watch_node(NULL, B_WATCH_MOUNT, this);
789 		}
790 
791 		// loop through reported node_ref's movemodes:
792 		// move or copy: start watching node_ref
793 		// remove: stop watching node_ref
794 		int32 index = 0;
795 		TClipboardNodeRef *tcnode = NULL;
796 		ssize_t size;
797 		while (reportMessage->FindData("tcnode", T_CLIPBOARD_NODE, index, (const void**)&tcnode, &size) == B_OK) {
798 			if (tcnode->moveMode == kDelete) {
799 				watch_node(&tcnode->node, B_STOP_WATCHING, this);
800 			} else {
801 				watch_node(&tcnode->node, B_STOP_WATCHING, this);
802 				TTracker::WatchNode(&tcnode->node, B_WATCH_NAME, this);
803 				fRefsInClipboard = true;
804 			}
805 			index++;
806 		}
807 
808 		// send report
809 		int32 items = fNotifyList.CountItems();
810 		for (int32 i = 0;i < items;i++) {
811 			fNotifyList.ItemAt(i)->SendMessage(reportMessage);
812 		}
813 		Unlock();
814 	}
815 }
816 
817 
818 void
819 BClipboardRefsWatcher::MessageReceived(BMessage *message)
820 {
821 	if (message->what == B_CLIPBOARD_CHANGED && fRefsInClipboard) {
822 		if (!(fRefsInClipboard = FSClipboardHasRefs()))
823 			Clear();
824 		return;
825 	} else if (message->what != B_NODE_MONITOR) {
826 		_inherited::MessageReceived(message);
827 		return;
828 	}
829 
830 	switch (message->FindInt32("opcode")) {
831 		case B_ENTRY_MOVED:
832 		{
833 			ino_t toDir;
834 			ino_t fromDir;
835 			node_ref node;
836 			const char *name = NULL;
837 			message->FindInt64("from directory", &fromDir);
838 			message->FindInt64("to directory", &toDir);
839 			message->FindInt64("node", &node.node);
840 			message->FindInt32("device", &node.device);
841 			message->FindString("name", &name);
842 			entry_ref ref(node.device, toDir, name);
843 			UpdateNode(&node, &ref);
844 			break;
845 		}
846 
847 		case B_DEVICE_UNMOUNTED:
848 		{
849 			dev_t device;
850 			message->FindInt32("device", &device);
851 			RemoveNodesByDevice(device);
852 			break;
853 		}
854 
855 		case B_ENTRY_REMOVED:
856 		{
857 			node_ref node;
858 			message->FindInt64("node", &node.node);
859 			message->FindInt32("device", &node.device);
860 			RemoveNode(&node, true);
861 			break;
862 		}
863 	}
864 }
865