1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software..
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All 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
50static void MakeNodeFromName(node_ref* node, char* name);
51static inline void MakeRefName(char* refName, const node_ref* node);
52static inline void MakeModeName(char* modeName, const node_ref* node);
53static inline void MakeModeNameFromRefName(char* modeName, char* refName);
54static inline bool CompareModeAndRefName(const char* modeName,
55	const char* refName);
56
57#if 0
58static bool
59FSClipboardCheckIntegrity()
60{
61	return true;
62}
63#endif
64
65static void
66MakeNodeFromName(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
76static inline void
77MakeRefName(char* refName, const node_ref* node)
78{
79	sprintf(refName, "r%" B_PRIdDEV "_%" B_PRIdINO, node->device, node->node);
80}
81
82
83static inline void
84MakeModeName(char* modeName, const node_ref* node)
85{
86	sprintf(modeName, "m%" B_PRIdDEV "_%" B_PRIdINO, node->device, node->node);
87}
88
89
90static inline void
91MakeModeName(char* name)
92{
93	name[0] = 'm';
94}
95
96
97static inline void
98MakeModeNameFromRefName(char* modeName, char* refName)
99{
100	strcpy(modeName, refName);
101	modeName[0] = 'm';
102}
103
104
105static inline bool
106CompareModeAndRefName(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
119bool
120FSClipboardHasRefs()
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
149void
150FSClipboardStartWatch(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
168void
169FSClipboardStopWatch(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
187void
188FSClipboardClear()
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 */
205uint32
206FSClipboardAddPoses(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
315uint32
316FSClipboardRemovePoses(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 */
365bool
366FSClipboardPaste(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		delete duplicateList;
517		return false;
518	}
519
520	// asynchronous calls take over ownership of the objects passed to it
521	if (moveList->CountItems() > 0) {
522		FSMoveToFolder(moveList, new BEntry(entry),
523			linksMode ? linksMode : kMoveSelectionTo);
524	} else
525		delete moveList;
526
527	if (copyList->CountItems() > 0)
528		FSMoveToFolder(copyList, new BEntry(entry), kCopySelectionTo);
529	else
530		delete copyList;
531
532	if (duplicateList->CountItems() > 0)
533		FSMoveToFolder(duplicateList, new BEntry(entry), kDuplicateSelection);
534	else
535		delete duplicateList;
536
537	return true;
538}
539
540
541// Seek node in clipboard, if found return it's moveMode
542// else return 0
543uint32
544FSClipboardFindNodeMode(Model* model, bool autoLock, bool updateRefIfNeeded)
545{
546	int32 moveMode = 0;
547	if (autoLock) {
548		if (!be_clipboard->Lock())
549			return 0;
550	}
551	bool remove = false;
552	bool change = false;
553
554	BMessage* clip = be_clipboard->Data();
555	if (clip != NULL) {
556		const node_ref* node = model->NodeRef();
557		char modeName[64];
558		MakeModeName(modeName, node);
559		if ((clip->FindInt32(modeName, &moveMode) == B_OK)) {
560			const entry_ref* ref = model->EntryRef();
561			entry_ref clipref;
562			char refName[64];
563			MakeRefName(refName, node);
564			if ((clip->FindRef(refName, &clipref) == B_OK)) {
565				if (clipref != *ref) {
566					if (updateRefIfNeeded) {
567						clip->ReplaceRef(refName, ref);
568						change = true;
569					} else {
570						clip->RemoveName(refName);
571						clip->RemoveName(modeName);
572						change = true;
573						remove = true;
574						moveMode = 0;
575					}
576				}
577			} else {
578				clip->RemoveName(modeName);
579				change = true;
580				remove = true;
581				moveMode = 0;
582			}
583		}
584	}
585	if (change)
586		be_clipboard->Commit();
587
588	if (autoLock)
589		be_clipboard->Unlock();
590
591	if (remove)
592		FSClipboardRemove(model);
593
594	return (uint32)moveMode;
595}
596
597
598void
599FSClipboardRemove(Model* model)
600{
601	BMessenger messenger(kTrackerSignature);
602	if (messenger.IsValid()) {
603		BMessage* report = new BMessage(kFSClipboardChanges);
604		TClipboardNodeRef tcnode;
605		tcnode.node = *model->NodeRef();
606		tcnode.moveMode = kDelete;
607		const entry_ref* ref = model->EntryRef();
608		report->AddInt32("device", ref->device);
609		report->AddInt64("directory", ref->directory);
610		report->AddBool("clearClipboard", false);
611		report->AddData("tcnode", T_CLIPBOARD_NODE, &tcnode, sizeof(tcnode),
612			true);
613		messenger.SendMessage(report);
614		delete report;
615	}
616}
617
618
619//	#pragma mark -
620
621
622BClipboardRefsWatcher::BClipboardRefsWatcher()
623	:	BLooper("ClipboardRefsWatcher", B_LOW_PRIORITY, 4096),
624	fNotifyList(10, false)
625{
626	watch_node(NULL, B_WATCH_MOUNT, this);
627	fRefsInClipboard = FSClipboardHasRefs();
628	be_clipboard->StartWatching(this);
629}
630
631
632BClipboardRefsWatcher::~BClipboardRefsWatcher()
633{
634	stop_watching(this);
635	be_clipboard->StopWatching(this);
636}
637
638
639void
640BClipboardRefsWatcher::AddToNotifyList(BMessenger target)
641{
642	if (Lock()) {
643		// add the messenger if it's not already in the list
644		// ToDo: why do we have to care about that?
645		BMessenger* messenger;
646		bool found = false;
647
648		for (int32 index = 0; (messenger = fNotifyList.ItemAt(index)) != NULL;
649				index++) {
650			if (*messenger == target) {
651				found = true;
652				break;
653			}
654		}
655		if (!found)
656			fNotifyList.AddItem(new BMessenger(target));
657
658		Unlock();
659	}
660}
661
662
663void
664BClipboardRefsWatcher::RemoveFromNotifyList(BMessenger target)
665{
666	if (Lock()) {
667		BMessenger* messenger;
668
669		for (int32 index = 0; (messenger = fNotifyList.ItemAt(index)) != NULL;
670				index++) {
671			if (*messenger == target) {
672				delete fNotifyList.RemoveItemAt(index);
673				break;
674			}
675		}
676		Unlock();
677	}
678}
679
680
681void
682BClipboardRefsWatcher::AddNode(const node_ref* node)
683{
684	TTracker::WatchNode(node, B_WATCH_NAME, this);
685	fRefsInClipboard = true;
686}
687
688
689void
690BClipboardRefsWatcher::RemoveNode(node_ref* node, bool removeFromClipboard)
691{
692	watch_node(node, B_STOP_WATCHING, this);
693
694	if (!removeFromClipboard)
695		return;
696
697	if (be_clipboard->Lock()) {
698		BMessage* clip = be_clipboard->Data();
699		if (clip != NULL) {
700			char name[64];
701			MakeRefName(name, node);
702			clip->RemoveName(name);
703			MakeModeName(name);
704			clip->RemoveName(name);
705
706			be_clipboard->Commit();
707		}
708		be_clipboard->Unlock();
709	}
710}
711
712
713void
714BClipboardRefsWatcher::RemoveNodesByDevice(dev_t device)
715{
716	if (!be_clipboard->Lock())
717		return;
718
719	BMessage* clip = be_clipboard->Data();
720	if (clip != NULL) {
721		char deviceName[6];
722		sprintf(deviceName, "r%" B_PRIdDEV "_", device);
723
724		int32 index = 0;
725		char* refName;
726		type_code type;
727		int32 count;
728		while (clip->GetInfo(B_REF_TYPE, index,
729#ifdef B_BEOS_VERSION_DANO
730			(const char**)
731#endif
732			&refName, &type, &count) == B_OK) {
733			if (!strncmp(deviceName, refName, strlen(deviceName))) {
734				clip->RemoveName(refName);
735				MakeModeName(refName);
736				clip->RemoveName(refName);
737
738				node_ref node;
739				MakeNodeFromName(&node, refName);
740				watch_node(&node, B_STOP_WATCHING, this);
741			}
742			index++;
743		}
744		be_clipboard->Commit();
745	}
746	be_clipboard->Unlock();
747}
748
749
750void
751BClipboardRefsWatcher::UpdateNode(node_ref* node, entry_ref* ref)
752{
753	if (!be_clipboard->Lock())
754		return;
755
756	BMessage* clip = be_clipboard->Data();
757	if (clip != NULL) {
758		char name[64];
759		MakeRefName(name, node);
760		if ((clip->ReplaceRef(name, ref)) != B_OK) {
761			clip->RemoveName(name);
762			MakeModeName(name);
763			clip->RemoveName(name);
764
765			RemoveNode(node);
766		}
767		be_clipboard->Commit();
768	}
769	be_clipboard->Unlock();
770}
771
772
773void
774BClipboardRefsWatcher::Clear()
775{
776	stop_watching(this);
777	watch_node(NULL, B_WATCH_MOUNT, this);
778
779	BMessage message(kFSClipboardChanges);
780	message.AddBool("clearClipboard", true);
781	if (Lock()) {
782		int32 items = fNotifyList.CountItems();
783		for (int32 i = 0;i < items;i++) {
784			fNotifyList.ItemAt(i)->SendMessage(&message);
785		}
786		Unlock();
787	}
788}
789
790
791//void
792//BClipboardRefsWatcher::UpdatePoseViews(bool clearClipboard,
793//	const node_ref* node)
794//{
795//	BMessage message(kFSClipboardChanges);
796//	message.AddInt32("device", node->device);
797//	message.AddInt64("directory", node->node);
798//	message.AddBool("clearClipboard", clearClipboard);
799//
800//	if (Lock()) {
801//		int32 items = fNotifyList.CountItems();
802//		for (int32 i = 0;i < items;i++) {
803//			fNotifyList.ItemAt(i)->SendMessage(&message);
804//		}
805//		Unlock();
806//	}
807//}
808
809
810void
811BClipboardRefsWatcher::UpdatePoseViews(BMessage* reportMessage)
812{
813	if (Lock()) {
814		// check if it was cleared, if so clear watching
815		bool clearClipboard = false;
816		if (reportMessage->FindBool("clearClipboard", &clearClipboard) == B_OK
817			&& clearClipboard) {
818			stop_watching(this);
819			watch_node(NULL, B_WATCH_MOUNT, this);
820		}
821
822		// loop through reported node_ref's movemodes:
823		// move or copy: start watching node_ref
824		// remove: stop watching node_ref
825		int32 index = 0;
826		TClipboardNodeRef* tcnode = NULL;
827		ssize_t size;
828		while (reportMessage->FindData("tcnode", T_CLIPBOARD_NODE, index,
829				(const void**)&tcnode, &size) == B_OK) {
830			if (tcnode->moveMode == kDelete) {
831				watch_node(&tcnode->node, B_STOP_WATCHING, this);
832			} else {
833				watch_node(&tcnode->node, B_STOP_WATCHING, this);
834				TTracker::WatchNode(&tcnode->node, B_WATCH_NAME, this);
835				fRefsInClipboard = true;
836			}
837			index++;
838		}
839
840		// send report
841		int32 items = fNotifyList.CountItems();
842		for (int32 i = 0;i < items;i++) {
843			fNotifyList.ItemAt(i)->SendMessage(reportMessage);
844		}
845		Unlock();
846	}
847}
848
849
850void
851BClipboardRefsWatcher::MessageReceived(BMessage* message)
852{
853	if (message->what == B_CLIPBOARD_CHANGED && fRefsInClipboard) {
854		if (!(fRefsInClipboard = FSClipboardHasRefs()))
855			Clear();
856		return;
857	} else if (message->what != B_NODE_MONITOR) {
858		_inherited::MessageReceived(message);
859		return;
860	}
861
862	switch (message->FindInt32("opcode")) {
863		case B_ENTRY_MOVED:
864		{
865			ino_t toDir;
866			ino_t fromDir;
867			node_ref node;
868			const char* name = NULL;
869			message->FindInt64("from directory", &fromDir);
870			message->FindInt64("to directory", &toDir);
871			message->FindInt64("node", &node.node);
872			message->FindInt32("device", &node.device);
873			message->FindString("name", &name);
874			entry_ref ref(node.device, toDir, name);
875			UpdateNode(&node, &ref);
876			break;
877		}
878
879		case B_DEVICE_UNMOUNTED:
880		{
881			dev_t device;
882			message->FindInt32("device", &device);
883			RemoveNodesByDevice(device);
884			break;
885		}
886
887		case B_ENTRY_REMOVED:
888		{
889			node_ref node;
890			message->FindInt64("node", &node.node);
891			message->FindInt32("device", &node.device);
892			RemoveNode(&node, true);
893			break;
894		}
895	}
896}
897