#include "FSClipboard.h"
#include <Clipboard.h>
#include <Alert.h>
#include <Catalog.h>
#include <Locale.h>
#include <NodeMonitor.h>
#include "Commands.h"
#include "FSUtils.h"
#include "Tracker.h"
static void MakeNodeFromName(node_ref* node, char* name);
static inline void MakeRefName(char* refName, const node_ref* node);
static inline void MakeModeName(char* modeName, const node_ref* node);
static inline void MakeModeNameFromRefName(char* modeName, char* refName);
static inline bool CompareModeAndRefName(const char* modeName,
const char* refName);
#if 0
static bool
FSClipboardCheckIntegrity()
{
return true;
}
#endif
static void
MakeNodeFromName(node_ref* node, char* name)
{
char* nodeString = strchr(name, '_');
if (nodeString != NULL) {
node->node = strtoll(nodeString + 1, (char**)NULL, 10);
node->device = atoi(name + 1);
}
}
static inline void
MakeRefName(char* refName, const node_ref* node)
{
sprintf(refName, "r%" B_PRIdDEV "_%" B_PRIdINO, node->device, node->node);
}
static inline void
MakeModeName(char* modeName, const node_ref* node)
{
sprintf(modeName, "m%" B_PRIdDEV "_%" B_PRIdINO, node->device, node->node);
}
static inline void
MakeModeName(char* name)
{
name[0] = 'm';
}
static inline void
MakeModeNameFromRefName(char* modeName, char* refName)
{
strcpy(modeName, refName);
modeName[0] = 'm';
}
static inline bool
CompareModeAndRefName(const char* modeName, const char* refName)
{
return !strcmp(refName + 1, modeName + 1);
}
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "FSClipBoard"
bool
FSClipboardHasRefs()
{
bool result = false;
if (be_clipboard->Lock()) {
BMessage* clip = be_clipboard->Data();
if (clip != NULL) {
#ifdef B_BEOS_VERSION_DANO
const
#endif
char* refName;
#ifdef B_BEOS_VERSION_DANO
const
#endif
char* modeName;
uint32 type;
int32 count;
if (clip->GetInfo(B_REF_TYPE, 0, &refName, &type, &count) == B_OK
&& clip->GetInfo(B_INT32_TYPE, 0, &modeName, &type, &count)
== B_OK) {
result = CompareModeAndRefName(modeName, refName);
}
}
be_clipboard->Unlock();
}
return result;
}
void
FSClipboardStartWatch(BMessenger target)
{
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL && tracker->ClipboardRefsWatcher() != NULL)
tracker->ClipboardRefsWatcher()->AddToNotifyList(target);
else {
BMessenger messenger(kTrackerSignature);
if (messenger.IsValid()) {
BMessage message(kStartWatchClipboardRefs);
message.AddMessenger("target", target);
messenger.SendMessage(&message);
}
}
}
void
FSClipboardStopWatch(BMessenger target)
{
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL && tracker->ClipboardRefsWatcher() != NULL)
tracker->ClipboardRefsWatcher()->AddToNotifyList(target);
else {
BMessenger messenger(kTrackerSignature);
if (messenger.IsValid()) {
BMessage message(kStopWatchClipboardRefs);
message.AddMessenger("target", target);
messenger.SendMessage(&message);
}
}
}
void
FSClipboardClear()
{
if (!be_clipboard->Lock())
return;
be_clipboard->Clear();
be_clipboard->Commit();
be_clipboard->Unlock();
}
uint32
FSClipboardAddPoses(const node_ref* directory, PoseList* list,
uint32 moveMode, bool clearClipboard)
{
uint32 refsAdded = 0;
int32 listCount = list->CountItems();
if (listCount == 0 || !be_clipboard->Lock())
return 0;
BMessage updateMessage(kFSClipboardChanges);
updateMessage.AddInt32("device", directory->device);
updateMessage.AddInt64("directory", directory->node);
updateMessage.AddBool("clearClipboard", clearClipboard);
TClipboardNodeRef clipNode;
clipNode.moveMode = moveMode;
if (clearClipboard)
be_clipboard->Clear();
BMessage* clip = be_clipboard->Data();
if (clip != NULL) {
for (int32 index = 0; index < listCount; index++) {
char refName[64], modeName[64];
BPose* pose = (BPose*)list->ItemAt(index);
Model* model = pose->TargetModel();
const node_ref* node = model->NodeRef();
BEntry entry;
model->GetEntry(&entry);
if (model->IsVolume()
|| model->IsRoot()
|| model->IsTrash()
|| model->IsDesktop())
continue;
MakeRefName(refName, node);
MakeModeNameFromRefName(modeName, refName);
if (clearClipboard) {
if (clip->AddInt32(modeName, (int32)moveMode) == B_OK) {
if (clip->AddRef(refName, model->EntryRef()) == B_OK) {
pose->SetClipboardMode(moveMode);
clipNode.node = *node;
updateMessage.AddData("tcnode", T_CLIPBOARD_NODE,
&clipNode, sizeof(TClipboardNodeRef), true,
listCount);
refsAdded++;
} else
clip->RemoveName(modeName);
}
} else {
if (clip->ReplaceInt32(modeName, (int32)moveMode) == B_OK) {
if (clip->ReplaceRef(refName, model->EntryRef()) == B_OK) {
pose->SetClipboardMode(moveMode);
clipNode.node = *node;
updateMessage.AddData("tcnode", T_CLIPBOARD_NODE,
&clipNode, sizeof(TClipboardNodeRef), true,
listCount);
refsAdded++;
} else {
clip->RemoveName(modeName);
clipNode.node = *node;
clipNode.moveMode = kDelete;
updateMessage.AddData("tcnode", T_CLIPBOARD_NODE,
&clipNode, sizeof(TClipboardNodeRef), true,
listCount);
clipNode.moveMode = moveMode;
}
} else {
if (clip->AddRef(refName, model->EntryRef()) == B_OK
&& clip->AddInt32(modeName, (int32)moveMode) == B_OK) {
pose->SetClipboardMode(moveMode);
clipNode.node = *node;
updateMessage.AddData("tcnode", T_CLIPBOARD_NODE,
&clipNode, sizeof(TClipboardNodeRef), true,
listCount);
refsAdded++;
} else {
clip->RemoveName(modeName);
clip->RemoveName(refName);
}
}
}
}
be_clipboard->Commit();
}
be_clipboard->Unlock();
BMessenger(kTrackerSignature).SendMessage(&updateMessage);
return refsAdded;
}
uint32
FSClipboardRemovePoses(const node_ref* directory, PoseList* list)
{
if (!be_clipboard->Lock())
return 0;
BMessage updateMessage(kFSClipboardChanges);
updateMessage.AddInt32("device", directory->device);
updateMessage.AddInt64("directory", directory->node);
updateMessage.AddBool("clearClipboard", false);
TClipboardNodeRef clipNode;
clipNode.moveMode = kDelete;
uint32 refsRemoved = 0;
BMessage* clip = be_clipboard->Data();
if (clip != NULL) {
int32 listCount = list->CountItems();
for (int32 index = 0; index < listCount; index++) {
char refName[64], modeName[64];
BPose* pose = (BPose*)list->ItemAt(index);
clipNode.node = *pose->TargetModel()->NodeRef();
MakeRefName(refName, &clipNode.node);
MakeModeName(modeName);
if (clip->RemoveName(refName) == B_OK
&& clip->RemoveName(modeName)) {
updateMessage.AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
sizeof(TClipboardNodeRef), true, listCount);
refsRemoved++;
}
}
be_clipboard->Commit();
}
be_clipboard->Unlock();
BMessenger(kTrackerSignature).SendMessage(&updateMessage);
return refsRemoved;
}
bool
FSClipboardPaste(Model* model, uint32 linksMode)
{
if (!FSClipboardHasRefs())
return false;
BMessenger tracker(kTrackerSignature);
node_ref* destNodeRef = (node_ref*)model->NodeRef();
BObjectList<entry_ref, true>* moveList = new BObjectList<entry_ref, true>(0);
BObjectList<entry_ref, true>* copyList = new BObjectList<entry_ref, true>(0);
BObjectList<entry_ref, true>* duplicateList = new BObjectList<entry_ref, true>(0);
if ((be_clipboard->Lock())) {
BMessage* clip = be_clipboard->Data();
if (clip != NULL) {
char modeName[64];
uint32 moveMode = 0;
BMessage updateMessage(kFSClipboardChanges);
node_ref updateNodeRef;
updateNodeRef.device = -1;
char* refName;
type_code type;
int32 count;
for (int32 index = 0; clip->GetInfo(B_REF_TYPE, index,
#ifdef B_BEOS_VERSION_DANO
(const char**)
#endif
&refName, &type, &count) == B_OK; index++) {
entry_ref ref;
if (clip->FindRef(refName, &ref) != B_OK)
continue;
if (updateNodeRef.device != ref.device || updateNodeRef.node != ref.directory) {
if (!updateMessage.IsEmpty()) {
tracker.SendMessage(&updateMessage);
updateMessage.MakeEmpty();
}
updateNodeRef.device = ref.device;
updateNodeRef.node = ref.directory;
updateMessage.AddInt32("device", updateNodeRef.device);
updateMessage.AddInt64("directory", updateNodeRef.node);
}
MakeModeNameFromRefName(modeName, refName);
if (!linksMode && clip->FindInt32(modeName, (int32*)&moveMode) != B_OK)
continue;
BEntry entry(&ref);
uint32 newMoveMode = 0;
bool sameDirectory = destNodeRef->device == ref.device
&& destNodeRef->node == ref.directory;
if (!entry.Exists()) {
clip->RemoveName(refName);
clip->RemoveName(modeName);
newMoveMode = kDelete;
} else {
if (!sameDirectory) {
if (linksMode || moveMode == kMoveSelectionTo) {
moveList->AddItem(new entry_ref(ref));
} else if (moveMode == kCopySelectionTo)
copyList->AddItem(new entry_ref(ref));
} else if (moveMode != kMoveSelectionTo) {
duplicateList->AddItem(new entry_ref(ref));
}
if (moveMode == kMoveSelectionTo)
newMoveMode = kCopySelectionTo;
}
if (newMoveMode != 0) {
clip->ReplaceInt32(modeName, kCopySelectionTo);
TClipboardNodeRef clipNode;
MakeNodeFromName(&clipNode.node, modeName);
clipNode.moveMode = kDelete;
updateMessage.AddData("tcnode", T_CLIPBOARD_NODE, &clipNode,
sizeof(TClipboardNodeRef), true);
}
}
be_clipboard->Commit();
if (!updateMessage.IsEmpty()) {
tracker.SendMessage(&updateMessage);
updateMessage.MakeEmpty();
}
}
be_clipboard->Unlock();
}
bool okToMove = true;
if (model->IsRoot()) {
BAlert* alert = new BAlert("",
B_TRANSLATE("You must drop items on one of the disk icons "
"in the \"Disks\" window."), B_TRANSLATE("Cancel"), NULL, NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
okToMove = false;
}
BEntry entry;
model->GetEntry(&entry);
if (copyList->CountItems() > 0 && model->IsTrash()) {
BAlert* alert = new BAlert("",
B_TRANSLATE("Sorry, you can't copy items to the Trash."),
B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
okToMove = false;
}
if (!okToMove) {
delete moveList;
delete copyList;
delete duplicateList;
return false;
}
if (moveList->CountItems() > 0)
FSMoveToFolder(moveList, new BEntry(entry), linksMode ? linksMode : kMoveSelectionTo);
else
delete moveList;
if (copyList->CountItems() > 0)
FSMoveToFolder(copyList, new BEntry(entry), kCopySelectionTo);
else
delete copyList;
if (duplicateList->CountItems() > 0)
FSMoveToFolder(duplicateList, new BEntry(entry), kDuplicateSelection);
else
delete duplicateList;
return true;
}
uint32
FSClipboardFindNodeMode(Model* model, bool autoLock, bool updateRefIfNeeded)
{
int32 moveMode = 0;
if (autoLock) {
if (!be_clipboard->Lock())
return 0;
}
bool remove = false;
bool change = false;
BMessage* clip = be_clipboard->Data();
if (clip != NULL) {
const node_ref* node = model->NodeRef();
char modeName[64];
MakeModeName(modeName, node);
if ((clip->FindInt32(modeName, &moveMode) == B_OK)) {
const entry_ref* ref = model->EntryRef();
entry_ref clipref;
char refName[64];
MakeRefName(refName, node);
if ((clip->FindRef(refName, &clipref) == B_OK)) {
if (clipref != *ref) {
if (updateRefIfNeeded) {
clip->ReplaceRef(refName, ref);
change = true;
} else {
clip->RemoveName(refName);
clip->RemoveName(modeName);
change = true;
remove = true;
moveMode = 0;
}
}
} else {
clip->RemoveName(modeName);
change = true;
remove = true;
moveMode = 0;
}
}
}
if (change)
be_clipboard->Commit();
if (autoLock)
be_clipboard->Unlock();
if (remove)
FSClipboardRemove(model);
return (uint32)moveMode;
}
void
FSClipboardRemove(Model* model)
{
BMessenger messenger(kTrackerSignature);
if (messenger.IsValid()) {
BMessage* report = new BMessage(kFSClipboardChanges);
TClipboardNodeRef tcnode;
tcnode.node = *model->NodeRef();
tcnode.moveMode = kDelete;
const entry_ref* ref = model->EntryRef();
report->AddInt32("device", ref->device);
report->AddInt64("directory", ref->directory);
report->AddBool("clearClipboard", false);
report->AddData("tcnode", T_CLIPBOARD_NODE, &tcnode, sizeof(tcnode),
true);
messenger.SendMessage(report);
delete report;
}
}
BClipboardRefsWatcher::BClipboardRefsWatcher()
: BLooper("ClipboardRefsWatcher", B_LOW_PRIORITY, 4096),
fNotifyList(10)
{
watch_node(NULL, B_WATCH_MOUNT, this);
fRefsInClipboard = FSClipboardHasRefs();
be_clipboard->StartWatching(this);
}
BClipboardRefsWatcher::~BClipboardRefsWatcher()
{
stop_watching(this);
be_clipboard->StopWatching(this);
}
void
BClipboardRefsWatcher::AddToNotifyList(BMessenger target)
{
if (Lock()) {
BMessenger* messenger;
bool found = false;
for (int32 index = 0; (messenger = fNotifyList.ItemAt(index)) != NULL;
index++) {
if (*messenger == target) {
found = true;
break;
}
}
if (!found)
fNotifyList.AddItem(new BMessenger(target));
Unlock();
}
}
void
BClipboardRefsWatcher::RemoveFromNotifyList(BMessenger target)
{
if (Lock()) {
BMessenger* messenger;
for (int32 index = 0; (messenger = fNotifyList.ItemAt(index)) != NULL;
index++) {
if (*messenger == target) {
delete fNotifyList.RemoveItemAt(index);
break;
}
}
Unlock();
}
}
void
BClipboardRefsWatcher::AddNode(const node_ref* node)
{
TTracker::WatchNode(node, B_WATCH_NAME, this);
fRefsInClipboard = true;
}
void
BClipboardRefsWatcher::RemoveNode(node_ref* node, bool removeFromClipboard)
{
watch_node(node, B_STOP_WATCHING, this);
if (!removeFromClipboard)
return;
if (be_clipboard->Lock()) {
BMessage* clip = be_clipboard->Data();
if (clip != NULL) {
char name[64];
MakeRefName(name, node);
clip->RemoveName(name);
MakeModeName(name);
clip->RemoveName(name);
be_clipboard->Commit();
}
be_clipboard->Unlock();
}
}
void
BClipboardRefsWatcher::RemoveNodesByDevice(dev_t device)
{
if (!be_clipboard->Lock())
return;
BMessage* clip = be_clipboard->Data();
if (clip != NULL) {
char deviceName[6];
sprintf(deviceName, "r%" B_PRIdDEV "_", device);
int32 index = 0;
char* refName;
type_code type;
int32 count;
while (clip->GetInfo(B_REF_TYPE, index,
#ifdef B_BEOS_VERSION_DANO
(const char**)
#endif
&refName, &type, &count) == B_OK) {
if (!strncmp(deviceName, refName, strlen(deviceName))) {
clip->RemoveName(refName);
MakeModeName(refName);
clip->RemoveName(refName);
node_ref node;
MakeNodeFromName(&node, refName);
watch_node(&node, B_STOP_WATCHING, this);
}
index++;
}
be_clipboard->Commit();
}
be_clipboard->Unlock();
}
void
BClipboardRefsWatcher::UpdateNode(node_ref* node, entry_ref* ref)
{
if (!be_clipboard->Lock())
return;
BMessage* clip = be_clipboard->Data();
if (clip != NULL) {
char name[64];
MakeRefName(name, node);
if ((clip->ReplaceRef(name, ref)) != B_OK) {
clip->RemoveName(name);
MakeModeName(name);
clip->RemoveName(name);
RemoveNode(node);
}
be_clipboard->Commit();
}
be_clipboard->Unlock();
}
void
BClipboardRefsWatcher::Clear()
{
stop_watching(this);
watch_node(NULL, B_WATCH_MOUNT, this);
BMessage message(kFSClipboardChanges);
message.AddBool("clearClipboard", true);
if (Lock()) {
int32 items = fNotifyList.CountItems();
for (int32 i = 0;i < items;i++) {
fNotifyList.ItemAt(i)->SendMessage(&message);
}
Unlock();
}
}
void
BClipboardRefsWatcher::UpdatePoseViews(BMessage* reportMessage)
{
if (Lock()) {
bool clearClipboard = false;
if (reportMessage->FindBool("clearClipboard", &clearClipboard) == B_OK
&& clearClipboard) {
stop_watching(this);
watch_node(NULL, B_WATCH_MOUNT, this);
}
int32 index = 0;
TClipboardNodeRef* tcnode = NULL;
ssize_t size;
while (reportMessage->FindData("tcnode", T_CLIPBOARD_NODE, index,
(const void**)&tcnode, &size) == B_OK) {
if (tcnode->moveMode == kDelete) {
watch_node(&tcnode->node, B_STOP_WATCHING, this);
} else {
watch_node(&tcnode->node, B_STOP_WATCHING, this);
TTracker::WatchNode(&tcnode->node, B_WATCH_NAME, this);
fRefsInClipboard = true;
}
index++;
}
int32 items = fNotifyList.CountItems();
for (int32 i = 0;i < items;i++) {
fNotifyList.ItemAt(i)->SendMessage(reportMessage);
}
Unlock();
}
}
void
BClipboardRefsWatcher::MessageReceived(BMessage* message)
{
if (message->what == B_CLIPBOARD_CHANGED && fRefsInClipboard) {
if (!(fRefsInClipboard = FSClipboardHasRefs()))
Clear();
return;
} else if (message->what != B_NODE_MONITOR) {
_inherited::MessageReceived(message);
return;
}
switch (message->GetInt32("opcode", 0)) {
case B_ENTRY_MOVED:
{
ino_t toDir;
ino_t fromDir;
node_ref node;
const char* name = NULL;
message->FindInt64("from directory", &fromDir);
message->FindInt64("to directory", &toDir);
message->FindInt64("node", &node.node);
message->FindInt32("device", &node.device);
message->FindString("name", &name);
entry_ref ref(node.device, toDir, name);
UpdateNode(&node, &ref);
break;
}
case B_DEVICE_UNMOUNTED:
{
dev_t device;
message->FindInt32("device", &device);
RemoveNodesByDevice(device);
break;
}
case B_ENTRY_REMOVED:
{
node_ref node;
message->FindInt64("node", &node.node);
message->FindInt32("device", &node.device);
RemoveNode(&node, true);
break;
}
}
}