xref: /haiku/src/kits/tracker/FSClipboard.cpp (revision fe514848c28f26b761e6a5c103bf87c2d148a19b)
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
MakeNodeFromName(node_ref * node,char * name)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
MakeRefName(char * refName,const node_ref * node)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
MakeModeName(char * modeName,const node_ref * node)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
MakeModeName(char * name)91 MakeModeName(char* name)
92 {
93 	name[0] = 'm';
94 }
95 
96 
97 static inline void
MakeModeNameFromRefName(char * modeName,char * refName)98 MakeModeNameFromRefName(char* modeName, char* refName)
99 {
100 	strcpy(modeName, refName);
101 	modeName[0] = 'm';
102 }
103 
104 
105 static inline bool
CompareModeAndRefName(const char * modeName,const char * refName)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
FSClipboardHasRefs()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
FSClipboardStartWatch(BMessenger target)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
FSClipboardStopWatch(BMessenger target)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
FSClipboardClear()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
FSClipboardAddPoses(const node_ref * directory,PoseList * list,uint32 moveMode,bool clearClipboard)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
FSClipboardRemovePoses(const node_ref * directory,PoseList * list)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
FSClipboardPaste(Model * model,uint32 linksMode)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(kFSClipboardChanges);
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 || updateNodeRef.node != ref.directory) {
405 					if (!updateMessage.IsEmpty()) {
406 						tracker.SendMessage(&updateMessage);
407 						updateMessage.MakeEmpty();
408 					}
409 
410 					updateNodeRef.device = ref.device;
411 					updateNodeRef.node = ref.directory;
412 					updateMessage.AddInt32("device", updateNodeRef.device);
413 					updateMessage.AddInt64("directory", updateNodeRef.node);
414 				}
415 
416 				// we need this data later on
417 				MakeModeNameFromRefName(modeName, refName);
418 				if (!linksMode && clip->FindInt32(modeName, (int32*)&moveMode) != B_OK)
419 					continue;
420 
421 				BEntry entry(&ref);
422 
423 				uint32 newMoveMode = 0;
424 				bool sameDirectory = destNodeRef->device == ref.device
425 					&& destNodeRef->node == ref.directory;
426 
427 				if (!entry.Exists()) {
428 					// The entry doesn't exist anymore, so we'll remove
429 					// that entry from the clipboard as well
430 					clip->RemoveName(refName);
431 					clip->RemoveName(modeName);
432 
433 					newMoveMode = kDelete;
434 				} else {
435 					// the entry does exist, so lets see what we will do with it
436 					if (!sameDirectory) {
437 						if (linksMode || moveMode == kMoveSelectionTo) {
438 							// the linksMode uses the moveList as well
439 							moveList->AddItem(new entry_ref(ref));
440 						} else if (moveMode == kCopySelectionTo)
441 							copyList->AddItem(new entry_ref(ref));
442 					} else if (moveMode != kMoveSelectionTo) {
443 						// we are copying a file into its same directory, do a duplicate
444 						duplicateList->AddItem(new entry_ref(ref));
445 					}
446 
447 					// Whether the entry changed directories or not we want to copy that entry
448 					// next time, even if the items don't have to be moved (source == target).
449 					if (moveMode == kMoveSelectionTo)
450 						newMoveMode = kCopySelectionTo;
451 				}
452 
453 				// add the change to the update message (if necessary)
454 				if (newMoveMode != 0) {
455 					clip->ReplaceInt32(modeName, kCopySelectionTo);
456 
457 					TClipboardNodeRef clipNode;
458 					MakeNodeFromName(&clipNode.node, modeName);
459 					clipNode.moveMode = kDelete;
460 					updateMessage.AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
461 						sizeof(TClipboardNodeRef), true);
462 				}
463 			}
464 			be_clipboard->Commit();
465 
466 			// send notification for the last directory
467 			if (!updateMessage.IsEmpty()) {
468 				tracker.SendMessage(&updateMessage);
469 				updateMessage.MakeEmpty();
470 			}
471 		}
472 		be_clipboard->Unlock();
473 	}
474 
475 	bool okToMove = true;
476 
477 	// can't copy/paste to root('/') directory
478 	if (model->IsRoot()) {
479 		BAlert* alert = new BAlert("",
480 			B_TRANSLATE("You must drop items on one of the disk icons "
481 			"in the \"Disks\" window."), B_TRANSLATE("Cancel"), NULL, NULL,
482 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
483 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
484 		alert->Go();
485 		okToMove = false;
486 	}
487 
488 	BEntry entry;
489 	model->GetEntry(&entry);
490 
491 	// can't copy items into the trash
492 	if (copyList->CountItems() > 0 && model->IsTrash()) {
493 		BAlert* alert = new BAlert("",
494 			B_TRANSLATE("Sorry, you can't copy items to the Trash."),
495 			B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
496 			B_WARNING_ALERT);
497 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
498 		alert->Go();
499 		okToMove = false;
500 	}
501 
502 	if (!okToMove) {
503 		// there was some problem with our target, so we bail out here
504 		delete moveList;
505 		delete copyList;
506 		delete duplicateList;
507 		return false;
508 	}
509 
510 	// asynchronous calls take over ownership of the objects passed to it
511 	if (moveList->CountItems() > 0)
512 		FSMoveToFolder(moveList, new BEntry(entry), linksMode ? linksMode : kMoveSelectionTo);
513 	else
514 		delete moveList;
515 
516 	if (copyList->CountItems() > 0)
517 		FSMoveToFolder(copyList, new BEntry(entry), kCopySelectionTo);
518 	else
519 		delete copyList;
520 
521 	if (duplicateList->CountItems() > 0)
522 		FSMoveToFolder(duplicateList, new BEntry(entry), kDuplicateSelection);
523 	else
524 		delete duplicateList;
525 
526 	return true;
527 }
528 
529 
530 // Seek node in clipboard, if found return it's moveMode
531 // else return 0
532 uint32
FSClipboardFindNodeMode(Model * model,bool autoLock,bool updateRefIfNeeded)533 FSClipboardFindNodeMode(Model* model, bool autoLock, bool updateRefIfNeeded)
534 {
535 	int32 moveMode = 0;
536 	if (autoLock) {
537 		if (!be_clipboard->Lock())
538 			return 0;
539 	}
540 	bool remove = false;
541 	bool change = false;
542 
543 	BMessage* clip = be_clipboard->Data();
544 	if (clip != NULL) {
545 		const node_ref* node = model->NodeRef();
546 		char modeName[64];
547 		MakeModeName(modeName, node);
548 		if ((clip->FindInt32(modeName, &moveMode) == B_OK)) {
549 			const entry_ref* ref = model->EntryRef();
550 			entry_ref clipref;
551 			char refName[64];
552 			MakeRefName(refName, node);
553 			if ((clip->FindRef(refName, &clipref) == B_OK)) {
554 				if (clipref != *ref) {
555 					if (updateRefIfNeeded) {
556 						clip->ReplaceRef(refName, ref);
557 						change = true;
558 					} else {
559 						clip->RemoveName(refName);
560 						clip->RemoveName(modeName);
561 						change = true;
562 						remove = true;
563 						moveMode = 0;
564 					}
565 				}
566 			} else {
567 				clip->RemoveName(modeName);
568 				change = true;
569 				remove = true;
570 				moveMode = 0;
571 			}
572 		}
573 	}
574 	if (change)
575 		be_clipboard->Commit();
576 
577 	if (autoLock)
578 		be_clipboard->Unlock();
579 
580 	if (remove)
581 		FSClipboardRemove(model);
582 
583 	return (uint32)moveMode;
584 }
585 
586 
587 void
FSClipboardRemove(Model * model)588 FSClipboardRemove(Model* model)
589 {
590 	BMessenger messenger(kTrackerSignature);
591 	if (messenger.IsValid()) {
592 		BMessage* report = new BMessage(kFSClipboardChanges);
593 		TClipboardNodeRef tcnode;
594 		tcnode.node = *model->NodeRef();
595 		tcnode.moveMode = kDelete;
596 		const entry_ref* ref = model->EntryRef();
597 		report->AddInt32("device", ref->device);
598 		report->AddInt64("directory", ref->directory);
599 		report->AddBool("clearClipboard", false);
600 		report->AddData("tcnode", T_CLIPBOARD_NODE, &tcnode, sizeof(tcnode),
601 			true);
602 		messenger.SendMessage(report);
603 		delete report;
604 	}
605 }
606 
607 
608 //	#pragma mark -
609 
610 
BClipboardRefsWatcher()611 BClipboardRefsWatcher::BClipboardRefsWatcher()
612 	:	BLooper("ClipboardRefsWatcher", B_LOW_PRIORITY, 4096),
613 	fNotifyList(10, false)
614 {
615 	watch_node(NULL, B_WATCH_MOUNT, this);
616 	fRefsInClipboard = FSClipboardHasRefs();
617 	be_clipboard->StartWatching(this);
618 }
619 
620 
~BClipboardRefsWatcher()621 BClipboardRefsWatcher::~BClipboardRefsWatcher()
622 {
623 	stop_watching(this);
624 	be_clipboard->StopWatching(this);
625 }
626 
627 
628 void
AddToNotifyList(BMessenger target)629 BClipboardRefsWatcher::AddToNotifyList(BMessenger target)
630 {
631 	if (Lock()) {
632 		// add the messenger if it's not already in the list
633 		// ToDo: why do we have to care about that?
634 		BMessenger* messenger;
635 		bool found = false;
636 
637 		for (int32 index = 0; (messenger = fNotifyList.ItemAt(index)) != NULL;
638 				index++) {
639 			if (*messenger == target) {
640 				found = true;
641 				break;
642 			}
643 		}
644 		if (!found)
645 			fNotifyList.AddItem(new BMessenger(target));
646 
647 		Unlock();
648 	}
649 }
650 
651 
652 void
RemoveFromNotifyList(BMessenger target)653 BClipboardRefsWatcher::RemoveFromNotifyList(BMessenger target)
654 {
655 	if (Lock()) {
656 		BMessenger* messenger;
657 
658 		for (int32 index = 0; (messenger = fNotifyList.ItemAt(index)) != NULL;
659 				index++) {
660 			if (*messenger == target) {
661 				delete fNotifyList.RemoveItemAt(index);
662 				break;
663 			}
664 		}
665 		Unlock();
666 	}
667 }
668 
669 
670 void
AddNode(const node_ref * node)671 BClipboardRefsWatcher::AddNode(const node_ref* node)
672 {
673 	TTracker::WatchNode(node, B_WATCH_NAME, this);
674 	fRefsInClipboard = true;
675 }
676 
677 
678 void
RemoveNode(node_ref * node,bool removeFromClipboard)679 BClipboardRefsWatcher::RemoveNode(node_ref* node, bool removeFromClipboard)
680 {
681 	watch_node(node, B_STOP_WATCHING, this);
682 
683 	if (!removeFromClipboard)
684 		return;
685 
686 	if (be_clipboard->Lock()) {
687 		BMessage* clip = be_clipboard->Data();
688 		if (clip != NULL) {
689 			char name[64];
690 			MakeRefName(name, node);
691 			clip->RemoveName(name);
692 			MakeModeName(name);
693 			clip->RemoveName(name);
694 
695 			be_clipboard->Commit();
696 		}
697 		be_clipboard->Unlock();
698 	}
699 }
700 
701 
702 void
RemoveNodesByDevice(dev_t device)703 BClipboardRefsWatcher::RemoveNodesByDevice(dev_t device)
704 {
705 	if (!be_clipboard->Lock())
706 		return;
707 
708 	BMessage* clip = be_clipboard->Data();
709 	if (clip != NULL) {
710 		char deviceName[6];
711 		sprintf(deviceName, "r%" B_PRIdDEV "_", device);
712 
713 		int32 index = 0;
714 		char* refName;
715 		type_code type;
716 		int32 count;
717 		while (clip->GetInfo(B_REF_TYPE, index,
718 #ifdef B_BEOS_VERSION_DANO
719 			(const char**)
720 #endif
721 			&refName, &type, &count) == B_OK) {
722 			if (!strncmp(deviceName, refName, strlen(deviceName))) {
723 				clip->RemoveName(refName);
724 				MakeModeName(refName);
725 				clip->RemoveName(refName);
726 
727 				node_ref node;
728 				MakeNodeFromName(&node, refName);
729 				watch_node(&node, B_STOP_WATCHING, this);
730 			}
731 			index++;
732 		}
733 		be_clipboard->Commit();
734 	}
735 	be_clipboard->Unlock();
736 }
737 
738 
739 void
UpdateNode(node_ref * node,entry_ref * ref)740 BClipboardRefsWatcher::UpdateNode(node_ref* node, entry_ref* ref)
741 {
742 	if (!be_clipboard->Lock())
743 		return;
744 
745 	BMessage* clip = be_clipboard->Data();
746 	if (clip != NULL) {
747 		char name[64];
748 		MakeRefName(name, node);
749 		if ((clip->ReplaceRef(name, ref)) != B_OK) {
750 			clip->RemoveName(name);
751 			MakeModeName(name);
752 			clip->RemoveName(name);
753 
754 			RemoveNode(node);
755 		}
756 		be_clipboard->Commit();
757 	}
758 	be_clipboard->Unlock();
759 }
760 
761 
762 void
Clear()763 BClipboardRefsWatcher::Clear()
764 {
765 	stop_watching(this);
766 	watch_node(NULL, B_WATCH_MOUNT, this);
767 
768 	BMessage message(kFSClipboardChanges);
769 	message.AddBool("clearClipboard", true);
770 	if (Lock()) {
771 		int32 items = fNotifyList.CountItems();
772 		for (int32 i = 0;i < items;i++) {
773 			fNotifyList.ItemAt(i)->SendMessage(&message);
774 		}
775 		Unlock();
776 	}
777 }
778 
779 
780 //void
781 //BClipboardRefsWatcher::UpdatePoseViews(bool clearClipboard,
782 //	const node_ref* node)
783 //{
784 //	BMessage message(kFSClipboardChanges);
785 //	message.AddInt32("device", node->device);
786 //	message.AddInt64("directory", node->node);
787 //	message.AddBool("clearClipboard", clearClipboard);
788 //
789 //	if (Lock()) {
790 //		int32 items = fNotifyList.CountItems();
791 //		for (int32 i = 0;i < items;i++) {
792 //			fNotifyList.ItemAt(i)->SendMessage(&message);
793 //		}
794 //		Unlock();
795 //	}
796 //}
797 
798 
799 void
UpdatePoseViews(BMessage * reportMessage)800 BClipboardRefsWatcher::UpdatePoseViews(BMessage* reportMessage)
801 {
802 	if (Lock()) {
803 		// check if it was cleared, if so clear watching
804 		bool clearClipboard = false;
805 		if (reportMessage->FindBool("clearClipboard", &clearClipboard) == B_OK
806 			&& clearClipboard) {
807 			stop_watching(this);
808 			watch_node(NULL, B_WATCH_MOUNT, this);
809 		}
810 
811 		// loop through reported node_ref's movemodes:
812 		// move or copy: start watching node_ref
813 		// remove: stop watching node_ref
814 		int32 index = 0;
815 		TClipboardNodeRef* tcnode = NULL;
816 		ssize_t size;
817 		while (reportMessage->FindData("tcnode", T_CLIPBOARD_NODE, index,
818 				(const void**)&tcnode, &size) == B_OK) {
819 			if (tcnode->moveMode == kDelete) {
820 				watch_node(&tcnode->node, B_STOP_WATCHING, this);
821 			} else {
822 				watch_node(&tcnode->node, B_STOP_WATCHING, this);
823 				TTracker::WatchNode(&tcnode->node, B_WATCH_NAME, this);
824 				fRefsInClipboard = true;
825 			}
826 			index++;
827 		}
828 
829 		// send report
830 		int32 items = fNotifyList.CountItems();
831 		for (int32 i = 0;i < items;i++) {
832 			fNotifyList.ItemAt(i)->SendMessage(reportMessage);
833 		}
834 		Unlock();
835 	}
836 }
837 
838 
839 void
MessageReceived(BMessage * message)840 BClipboardRefsWatcher::MessageReceived(BMessage* message)
841 {
842 	if (message->what == B_CLIPBOARD_CHANGED && fRefsInClipboard) {
843 		if (!(fRefsInClipboard = FSClipboardHasRefs()))
844 			Clear();
845 		return;
846 	} else if (message->what != B_NODE_MONITOR) {
847 		_inherited::MessageReceived(message);
848 		return;
849 	}
850 
851 	switch (message->FindInt32("opcode")) {
852 		case B_ENTRY_MOVED:
853 		{
854 			ino_t toDir;
855 			ino_t fromDir;
856 			node_ref node;
857 			const char* name = NULL;
858 			message->FindInt64("from directory", &fromDir);
859 			message->FindInt64("to directory", &toDir);
860 			message->FindInt64("node", &node.node);
861 			message->FindInt32("device", &node.device);
862 			message->FindString("name", &name);
863 			entry_ref ref(node.device, toDir, name);
864 			UpdateNode(&node, &ref);
865 			break;
866 		}
867 
868 		case B_DEVICE_UNMOUNTED:
869 		{
870 			dev_t device;
871 			message->FindInt32("device", &device);
872 			RemoveNodesByDevice(device);
873 			break;
874 		}
875 
876 		case B_ENTRY_REMOVED:
877 		{
878 			node_ref node;
879 			message->FindInt64("node", &node.node);
880 			message->FindInt32("device", &node.device);
881 			RemoveNode(&node, true);
882 			break;
883 		}
884 	}
885 }
886