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