haiku/src/kits/tracker/Tracker.cpp
Alexandre Deckner 6dee6b8c17 * Do the locking and lock check in an outer 'if', the window could be left
unlocked if the whole condition wasn't true. Thanks stippi!


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28276 a95241bf-73f2-0310-859d-f6bbb57e9c96
2008-10-22 09:02:17 +00:00

1555 lines
38 KiB
C++

/*
Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <Alert.h>
#include <Autolock.h>
#include <Debug.h>
#include <FindDirectory.h>
#include <fs_attr.h>
#include <fs_info.h>
#include <image.h>
#include <MenuItem.h>
#include <NodeInfo.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <Roster.h>
#include <StopWatch.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include "Attributes.h"
#include "AutoLock.h"
#include "AutoMounter.h"
#include "AutoMounterSettings.h"
#include "BackgroundImage.h"
#include "Bitmaps.h"
#include "Commands.h"
#include "ContainerWindow.h"
#include "DeskWindow.h"
#include "FindPanel.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 "Tracker.h"
#include "TrackerSettings.h"
#include "TrashWatcher.h"
#include "FunctionObject.h"
#include "TrackerSettings.h"
#include "TrackerSettingsWindow.h"
#include "TaskLoop.h"
#include "Thread.h"
#include "Utilities.h"
#include "VolumeWindow.h"
// PPC binary compatibility.
#include "AboutBox.cpp"
// prototypes for some private kernel calls that will some day be public
#ifndef _IMPEXP_ROOT
# define _IMPEXP_ROOT
#endif
extern "C" _IMPEXP_ROOT int _kset_fd_limit_(int num);
extern "C" _IMPEXP_ROOT int _kset_mon_limit_(int num);
// from priv_syscalls.h
const int32 DEFAULT_MON_NUM = 4096;
// copied from fsil.c
const int8 kOpenWindowNoFlags = 0;
const int8 kOpenWindowMinimized = 1;
const int8 kOpenWindowHasState = 2;
const uint32 PSV_MAKE_PRINTER_ACTIVE_QUIETLY = 'pmaq';
// from pr_server.h
namespace BPrivate {
NodePreloader *gPreloader = NULL;
void
InitIconPreloader()
{
static int32 lock = 0;
if (atomic_add(&lock, 1) != 0) {
// Just wait for the icon cache to be instantiated
int32 tries = 20;
while (IconCache::sIconCache == NULL && tries-- > 0)
snooze(10000);
return;
}
if (IconCache::sIconCache != NULL)
return;
// only start the node preloader if its Tracker or the Deskbar itself - don't
// start it for file panels
bool preload = dynamic_cast<TTracker *>(be_app) != NULL;
if (!preload) {
// check for deskbar
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);
}
} // namespace BPrivate
uint32
GetVolumeFlags(Model *model)
{
fs_info info;
if (model->IsVolume()) {
// search for the correct volume
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;
}
static void
HideVarDir()
{
BPath path;
status_t err = find_directory(B_COMMON_VAR_DIRECTORY, &path);
if (err != B_OK){
PRINT(("var err = %s\n", strerror(err)));
return;
}
BDirectory varDirectory(path.Path());
if (varDirectory.InitCheck() == B_OK) {
PoseInfo info;
// make var dir invisible
info.fInvisible = true;
info.fInitedDirectory = -1;
if (varDirectory.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &info, sizeof(info))
== sizeof(info))
varDirectory.RemoveAttr(kAttrPoseInfoForeign);
}
}
// #pragma mark -
TTracker::TTracker()
: BApplication(kTrackerSignature),
fSettingsWindow(NULL)
{
// set the cwd to /boot/home, anything that's launched
// from Tracker will automatically inherit this
BPath homePath;
if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK)
chdir(homePath.Path());
_kset_fd_limit_(512);
// ask for a bunch more file descriptors so that nested copying
// works well
fNodeMonitorCount = DEFAULT_MON_NUM;
#ifdef CHECK_OPEN_MODEL_LEAKS
InitOpenModelDumping();
#endif
InitIconPreloader();
#ifdef LEAK_CHECKING
SetNewLeakChecking(true);
SetMallocLeakChecking(true);
#endif
//This is how often it should update the free space bar on the volume icons
SetPulseRate(1000000);
}
TTracker::~TTracker()
{
}
bool
TTracker::QuitRequested()
{
// don't allow user quitting
if (CurrentMessage() && CurrentMessage()->FindBool("shortcut"))
return false;
gStatusWindow->AttemptToQuit();
// try quitting the copy/move/empty trash threads
BVolume bootVolume;
DEBUG_ONLY(status_t err =) BVolumeRoster().GetBootVolume(&bootVolume);
ASSERT(err == B_OK);
BMessage message;
AutoLock<WindowList> lock(&fWindowList);
// save open windows in a message inside an attribute of the desktop
int32 count = fWindowList.CountItems();
for (int32 i = 0; i < count; i++) {
BContainerWindow *window = dynamic_cast<BContainerWindow *>
(fWindowList.ItemAt(i));
if (window && window->Lock()) {
if (window->TargetModel()
&& !window->PoseView()->IsDesktopWindow()) {
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());
// save state for every window which is
// a) already open on another workspace
// b) on a volume not capable of writing attributes
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);
// This is to prevent its state to be saved to the node when closed.
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();
// write windows to open on disk
BDirectory deskDir;
if (!BootedInSafeMode() && FSGetDeskDir(&deskDir, bootVolume.Device()) == B_OK) {
// if message is empty, delete the corresponding attribute
if (message.CountNames(B_ANY_TYPE)) {
size_t size = (size_t)message.FlattenedSize();
char *buffer = new char[size];
message.Flatten(buffer, (ssize_t)size);
deskDir.WriteAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, size);
delete [] buffer;
} else
deskDir.RemoveAttr(kAttrOpenWindows);
}
for (int32 count = 0; count < 50; count++) {
// wait 5 seconds for the copiing/moving to quit
if (gStatusWindow->AttemptToQuit())
break;
snooze(100000);
}
return _inherited::QuitRequested();
}
void
TTracker::Quit()
{
TrackerSettings().SaveSettings(false);
fAutoMounter->Lock();
fAutoMounter->QuitRequested(); // automounter does some stuff in QuitRequested
fAutoMounter->Quit(); // but we really don't care if it is cooperating or not
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 kOpenPreviouslyOpenWindows:
if (!BootedInSafeMode())
_OpenPreviouslyOpenedWindows();
break;
case kGetInfo:
OpenInfoWindows(message);
break;
case kMoveToTrash:
MoveRefsToTrash(message);
break;
case kCloseWindowAndChildren:
{
const node_ref *itemNode;
int32 bytes;
message->FindData("node_ref", B_RAW_TYPE,
(const void **)&itemNode, &bytes);
CloseWindowAndChildren(itemNode);
break;
}
case kCloseAllWindows:
CloseAllWindows();
break;
case kFindButton:
(new FindWindow())->Show();
break;
case kEditQuery:
EditQueries(message);
break;
case kUnmountVolume:
// When the user attempts to unmount a volume from the mount
// context menu, this is where the message gets received.
// Save pose locations and forward this to the automounter
SaveAllPoseLocations();
fAutoMounter->PostMessage(message);
break;
case kRunAutomounterSettings:
AutomountSettingsDialog::RunAutomountSettings(fAutoMounter);
break;
case kVolumeMounted:
{
// This is sent to us by the AutoMounter whenever it mounts
// a new volume - we use it to restore the previously opened
// windows from that volume in case it has been mounted
// during the AutoMounter's initial rescan
const char* path;
bool initial;
if (message->FindBool("initial rescan", &initial) != B_OK
|| message->FindString("path", &path) != B_OK
|| !initial)
break;
_OpenPreviouslyOpenedWindows(path);
break;
}
case kShowSplash:
run_be_about();
break;
case kAddPrinter:
// show the addprinter window
run_add_printer_panel();
break;
case kMakeActivePrinter:
// get the current selection
SetDefaultPrinter(message);
break;
#ifdef MOUNT_MENU_IN_DESKBAR
case 'gmtv':
{
// Someone (probably the deskbar) has requested a list of
// mountable volumes.
BMessage reply;
AutoMounterLoop()->EachMountableItemAndFloppy(&AddMountableItemToMessage,
&reply);
message->SendReply(&reply);
break;
}
#endif
case kMountVolume:
case kMountAllNow:
AutoMounterLoop()->PostMessage(message);
break;
case kRestoreBackgroundImage:
{
BDeskWindow *desktop = GetDeskWindow();
AutoLock<BWindow> lock(desktop);
desktop->UpdateDesktopBackgroundImages();
}
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;
message->FindMessenger("target", &messenger);
if (messenger.IsValid())
fClipboardRefsWatcher->RemoveFromNotifyList(messenger);
break;
}
case kFSClipboardChanges:
{
fClipboardRefsWatcher->UpdatePoseViews(message);
break;
}
case kShowVolumeSpaceBar:
case kSpaceBarColorChanged: {
gPeriodicUpdatePoses.DoPeriodicUpdate(true);
break;
}
default:
_inherited::MessageReceived(message);
break;
}
}
void
TTracker::Pulse()
{
if (!TrackerSettings().ShowVolumeSpaceBar())
return;
// update the volume icon's free space bars
gPeriodicUpdatePoses.DoPeriodicUpdate(false);
}
void
TTracker::SetDefaultPrinter(const BMessage *message)
{
// get the first item selected
int32 count = 0;
uint32 type = 0;
message->GetInfo("refs", &type, &count);
if (count <= 0)
return;
// will make the first item the default printer, disregards any other files
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
// create a message for the print server
BMessenger messenger("application/x-vnd.Be-PSRV", -1);
if (!messenger.IsValid())
return;
// send the selection to the print server
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> *srcList = new BObjectList<entry_ref>(count, true);
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)
// if we have a window open for this entry, ask the pose to
// delete it, this will select the next entry
window->PoseView()->MoveEntryToTrash(&ref);
else
// add all others to a list that gets deleted separately
srcList->AddItem(new entry_ref(ref));
}
if (srcList->CountItems())
// async move to trash
FSMoveToTrash(srcList);
}
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)
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) {
refsReceived = *messageToBundle;
refsReceived.what = B_REFS_RECEIVED;
}
refsReceived.AddRef("refs", launchThis);
// synchronous launch, we are already in our own thread
if (TrackerLaunch(&refsReceived, false) == B_OK) {
// if launched fine, close parent window in a bit
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();
bool brokenLinkWithSpecificHandler = false;
BString brokenLinkPreferredApp;
if (result != B_OK) {
model = new Model(ref, false);
if (model->IsSymLink() && !model->LinkTo()) {
model->GetPreferredAppForBrokenSymLink(brokenLinkPreferredApp);
if (brokenLinkPreferredApp.Length() && brokenLinkPreferredApp != kTrackerSignature)
brokenLinkWithSpecificHandler = true;
}
if (!brokenLinkWithSpecificHandler) {
delete model;
(new BAlert("", "There was an error resolving the link.",
"Cancel", 0, 0,
B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go();
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) {
// if folder or query has a preferred handler and it's not the
// Tracker, open it by sending refs to the handling app
// if we are responding to the final open of OpenWith, just
// skip this and proceed to opening the container with Tracker
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) {
// special case opening plain folders, queries or using open with
OpenContainerWindow(model, 0, selector, kRestoreDecor); // window adopts model
if (nodeToClose)
CloseParentWaitingForChildSoon(ref, nodeToClose);
} else if (model->IsQueryTemplate()) {
// query template - open new find window
(new FindWindow(model->EntryRef()))->Show();
if (nodeToClose)
CloseParentWaitingForChildSoon(ref, nodeToClose);
} else {
delete model;
// run Launch in a separate thread
// and close parent if successfull
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);
if (brokenLinkWithSpecificHandler)
// This cruft is to support a hacky workaround for double-clicking
// broken refs for cifs; should get fixed in R5
LaunchBrokenLink(brokenLinkPreferredApp.String(), &refsReceived);
else
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(0, message, selector);
// window adopts model
break;
case kOpenWith:
{
// Open With resulted in passing refs and a handler, open the files
// with the handling app
message->RemoveName("handler");
// have to find out if handling app is the Tracker
// if it is, just pass it to the active Tracker, no matter which Tracker
// was chosen to handle the refs
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) {
// handling app not Tracker, pass entries to the apps RefsReceived
TrackerLaunch(&handlingApp, message, true);
break;
}
// fall thru, opening refs by the Tracker, as if they were double clicked
}
case kOpen:
{
// copy over "Poses" messenger so that refs received recipients know
// where the open came from
BMessage *bundleThis = NULL;
BMessenger messenger;
if (message->FindMessenger("TrackerViewToken", &messenger) == B_OK) {
bundleThis = new BMessage();
bundleThis->AddMessenger("TrackerViewToken", messenger);
}
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);
}
delete bundleThis;
break;
}
}
}
void
TTracker::ArgvReceived(int32 argc, char **argv)
{
BMessage *message = CurrentMessage();
const char *currentWorkingDirectoryPath = NULL;
entry_ref ref;
if (message->FindString("cwd", &currentWorkingDirectoryPath) == 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;
if (checkAlreadyOpen && openSelector != kRunOpenWithWindow)
// find out if window already open
window = FindContainerWindow(model->NodeRef());
bool someWindowActivated = false;
uint32 workspace = (uint32)(1 << current_workspace());
int32 windowCount = 0;
while (window) {
// At least one window open, just pull to front
// make sure we don't jerk workspaces around
uint32 windowWorkspaces = window->Workspaces();
if (windowWorkspaces & workspace) {
window->Activate();
someWindowActivated = true;
}
window = FindContainerWindow(model->NodeRef(), ++windowCount);
}
if (someWindowActivated) {
delete model;
return;
} // If no window was actiated, (none in the current workspace
// we open a new one.
if (openSelector == kRunOpenWithWindow) {
BMessage *refList = NULL;
if (!originalRefsList) {
// when passing just a single model, stuff it's entry in a single
// element list anyway
ASSERT(model);
refList = new BMessage;
refList->AddRef("refs", model->EntryRef());
delete model;
model = NULL;
} else
// clone the message, window adopts it for it's own use
refList = new BMessage(*originalRefsList);
window = new OpenWithContainerWindow(refList, &fWindowList);
} else if (model->IsRoot()) {
// window will adopt the model
window = new BVolumeWindow(&fWindowList, openFlags);
} else if (model->IsQuery()) {
// window will adopt the model
window = new BQueryContainerWindow(&fWindowList, openFlags);
} else
// window will adopt the model
window = new BContainerWindow(&fWindowList, openFlags);
if (model)
window->CreatePoseView(model);
BMessage restoreStateMessage(kRestoreState);
if (stateMessage)
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 {
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)
return window;
}
TRESPASS();
return NULL;
}
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 && 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 && 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 && 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) {
AutoLock<BWindow> lock(window);
if (window->ActiveOnDevice(device))
return true;
}
}
return false;
}
void
TTracker::CloseActiveQueryWindows(dev_t device)
{
// used when trying to unmount a volume - an active query would prevent that from
// happening
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) {
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--) {
// wait a bit for windows to fully close
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) {
AutoLock<BWindow> lock(window);
BDeskWindow *deskWindow = dynamic_cast<BDeskWindow *>(window);
if (deskWindow)
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;
// make a list of all windows to be closed
// count from end to beginning so we can remove items safely
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)) {
// ToDo:
// get rid of the Remove here, BContainerWindow::Quit does it
fWindowList.RemoveItemAt(index);
closeList.AddItem(window);
}
}
}
// now really close the windows
int32 numItems = closeList.CountItems();
for (int32 index = 0; index < numItems; index++) {
BContainerWindow *window = closeList.ItemAt(index);
window->PostMessage(B_QUIT_REQUESTED);
}
}
void
TTracker::CloseAllWindows()
{
// this is a response to the DeskBar sending us a B_QUIT, when it really
// means to say close all your windows. It might be better to have it
// send a kCloseAllWindows message and have windowless apps stay running,
// which is what we will do for the Tracker
AutoLock<WindowList> lock(&fWindowList);
int32 count = CountWindows();
for (int32 index = 0; index < count; index++) {
BWindow *window = WindowAt(index);
// avoid the desktop
if (!dynamic_cast<BDeskWindow *>(window)
&& !dynamic_cast<BStatusWindow *>(window))
window->PostMessage(B_QUIT_REQUESTED);
}
// count from end to beginning so we can remove items safely
for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
BWindow *window = fWindowList.ItemAt(index);
if (!dynamic_cast<BDeskWindow *>(window)
&& !dynamic_cast<BStatusWindow *>(window))
// ToDo:
// get rid of the Remove here, BContainerWindow::Quit does it
fWindowList.RemoveItemAt(index);
}
}
void
TTracker::_OpenPreviouslyOpenedWindows(const char* pathFilter)
{
size_t filterLength = 0;
if (pathFilter != NULL)
filterLength = strlen(pathFilter);
BVolume bootVolume;
BVolumeRoster().GetBootVolume(&bootVolume);
BDirectory deskDir;
attr_info attrInfo;
if (FSGetDeskDir(&deskDir, bootVolume.Device()) != 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))
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;
}
}
// Open disks window if needed
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();
HideVarDir();
fTrashWatcher = new BTrashWatcher();
fTrashWatcher->Run();
fClipboardRefsWatcher = new BClipboardRefsWatcher();
fClipboardRefsWatcher->Run();
fAutoMounter = new AutoMounter();
fAutoMounter->Run();
fTaskLoop = new StandAloneTaskLoop(true);
// open desktop window
BContainerWindow *deskWindow = NULL;
BVolume bootVolume;
BVolumeRoster().GetBootVolume(&bootVolume);
BDirectory deskDir;
if (FSGetDeskDir(&deskDir, bootVolume.Device()) == B_OK) {
// create desktop
BEntry entry;
deskDir.GetEntry(&entry);
Model *model = new Model(&entry);
if (model->InitCheck() == B_OK) {
AutoLock<WindowList> lock(&fWindowList);
deskWindow = new BDeskWindow(&fWindowList);
AutoLock<BWindow> windowLock(deskWindow);
deskWindow->CreatePoseView(model);
deskWindow->Init();
if (TrackerSettings().ShowDisksIcon()) {
// create model for root of everything
BEntry entry("/");
Model model(&entry);
if (model.InitCheck() == B_OK) {
// add the root icon to desktop window
BMessage message;
message.what = B_NODE_MONITOR;
message.AddInt32("opcode", B_ENTRY_CREATED);
message.AddInt32("device", model.NodeRef()->device);
message.AddInt64("node", model.NodeRef()->node);
message.AddInt64("directory", model.EntryRef()->directory);
message.AddString("name", model.EntryRef()->name);
deskWindow->PostMessage(&message, deskWindow->PoseView());
}
}
} else
delete model;
}
// kick off building the mime type list for find panels, etc.
fMimeTypeList = new MimeTypeList();
if (!BootedInSafeMode()) {
// kick of transient query killer
DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
}
}
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) {
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)
// parent window already closed, give up
return true;
// If child is a symbolic link, dereference it, so that
// FindContainerWindow will succeed.
BEntry entry(child, true);
entry_ref resolvedChild;
if (entry.GetRef(&resolvedChild) != B_OK)
resolvedChild = *child;
BContainerWindow *window = FindContainerWindow(&resolvedChild);
if (window) {
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) {
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))
// don't close the destop
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)
// parent window already closed, give up
return false;
AutoLock<BWindow> windowLock(window);
if (windowLock.IsLocked()) {
BPoseView *view = window->PoseView();
int32 index;
BPose *pose = view->FindPose(child, &index);
if (pose) {
view->SelectPose(pose, index);
return true;
}
}
return false;
}
const int32 kNodeMonitorBumpValue = 512;
status_t
TTracker::NeedMoreNodeMonitors()
{
fNodeMonitorCount += kNodeMonitorBumpValue;
PRINT(("bumping nodeMonitorCount to %d\n", fNodeMonitorCount));
return _kset_mon_limit_(fNodeMonitorCount);
}
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) {
// need to make sure this uses the same error value as
// the node monitor code
return result;
}
PRINT(("failed to start monitoring, trying to allocate more "
"node monitors\n"));
TTracker *tracker = dynamic_cast<TTracker *>(be_app);
if (!tracker) {
// we are the file panel only, just fail
return result;
}
result = tracker->NeedMoreNodeMonitors();
if (result != B_OK) {
PRINT(("failed to allocate more node monitors, %s\n",
strerror(result)));
return result;
}
// try again, this time with more node monitors
return watch_node(node, flags, target);
}
AutoMounter *
TTracker::AutoMounterLoop()
{
return fAutoMounter;
}
bool
TTracker::InTrashNode(const entry_ref *node) const
{
return FSInTrashDir(node);
}
bool
TTracker::TrashFull() const
{
return fTrashWatcher->CheckTrashDirs();
}
bool
TTracker::IsTrashNode(const node_ref *node) const
{
return fTrashWatcher->IsTrashNode(node);
}