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		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
542uint32
543FSClipboardFindNodeMode(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
597void
598FSClipboardRemove(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
621BClipboardRefsWatcher::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
631BClipboardRefsWatcher::~BClipboardRefsWatcher()
632{
633	stop_watching(this);
634	be_clipboard->StopWatching(this);
635}
636
637
638void
639BClipboardRefsWatcher::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
662void
663BClipboardRefsWatcher::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
680void
681BClipboardRefsWatcher::AddNode(const node_ref* node)
682{
683	TTracker::WatchNode(node, B_WATCH_NAME, this);
684	fRefsInClipboard = true;
685}
686
687
688void
689BClipboardRefsWatcher::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
712void
713BClipboardRefsWatcher::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
749void
750BClipboardRefsWatcher::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
772void
773BClipboardRefsWatcher::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
809void
810BClipboardRefsWatcher::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
849void
850BClipboardRefsWatcher::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