#include "Tracker.h"
#include <errno.h>
#include <fs_attr.h>
#include <fs_info.h>
#include <image.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/resource.h>
#include <unistd.h>
#include <Alert.h>
#include <Autolock.h>
#include <Catalog.h>
#include <Debug.h>
#include <FindDirectory.h>
#include <Locale.h>
#include <MenuItem.h>
#include <NodeInfo.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <PathMonitor.h>
#include <Roster.h>
#include <StopWatch.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <tracker_private.h>
#include "Attributes.h"
#include "AutoLock.h"
#include "BackgroundImage.h"
#include "Bitmaps.h"
#include "Commands.h"
#include "ContainerWindow.h"
#include "DeskWindow.h"
#include "FindPanel.h"
#include "FunctionObject.h"
#include "FSClipboard.h"
#include "FSUtils.h"
#include "InfoWindow.h"
#include "MimeTypes.h"
#include "MimeTypeList.h"
#include "NodePreloader.h"
#include "OpenWithWindow.h"
#include "PoseView.h"
#include "QueryContainerWindow.h"
#include "StatusWindow.h"
#include "TaskLoop.h"
#include "Thread.h"
#include "TrackerSettings.h"
#include "TrackerSettingsWindow.h"
#include "TrackerString.h"
#include "TrashWatcher.h"
#include "VirtualDirectoryWindow.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Tracker"
#ifndef _IMPEXP_ROOT
# define _IMPEXP_ROOT
#endif
const int32 DEFAULT_MON_NUM = 4096;
const int8 kOpenWindowNoFlags = 0;
const int8 kOpenWindowMinimized = 1;
const int8 kOpenWindowHasState = 2;
const uint32 PSV_MAKE_PRINTER_ACTIVE_QUIETLY = 'pmaq';
const int32 kNodeMonitorBumpValue = 512;
namespace BPrivate {
NodePreloader* gPreloader = NULL;
class LaunchLooper : public BLooper {
public:
LaunchLooper()
:
BLooper("launch looper")
{
}
virtual void
MessageReceived(BMessage* message)
{
void (*function)(const entry_ref*, const BMessage*, bool);
BMessage refs;
bool openWithOK;
entry_ref appRef;
if (message->FindPointer("function", (void**)&function) != B_OK
|| message->FindMessage("refs", &refs) != B_OK
|| message->FindBool("openWithOK", &openWithOK) != B_OK) {
printf("incomplete launch message\n");
return;
}
if (message->FindRef("appRef", &appRef) == B_OK)
function(&appRef, &refs, openWithOK);
else
function(NULL, &refs, openWithOK);
}
};
BLooper* gLaunchLooper = NULL;
void
InitIconPreloader()
{
static int32 lock = 0;
if (atomic_add(&lock, 1) != 0) {
int32 tries = 20;
while (IconCache::sIconCache == NULL && tries-- > 0)
snooze(10000);
return;
}
if (IconCache::sIconCache != NULL)
return;
bool preload = dynamic_cast<TTracker*>(be_app) != NULL;
if (!preload) {
app_info info;
if (be_app->GetAppInfo(&info) == B_OK
&& !strcmp(info.signature, kDeskbarSignature))
preload = true;
}
if (preload) {
gPreloader = NodePreloader::InstallNodePreloader("NodePreloader",
be_app);
}
IconCache::sIconCache = new IconCache();
atomic_add(&lock, -1);
}
}
uint32
GetVolumeFlags(Model* model)
{
fs_info info;
if (model->IsVolume()) {
int32 cookie = 0;
dev_t device;
while ((device = next_dev(&cookie)) >= B_OK) {
if (fs_stat_dev(device,&info))
continue;
if (!strcmp(info.volume_name,model->Name()))
return info.flags;
}
return B_FS_HAS_ATTR;
}
if (!fs_stat_dev(model->NodeRef()->device,&info))
return info.flags;
return B_FS_HAS_ATTR;
}
class TTracker::WatchingInterface : public BPathMonitor::BWatchingInterface {
public:
virtual status_t WatchNode(const node_ref* node, uint32 flags,
const BMessenger& target)
{
return TTracker::WatchNode(node, flags, target);
}
virtual status_t WatchNode(const node_ref* node, uint32 flags,
const BHandler* handler, const BLooper* looper = NULL)
{
return TTracker::WatchNode(node, flags, BMessenger(handler, looper));
}
};
TTracker::TTracker()
:
BApplication(kTrackerSignature),
fMimeTypeList(NULL),
fClipboardRefsWatcher(NULL),
fTrashWatcher(NULL),
fTaskLoop(NULL),
fNodeMonitorCount(-1),
fWatchingInterface(new WatchingInterface),
fSettingsWindow(NULL)
{
BPathMonitor::SetWatchingInterface(fWatchingInterface);
BPath homePath;
if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK)
chdir(homePath.Path());
struct rlimit rl;
rl.rlim_cur = 512;
rl.rlim_max = RLIM_SAVED_MAX;
setrlimit(RLIMIT_NOFILE, &rl);
fNodeMonitorCount = DEFAULT_MON_NUM;
gLocalizedNamePreferred
= BLocaleRoster::Default()->IsFilesystemTranslationPreferred();
#ifdef CHECK_OPEN_MODEL_LEAKS
InitOpenModelDumping();
#endif
InitIconPreloader();
#ifdef LEAK_CHECKING
SetNewLeakChecking(true);
SetMallocLeakChecking(true);
#endif
SetPulseRate(1000000);
gLaunchLooper = new LaunchLooper();
gLaunchLooper->Run();
AutoLock<WindowList> lock(&fWindowList);
BContainerWindow* deskWindow = new BDeskWindow(&fWindowList);
AutoLock<BWindow> windowLock(deskWindow);
deskWindow->Init();
fTrashWatcher = new BTrashWatcher();
}
TTracker::~TTracker()
{
gLaunchLooper->Lock();
gLaunchLooper->Quit();
BPathMonitor::SetWatchingInterface(NULL);
delete fWatchingInterface;
delete fMimeTypeList;
}
bool
TTracker::QuitRequested()
{
if (CurrentMessage() != NULL && CurrentMessage()->FindBool("shortcut")) {
int32 index = 0;
BWindow* window = NULL;
while ((window = WindowAt(index++)) != NULL) {
if (window == fSettingsWindow) {
if (fSettingsWindow->Lock()) {
if (!fSettingsWindow->IsHidden()
&& fSettingsWindow->IsActive()) {
fSettingsWindow->Hide();
}
fSettingsWindow->Unlock();
}
break;
}
}
return false;
}
gStatusWindow->AttemptToQuit();
BMessage message;
AutoLock<WindowList> lock(&fWindowList);
int32 count = fWindowList.CountItems();
for (int32 i = 0; i < count; i++) {
BContainerWindow* window
= dynamic_cast<BContainerWindow*>(fWindowList.ItemAt(i));
if (window != NULL && window->Lock()) {
if (window->TargetModel() != NULL && window->PoseView() != NULL
&& !window->PoseView()->IsDesktopView()) {
if (window->TargetModel()->IsRoot()) {
message.AddBool("open_disks_window", true);
} else {
BEntry entry;
BPath path;
const entry_ref* ref = window->TargetModel()->EntryRef();
if (entry.SetTo(ref) == B_OK
&& entry.GetPath(&path) == B_OK) {
int8 flags = window->IsMinimized()
? kOpenWindowMinimized : kOpenWindowNoFlags;
uint32 deviceFlags
= GetVolumeFlags(window->TargetModel());
if (window != FindContainerWindow(ref)
|| (deviceFlags
& (B_FS_HAS_ATTR | B_FS_IS_READONLY))
!= B_FS_HAS_ATTR) {
BMessage stateMessage;
window->SaveState(stateMessage);
window->SetSaveStateEnabled(false);
message.AddMessage("window state", &stateMessage);
flags |= kOpenWindowHasState;
}
const char* target;
bool pathAlreadyExists = false;
for (int32 index = 0;
message.FindString("paths", index, &target)
== B_OK; index++) {
if (!strcmp(target,path.Path())) {
pathAlreadyExists = true;
break;
}
}
if (!pathAlreadyExists)
message.AddString("paths", path.Path());
message.AddInt8(path.Path(), flags);
}
}
}
window->Unlock();
}
}
lock.Unlock();
BDirectory deskDir;
if (!BootedInSafeMode() && FSGetDeskDir(&deskDir) == B_OK) {
if (message.CountNames(B_ANY_TYPE)) {
ssize_t size = message.FlattenedSize();
if (size > 0) {
char* buffer = new char[size];
message.Flatten(buffer, size);
deskDir.WriteAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer,
size);
delete[] buffer;
}
} else
deskDir.RemoveAttr(kAttrOpenWindows);
}
for (int32 count = 0; count < 50; count++) {
if (gStatusWindow->AttemptToQuit())
break;
snooze(100000);
}
return _inherited::QuitRequested();
}
void
TTracker::Quit()
{
TrackerSettings().SaveSettings(false);
fClipboardRefsWatcher->Lock();
fClipboardRefsWatcher->Quit();
fTrashWatcher->Lock();
fTrashWatcher->Quit();
WellKnowEntryList::Quit();
delete gPreloader;
delete fTaskLoop;
delete IconCache::sIconCache;
_inherited::Quit();
}
void
TTracker::MessageReceived(BMessage* message)
{
if (HandleScriptingMessage(message))
return;
switch (message->what) {
case kGetInfo:
OpenInfoWindows(message);
break;
case kMoveSelectionToTrash:
MoveRefsToTrash(message);
break;
case kSelect:
SelectRefs(message);
break;
case kCloseWindowAndChildren:
{
const node_ref* itemNode;
ssize_t bytes;
if (message->FindData("node_ref", B_RAW_TYPE,
(const void**)&itemNode, &bytes) == B_OK) {
CloseWindowAndChildren(itemNode);
}
break;
}
case kCloseAllWindows:
CloseAllWindows();
break;
case kCloseAllInWorkspace:
CloseAllInWorkspace();
break;
case kFindButton:
(new FindWindow())->Show();
break;
case kEditQuery:
EditQueries(message);
break;
case kShowSplash:
run_be_about();
break;
case kAddPrinter:
run_add_printer_panel();
break;
case kMakeActivePrinter:
SetDefaultPrinter(message);
break;
#ifdef MOUNT_MENU_IN_DESKBAR
case 'gmtv':
{
BMessage reply;
AutoMounterLoop()->EachMountableItemAndFloppy(
&AddMountableItemToMessage, &reply);
message->SendReply(&reply);
break;
}
#endif
case kUnmountVolume:
SaveAllPoseLocations();
case kMountVolume:
case kMountAllNow:
MountServer().SendMessage(message);
break;
case kRestoreBackgroundImage:
{
BDeskWindow* desktop = GetDeskWindow();
AutoLock<BWindow> lock(desktop);
desktop->PostMessage(message, desktop);
break;
}
case kRunAutomounterSettings:
ShowSettingsWindow();
fSettingsWindow->ShowPage(
TrackerSettingsWindow::kAutomountSettings);
break;
case kShowSettingsWindow:
ShowSettingsWindow();
break;
case kFavoriteCountChangedExternally:
SendNotices(kFavoriteCountChangedExternally, message);
break;
case kStartWatchClipboardRefs:
{
BMessenger messenger;
message->FindMessenger("target", &messenger);
if (messenger.IsValid())
fClipboardRefsWatcher->AddToNotifyList(messenger);
break;
}
case kStopWatchClipboardRefs:
{
BMessenger messenger;
if (message->FindMessenger("target", &messenger) == B_OK
&& messenger.IsValid()) {
fClipboardRefsWatcher->RemoveFromNotifyList(messenger);
}
break;
}
case kFSClipboardChanges:
fClipboardRefsWatcher->UpdatePoseViews(message);
break;
case kShowVolumeSpaceBar:
case kSpaceBarColorChanged:
gPeriodicUpdatePoses.DoPeriodicUpdate(true);
break;
case B_LOCALE_CHANGED:
{
BLocaleRoster::Default()->Refresh();
bool localize;
if (message->FindBool("filesys", &localize) == B_OK)
gLocalizedNamePreferred = localize;
break;
}
case kUpdateThumbnail:
{
node_ref noderef;
if (message->FindNodeRef("noderef", &noderef) == B_OK) {
AutoLock<WindowList> lock(&fWindowList);
int32 count = fWindowList.CountItems();
for (int32 index = 0; index < count; index++) {
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
fWindowList.ItemAt(index));
if (window == NULL)
continue;
AutoLock<BWindow> windowLock(window);
if (!windowLock.IsLocked())
continue;
BPoseView* poseView = window->PoseView();
if (poseView == NULL)
continue;
BPose* pose = poseView->FindPose(&noderef);
if (pose != NULL) {
poseView->UpdateIcon(pose);
break;
}
}
}
break;
}
default:
_inherited::MessageReceived(message);
break;
}
}
void
TTracker::Pulse()
{
if (!TrackerSettings().ShowVolumeSpaceBar())
return;
gPeriodicUpdatePoses.DoPeriodicUpdate(false);
}
void
TTracker::SetDefaultPrinter(const BMessage* message)
{
int32 count = 0;
uint32 type = 0;
message->GetInfo("refs", &type, &count);
if (count <= 0)
return;
entry_ref ref;
ASSERT(message->FindRef("refs", 0, &ref) == B_OK);
if (message->FindRef("refs", 0, &ref) != B_OK)
return;
#if B_BEOS_VERSION_DANO
set_default_printer(ref.name);
#else
BMessenger messenger("application/x-vnd.Be-PSRV", -1);
if (!messenger.IsValid())
return;
BMessage makeActiveMessage(PSV_MAKE_PRINTER_ACTIVE_QUIETLY);
makeActiveMessage.AddString("printer", ref.name);
BMessage reply;
messenger.SendMessage(&makeActiveMessage, &reply);
#endif
}
void
TTracker::MoveRefsToTrash(const BMessage* message)
{
int32 count;
uint32 type;
message->GetInfo("refs", &type, &count);
if (count <= 0)
return;
BObjectList<entry_ref, true>* srcList = new BObjectList<entry_ref, true>(count);
for (int32 index = 0; index < count; index++) {
entry_ref ref;
ASSERT(message->FindRef("refs", index, &ref) == B_OK);
if (message->FindRef("refs", index, &ref) != B_OK)
continue;
AutoLock<WindowList> lock(&fWindowList);
BContainerWindow* window = FindParentContainerWindow(&ref);
if (window != NULL) {
window->PoseView()->MoveEntryToTrash(&ref);
} else {
srcList->AddItem(new entry_ref(ref));
}
}
FSMoveToTrash(srcList);
}
void
TTracker::SelectRefs(const BMessage* message)
{
uint32 type = 0;
int32 count = 0;
message->GetInfo("refs", &type, &count);
for (int32 index = 0; index < count; index++) {
entry_ref ref;
message->FindRef("refs", index, &ref);
BEntry entry(&ref, true);
if (entry.InitCheck() != B_OK || !entry.Exists())
continue;
AutoLock<WindowList> lock(&fWindowList);
BContainerWindow* window = FindParentContainerWindow(&ref);
if (window == NULL)
continue;
char name[B_FILE_NAME_LENGTH];
if (entry.GetName(name) != B_OK)
continue;
BString expression;
expression << "^";
expression << name;
expression << "$";
BMessage* selectMessage = new BMessage(kSelectMatchingEntries);
selectMessage->AddInt32("ExpressionType", kRegexpMatch);
selectMessage->AddString("Expression", expression);
selectMessage->AddBool("InvertSelection", false);
selectMessage->AddBool("IgnoreCase", false);
window->Activate();
snooze(100000);
window->PostMessage(selectMessage);
}
}
template <class T, class FT>
class EntryAndNodeDoSoonWithMessageFunctor : public
FunctionObjectWithResult<bool> {
public:
EntryAndNodeDoSoonWithMessageFunctor(FT func, T* target,
const entry_ref* child, const node_ref* parent,
const BMessage* message)
:
fFunc(func),
fTarget(target),
fNode(*parent),
fEntry(*child)
{
fSendMessage = message != NULL;
if (message != NULL)
fMessage = *message;
}
virtual ~EntryAndNodeDoSoonWithMessageFunctor() {}
virtual void operator()()
{
result = (fTarget->*fFunc)(&fEntry, &fNode,
fSendMessage ? &fMessage : NULL);
}
protected:
FT fFunc;
T* fTarget;
node_ref fNode;
entry_ref fEntry;
BMessage fMessage;
bool fSendMessage;
};
bool
TTracker::LaunchAndCloseParentIfOK(const entry_ref* launchThis,
const node_ref* closeThis, const BMessage* messageToBundle)
{
BMessage refsReceived(B_REFS_RECEIVED);
if (messageToBundle != NULL) {
refsReceived = *messageToBundle;
refsReceived.what = B_REFS_RECEIVED;
}
refsReceived.AddRef("refs", launchThis);
if (TrackerLaunch(&refsReceived, false) == B_OK) {
fTaskLoop->RunLater(NewMemberFunctionObject(&TTracker::CloseParent,
this, *closeThis), 1000000);
}
return false;
}
status_t
TTracker::OpenRef(const entry_ref* ref, const node_ref* nodeToClose,
const node_ref* nodeToSelect, OpenSelector selector,
const BMessage* messageToBundle)
{
Model* model = NULL;
BEntry entry(ref, true);
status_t result = entry.InitCheck();
if (result != B_OK) {
BAlert* alert = new BAlert("",
B_TRANSLATE("There was an error resolving the link."),
B_TRANSLATE_COMMENT("Get info", "Tracker's 'Get info' panel [ALT+I]"),
B_TRANSLATE("Cancel"), 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
int32 choice = alert->Go();
if (choice == 0) {
BMessenger tracker(kTrackerSignature);
BMessage message(kGetInfo);
message.AddRef("refs", ref);
tracker.SendMessage(&message);
}
return result;
} else
model = new Model(&entry);
result = model->InitCheck();
if (result != B_OK) {
delete model;
return result;
}
bool openAsContainer = model->IsContainer();
if (openAsContainer && selector != kOpenWith) {
model->OpenNode();
BNodeInfo nodeInfo(model->Node());
char preferredApp[B_MIME_TYPE_LENGTH];
if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
&& strcasecmp(preferredApp, kTrackerSignature) != 0) {
openAsContainer = false;
}
model->CloseNode();
}
if (openAsContainer || selector == kRunOpenWithWindow) {
OpenContainerWindow(model, NULL, selector, kRestoreDecor);
if (nodeToClose)
CloseParentWaitingForChildSoon(ref, nodeToClose);
} else if (model->IsQueryTemplate()) {
(new FindWindow(model->EntryRef()))->Show();
delete model;
if (nodeToClose)
CloseParentWaitingForChildSoon(ref, nodeToClose);
} else {
delete model;
if (nodeToClose) {
Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker,
bool (TTracker::*)(const entry_ref*, const node_ref*,
const BMessage*)>(&TTracker::LaunchAndCloseParentIfOK, this,
ref, nodeToClose, messageToBundle));
} else {
BMessage refsReceived(B_REFS_RECEIVED);
if (messageToBundle) {
refsReceived = *messageToBundle;
refsReceived.what = B_REFS_RECEIVED;
}
refsReceived.AddRef("refs", ref);
TrackerLaunch(&refsReceived, true);
}
}
if (nodeToSelect)
SelectChildInParentSoon(ref, nodeToSelect);
return B_OK;
}
void
TTracker::RefsReceived(BMessage* message)
{
OpenSelector selector = kOpen;
if (message->HasInt32("launchUsingSelector"))
selector = kRunOpenWithWindow;
entry_ref handlingApp;
if (message->FindRef("handler", &handlingApp) == B_OK)
selector = kOpenWith;
int32 count;
uint32 type;
message->GetInfo("refs", &type, &count);
switch (selector) {
case kRunOpenWithWindow:
OpenContainerWindow(NULL, message, selector);
break;
case kOpenWith:
{
message->RemoveName("handler");
char signature[B_MIME_TYPE_LENGTH];
signature[0] = '\0';
{
BFile handlingNode(&handlingApp, O_RDONLY);
BAppFileInfo appInfo(&handlingNode);
appInfo.GetSignature(signature);
}
if (strcasecmp(signature, kTrackerSignature) != 0) {
TrackerLaunch(&handlingApp, message, true);
break;
}
}
case kOpen:
{
BMessage* bundleThis = NULL;
BMessage stackBundleThis;
BMessenger messenger;
if (message->FindMessenger("TrackerViewToken", &messenger)
== B_OK) {
bundleThis = &stackBundleThis;
bundleThis->AddMessenger("TrackerViewToken", messenger);
} else {
for (int32 i = 0;; i++) {
char* name;
type_code type;
int32 count;
status_t error = message->GetInfo(B_ANY_TYPE, i, &name,
&type, &count);
if (error != B_OK)
break;
if (strncmp(name, "be:", 3) != 0)
continue;
for (int32 k = 0; k < count; k++) {
const void* data;
ssize_t size;
if (message->FindData(name, type, k, &data, &size)
!= B_OK) {
break;
}
if (stackBundleThis.AddData(name, type, data, size)
!= B_OK) {
break;
}
bundleThis = &stackBundleThis;
}
}
}
for (int32 index = 0; index < count; index++) {
entry_ref ref;
message->FindRef("refs", index, &ref);
const node_ref* nodeToClose = NULL;
const node_ref* nodeToSelect = NULL;
ssize_t numBytes;
message->FindData("nodeRefsToClose", B_RAW_TYPE, index,
(const void**)&nodeToClose, &numBytes);
message->FindData("nodeRefToSelect", B_RAW_TYPE, index,
(const void**)&nodeToSelect, &numBytes);
OpenRef(&ref, nodeToClose, nodeToSelect, selector,
bundleThis);
}
break;
}
}
}
void
TTracker::ArgvReceived(int32 argc, char** argv)
{
BMessage* message = CurrentMessage();
const char* currentWorkingDirectoryPath = NULL;
entry_ref ref;
if (message->FindString("cwd", ¤tWorkingDirectoryPath) == B_OK) {
BDirectory workingDirectory(currentWorkingDirectoryPath);
for (int32 index = 1; index < argc; index++) {
BEntry entry;
if (entry.SetTo(&workingDirectory, argv[index]) == B_OK
&& entry.GetRef(&ref) == B_OK) {
OpenRef(&ref);
} else if (get_ref_for_path(argv[index], &ref) == B_OK)
OpenRef(&ref);
}
}
}
void
TTracker::OpenContainerWindow(Model* model, BMessage* originalRefsList,
OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen,
const BMessage* stateMessage)
{
AutoLock<WindowList> lock(&fWindowList);
BContainerWindow* window = NULL;
const node_ref* modelNodeRef = model->NodeRef();
if (checkAlreadyOpen && openSelector != kRunOpenWithWindow) {
window = FindContainerWindow(modelNodeRef);
}
bool someWindowActivated = false;
uint32 workspace = (uint32)(1 << current_workspace());
int32 windowCount = 0;
while (window != NULL) {
if ((window->Workspaces() & workspace) != 0
&& (!model->IsDesktop() || !TrackerSettings().SingleWindowBrowse())) {
window->Activate();
someWindowActivated = true;
}
window = FindContainerWindow(model->NodeRef(), ++windowCount);
}
if (someWindowActivated) {
delete model;
return;
}
if (openSelector == kRunOpenWithWindow) {
BMessage* refList = NULL;
if (originalRefsList == NULL) {
ASSERT(model != NULL);
refList = new BMessage;
refList->AddRef("refs", model->EntryRef());
delete model;
model = NULL;
} else {
refList = new BMessage(*originalRefsList);
}
window = new OpenWithContainerWindow(refList, &fWindowList);
} else if (model->IsQuery()) {
window = new BQueryContainerWindow(&fWindowList, openFlags);
} else if (model->IsVirtualDirectory()) {
window = new VirtualDirectoryWindow(&fWindowList, openFlags);
} else {
window = new BContainerWindow(&fWindowList, openFlags);
}
if (model != NULL && window != NULL && window->LockLooper()) {
window->CreatePoseView(model);
if (window->PoseView() == NULL) {
window->PostMessage(B_QUIT_REQUESTED);
window->UnlockLooper();
return;
}
window->UnlockLooper();
}
BMessage restoreStateMessage(kRestoreState);
if (stateMessage != NULL)
restoreStateMessage.AddMessage("state", stateMessage);
window->PostMessage(&restoreStateMessage);
}
void
TTracker::EditQueries(const BMessage* message)
{
bool editOnlyIfTemplate;
if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK)
editOnlyIfTemplate = false;
type_code type;
int32 count;
message->GetInfo("refs", &type, &count);
for (int32 index = 0; index < count; index++) {
entry_ref ref;
message->FindRef("refs", index, &ref);
BEntry entry(&ref, true);
if (entry.InitCheck() == B_OK && entry.Exists())
(new FindWindow(&ref, editOnlyIfTemplate))->Show();
}
}
void
TTracker::OpenInfoWindows(BMessage* message)
{
type_code type;
int32 count;
message->GetInfo("refs", &type, &count);
for (int32 index = 0; index < count; index++) {
entry_ref ref;
message->FindRef("refs", index, &ref);
BEntry entry;
if (entry.SetTo(&ref) == B_OK) {
Model* model = new Model(&entry);
if (model->InitCheck() != B_OK) {
delete model;
continue;
}
AutoLock<WindowList> lock(&fWindowList);
BInfoWindow* wind = FindInfoWindow(model->NodeRef());
if (wind) {
wind->Activate();
delete model;
} else {
model->SniffMimeIfNeeded();
wind = new BInfoWindow(model, index, &fWindowList);
wind->PostMessage(kRestoreState);
}
}
}
}
BDeskWindow*
TTracker::GetDeskWindow() const
{
int32 count = fWindowList.CountItems();
for (int32 index = 0; index < count; index++) {
BDeskWindow* window = dynamic_cast<BDeskWindow*>(
fWindowList.ItemAt(index));
if (window != NULL)
return window;
}
TRESPASS();
return NULL;
}
void
TTracker::PostMessageToAllContainerWindows(BMessage& message) const
{
ASSERT(fWindowList.IsLocked());
int32 count = fWindowList.CountItems();
for (int32 index = 0; index < count; index++) {
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
fWindowList.ItemAt(index));
if (window != NULL)
window->PostMessage(&message);
}
}
BContainerWindow*
TTracker::FindContainerWindow(const node_ref* node, int32 number) const
{
ASSERT(fWindowList.IsLocked());
int32 count = fWindowList.CountItems();
int32 windowsFound = 0;
for (int32 index = 0; index < count; index++) {
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
fWindowList.ItemAt(index));
if (window != NULL && window->IsShowing(node)
&& number == windowsFound++) {
return window;
}
}
return NULL;
}
BContainerWindow*
TTracker::FindContainerWindow(const entry_ref* entry, int32 number) const
{
ASSERT(fWindowList.IsLocked());
int32 count = fWindowList.CountItems();
int32 windowsFound = 0;
for (int32 index = 0; index < count; index++) {
BContainerWindow* window = dynamic_cast<BContainerWindow*>
(fWindowList.ItemAt(index));
if (window && window->IsShowing(entry) && number == windowsFound++)
return window;
}
return NULL;
}
bool
TTracker::EntryHasWindowOpen(const entry_ref* entry)
{
AutoLock<WindowList> lock(&fWindowList);
return FindContainerWindow(entry) != NULL;
}
BContainerWindow*
TTracker::FindParentContainerWindow(const entry_ref* ref) const
{
BEntry entry(ref);
BEntry parent;
if (entry.GetParent(&parent) != B_OK)
return NULL;
entry_ref parentRef;
parent.GetRef(&parentRef);
ASSERT(fWindowList.IsLocked());
int32 count = fWindowList.CountItems();
for (int32 index = 0; index < count; index++) {
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
fWindowList.ItemAt(index));
if (window != NULL && window->IsShowing(&parentRef))
return window;
}
return NULL;
}
BInfoWindow*
TTracker::FindInfoWindow(const node_ref* node) const
{
ASSERT(fWindowList.IsLocked());
int32 count = fWindowList.CountItems();
for (int32 index = 0; index < count; index++) {
BInfoWindow* window = dynamic_cast<BInfoWindow*>(
fWindowList.ItemAt(index));
if (window != NULL && window->IsShowing(node))
return window;
}
return NULL;
}
bool
TTracker::QueryActiveForDevice(dev_t device)
{
AutoLock<WindowList> lock(&fWindowList);
int32 count = fWindowList.CountItems();
for (int32 index = 0; index < count; index++) {
BQueryContainerWindow* window = dynamic_cast<BQueryContainerWindow*>(
fWindowList.ItemAt(index));
if (window != NULL) {
AutoLock<BWindow> lock(window);
if (window->ActiveOnDevice(device))
return true;
}
}
return false;
}
void
TTracker::CloseActiveQueryWindows(dev_t device)
{
bool closed = false;
AutoLock<WindowList> lock(fWindowList);
for (int32 index = fWindowList.CountItems(); index >= 0; index--) {
BQueryContainerWindow* window
= dynamic_cast<BQueryContainerWindow*>(fWindowList.ItemAt(index));
if (window != NULL) {
AutoLock<BWindow> lock(window);
if (window->ActiveOnDevice(device)) {
window->PostMessage(B_QUIT_REQUESTED);
closed = true;
}
}
}
lock.Unlock();
if (closed) {
for (int32 timeout = 30; timeout; timeout--) {
if (!QueryActiveForDevice(device))
return;
snooze(100000);
}
}
}
void
TTracker::SaveAllPoseLocations()
{
int32 numWindows = fWindowList.CountItems();
for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) {
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
fWindowList.ItemAt(windowIndex));
if (window != NULL) {
AutoLock<BWindow> lock(window);
BDeskWindow* deskWindow = dynamic_cast<BDeskWindow*>(window);
if (deskWindow != NULL)
deskWindow->SaveDesktopPoseLocations();
else
window->PoseView()->SavePoseLocations();
}
}
}
void
TTracker::CloseWindowAndChildren(const node_ref* node)
{
BDirectory dir(node);
if (dir.InitCheck() != B_OK)
return;
AutoLock<WindowList> lock(&fWindowList);
BObjectList<BContainerWindow> closeList;
for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
fWindowList.ItemAt(index));
if (window && window->TargetModel()) {
BEntry wind_entry;
wind_entry.SetTo(window->TargetModel()->EntryRef());
if ((*window->TargetModel()->NodeRef() == *node)
|| dir.Contains(&wind_entry)) {
fWindowList.RemoveItemAt(index);
closeList.AddItem(window);
}
}
}
int32 numItems = closeList.CountItems();
for (int32 index = 0; index < numItems; index++) {
BContainerWindow* window = closeList.ItemAt(index);
window->PostMessage(B_QUIT_REQUESTED);
}
}
void
TTracker::CloseAllInWorkspace()
{
AutoLock<WindowList> lock(&fWindowList);
int32 currentWorkspace = 1 << current_workspace();
for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
BWindow* window = fWindowList.ItemAt(index);
if (window != NULL && (window->Workspaces() & currentWorkspace) != 0) {
if (dynamic_cast<BDeskWindow*>(window) == NULL
&& dynamic_cast<BStatusWindow*>(window) == NULL) {
window->PostMessage(B_QUIT_REQUESTED);
}
}
}
}
void
TTracker::CloseAllWindows()
{
AutoLock<WindowList> lock(&fWindowList);
int32 count = CountWindows();
for (int32 index = 0; index < count; index++) {
BWindow* window = WindowAt(index);
if (dynamic_cast<BDeskWindow*>(window) == NULL
&& dynamic_cast<BStatusWindow*>(window) == NULL) {
window->PostMessage(B_QUIT_REQUESTED);
}
}
for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
BWindow* window = fWindowList.ItemAt(index);
if (dynamic_cast<BDeskWindow*>(window) == NULL
&& dynamic_cast<BStatusWindow*>(window) == NULL) {
fWindowList.RemoveItemAt(index);
}
}
}
void
TTracker::_OpenPreviouslyOpenedWindows(const char* pathFilter)
{
size_t filterLength = 0;
if (pathFilter != NULL)
filterLength = strlen(pathFilter);
BDirectory deskDir;
attr_info attrInfo;
if (FSGetDeskDir(&deskDir) != B_OK
|| deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) != B_OK) {
return;
}
char* buffer = (char*)malloc((size_t)attrInfo.size);
BMessage message;
if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer,
(size_t)attrInfo.size) != attrInfo.size
|| message.Unflatten(buffer) != B_OK) {
free(buffer);
return;
}
free(buffer);
node_ref nodeRef;
deskDir.GetNodeRef(&nodeRef);
int32 stateMessageCounter = 0;
const char* path;
for (int32 i = 0; message.FindString("paths", i, &path) == B_OK; i++) {
if (strncmp(path, pathFilter, filterLength) != 0)
continue;
BEntry entry(path, true);
if (entry.InitCheck() != B_OK)
continue;
int8 flags = 0;
for (int32 j = 0; message.FindInt8(path, j, &flags) == B_OK; j++) {
Model* model = new Model(&entry);
if (model->InitCheck() == B_OK && model->IsContainer()) {
BMessage state;
bool restoreStateFromMessage = false;
if ((flags & kOpenWindowHasState) != 0
&& message.FindMessage("window state",
stateMessageCounter++, &state) == B_OK) {
restoreStateFromMessage = true;
}
if (restoreStateFromMessage) {
OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
| kRestoreDecor, false, &state);
} else {
OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
| kRestoreDecor);
}
} else
delete model;
}
}
if (pathFilter == NULL && TrackerSettings().ShowDisksIcon()
&& message.HasBool("open_disks_window")) {
BEntry entry("/");
Model* model = new Model(&entry);
if (model->InitCheck() == B_OK)
OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace);
else
delete model;
}
}
void
TTracker::ReadyToRun()
{
gStatusWindow = new BStatusWindow();
InitMimeTypes();
InstallDefaultTemplates();
InstallIndices();
InstallTemporaryBackgroundImages();
fTrashWatcher->Run();
fClipboardRefsWatcher = new BClipboardRefsWatcher();
fClipboardRefsWatcher->Run();
fTaskLoop = new StandAloneTaskLoop(true);
fMimeTypeList = new MimeTypeList();
if (!BootedInSafeMode()) {
DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
_OpenPreviouslyOpenedWindows();
}
}
MimeTypeList*
TTracker::MimeTypes() const
{
return fMimeTypeList;
}
void
TTracker::SelectChildInParentSoon(const entry_ref* parent,
const node_ref* child)
{
fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
(&TTracker::SelectChildInParent, this, parent, child),
100000, 200000, 5000000);
}
void
TTracker::CloseParentWaitingForChildSoon(const entry_ref* child,
const node_ref* parent)
{
fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
(&TTracker::CloseParentWaitingForChild, this, child, parent),
200000, 100000, 5000000);
}
void
TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose)
{
fTaskLoop->RunLater(NewMemberFunctionObject
(&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose),
100000);
}
void
TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose)
{
AutoLock<WindowList> lock(&fWindowList);
BContainerWindow* parentWindow = FindContainerWindow(&parent);
if (parentWindow != NULL) {
AutoLock<BWindow> lock(parentWindow);
parentWindow->PoseView()->SelectPoseAtLocation(pointInPose);
}
}
bool
TTracker::CloseParentWaitingForChild(const entry_ref* child,
const node_ref* parent)
{
AutoLock<WindowList> lock(&fWindowList);
BContainerWindow* parentWindow = FindContainerWindow(parent);
if (parentWindow == NULL) {
return true;
}
BEntry entry(child, true);
entry_ref resolvedChild;
if (entry.GetRef(&resolvedChild) != B_OK)
resolvedChild = *child;
BContainerWindow* window = FindContainerWindow(&resolvedChild);
if (window != NULL) {
AutoLock<BWindow> lock(window);
if (!window->IsHidden())
return CloseParentWindowCommon(parentWindow);
}
return false;
}
void
TTracker::CloseParent(node_ref parent)
{
AutoLock<WindowList> lock(&fWindowList);
if (!lock)
return;
CloseParentWindowCommon(FindContainerWindow(&parent));
}
void
TTracker::ShowSettingsWindow()
{
if (fSettingsWindow == NULL) {
fSettingsWindow = new TrackerSettingsWindow();
fSettingsWindow->Show();
} else {
if (fSettingsWindow->Lock()) {
if (fSettingsWindow->IsHidden())
fSettingsWindow->Show();
else
fSettingsWindow->Activate();
fSettingsWindow->Unlock();
}
}
}
bool
TTracker::CloseParentWindowCommon(BContainerWindow* window)
{
ASSERT(fWindowList.IsLocked());
if (dynamic_cast<BDeskWindow*>(window) != NULL) {
return false;
}
window->PostMessage(B_QUIT_REQUESTED);
return true;
}
bool
TTracker::SelectChildInParent(const entry_ref* parent, const node_ref* child)
{
AutoLock<WindowList> lock(&fWindowList);
BContainerWindow* window = FindContainerWindow(parent);
if (window == NULL) {
return false;
}
AutoLock<BWindow> windowLock(window);
if (windowLock.IsLocked()) {
BPoseView* view = window->PoseView();
int32 index;
BPose* pose = view->FindPose(child, &index);
if (pose != NULL) {
view->SelectPose(pose, index);
return true;
}
}
return false;
}
status_t
TTracker::NeedMoreNodeMonitors()
{
fNodeMonitorCount += kNodeMonitorBumpValue;
PRINT(("bumping nodeMonitorCount to %" B_PRId32 "\n", fNodeMonitorCount));
struct rlimit rl;
rl.rlim_cur = fNodeMonitorCount;
rl.rlim_max = RLIM_SAVED_MAX;
if (setrlimit(RLIMIT_NOVMON, &rl) < 0) {
fNodeMonitorCount -= kNodeMonitorBumpValue;
return errno;
}
return B_OK;
}
status_t
TTracker::WatchNode(const node_ref* node, uint32 flags, BMessenger target)
{
status_t result = watch_node(node, flags, target);
if (result == B_OK || result != B_NO_MEMORY) {
return result;
}
PRINT(("failed to start monitoring, trying to allocate more "
"node monitors\n"));
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker == NULL) {
return result;
}
result = tracker->NeedMoreNodeMonitors();
if (result != B_OK) {
PRINT(("failed to allocate more node monitors, %s\n",
strerror(result)));
return result;
}
return watch_node(node, flags, target);
}
BMessenger
TTracker::MountServer() const
{
return BMessenger(kMountServerSignature);
}
bool
TTracker::TrashFull() const
{
return fTrashWatcher->CheckTrashDirs();
}
bool
TTracker::IsTrashNode(const node_ref* node) const
{
return fTrashWatcher->IsTrashNode(node);
}
bool
TTracker::InTrashNode(const entry_ref* ref) const
{
return FSInTrashDir(ref);
}