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