/*
* Copyright 2006 - 2011, Stephan Aßmus <superstippi@gmx.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "MainWindow.h"
#include <stdio.h>
#include <Directory.h>
#include <File.h>
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <GroupLayout.h>
#include <Menu.h>
#include <MenuItem.h>
#include <Messenger.h>
#include <Path.h>
#include <Roster.h>
#include <Screen.h>
#include "support.h"
#include "App.h"
#include "LaunchButton.h"
#include "NamePanel.h"
#include "PadView.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "LaunchBox"
MainWindow::MainWindow(const char* name, BRect frame, bool addDefaultButtons)
:
BWindow(frame, name, B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
| B_WILL_ACCEPT_FIRST_CLICK | B_NO_WORKSPACE_ACTIVATION
| B_AUTO_UPDATE_SIZE_LIMITS | B_SAME_POSITION_IN_ALL_WORKSPACES,
B_ALL_WORKSPACES),
fSettings(new BMessage('sett')),
fPadView(new PadView("pad view")),
fAutoRaise(false),
fShowOnAllWorkspaces(true)
{
bool buttonsAdded = false;
if (load_settings(fSettings, "main_settings", "LaunchBox") >= B_OK)
buttonsAdded = LoadSettings(fSettings);
if (!buttonsAdded) {
if (addDefaultButtons)
_AddDefaultButtons();
else
_AddEmptyButtons();
}
SetLayout(new BGroupLayout(B_HORIZONTAL));
AddChild(fPadView);
}
MainWindow::MainWindow(const char* name, BRect frame, BMessage* settings)
:
BWindow(frame, name,
B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
| B_WILL_ACCEPT_FIRST_CLICK | B_NO_WORKSPACE_ACTIVATION
| B_AUTO_UPDATE_SIZE_LIMITS | B_SAME_POSITION_IN_ALL_WORKSPACES,
B_ALL_WORKSPACES),
fSettings(settings),
fPadView(new PadView("pad view")),
fAutoRaise(false),
fShowOnAllWorkspaces(true)
{
if (!LoadSettings(settings))
_AddEmptyButtons();
SetLayout(new BGroupLayout(B_HORIZONTAL));
AddChild(fPadView);
}
MainWindow::~MainWindow()
{
delete fSettings;
}
bool
MainWindow::QuitRequested()
{
int32 padWindowCount = 0;
for (int32 i = 0; BWindow* window = be_app->WindowAt(i); i++) {
if (dynamic_cast<MainWindow*>(window))
padWindowCount++;
}
bool canClose = true;
if (padWindowCount == 1) {
be_app->PostMessage(B_QUIT_REQUESTED);
canClose = false;
} else {
BAlert* alert = new BAlert(B_TRANSLATE("last chance"),
B_TRANSLATE("Really close this pad?\n"
"(The pad will not be remembered.)"),
B_TRANSLATE("Close"), B_TRANSLATE("Cancel"), NULL);
alert->SetShortcut(1, B_ESCAPE);
if (alert->Go() == 1)
canClose = false;
}
return canClose;
}
void
MainWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_LAUNCH:
{
BView* pointer;
if (message->FindPointer("be:source", (void**)&pointer) < B_OK)
break;
LaunchButton* button = dynamic_cast<LaunchButton*>(pointer);
if (button == NULL)
break;
BString errorMessage;
bool launchedByRef = false;
if (button->Ref()) {
BEntry entry(button->Ref(), true);
if (entry.IsDirectory()) {
// open in Tracker
BMessenger messenger("application/x-vnd.Be-TRAK");
if (messenger.IsValid()) {
BMessage trackerMessage(B_REFS_RECEIVED);
trackerMessage.AddRef("refs", button->Ref());
status_t ret = messenger.SendMessage(&trackerMessage);
if (ret < B_OK) {
errorMessage = B_TRANSLATE("Failed to send "
"'open folder' command to Tracker.\n\nError: ");
errorMessage << strerror(ret);
} else
launchedByRef = true;
} else
errorMessage = ("Failed to open folder - is Tracker "
"running?");
} else {
status_t ret = be_roster->Launch(button->Ref());
if (ret < B_OK && ret != B_ALREADY_RUNNING) {
BString errStr(B_TRANSLATE("Failed to launch '%1'.\n"
"\nError:"));
BPath path(button->Ref());
if (path.InitCheck() >= B_OK)
errStr.ReplaceFirst("%1", path.Path());
else
errStr.ReplaceFirst("%1", button->Ref()->name);
errorMessage << errStr.String() << " ";
errorMessage << strerror(ret);
} else
launchedByRef = true;
}
}
if (!launchedByRef && button->AppSignature()) {
status_t ret = be_roster->Launch(button->AppSignature());
if (ret != B_OK && ret != B_ALREADY_RUNNING) {
BString errStr(B_TRANSLATE("\n\nFailed to launch application "
"with signature '%2'.\n\nError:"));
errStr.ReplaceFirst("%2", button->AppSignature());
errorMessage << errStr.String() << " ";
errorMessage << strerror(ret);
} else {
// clear error message on success (might have been
// filled when trying to launch by ref)
errorMessage = "";
}
} else if (!launchedByRef) {
errorMessage = B_TRANSLATE("Failed to launch 'something', "
"error in Pad data.");
}
if (errorMessage.Length() > 0) {
BAlert* alert = new BAlert("error", errorMessage.String(),
B_TRANSLATE("Bummer"), NULL, NULL, B_WIDTH_FROM_WIDEST);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go(NULL);
}
break;
}
case MSG_ADD_SLOT:
{
LaunchButton* button;
if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
fPadView->AddButton(new LaunchButton("launch button",
NULL, new BMessage(MSG_LAUNCH)), button);
}
break;
}
case MSG_CLEAR_SLOT:
{
LaunchButton* button;
if (message->FindPointer("be:source", (void**)&button) >= B_OK)
button->SetTo((entry_ref*)NULL);
break;
}
case MSG_REMOVE_SLOT:
{
LaunchButton* button;
if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
if (fPadView->RemoveButton(button))
delete button;
}
break;
}
case MSG_SET_DESCRIPTION:
{
LaunchButton* button;
if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
const char* name;
if (message->FindString("name", &name) >= B_OK) {
// message comes from a previous name panel
button->SetDescription(name);
BRect namePanelFrame;
if (message->FindRect("frame", &namePanelFrame) == B_OK) {
((App*)be_app)->SetNamePanelSize(
namePanelFrame.Size());
}
} else {
// message comes from pad view
entry_ref* ref = button->Ref();
if (ref) {
BString helper(B_TRANSLATE("Description for '%3'"));
helper.ReplaceFirst("%3", ref->name);
// Place the name panel besides the pad, but give it
// the user configured size.
BPoint origin = B_ORIGIN;
BSize size = ((App*)be_app)->NamePanelSize();
NamePanel* panel = new NamePanel(helper.String(),
button->Description(), this, this,
new BMessage(*message), size);
panel->Layout(true);
size = panel->Frame().Size();
BScreen screen(this);
BPoint mousePos;
uint32 buttons;
fPadView->GetMouse(&mousePos, &buttons, false);
fPadView->ConvertToScreen(&mousePos);
if (fPadView->Orientation() == B_HORIZONTAL) {
// Place above or below the pad
origin.x = mousePos.x - size.width / 2;
if (screen.Frame().bottom - Frame().bottom
> size.height + 20) {
origin.y = Frame().bottom + 10;
} else {
origin.y = Frame().top - 10 - size.height;
}
} else {
// Place left or right of the pad
origin.y = mousePos.y - size.height / 2;
if (screen.Frame().right - Frame().right
> size.width + 20) {
origin.x = Frame().right + 10;
} else {
origin.x = Frame().left - 10 - size.width;
}
}
panel->MoveTo(origin);
panel->Show();
}
}
}
break;
}
case MSG_ADD_WINDOW:
{
BMessage settings('sett');
SaveSettings(&settings);
message->AddMessage("window", &settings);
be_app->PostMessage(message);
break;
}
case MSG_SHOW_BORDER:
SetLook(B_TITLED_WINDOW_LOOK);
break;
case MSG_HIDE_BORDER:
SetLook(B_BORDERED_WINDOW_LOOK);
break;
case MSG_TOGGLE_AUTORAISE:
ToggleAutoRaise();
break;
case MSG_SHOW_ON_ALL_WORKSPACES:
fShowOnAllWorkspaces = !fShowOnAllWorkspaces;
if (fShowOnAllWorkspaces)
SetWorkspaces(B_ALL_WORKSPACES);
else
SetWorkspaces(1L << current_workspace());
break;
case MSG_OPEN_CONTAINING_FOLDER:
{
LaunchButton* button;
if (message->FindPointer("be:source", (void**)&button) == B_OK && button->Ref() != NULL) {
entry_ref target = *button->Ref();
BEntry openTarget(&target);
BMessage openMsg(B_REFS_RECEIVED);
BMessenger tracker("application/x-vnd.Be-TRAK");
openTarget.GetParent(&openTarget);
openTarget.GetRef(&target);
openMsg.AddRef("refs",&target);
tracker.SendMessage(&openMsg);
}
}
break;
case B_SIMPLE_DATA:
case B_REFS_RECEIVED:
case B_PASTE:
case B_MODIFIERS_CHANGED:
break;
default:
BWindow::MessageReceived(message);
break;
}
}
void
MainWindow::Show()
{
BWindow::Show();
_GetLocation();
}
void
MainWindow::ScreenChanged(BRect frame, color_space format)
{
_AdjustLocation(Frame());
}
void
MainWindow::WorkspaceActivated(int32 workspace, bool active)
{
if (fShowOnAllWorkspaces) {
if (!active)
_GetLocation();
else
_AdjustLocation(Frame());
}
}
void
MainWindow::FrameMoved(BPoint origin)
{
if (IsActive()) {
_GetLocation();
_NotifySettingsChanged();
}
}
void
MainWindow::FrameResized(float width, float height)
{
if (IsActive()) {
_GetLocation();
_NotifySettingsChanged();
}
BWindow::FrameResized(width, height);
}
void
MainWindow::ToggleAutoRaise()
{
fAutoRaise = !fAutoRaise;
if (fAutoRaise)
fPadView->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
else
fPadView->SetEventMask(0);
_NotifySettingsChanged();
}
bool
MainWindow::LoadSettings(const BMessage* message)
{
// restore window positioning
BPoint point;
bool useAdjust = false;
if (message->FindPoint("window position", &point) == B_OK) {
fScreenPosition = point;
useAdjust = true;
}
float borderDist;
if (message->FindFloat("border distance", &borderDist) == B_OK) {
fBorderDist = borderDist;
}
// restore window frame
BRect frame;
if (message->FindRect("window frame", &frame) == B_OK) {
if (useAdjust) {
_AdjustLocation(frame);
} else {
make_sure_frame_is_on_screen(frame, this);
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
}
}
// restore window look
window_look look;
if (message->FindInt32("window look", (int32*)&look) == B_OK)
SetLook(look);
// restore orientation
int32 orientation;
if (message->FindInt32("orientation", &orientation) == B_OK)
fPadView->SetOrientation((enum orientation)orientation);
// restore icon size
int32 iconSize;
if (message->FindInt32("icon size", &iconSize) == B_OK)
fPadView->SetIconSize(iconSize);
// restore ignore double click
bool ignoreDoubleClick;
if (message->FindBool("ignore double click", &ignoreDoubleClick) == B_OK)
fPadView->SetIgnoreDoubleClick(ignoreDoubleClick);
// restore buttons
const char* path;
bool buttonAdded = false;
for (int32 i = 0; message->FindString("path", i, &path) >= B_OK; i++) {
LaunchButton* button = new LaunchButton("launch button",
NULL, new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
BString signature;
if (message->FindString("signature", i, &signature) >= B_OK
&& signature.CountChars() > 0) {
button->SetTo(signature.String(), true);
}
BEntry entry(path, true);
entry_ref ref;
if (entry.Exists() && entry.GetRef(&ref) == B_OK)
button->SetTo(&ref);
const char* text;
if (message->FindString("description", i, &text) >= B_OK)
button->SetDescription(text);
buttonAdded = true;
}
// restore auto raise setting
bool autoRaise;
if (message->FindBool("auto raise", &autoRaise) == B_OK && autoRaise)
ToggleAutoRaise();
// store workspace setting
bool showOnAllWorkspaces;
if (message->FindBool("all workspaces", &showOnAllWorkspaces) == B_OK) {
fShowOnAllWorkspaces = showOnAllWorkspaces;
SetWorkspaces(showOnAllWorkspaces
? B_ALL_WORKSPACES : 1L << current_workspace());
}
if (!fShowOnAllWorkspaces) {
uint32 workspaces;
if (message->FindInt32("workspaces", (int32*)&workspaces) == B_OK)
SetWorkspaces(workspaces);
}
return buttonAdded;
}
void
MainWindow::SaveSettings(BMessage* message)
{
// make sure the positioning info is correct
_GetLocation();
// store window position
if (message->ReplacePoint("window position", fScreenPosition) != B_OK)
message->AddPoint("window position", fScreenPosition);
if (message->ReplaceFloat("border distance", fBorderDist) != B_OK)
message->AddFloat("border distance", fBorderDist);
// store window frame and look
if (message->ReplaceRect("window frame", Frame()) != B_OK)
message->AddRect("window frame", Frame());
if (message->ReplaceInt32("window look", Look()) != B_OK)
message->AddInt32("window look", Look());
// store orientation
if (message->ReplaceInt32("orientation",
(int32)fPadView->Orientation()) != B_OK)
message->AddInt32("orientation", (int32)fPadView->Orientation());
// store icon size
if (message->ReplaceInt32("icon size", fPadView->IconSize()) != B_OK)
message->AddInt32("icon size", fPadView->IconSize());
// store ignore double click
if (message->ReplaceBool("ignore double click",
fPadView->IgnoreDoubleClick()) != B_OK) {
message->AddBool("ignore double click", fPadView->IgnoreDoubleClick());
}
// store buttons
message->RemoveName("path");
message->RemoveName("description");
message->RemoveName("signature");
for (int32 i = 0; LaunchButton* button = fPadView->ButtonAt(i); i++) {
BPath path(button->Ref());
if (path.InitCheck() >= B_OK)
message->AddString("path", path.Path());
else
message->AddString("path", "");
message->AddString("description", button->Description());
if (button->AppSignature())
message->AddString("signature", button->AppSignature());
else
message->AddString("signature", "");
}
// store auto raise setting
if (message->ReplaceBool("auto raise", fAutoRaise) != B_OK)
message->AddBool("auto raise", fAutoRaise);
// store workspace setting
if (message->ReplaceBool("all workspaces", fShowOnAllWorkspaces) != B_OK)
message->AddBool("all workspaces", fShowOnAllWorkspaces);
if (message->ReplaceInt32("workspaces", Workspaces()) != B_OK)
message->AddInt32("workspaces", Workspaces());
}
void
MainWindow::_GetLocation()
{
BRect frame = Frame();
BPoint origin = frame.LeftTop();
BPoint center(origin.x + frame.Width() / 2.0,
origin.y + frame.Height() / 2.0);
BScreen screen(this);
BRect screenFrame = screen.Frame();
fScreenPosition.x = center.x / screenFrame.Width();
fScreenPosition.y = center.y / screenFrame.Height();
if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) {
// nearest to left or right border
if (fScreenPosition.x < 0.5)
fBorderDist = frame.left - screenFrame.left;
else
fBorderDist = screenFrame.right - frame.right;
} else {
// nearest to top or bottom border
if (fScreenPosition.y < 0.5)
fBorderDist = frame.top - screenFrame.top;
else
fBorderDist = screenFrame.bottom - frame.bottom;
}
}
void
MainWindow::_AdjustLocation(BRect frame)
{
BScreen screen(this);
BRect screenFrame = screen.Frame();
BPoint center(fScreenPosition.x * screenFrame.Width(),
fScreenPosition.y * screenFrame.Height());
BPoint frameCenter(frame.left + frame.Width() / 2.0,
frame.top + frame.Height() / 2.0);
frame.OffsetBy(center - frameCenter);
// ignore border dist when distance too large
if (fBorderDist < 10.0) {
// see which border we mean depending on screen position
BPoint offset(0.0, 0.0);
if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) {
// left or right border
if (fScreenPosition.x < 0.5)
offset.x = (screenFrame.left + fBorderDist) - frame.left;
else
offset.x = (screenFrame.right - fBorderDist) - frame.right;
} else {
// top or bottom border
if (fScreenPosition.y < 0.5)
offset.y = (screenFrame.top + fBorderDist) - frame.top;
else
offset.y = (screenFrame.bottom - fBorderDist) - frame.bottom;
}
frame.OffsetBy(offset);
}
make_sure_frame_is_on_screen(frame, this);
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
}
void
MainWindow::_AddDefaultButtons()
{
// Mail
LaunchButton* button = new LaunchButton("launch button", NULL,
new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
button->SetTo("application/x-vnd.Be-MAIL", true);
// StyledEdit
button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
button->SetTo("application/x-vnd.Haiku-StyledEdit", true);
// ShowImage
button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
button->SetTo("application/x-vnd.Haiku-ShowImage", true);
// MediaPlayer
button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
button->SetTo("application/x-vnd.Haiku-MediaPlayer", true);
// DeskCalc
button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
button->SetTo("application/x-vnd.Haiku-DeskCalc", true);
// Terminal
button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
button->SetTo("application/x-vnd.Haiku-Terminal", true);
}
void
MainWindow::_AddEmptyButtons()
{
LaunchButton* button = new LaunchButton("launch button", NULL,
new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
fPadView->AddButton(button);
}
void
MainWindow::_NotifySettingsChanged()
{
be_app->PostMessage(MSG_SETTINGS_CHANGED);
}
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.