#include "DeskWindow.h"
#include <AppFileInfo.h>
#include <Catalog.h>
#include <Debug.h>
#include <FindDirectory.h>
#include <Locale.h>
#include <Messenger.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <PathFinder.h>
#include <PathMonitor.h>
#include <PopUpMenu.h>
#include <Resources.h>
#include <Screen.h>
#include <String.h>
#include <StringList.h>
#include <Volume.h>
#include <WindowPrivate.h>
#include <fcntl.h>
#include <unistd.h>
#include "Attributes.h"
#include "AutoLock.h"
#include "BackgroundImage.h"
#include "Commands.h"
#include "FSUtils.h"
#include "IconMenuItem.h"
#include "KeyInfos.h"
#include "MountMenu.h"
#include "PoseView.h"
#include "Shortcuts.h"
#include "TemplatesMenu.h"
#include "Tracker.h"
const char* kShelfPath = "tracker_shelf";
const char* kShortcutsSettings = "shortcuts_settings";
const char* kDefaultShortcut = "BEOS:default_shortcut";
const uint32 kDefaultModifiers = B_OPTION_KEY | B_COMMAND_KEY;
static struct AddOnInfo*
MatchOne(struct AddOnInfo* item, void* castToName)
{
if (strcmp(item->model->Name(), (const char*)castToName) == 0) {
return item;
}
return 0;
}
static void
AddOneShortcut(Model* model, char key, uint32 modifiers, BDeskWindow* window)
{
if (key == '\0')
return;
BMessage* runAddOn = new BMessage(kLoadAddOn);
runAddOn->AddRef("refs", model->EntryRef());
window->AddShortcut(key, modifiers, runAddOn);
}
static struct AddOnInfo*
RevertToDefault(struct AddOnInfo* item, void* castToWindow)
{
if (item->key != item->defaultKey || item->modifiers != kDefaultModifiers) {
BDeskWindow* window = static_cast<BDeskWindow*>(castToWindow);
if (window != NULL) {
window->RemoveShortcut(item->key, item->modifiers);
item->key = item->defaultKey;
item->modifiers = kDefaultModifiers;
AddOneShortcut(item->model, item->key, item->modifiers, window);
}
}
return 0;
}
static struct AddOnInfo*
FindElement(struct AddOnInfo* item, void* castToOther)
{
Model* other = static_cast<Model*>(castToOther);
if (*item->model->EntryRef() == *other->EntryRef())
return item;
return 0;
}
static void
LoadAddOnDir(BDirectory directory, BDeskWindow* window,
LockingList<AddOnInfo, true>* list)
{
BEntry entry;
while (directory.GetNextEntry(&entry) == B_OK) {
Model* model = new Model(&entry);
if (model->InitCheck() == B_OK && model->IsSymLink()) {
Model* resolved = new Model(model->EntryRef(), true, true);
if (resolved->InitCheck() == B_OK)
model->SetLinkTo(resolved);
else
delete resolved;
}
if (model->InitCheck() != B_OK
|| !model->ResolveIfLink()->IsExecutable()) {
delete model;
continue;
}
char* name = strdup(model->Name());
if (!list->EachElement(MatchOne, name)) {
struct AddOnInfo* item = new struct AddOnInfo;
item->model = model;
item->has_populate_menu = B_NO_INIT;
BResources resources(model->ResolveIfLink()->EntryRef());
size_t size;
char* shortcut = (char*)resources.LoadResource(B_STRING_TYPE,
kDefaultShortcut, &size);
if (shortcut == NULL || strlen(shortcut) > 1)
item->key = '\0';
else
item->key = shortcut[0];
AddOneShortcut(model, item->key, kDefaultModifiers, window);
item->defaultKey = item->key;
item->modifiers = kDefaultModifiers;
list->AddItem(item);
BFile file(item->model->EntryRef(), B_READ_ONLY);
if (file.InitCheck() == B_OK) {
BAppFileInfo info(&file);
if (info.InitCheck() == B_OK) {
BMessage types;
if (info.GetSupportedTypes(&types) == B_OK) {
int32 i = 0;
BString supportedType;
while (types.FindString("types", i, &supportedType) == B_OK) {
item->supportedTypes.Add(supportedType);
i++;
}
}
}
}
}
free(name);
}
node_ref nodeRef;
directory.GetNodeRef(&nodeRef);
TTracker::WatchNode(&nodeRef, B_WATCH_DIRECTORY, window);
}
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "DeskWindow"
BDeskWindow::BDeskWindow(LockingList<BWindow>* windowList, uint32 openFlags)
:
BContainerWindow(windowList, openFlags, kDesktopWindowLook, kDesktopWindowFeel,
B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK | B_NOT_ZOOMABLE | B_NOT_CLOSABLE
| B_NOT_MINIMIZABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS,
B_ALL_WORKSPACES, false),
fDeskShelf(NULL),
fShortcutsSettings(NULL)
{
BDirectory deskDir;
if (FSGetDeskDir(&deskDir) == B_OK) {
BEntry entry;
deskDir.GetEntry(&entry);
Model* model = new Model(&entry, true);
if (model->InitCheck() == B_OK)
CreatePoseView(model);
else
delete model;
}
}
BDeskWindow::~BDeskWindow()
{
SaveDesktopPoseLocations();
PoseView()->DisableSaveLocation();
PoseView()->StopSettingsWatch();
stop_watching(this);
}
void
BDeskWindow::Init(const BMessage*)
{
BScreen screen(this);
fOldFrame = screen.Frame();
ResizeTo(fOldFrame.Width(), fOldFrame.Height());
InitKeyIndices();
InitAddOnsList(false);
ApplyShortcutPreferences(false);
_inherited::Init();
entry_ref ref;
BPath path;
if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) {
path.Append(kShelfPath);
close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR
| S_IRGRP | S_IROTH));
if (get_ref_for_path(path.Path(), &ref) == B_OK)
fDeskShelf = new BShelf(&ref, PoseView());
if (fDeskShelf != NULL)
fDeskShelf->SetDisplaysZombies(true);
}
BMessage* message = new BMessage(kIconMode);
AddShortcut('1', B_COMMAND_KEY, message, PoseView());
message = new BMessage(kMiniIconMode);
AddShortcut('2', B_COMMAND_KEY, message, PoseView());
message = new BMessage(kIconMode);
message->AddInt32("scale", 1);
AddShortcut('+', B_COMMAND_KEY, message, PoseView());
message = new BMessage(kIconMode);
message->AddInt32("scale", 0);
AddShortcut('-', B_COMMAND_KEY, message, PoseView());
if (TrackerSettings().ShowDisksIcon()) {
BEntry entry("/");
Model model(&entry);
if (model.InitCheck() == B_OK) {
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);
PostMessage(&message, PoseView());
}
}
}
void
BDeskWindow::InitAddOnsList(bool update)
{
AutoLock<LockingList<AddOnInfo, true> > lock(fAddOnsList);
if (!lock.IsLocked())
return;
if (update) {
for (int i = fAddOnsList->CountItems() - 1; i >= 0; i--) {
AddOnInfo* item = fAddOnsList->ItemAt(i);
RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY);
}
fAddOnsList->MakeEmpty(true);
}
BStringList addOnPaths;
BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker", addOnPaths);
int32 count = addOnPaths.CountStrings();
for (int32 i = 0; i < count; i++)
LoadAddOnDir(BDirectory(addOnPaths.StringAt(i)), this, fAddOnsList);
}
void
BDeskWindow::ApplyShortcutPreferences(bool update)
{
AutoLock<LockingList<AddOnInfo, true> > lock(fAddOnsList);
if (!lock.IsLocked())
return;
if (!update) {
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
path.Append(kShortcutsSettings);
BPathMonitor::StartWatching(path.Path(), B_WATCH_STAT | B_WATCH_FILES_ONLY, this);
fShortcutsSettings = new char[strlen(path.Path()) + 1];
strcpy(fShortcutsSettings, path.Path());
}
}
fAddOnsList->EachElement(RevertToDefault, this);
BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY);
BMessage fileMsg;
if (shortcutSettings.InitCheck() != B_OK || fileMsg.Unflatten(&shortcutSettings) != B_OK)
return;
int32 i = 0;
BMessage message;
while (fileMsg.FindMessage("spec", i++, &message) == B_OK) {
int32 key;
if (message.FindInt32("key", &key) != B_OK)
continue;
BString command;
if (message.FindString("command", &command) != B_OK)
continue;
bool isInAddOns = false;
BStringList addOnPaths;
BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker/", addOnPaths);
for (int32 i = 0; i < addOnPaths.CountStrings(); i++) {
if (command.StartsWith(addOnPaths.StringAt(i))) {
isInAddOns = true;
break;
}
}
if (!isInAddOns)
continue;
BEntry entry(command);
if (entry.InitCheck() != B_OK)
continue;
const char* shortcut = GetKeyName(key);
if (strlen(shortcut) != 1)
continue;
uint32 modifiers = B_COMMAND_KEY;
int32 value;
if (message.FindInt32("mcidx", 0, &value) == B_OK)
modifiers |= (value != 0 ? B_SHIFT_KEY : 0);
if (message.FindInt32("mcidx", 1, &value) == B_OK)
modifiers |= (value != 0 ? B_CONTROL_KEY : 0);
if (message.FindInt32("mcidx", 3, &value) == B_OK)
modifiers |= (value != 0 ? B_OPTION_KEY : 0);
Model model(&entry);
AddOnInfo* item = fAddOnsList->EachElement(FindElement, &model);
if (item != NULL) {
if (item->key != '\0')
RemoveShortcut(item->key, item->modifiers);
item->key = shortcut[0];
item->modifiers = modifiers;
AddOneShortcut(&model, item->key, item->modifiers, this);
}
}
}
void
BDeskWindow::Quit()
{
if (fNavigationItem != NULL) {
BMenu* menu = fNavigationItem->Menu();
if (menu != NULL)
menu->RemoveItem(fNavigationItem);
delete fNavigationItem;
fNavigationItem = NULL;
}
fAddOnsList->MakeEmpty(true);
delete fAddOnsList;
delete fDeskShelf;
_inherited::Quit();
}
BPoseView*
BDeskWindow::NewPoseView(Model* model, uint32 viewMode)
{
return new DesktopPoseView(model, viewMode);
}
void
BDeskWindow::CreatePoseView(Model* model)
{
fPoseView = NewPoseView(model, kIconMode);
fPoseView->SetIconMapping(false);
fPoseView->SetEnsurePosesVisible(true);
fPoseView->SetAutoScroll(false);
BScreen screen(this);
rgb_color desktopColor = screen.DesktopColor();
if (desktopColor.alpha != 255) {
desktopColor.alpha = 255;
#if B_BEOS_VERSION > B_BEOS_VERSION_5
screen.SetDesktopColor(desktopColor);
#endif
}
fPoseView->SetViewColor(desktopColor);
fPoseView->SetLowColor(desktopColor);
fPoseView->SetResizingMode(B_FOLLOW_ALL);
fPoseView->ResizeTo(Bounds().Size());
AddChild(fPoseView);
PoseView()->StartSettingsWatch();
}
void
BDeskWindow::WorkspaceActivated(int32 workspace, bool state)
{
if (fBackgroundImage != NULL)
fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state);
}
void
BDeskWindow::SaveDesktopPoseLocations()
{
PoseView()->SavePoseLocations(&fOldFrame);
}
void
BDeskWindow::ScreenChanged(BRect frame, color_space space)
{
bool frameChanged = (frame != fOldFrame);
SaveDesktopPoseLocations();
fOldFrame = frame;
ResizeTo(frame.Width(), frame.Height());
if (fBackgroundImage != NULL)
fBackgroundImage->ScreenChanged(frame, space);
PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0);
}
void
BDeskWindow::Show()
{
if (fBackgroundImage != NULL)
fBackgroundImage->Show(PoseView(), current_workspace());
PoseView()->CheckPoseVisibility();
_inherited::Show();
}
bool
BDeskWindow::ShouldAddScrollBars() const
{
return false;
}
bool
BDeskWindow::ShouldAddMenus() const
{
return false;
}
bool
BDeskWindow::ShouldAddContainerView() const
{
return false;
}
void
BDeskWindow::MessageReceived(BMessage* message)
{
if (message->WasDropped()) {
const rgb_color* color;
ssize_t size;
if (message->FindData("RGBColor", 'RGBC',
(const void**)&color, &size) == B_OK) {
BScreen(this).SetDesktopColor(*color);
PoseView()->SetViewColor(*color);
PoseView()->SetLowColor(*color);
status_t initStatus;
BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1,
&initStatus);
if (initStatus == B_OK)
messenger.SendMessage(message);
return;
}
}
switch (message->what) {
case B_PATH_MONITOR:
{
const char* path = "";
if (message->FindString("watched_path", &path) == B_OK
&& strcmp(path, fShortcutsSettings) == 0) {
ApplyShortcutPreferences(true);
}
break;
}
case B_NODE_MONITOR:
{
PRINT(("will update addon shortcuts\n"));
InitAddOnsList(true);
ApplyShortcutPreferences(true);
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL) {
BMessage message(kRebuildAddOnMenus);
tracker->PostMessageToAllContainerWindows(message);
}
break;
}
default:
_inherited::MessageReceived(message);
break;
}
}