Files
haiku-beta6/src/kits/interface/Window.cpp
T

2962 lines
62 KiB
C++
Raw Normal View History

/*
* Copyright 2001-2005, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Adrian Oanca <[email protected]>
* Axel Dörfler, [email protected]
* Stephan Aßmus, <[email protected]>
*/
2003-08-31 17:34:24 +00:00
#include <Application.h>
2005-11-21 16:58:36 +00:00
#include <Autolock.h>
#include <Button.h>
#include <MenuBar.h>
2004-06-27 21:32:52 +00:00
#include <MenuItem.h>
#include <MessageQueue.h>
2003-04-17 20:41:54 +00:00
#include <MessageRunner.h>
2005-11-21 16:58:36 +00:00
#include <PropertyInfo.h>
2003-04-17 20:41:54 +00:00
#include <Roster.h>
2005-11-21 16:58:36 +00:00
#include <Screen.h>
#include <String.h>
#include <Window.h>
2003-08-31 17:34:24 +00:00
#include <AppMisc.h>
2005-11-21 16:58:36 +00:00
#include <ApplicationPrivate.h>
#include <MessagePrivate.h>
2004-01-13 00:20:09 +00:00
#include <PortLink.h>
2003-08-31 17:34:24 +00:00
#include <ServerProtocol.h>
2003-04-17 20:41:54 +00:00
#include <TokenSpace.h>
#include <ctype.h>
2003-08-31 17:34:24 +00:00
#include <stdio.h>
#include <math.h>
//#define DEBUG_WIN
#ifdef DEBUG_WIN
# include <stdio.h>
# define STRACE(x) printf x
#else
# define STRACE(x) ;
#endif
2005-11-23 15:17:58 +00:00
struct BWindow::unpack_cookie {
unpack_cookie();
BMessage* message;
int32 index;
BHandler* focus;
int32 focus_token;
int32 last_view_token;
bool found_focus;
bool tokens_scanned;
};
class BWindow::Shortcut {
public:
Shortcut(uint32 key, uint32 modifiers, BMenuItem* item);
Shortcut(uint32 key, uint32 modifiers, BMessage* message, BHandler* target);
~Shortcut();
bool Matches(uint32 key, uint32 modifiers) const;
BMenuItem* MenuItem() const { return fMenuItem; }
BMessage* Message() const { return fMessage; }
BHandler* Target() const { return fTarget; }
static uint32 AllowedModifiers();
static uint32 PrepareKey(uint32 key);
static uint32 PrepareModifiers(uint32 modifiers);
private:
uint32 fKey;
uint32 fModifiers;
BMenuItem* fMenuItem;
BMessage* fMessage;
BHandler* fTarget;
};
using BPrivate::gDefaultTokens;
static property_info sWindowPropInfo[] = {
{
"Feel", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
},
{
"Flags", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
},
{
"Frame", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER }, NULL, 0, { B_RECT_TYPE }
},
{
"Hidden", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
},
{
"Look", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
},
{
"Title", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER }, NULL, 0, { B_STRING_TYPE }
},
{
"Workspaces", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE}
},
{
"MenuBar", {},
{ B_DIRECT_SPECIFIER }, NULL, 0, {}
},
{
"View", {}, {}, NULL, 0, {}
},
{
"Minimize", { B_GET_PROPERTY, B_SET_PROPERTY },
{ B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
},
{}
};
void
_set_menu_sem_(BWindow *window, sem_id sem)
{
if (window != NULL)
window->fMenuSem = sem;
}
// #pragma mark -
2005-11-23 15:17:58 +00:00
BWindow::unpack_cookie::unpack_cookie()
:
message((BMessage*)~0UL),
// message == NULL is our exit condition
index(0),
focus_token(B_NULL_TOKEN),
last_view_token(B_NULL_TOKEN),
found_focus(false),
tokens_scanned(false)
{
}
// #pragma mark -
BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMenuItem* item)
:
fKey(PrepareKey(key)),
fModifiers(PrepareModifiers(modifiers)),
fMenuItem(item),
fMessage(NULL),
fTarget(NULL)
{
}
BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMessage* message,
BHandler* target)
:
fKey(PrepareKey(key)),
fModifiers(PrepareModifiers(modifiers)),
fMenuItem(NULL),
fMessage(message),
fTarget(target)
{
}
BWindow::Shortcut::~Shortcut()
{
// we own the message, if any
delete fMessage;
}
bool
BWindow::Shortcut::Matches(uint32 key, uint32 modifiers) const
{
return fKey == key && fModifiers == modifiers;
}
/*static*/
uint32
BWindow::Shortcut::AllowedModifiers()
{
return B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY
| B_CONTROL_KEY | B_MENU_KEY;
}
/*static*/
uint32
BWindow::Shortcut::PrepareModifiers(uint32 modifiers)
{
return (modifiers & AllowedModifiers()) | B_COMMAND_KEY;
}
/*static*/
uint32
BWindow::Shortcut::PrepareKey(uint32 key)
{
return tolower(key);
// TODO: support unicode and/or more intelligent key mapping
}
// #pragma mark -
BWindow::BWindow(BRect frame, const char* title, window_type type,
uint32 flags, uint32 workspace)
: BLooper(title)
{
window_look look;
window_feel feel;
_DecomposeType(type, &look, &feel);
_InitData(frame, title, look, feel, flags, workspace);
}
BWindow::BWindow(BRect frame, const char* title, window_look look, window_feel feel,
uint32 flags, uint32 workspace)
: BLooper(title)
{
_InitData(frame, title, look, feel, flags, workspace);
}
BWindow::BWindow(BMessage* data)
: BLooper(data)
{
2003-04-17 20:41:54 +00:00
data->FindRect("_frame", &fFrame);
const char *title;
2003-04-17 20:41:54 +00:00
data->FindString("_title", &title);
window_look look;
data->FindInt32("_wlook", (int32 *)&look);
window_feel feel;
data->FindInt32("_wfeel", (int32 *)&feel);
if (data->FindInt32("_flags", (int32 *)&fFlags) != B_OK)
fFlags = 0;
uint32 workspaces;
data->FindInt32("_wspace", (int32 *)&workspaces);
uint32 type;
if (data->FindInt32("_type", (int32*)&type) == B_OK)
_DecomposeType((window_type)type, &fLook, &fFeel);
2003-04-17 20:41:54 +00:00
// connect to app_server and initialize data
_InitData(fFrame, title, look, feel, fFlags, workspaces);
if (data->FindFloat("_zoom", 0, &fMaxZoomWidth) == B_OK
&& data->FindFloat("_zoom", 1, &fMaxZoomHeight) == B_OK)
SetZoomLimits(fMaxZoomWidth, fMaxZoomHeight);
if (data->FindFloat("_sizel", 0, &fMinWidth) == B_OK
&& data->FindFloat("_sizel", 1, &fMinHeight) == B_OK
&& data->FindFloat("_sizel", 2, &fMaxWidth) == B_OK
&& data->FindFloat("_sizel", 3, &fMaxHeight) == B_OK)
SetSizeLimits(fMinWidth, fMaxWidth,
fMinHeight, fMaxHeight);
if (data->FindInt64("_pulse", &fPulseRate) == B_OK)
SetPulseRate(fPulseRate);
2003-04-17 20:41:54 +00:00
BMessage msg;
int32 i = 0;
2003-04-17 20:41:54 +00:00
while ( data->FindMessage("_views", i++, &msg) == B_OK){
BArchivable *obj = instantiate_object(&msg);
BView *child = dynamic_cast<BView *>(obj);
2003-04-17 20:41:54 +00:00
if (child)
AddChild(child);
2003-04-17 20:41:54 +00:00
}
}
BWindow::BWindow(BRect frame, int32 bitmapToken)
: BLooper("offscreen bitmap")
{
// TODO: Implement for real
_DecomposeType(B_UNTYPED_WINDOW, &fLook, &fFeel);
_InitData(frame, "offscreen", fLook, fFeel, 0, 0, bitmapToken);
}
BWindow::~BWindow()
{
// remove all existing shortcuts
int32 noOfItems = fShortcuts.CountItems();
for (int32 index = noOfItems - 1; index >= 0; index--) {
delete (Shortcut *)fShortcuts.ItemAt(index);
2003-03-12 18:02:37 +00:00
}
2004-01-13 00:20:09 +00:00
// TODO: release other dynamically-allocated objects
// Deleting this semaphore will tell open menus to quit.
if (fMenuSem > 0)
delete_sem(fMenuSem);
// disable pulsing
SetPulseRate(0);
// tell app_server about our demise
fLink->StartMessage(AS_DELETE_WINDOW);
fLink->Flush();
// the sender port belongs to the app_server
delete_port(fLink->ReceiverPort());
delete fLink;
}
BArchivable *
BWindow::Instantiate(BMessage *data)
{
if (!validate_instantiation(data , "BWindow"))
return NULL;
return new BWindow(data);
}
status_t
BWindow::Archive(BMessage* data, bool deep) const
{
status_t retval = BLooper::Archive(data, deep);
2003-04-17 20:41:54 +00:00
if (retval != B_OK)
return retval;
2003-04-17 20:41:54 +00:00
data->AddRect("_frame", fFrame);
data->AddString("_title", fTitle);
data->AddInt32("_wlook", fLook);
data->AddInt32("_wfeel", fFeel);
if (fFlags)
data->AddInt32("_flags", fFlags);
data->AddInt32("_wspace", (uint32)Workspaces());
if (!_ComposeType(fLook, fFeel))
2003-04-17 20:41:54 +00:00
data->AddInt32("_type", (uint32)Type());
if (fMaxZoomWidth != 32768.0 || fMaxZoomHeight != 32768.0) {
2003-04-17 20:41:54 +00:00
data->AddFloat("_zoom", fMaxZoomWidth);
data->AddFloat("_zoom", fMaxZoomHeight);
}
if (fMinWidth != 0.0 || fMinHeight != 0.0
|| fMaxWidth != 32768.0 || fMaxHeight != 32768.0) {
data->AddFloat("_sizel", fMinWidth);
data->AddFloat("_sizel", fMinHeight);
data->AddFloat("_sizel", fMaxWidth);
data->AddFloat("_sizel", fMaxHeight);
}
2003-04-17 20:41:54 +00:00
if (fPulseRate != 500000)
data->AddInt64("_pulse", fPulseRate);
if (deep) {
int32 noOfViews = CountChildren();
for (int32 i = 0; i < noOfViews; i++){
BMessage childArchive;
if (ChildAt(i)->Archive(&childArchive, deep) == B_OK)
data->AddMessage("_views", &childArchive);
2003-04-17 20:41:54 +00:00
}
}
2003-04-17 20:41:54 +00:00
return B_OK;
}
2003-04-17 20:41:54 +00:00
void
BWindow::Quit()
{
if (!IsLocked()) {
const char *name = Name();
2003-04-17 20:41:54 +00:00
if (!name)
name = "no-name";
printf("ERROR - you must Lock a looper before calling Quit(), "
"team=%ld, looper=%s\n", Team(), name);
}
// Try to lock
2003-04-17 20:41:54 +00:00
if (!Lock()){
// We're toast already
2003-04-17 20:41:54 +00:00
return;
}
while (!IsHidden()) {
Hide();
}
// ... also its children
2003-09-08 21:06:34 +00:00
//detachTopView();
2003-04-17 20:41:54 +00:00
if (fFlags & B_QUIT_ON_WINDOW_CLOSE)
be_app->PostMessage(B_QUIT_REQUESTED);
2003-04-17 20:41:54 +00:00
BLooper::Quit();
}
void
BWindow::AddChild(BView *child, BView *before)
{
fTopView->AddChild(child, before);
}
bool
BWindow::RemoveChild(BView *child)
{
return fTopView->RemoveChild(child);
}
int32
BWindow::CountChildren() const
{
return fTopView->CountChildren();
}
BView *
BWindow::ChildAt(int32 index) const
{
return fTopView->ChildAt(index);
}
void
BWindow::Minimize(bool minimize)
{
if (IsModal())
return;
if (IsFloating())
return;
if (fMinimized == minimize)
return;
fMinimized = minimize;
Lock();
fLink->StartMessage(AS_WINDOW_MINIMIZE);
fLink->Attach<bool>(minimize);
fLink->Flush();
Unlock();
}
status_t
BWindow::SendBehind(const BWindow *window)
{
2003-04-17 20:41:54 +00:00
if (!window)
return B_ERROR;
Lock();
fLink->StartMessage(AS_SEND_BEHIND);
fLink->Attach<int32>(_get_object_token_(window));
2005-10-30 11:23:37 +00:00
fLink->Attach<team_id>(Team());
2005-11-28 15:47:41 +00:00
status_t status = B_ERROR;
fLink->FlushWithReply(status);
Unlock();
2005-11-28 15:47:41 +00:00
return status;
}
void
BWindow::Flush() const
{
const_cast<BWindow *>(this)->Lock();
fLink->Flush();
const_cast<BWindow *>(this)->Unlock();
}
2003-08-31 17:34:24 +00:00
void
BWindow::Sync() const
{
const_cast<BWindow*>(this)->Lock();
fLink->StartMessage(AS_SYNC);
// ToDo: why with reply?
int32 code;
fLink->FlushWithReply(code);
const_cast<BWindow*>(this)->Unlock();
}
void
BWindow::DisableUpdates()
{
Lock();
fLink->StartMessage(AS_DISABLE_UPDATES);
fLink->Flush();
Unlock();
}
void
BWindow::EnableUpdates()
{
Lock();
fLink->StartMessage(AS_ENABLE_UPDATES);
fLink->Flush();
Unlock();
}
void
BWindow::BeginViewTransaction()
{
if (!fInTransaction) {
2003-08-31 17:34:24 +00:00
Lock();
fLink->StartMessage(AS_BEGIN_TRANSACTION);
2003-08-31 17:34:24 +00:00
Unlock();
fInTransaction = true;
}
}
void
BWindow::EndViewTransaction()
{
if (fInTransaction) {
2003-08-31 17:34:24 +00:00
Lock();
fLink->StartMessage(AS_END_TRANSACTION);
fLink->Flush();
2003-08-31 17:34:24 +00:00
Unlock();
fInTransaction = false;
}
}
bool
BWindow::IsFront() const
{
if (IsActive())
return true;
if (IsModal())
return true;
return false;
}
void
BWindow::MessageReceived(BMessage *msg)
{
if (!msg->HasSpecifiers())
return BLooper::MessageReceived(msg);
2003-04-17 20:41:54 +00:00
BMessage replyMsg(B_REPLY);
bool handled = false;
switch (msg->what) {
case B_GET_PROPERTY:
case B_SET_PROPERTY: {
BMessage specifier;
int32 what;
const char *prop;
int32 index;
if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
break;
if (!strcmp(prop, "Feel")) {
if (msg->what == B_GET_PROPERTY) {
replyMsg.AddInt32("result", (uint32)Feel());
handled = true;
} else {
uint32 newFeel;
if (msg->FindInt32("data", (int32 *)&newFeel) == B_OK) {
SetFeel((window_feel)newFeel);
handled = true;
}
}
} else if (!strcmp(prop, "Flags")) {
if (msg->what == B_GET_PROPERTY) {
replyMsg.AddInt32("result", Flags());
handled = true;
} else {
uint32 newFlags;
if (msg->FindInt32("data", (int32 *)&newFlags) == B_OK) {
SetFlags(newFlags);
handled = true;
}
}
} else if (!strcmp(prop, "Frame")) {
if (msg->what == B_GET_PROPERTY) {
replyMsg.AddRect("result", Frame());
handled = true;
} else {
BRect newFrame;
if (msg->FindRect("data", &newFrame) == B_OK) {
MoveTo(newFrame.LeftTop());
ResizeTo(newFrame.Width(), newFrame.Height());
handled = true;
}
}
} else if (!strcmp(prop, "Hidden")) {
if (msg->what == B_GET_PROPERTY) {
replyMsg.AddBool("result", IsHidden());
handled = true;
} else {
bool hide;
if (msg->FindBool("data", &hide) == B_OK) {
if (hide) {
if (!IsHidden())
Hide();
} else if (IsHidden())
Show();
handled = true;
}
}
} else if (!strcmp(prop, "Look")) {
if (msg->what == B_GET_PROPERTY) {
replyMsg.AddInt32("result", (uint32)Look());
handled = true;
} else {
uint32 newLook;
if (msg->FindInt32("data", (int32 *)&newLook) == B_OK) {
SetLook((window_look)newLook);
handled = true;
}
}
} else if (!strcmp(prop, "Title")) {
if (msg->what == B_GET_PROPERTY) {
replyMsg.AddString("result", Title());
handled = true;
} else {
const char *newTitle = NULL;
if (msg->FindString("data", &newTitle) == B_OK) {
SetTitle(newTitle);
handled = true;
}
}
} else if (!strcmp(prop, "Workspaces")) {
if (msg->what == B_GET_PROPERTY) {
replyMsg.AddInt32( "result", Workspaces());
handled = true;
} else {
uint32 newWorkspaces;
if (msg->FindInt32("data", (int32 *)&newWorkspaces) == B_OK) {
SetWorkspaces(newWorkspaces);
handled = true;
}
}
} else if (!strcmp(prop, "Minimize")) {
if (msg->what == B_GET_PROPERTY) {
replyMsg.AddBool("result", IsMinimized());
handled = true;
} else {
bool minimize;
if (msg->FindBool("data", &minimize) == B_OK) {
Minimize(minimize);
handled = true;
}
}
}
break;
}
}
2003-04-17 20:41:54 +00:00
if (handled) {
if (msg->what == B_SET_PROPERTY)
replyMsg.AddInt32("error", B_OK);
} else {
replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
replyMsg.AddString("message", "Didn't understand the specifier(s)");
}
msg->SendReply(&replyMsg);
}
void
BWindow::DispatchMessage(BMessage *msg, BHandler *target)
{
if (!msg)
2003-04-17 20:41:54 +00:00
return;
switch (msg->what) {
case B_ZOOM:
Zoom();
break;
case B_MINIMIZE:
{
bool minimize;
if (msg->FindBool("minimize", &minimize) == B_OK)
Minimize(minimize);
break;
}
case B_WINDOW_RESIZED:
{
int32 width, height;
if (msg->FindInt32("width", &width) == B_OK
&& msg->FindInt32("height", &height) == B_OK) {
fFrame.right = fFrame.left + width;
fFrame.bottom = fFrame.top + height;
// Resize views according to their resize modes - this
// saves us some server communication, as the server
// does the same with our views on its side.
fTopView->_ResizeBy(width - fTopView->Bounds().Width(),
height - fTopView->Bounds().Height());
FrameResized(width, height);
}
break;
}
case B_WINDOW_MOVED:
{
BPoint origin;
if (msg->FindPoint("where", &origin) == B_OK) {
fFrame.OffsetTo(origin);
FrameMoved(origin);
}
break;
}
case B_WINDOW_ACTIVATED:
2005-11-21 16:58:36 +00:00
if (target == this) {
bool active;
if (msg->FindBool("active", &active) == B_OK
&& active != fActive) {
fActive = active;
WindowActivated(active);
// call hook function 'WindowActivated(bool)' for all
// views attached to this window.
fTopView->_Activate(active);
}
} else
target->MessageReceived(msg);
break;
case B_SCREEN_CHANGED:
2005-11-21 16:58:36 +00:00
if (target == this) {
BRect frame;
uint32 mode;
if (msg->FindRect("frame", &frame) == B_OK
&& msg->FindInt32("mode", (int32 *)&mode) == B_OK)
ScreenChanged(frame, (color_space)mode);
} else
target->MessageReceived(msg);
break;
case B_WORKSPACE_ACTIVATED:
2005-11-21 16:58:36 +00:00
if (target == this) {
uint32 workspace;
bool active;
if (msg->FindInt32("workspace", (int32 *)&workspace) == B_OK
&& msg->FindBool("active", &active) == B_OK)
WorkspaceActivated(workspace, active);
} else
target->MessageReceived(msg);
break;
case B_WORKSPACES_CHANGED:
2005-11-21 16:58:36 +00:00
if (target == this) {
uint32 oldWorkspace, newWorkspace;
if (msg->FindInt32("old", (int32 *)&oldWorkspace) == B_OK
&& msg->FindInt32("new", (int32 *)&newWorkspace) == B_OK)
WorkspacesChanged(oldWorkspace, newWorkspace);
} else
target->MessageReceived(msg);
break;
case B_KEY_DOWN:
{
uint32 modifiers;
int32 rawChar;
const char *string = NULL;
msg->FindInt32("modifiers", (int32*)&modifiers);
msg->FindInt32("raw_char", &rawChar);
msg->FindString("bytes", &string);
2005-11-21 16:58:36 +00:00
// TODO: cannot use "string" here if we support having different
// font encoding per view (it's supposed to be converted by
// _HandleKeyDown() one day)
if (!_HandleKeyDown(string[0], (uint32)modifiers)) {
2005-11-21 16:58:36 +00:00
if (BView* view = dynamic_cast<BView*>(target))
view->KeyDown(string, strlen(string));
else
2005-11-21 16:58:36 +00:00
target->MessageReceived(msg);
}
break;
}
case B_KEY_UP:
{
const char *string = NULL;
msg->FindString("bytes", &string);
2005-11-21 16:58:36 +00:00
// TODO: same as above
if (BView* view = dynamic_cast<BView*>(target))
view->KeyUp(string, strlen(string));
else
target->MessageReceived(msg);
break;
2005-11-21 16:58:36 +00:00
}
case B_MOUSE_DOWN:
{
BPoint where;
2005-11-23 15:17:58 +00:00
msg->FindPoint("be:view_where", &where);
2005-11-23 15:17:58 +00:00
if (BView *view = dynamic_cast<BView *>(target))
view->MouseDown(where);
else
target->MessageReceived(msg);
break;
}
case B_MOUSE_UP:
{
BPoint where;
2005-11-23 15:17:58 +00:00
msg->FindPoint("be:view_where", &where);
if (BView *view = dynamic_cast<BView *>(target))
view->MouseUp(where);
else
target->MessageReceived(msg);
break;
}
case B_MOUSE_MOVED:
{
BPoint where;
uint32 buttons;
uint32 transit;
2005-11-23 15:17:58 +00:00
msg->FindPoint("be:view_where", &where);
msg->FindInt32("buttons", (int32 *)&buttons);
2005-11-23 15:17:58 +00:00
msg->FindInt32("be:transit", (int32 *)&transit);
if (BView *view = dynamic_cast<BView *>(target))
view->MouseMoved(where, transit, NULL);
else
target->MessageReceived(msg);
break;
}
case B_PULSE:
2005-11-21 16:58:36 +00:00
if (target == this && fPulseEnabled) {
fTopView->_Pulse();
fLink->Flush();
2005-11-21 16:58:36 +00:00
} else
target->MessageReceived(msg);
break;
case _UPDATE_:
{
STRACE(("info:BWindow handling _UPDATE_.\n"));
BRect updateRect;
int32 token;
msg->FindRect("_rect", &updateRect);
msg->FindInt32("_token", &token);
2005-11-21 16:58:36 +00:00
// TODO: why is "_token" ignored?
fLink->StartMessage(AS_BEGIN_UPDATE);
2005-11-21 16:58:36 +00:00
fTopView->_Draw(updateRect);
fLink->StartMessage(AS_END_UPDATE);
fLink->Flush();
break;
}
2005-10-17 18:13:16 +00:00
case B_VIEW_RESIZED:
case B_VIEW_MOVED:
{
// NOTE: The problem with this implementation is that BView::Window()->CurrentMessage()
// will show this message, and not what it used to be on R5. This might break apps and
// we need to fix this here or change the way this feature is implemented. However, this
// implementation shows what has to be done when Layers are moved or resized inside the
// app_server. This message is generated from Layer::MoveByHook() and ResizeByHook() in
// Layer::_AddToViewsWithInvalidCoords().
2005-10-17 18:13:16 +00:00
int32 token;
BPoint frameLeftTop;
float width;
float height;
BView *view;
for (int32 i = 0; CurrentMessage() && msg->FindInt32("_token", i, &token) >= B_OK; i++) {
2005-10-17 18:13:16 +00:00
if (token >= 0) {
msg->FindPoint("where", i, &frameLeftTop);
msg->FindFloat("width", i, &width);
msg->FindFloat("height", i, &height);
if ((view = _FindView(token)) != NULL) {
2005-10-17 18:13:16 +00:00
// update the views offset in parent
if (view->LeftTop() != frameLeftTop) {
view->fParentOffset = frameLeftTop;
// optionally call FrameMoved
if (view->fFlags & B_FRAME_EVENTS) {
STRACE(("Calling BView(%s)::FrameMoved( %.1f, %.1f )\n", view->Name(),
frameLeftTop.x, frameLeftTop.y));
view->FrameMoved(frameLeftTop);
}
}
// update the views width and height
if (view->fBounds.Width() != width || view->fBounds.Height() != height) {
// TODO: does this work when a views left/top side is resized?
view->fBounds.right = view->fBounds.left + width;
view->fBounds.bottom = view->fBounds.top + height;
// optionally call FrameResized
if (view->fFlags & B_FRAME_EVENTS) {
STRACE(("Calling BView(%s)::FrameResized( %f, %f )\n", view->Name(), width, height));
view->FrameResized(width, height);
}
}
} else {
fprintf(stderr, "B_VIEW_RESIZED ***PANIC: BW: Can't find view with ID: %ld !***\n", token);
2005-10-17 18:13:16 +00:00
}
}
}
break;
}
2005-05-07 13:24:25 +00:00
case _MENUS_DONE_:
MenusEnded();
break;
// These two are obviously some kind of old scripting messages
// this is NOT an app_server message and we have to be cautious
case B_WINDOW_MOVE_BY:
{
BPoint offset;
if (msg->FindPoint("data", &offset) == B_OK)
MoveBy(offset.x, offset.y);
else
msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
break;
}
// this is NOT an app_server message and we have to be cautious
case B_WINDOW_MOVE_TO:
{
BPoint origin;
if (msg->FindPoint("data", &origin) == B_OK)
MoveTo(origin);
else
msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
break;
}
default:
BLooper::DispatchMessage(msg, target);
break;
}
2003-06-03 18:42:22 +00:00
}
void
BWindow::FrameMoved(BPoint new_position)
{
// does nothing
// Hook function
}
void
BWindow::FrameResized(float new_width, float new_height)
{
// does nothing
// Hook function
}
void
BWindow::WorkspacesChanged(uint32 old_ws, uint32 new_ws)
{
// does nothing
// Hook function
}
void
BWindow::WorkspaceActivated(int32 ws, bool state)
{
// does nothing
// Hook function
}
void
BWindow::MenusBeginning()
{
// does nothing
// Hook function
}
void
BWindow::MenusEnded()
{
// does nothing
// Hook function
}
void
BWindow::SetSizeLimits(float minWidth, float maxWidth,
float minHeight, float maxHeight)
{
if (minWidth > maxWidth || minHeight > maxHeight)
2003-04-17 20:41:54 +00:00
return;
if (Lock()) {
fLink->StartMessage(AS_SET_SIZE_LIMITS);
fLink->Attach<float>(minWidth);
fLink->Attach<float>(maxWidth);
fLink->Attach<float>(minHeight);
fLink->Attach<float>(maxHeight);
2003-04-17 20:41:54 +00:00
int32 code;
if (fLink->FlushWithReply(code) == B_OK
2005-11-28 15:47:41 +00:00
&& code == B_OK) {
// read the values that were really enforced on
// the server side (the window frame could have
// been changed, too)
fLink->Read<BRect>(&fFrame);
fLink->Read<float>(&fMinWidth);
fLink->Read<float>(&fMaxWidth);
fLink->Read<float>(&fMinHeight);
fLink->Read<float>(&fMaxHeight);
}
Unlock();
2003-04-17 20:41:54 +00:00
}
}
void
BWindow::GetSizeLimits(float *minWidth, float *maxWidth,
float *minHeight, float *maxHeight)
{
// TODO: What about locking?!?
*minHeight = fMinHeight;
*minWidth = fMinWidth;
*maxHeight = fMaxHeight;
*maxWidth = fMaxWidth;
}
void
BWindow::SetZoomLimits(float maxWidth, float maxHeight)
{
// TODO: What about locking?!?
if (maxWidth > fMaxWidth)
maxWidth = fMaxWidth;
2003-04-17 20:41:54 +00:00
else
fMaxZoomWidth = maxWidth;
2003-04-17 20:41:54 +00:00
if (maxHeight > fMaxHeight)
maxHeight = fMaxHeight;
2003-04-17 20:41:54 +00:00
else
fMaxZoomHeight = maxHeight;
}
void
BWindow::Zoom(BPoint rec_position, float rec_width, float rec_height)
{
// this is also a Hook function!
MoveTo(rec_position);
ResizeTo(rec_width, rec_height);
}
void
BWindow::Zoom()
{
// TODO: broken.
// TODO: What about locking?!?
float minWidth, minHeight;
BScreen screen;
/*
from BeBook:
However, if the window's rectangle already matches these "zoom" dimensions
(give or take a few pixels), Zoom() passes the window's previous
("non-zoomed") size and location. (??????)
*/
if (Frame().Width() == fMaxZoomWidth && Frame().Height() == fMaxZoomHeight) {
BPoint position(Frame().left, Frame().top);
Zoom(position, fMaxZoomWidth, fMaxZoomHeight);
return;
}
/* From BeBook:
The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced from
the smallest of three rectangles:
*/
// 1) the rectangle defined by SetZoomLimits(),
minHeight = fMaxZoomHeight;
minWidth = fMaxZoomWidth;
// 2) the rectangle defined by SetSizeLimits()
if (fMaxHeight < minHeight)
minHeight = fMaxHeight;
if (fMaxWidth < minWidth)
minWidth = fMaxWidth;
// 3) the screen rectangle
if (screen.Frame().Width() < minWidth)
minWidth = screen.Frame().Width();
if (screen.Frame().Height() < minHeight)
minHeight = screen.Frame().Height();
Zoom(Frame().LeftTop(), minWidth, minHeight);
}
2003-04-17 20:41:54 +00:00
void
BWindow::ScreenChanged(BRect screen_size, color_space depth)
{
// Hook function
// does nothing
}
2003-04-17 20:41:54 +00:00
void
BWindow::SetPulseRate(bigtime_t rate)
{
// TODO: What about locking?!?
if (rate < 0)
2003-04-17 20:41:54 +00:00
return;
// ToDo: isn't fPulseRunner enough? Why fPulseEnabled?
if (fPulseRate == 0 && !fPulseEnabled) {
fPulseRunner = new BMessageRunner(BMessenger(this),
new BMessage(B_PULSE), rate);
fPulseRate = rate;
fPulseEnabled = true;
2003-04-17 20:41:54 +00:00
return;
}
if (rate == 0 && fPulseEnabled) {
delete fPulseRunner;
fPulseRunner = NULL;
fPulseRate = rate;
fPulseEnabled = false;
2003-04-17 20:41:54 +00:00
return;
}
2003-04-17 20:41:54 +00:00
fPulseRunner->SetInterval(rate);
}
2004-06-27 21:32:52 +00:00
bigtime_t
BWindow::PulseRate() const
{
// TODO: What about locking?!?
return fPulseRate;
}
2004-06-27 21:32:52 +00:00
void
BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem *item)
{
Shortcut* shortcut = new Shortcut(key, modifiers, item);
// removes the shortcut if it already exists!
RemoveShortcut(key, modifiers);
fShortcuts.AddItem(shortcut);
}
2004-06-27 21:32:52 +00:00
void
BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage *message)
2004-06-27 21:32:52 +00:00
{
AddShortcut(key, modifiers, message, this);
2004-06-27 21:32:52 +00:00
}
void
BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage *message, BHandler *target)
2004-06-27 21:32:52 +00:00
{
if (message == NULL)
2003-04-17 20:41:54 +00:00
return;
Shortcut* shortcut = new Shortcut(key, modifiers, message, target);
// removes the shortcut if it already exists!
RemoveShortcut(key, modifiers);
fShortcuts.AddItem(shortcut);
}
void
BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
{
Shortcut* shortcut = _FindShortcut(key, modifiers);
if (shortcut != NULL) {
fShortcuts.RemoveItem(shortcut);
delete shortcut;
}
}
BButton *
BWindow::DefaultButton() const
{
// TODO: What about locking?!?
return fDefaultButton;
}
void
BWindow::SetDefaultButton(BButton *button)
{
// TODO: What about locking?!?
if (fDefaultButton == button)
return;
if (fDefaultButton != NULL) {
// tell old button it's no longer the default one
BButton *oldDefault = fDefaultButton;
oldDefault->MakeDefault(false);
oldDefault->Invalidate();
}
fDefaultButton = button;
if (button != NULL) {
// notify new default button
fDefaultButton->MakeDefault(true);
fDefaultButton->Invalidate();
}
}
bool
BWindow::NeedsUpdate() const
{
// TODO: What about locking?!?
const_cast<BWindow *>(this)->Lock();
fLink->StartMessage(AS_NEEDS_UPDATE);
2005-10-30 11:23:37 +00:00
int32 code = B_ERROR;
fLink->FlushWithReply(code);
const_cast<BWindow *>(this)->Unlock();
2005-10-30 11:23:37 +00:00
return code == B_OK;
}
void
BWindow::UpdateIfNeeded()
{
// TODO: What about locking?!?
// works only from this thread
if (find_thread(NULL) != Thread())
return;
// Since we're blocking the event loop, we need to retrieve
// all messages that are pending on the port.
2005-11-21 16:58:36 +00:00
_DequeueAll();
BMessageQueue *queue = MessageQueue();
queue->Lock();
// First process and remove any _UPDATE_ message in the queue
// According to Adi, there can only be one at a time
BMessage *msg;
for (int32 i = 0; (msg = queue->FindMessage(i)) != NULL; i++) {
if (msg->what == _UPDATE_) {
BWindow::DispatchMessage(msg, this);
// we need to make sure that no overridden method is called
// here; for BWindow::DispatchMessage() we now exactly what
// will happen
queue->RemoveMessage(msg);
delete msg;
break;
}
2003-04-17 20:41:54 +00:00
}
queue->Unlock();
}
BView *
BWindow::FindView(const char *viewName) const
{
BAutolock _(const_cast<BWindow*>(this));
return fTopView->FindView(viewName);
}
BView *
BWindow::FindView(BPoint point) const
{
BAutolock _(const_cast<BWindow*>(this));
return _FindView(fTopView, point);
}
BView *BWindow::CurrentFocus() const
{
return fFocus;
}
void
BWindow::Activate(bool active)
{
// TODO: What about locking?!?
if (IsHidden())
return;
Lock();
fLink->StartMessage(AS_ACTIVATE_WINDOW);
fLink->Attach<bool>(active);
fLink->Flush();
Unlock();
}
void
BWindow::WindowActivated(bool state)
{
// hook function
// does nothing
}
void
BWindow::ConvertToScreen(BPoint *point) const
{
point->x += fFrame.left;
point->y += fFrame.top;
}
BPoint
BWindow::ConvertToScreen(BPoint point) const
{
return point + fFrame.LeftTop();
}
void
BWindow::ConvertFromScreen(BPoint *point) const
{
point->x -= fFrame.left;
point->y -= fFrame.top;
}
BPoint
BWindow::ConvertFromScreen(BPoint point) const
{
return point - fFrame.LeftTop();
}
void
BWindow::ConvertToScreen(BRect *rect) const
{
rect->OffsetBy(fFrame.LeftTop());
}
BRect
BWindow::ConvertToScreen(BRect rect) const
{
return rect.OffsetByCopy(fFrame.LeftTop());
}
void
BWindow::ConvertFromScreen(BRect* rect) const
{
rect->OffsetBy(-fFrame.left, -fFrame.top);
}
BRect
BWindow::ConvertFromScreen(BRect rect) const
{
return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
}
bool
BWindow::IsMinimized() const
{
// Hiding takes precendence over minimization!!!
if (IsHidden())
return false;
return fMinimized;
}
BRect
BWindow::Bounds() const
{
return BRect(0, 0, fFrame.Width(), fFrame.Height());
}
BRect
BWindow::Frame() const
{
return fFrame;
}
const char *
BWindow::Title() const
{
return fTitle;
}
void
BWindow::SetTitle(const char *title)
{
if (title == NULL)
title = "";
free(fTitle);
fTitle = strdup(title);
// we will change BWindow's thread name to "w>window title"
char threadName[B_OS_NAME_LENGTH];
strcpy(threadName, "w>");
#ifdef __HAIKU__
strlcat(threadName, title, B_OS_NAME_LENGTH);
#else
int32 length = strlen(title);
length = min_c(length, B_OS_NAME_LENGTH - 3);
memcpy(threadName + 2, title, length);
threadName[length + 2] = '\0';
#endif
// change the handler's name
SetName(threadName);
// if the message loop has been started...
if (Thread() >= B_OK)
rename_thread(Thread(), threadName);
// we notify the app_server so we can actually see the change
if (Lock()) {
fLink->StartMessage(AS_SET_WINDOW_TITLE);
fLink->AttachString(fTitle);
fLink->Flush();
Unlock();
}
}
bool
BWindow::IsActive() const
{
return fActive;
}
void
BWindow::SetKeyMenuBar(BMenuBar *bar)
{
fKeyMenuBar = bar;
}
BMenuBar *
BWindow::KeyMenuBar() const
{
return fKeyMenuBar;
}
bool
BWindow::IsModal() const
{
return fFeel == B_MODAL_SUBSET_WINDOW_FEEL
|| fFeel == B_MODAL_APP_WINDOW_FEEL
|| fFeel == B_MODAL_ALL_WINDOW_FEEL;
}
bool
BWindow::IsFloating() const
{
return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL
|| fFeel == B_FLOATING_APP_WINDOW_FEEL
|| fFeel == B_FLOATING_ALL_WINDOW_FEEL;
}
status_t
BWindow::AddToSubset(BWindow *window)
{
if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
return B_BAD_VALUE;
2003-04-17 20:41:54 +00:00
Lock();
2005-11-28 15:47:41 +00:00
status_t status = B_ERROR;
fLink->StartMessage(AS_ADD_TO_SUBSET);
fLink->Attach<int32>(_get_object_token_(window));
2005-11-28 15:47:41 +00:00
fLink->FlushWithReply(status);
Unlock();
2005-11-28 15:47:41 +00:00
return status;
}
status_t
BWindow::RemoveFromSubset(BWindow *window)
{
if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
|| (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
&& fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
return B_BAD_VALUE;
2004-01-17 16:35:34 +00:00
Lock();
2005-11-28 15:47:41 +00:00
status_t status = B_ERROR;
fLink->StartMessage(AS_REMOVE_FROM_SUBSET);
fLink->Attach<int32>(_get_object_token_(window));
2005-11-28 15:47:41 +00:00
fLink->FlushWithReply(status);
Unlock();
2004-01-17 16:35:34 +00:00
2005-11-28 15:47:41 +00:00
return status;
}
status_t
BWindow::Perform(perform_code d, void *arg)
{
return BLooper::Perform(d, arg);
}
status_t
BWindow::SetType(window_type type)
{
window_look look;
window_feel feel;
_DecomposeType(type, &look, &feel);
status_t status = SetLook(look);
if (status == B_OK)
status = SetFeel(feel);
return status;
}
window_type
BWindow::Type() const
{
return _ComposeType(fLook, fFeel);
}
status_t
BWindow::SetLook(window_look look)
{
BAutolock locker(this);
fLink->StartMessage(AS_SET_LOOK);
fLink->Attach<int32>((int32)look);
2005-11-28 15:47:41 +00:00
status_t status = B_ERROR;
if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
fLook = look;
2005-11-28 15:47:41 +00:00
return status;
}
window_look
BWindow::Look() const
{
return fLook;
}
status_t
BWindow::SetFeel(window_feel feel)
{
BAutolock locker(this);
fLink->StartMessage(AS_SET_FEEL);
fLink->Attach<int32>((int32)feel);
status_t status = B_ERROR;
if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
fFeel = feel;
2004-01-13 00:20:09 +00:00
return status;
}
window_feel
BWindow::Feel() const
{
return fFeel;
}
status_t
BWindow::SetFlags(uint32 flags)
{
BAutolock locker(this);
2003-08-31 17:34:24 +00:00
fLink->StartMessage(AS_SET_FLAGS);
fLink->Attach<uint32>(flags);
2005-11-28 15:47:41 +00:00
int32 status = B_ERROR;
if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
fFlags = flags;
2005-11-28 15:47:41 +00:00
return status;
}
uint32
BWindow::Flags() const
{
return fFlags;
}
status_t
BWindow::SetWindowAlignment(window_alignment mode,
int32 h, int32 hOffset, int32 width, int32 widthOffset,
int32 v, int32 vOffset, int32 height, int32 heightOffset)
{
if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0
|| (hOffset >= 0 && hOffset <= h)
|| (vOffset >= 0 && vOffset <= v)
|| (widthOffset >= 0 && widthOffset <= width)
|| (heightOffset >= 0 && heightOffset <= height))
return B_BAD_VALUE;
2003-04-17 20:41:54 +00:00
// TODO: test if hOffset = 0 and set it to 1 if true.
Lock();
fLink->StartMessage(AS_SET_ALIGNMENT);
fLink->Attach<int32>((int32)mode);
fLink->Attach<int32>(h);
fLink->Attach<int32>(hOffset);
fLink->Attach<int32>(width);
fLink->Attach<int32>(widthOffset);
fLink->Attach<int32>(v);
fLink->Attach<int32>(vOffset);
fLink->Attach<int32>(height);
fLink->Attach<int32>(heightOffset);
2005-11-28 15:47:41 +00:00
status_t status = B_ERROR;
fLink->FlushWithReply(status);
Unlock();
2005-11-28 15:47:41 +00:00
return status;
}
status_t
BWindow::GetWindowAlignment(window_alignment *mode,
int32 *h, int32 *hOffset, int32 *width, int32 *widthOffset,
int32 *v, int32 *vOffset, int32 *height, int32 *heightOffset) const
{
const_cast<BWindow *>(this)->Lock();
fLink->StartMessage(AS_GET_ALIGNMENT);
2005-11-28 15:47:41 +00:00
status_t status = B_ERROR;
if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
fLink->Read<int32>((int32 *)mode);
fLink->Read<int32>(h);
fLink->Read<int32>(hOffset);
fLink->Read<int32>(width);
fLink->Read<int32>(widthOffset);
fLink->Read<int32>(v);
fLink->Read<int32>(hOffset);
fLink->Read<int32>(height);
fLink->Read<int32>(heightOffset);
}
const_cast<BWindow *>(this)->Unlock();
2005-11-28 15:47:41 +00:00
return status;
}
uint32
BWindow::Workspaces() const
{
uint32 workspaces = 0;
const_cast<BWindow *>(this)->Lock();
fLink->StartMessage(AS_GET_WORKSPACES);
2005-11-28 15:47:41 +00:00
status_t status;
if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
fLink->Read<uint32>(&workspaces);
const_cast<BWindow *>(this)->Unlock();
2003-08-31 17:34:24 +00:00
return workspaces;
}
void
BWindow::SetWorkspaces(uint32 workspaces)
{
// TODO: don't forget about Tracker's background window.
if (fFeel != B_NORMAL_WINDOW_FEEL)
return;
Lock();
fLink->StartMessage(AS_SET_WORKSPACES);
fLink->Attach<uint32>(workspaces);
fLink->Flush();
Unlock();
}
BView *
BWindow::LastMouseMovedView() const
{
return fLastMouseMovedView;
}
void
BWindow::MoveBy(float dx, float dy)
{
if (dx == 0.0 && dy == 0.0)
return;
Lock();
fLink->StartMessage(AS_WINDOW_MOVE);
fLink->Attach<float>(dx);
fLink->Attach<float>(dy);
fLink->Flush();
fFrame.OffsetBy(dx, dy);
Unlock();
}
void
BWindow::MoveTo(BPoint point)
{
Lock();
point.x = roundf(point.x);
point.y = roundf(point.y);
if (fFrame.left != point.x || fFrame.top != point.y) {
float xOffset = point.x - fFrame.left;
float yOffset = point.y - fFrame.top;
MoveBy(xOffset, yOffset);
}
Unlock();
}
void
BWindow::MoveTo(float x, float y)
{
MoveTo(BPoint(x, y));
}
void
BWindow::ResizeBy(float dx, float dy)
{
Lock();
dx = roundf(dx);
dy = roundf(dy);
// stay in minimum & maximum frame limits
if (fFrame.Width() + dx < fMinWidth)
dx = fMinWidth - fFrame.Width();
if (fFrame.Width() + dx > fMaxWidth)
dx = fMaxWidth - fFrame.Width();
if (fFrame.Height() + dy < fMinHeight)
dy = fMinHeight - fFrame.Height();
if (fFrame.Height() + dy > fMaxHeight)
dy = fMaxHeight - fFrame.Height();
if (dx != 0.0 || dy != 0.0) {
fLink->StartMessage(AS_WINDOW_RESIZE);
fLink->Attach<float>(dx);
fLink->Attach<float>(dy);
fLink->Flush();
fFrame.SetRightBottom(fFrame.RightBottom() + BPoint(dx, dy));
}
2004-08-02 21:07:55 +00:00
Unlock();
}
2004-05-16 19:07:12 +00:00
void
BWindow::ResizeTo(float width, float height)
{
Lock();
2004-05-16 19:07:12 +00:00
ResizeBy(width - fFrame.Width(), height - fFrame.Height());
Unlock();
2004-05-16 19:07:12 +00:00
}
void
BWindow::Show()
{
bool isLocked = this->IsLocked();
fShowLevel--;
if (fShowLevel == 0) {
STRACE(("BWindow(%s): sending AS_SHOW_WINDOW message...\n", Name()));
if (Lock()) {
fLink->StartMessage(AS_SHOW_WINDOW);
fLink->Flush();
Unlock();
}
2003-08-31 17:34:24 +00:00
}
// if it's the fist time Show() is called... start the Looper thread.
if (Thread() == B_ERROR) {
// normally this won't happen, but I want to be sure!
if (!isLocked)
Lock();
2003-08-31 17:34:24 +00:00
Run();
}
}
void
BWindow::Hide()
{
if (fShowLevel == 0 && Lock()) {
fLink->StartMessage(AS_HIDE_WINDOW);
fLink->Flush();
Unlock();
}
fShowLevel++;
}
bool
BWindow::IsHidden() const
{
return fShowLevel > 0;
}
bool
BWindow::QuitRequested()
{
return BLooper::QuitRequested();
}
thread_id
BWindow::Run()
{
return BLooper::Run();
}
status_t
BWindow::GetSupportedSuites(BMessage *data)
{
if (data == NULL)
return B_BAD_VALUE;
status_t status = data->AddString("Suites", "suite/vnd.Be-window");
if (status == B_OK) {
BPropertyInfo propertyInfo(sWindowPropInfo);
status = data->AddFlat("message", &propertyInfo);
if (status == B_OK)
status = BLooper::GetSupportedSuites(data);
}
return status;
}
BHandler *
BWindow::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
int32 what, const char *property)
{
if (msg->what == B_WINDOW_MOVE_BY
|| msg->what == B_WINDOW_MOVE_TO)
return this;
BPropertyInfo propertyInfo(sWindowPropInfo);
if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) {
if (!strcmp(property, "View")) {
// we will NOT pop the current specifier
return fTopView;
} else if (!strcmp(property, "MenuBar")) {
if (fKeyMenuBar) {
msg->PopSpecifier();
return fKeyMenuBar;
} else {
BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
replyMsg.AddString("message", "This window doesn't have a main MenuBar");
msg->SendReply(&replyMsg);
return NULL;
}
} else
return this;
}
return BLooper::ResolveSpecifier(msg, index, specifier, what, property);
}
// #pragma mark - Private Methods
2005-11-21 16:58:36 +00:00
void
BWindow::_InitData(BRect frame, const char* title, window_look look,
window_feel feel, uint32 flags, uint32 workspace, int32 bitmapToken)
{
STRACE(("BWindow::InitData()\n"));
if (be_app == NULL) {
debugger("You need a valid BApplication object before interacting with the app_server");
2003-04-17 20:41:54 +00:00
return;
}
2003-09-03 21:27:18 +00:00
frame.left = roundf(frame.left);
frame.top = roundf(frame.top);
frame.right = roundf(frame.right);
frame.bottom = roundf(frame.bottom);
fFrame = frame;
2003-04-17 20:41:54 +00:00
if (title == NULL)
title = "";
fTitle = strdup(title);
SetName(title);
fFeel = feel;
fLook = look;
fFlags = flags;
fInTransaction = false;
fActive = false;
fShowLevel = 1;
fTopView = NULL;
fFocus = NULL;
2003-04-17 20:41:54 +00:00
fLastMouseMovedView = NULL;
fKeyMenuBar = NULL;
fDefaultButton = NULL;
2003-04-17 20:41:54 +00:00
// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
// get sent to the application, and not one of our handlers
if ((fFlags & B_NOT_CLOSABLE) == 0)
AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2003-04-17 20:41:54 +00:00
AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
fPulseEnabled = false;
fPulseRate = 0;
fPulseRunner = NULL;
2003-04-17 20:41:54 +00:00
// TODO: is this correct??? should the thread loop be started???
2003-09-03 21:27:18 +00:00
//SetPulseRate( 500000 );
2003-04-17 20:41:54 +00:00
// TODO: see if you can use 'fViewsNeedPulse'
2003-04-17 20:41:54 +00:00
fIsFilePanel = false;
2003-04-17 20:41:54 +00:00
// TODO: see WHEN is this used!
fMaskActivated = false;
2003-04-17 20:41:54 +00:00
// TODO: see WHEN is this used!
fWaitingForMenu = false;
fMenuSem = -1;
2003-04-17 20:41:54 +00:00
fMinimized = false;
fMaxZoomHeight = 32768.0;
fMaxZoomWidth = 32768.0;
fMinHeight = 0.0;
fMinWidth = 0.0;
fMaxHeight = 32768.0;
fMaxWidth = 32768.0;
fLastViewToken = B_NULL_TOKEN;
// TODO: other initializations!
2003-04-17 20:41:54 +00:00
// Create the server-side window
port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, "w<app_server");
if (receivePort < B_OK) {
2003-08-31 17:34:24 +00:00
debugger("Could not create BWindow's receive port, used for interacting with the app_server!");
delete this;
return;
2003-04-17 20:41:54 +00:00
}
2003-08-31 17:34:24 +00:00
STRACE(("BWindow::InitData(): contacting app_server...\n"));
2004-08-08 22:35:25 +00:00
// HERE we are in BApplication's thread, so for locking we use be_app variable
// we'll lock the be_app to be sure we're the only one writing at BApplication's server port
bool locked = false;
if (!be_app->IsLocked()) {
be_app->Lock();
locked = true;
}
// let app_server know that a window has been created.
fLink = new BPrivate::PortLink(
BApplication::Private::ServerLink()->SenderPort(), receivePort);
if (bitmapToken < 0) {
fLink->StartMessage(AS_CREATE_WINDOW);
} else {
fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
fLink->Attach<int32>(bitmapToken);
}
fLink->Attach<BRect>(fFrame);
fLink->Attach<uint32>((uint32)fLook);
fLink->Attach<uint32>((uint32)fFeel);
fLink->Attach<uint32>(fFlags);
fLink->Attach<uint32>(workspace);
fLink->Attach<int32>(_get_object_token_(this));
fLink->Attach<port_id>(receivePort);
fLink->Attach<port_id>(fMsgPort);
fLink->AttachString(title);
port_id sendPort;
int32 code;
if (fLink->FlushWithReply(code) == B_OK
&& code == B_OK
&& fLink->Read<port_id>(&sendPort) == B_OK) {
fLink->SetSenderPort(sendPort);
// read the frame size and its limits that were really
// enforced on the server side
fLink->Read<BRect>(&fFrame);
fLink->Read<float>(&fMinWidth);
fLink->Read<float>(&fMaxWidth);
fLink->Read<float>(&fMinHeight);
fLink->Read<float>(&fMaxHeight);
fMaxZoomWidth = fMaxWidth;
fMaxZoomHeight = fMaxHeight;
} else
sendPort = -1;
if (locked)
be_app->Unlock();
STRACE(("Server says that our send port is %ld\n", sendPort));
STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2005-11-21 16:58:36 +00:00
_CreateTopView();
}
2005-11-21 16:58:36 +00:00
//! Reads all pending messages from the window port and put them into the queue.
void
2005-11-21 16:58:36 +00:00
BWindow::_DequeueAll()
{
// Get message count from port
int32 count = port_count(fMsgPort);
for (int32 i = 0; i < count; i++) {
BMessage *message = MessageFromPort(0);
if (message != NULL)
fQueue->AddMessage(message);
}
}
2005-11-23 15:17:58 +00:00
/*! This here is an almost complete code duplication to BLooper::task_looper()
but with some important differences:
a) it uses the _DetermineTarget() method to tell what the later target of
a message will be, if no explicit target is supplied.
b) it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
to all of its intended targets, and to add all fields the target would
expect in such a message.
This is important because the app_server sends all input events to the
preferred handler, and expects them to be correctly distributed to their
intended targets.
2005-11-21 16:58:36 +00:00
*/
void
BWindow::task_looper()
{
STRACE(("info: BWindow::task_looper() started.\n"));
2005-11-23 15:17:58 +00:00
// Check that looper is locked (should be)
AssertLocked();
Unlock();
if (IsLocked())
debugger("window must not be locked!");
2005-11-23 15:17:58 +00:00
// loop: As long as we are not terminating.
while (!fTerminating) {
// TODO: timeout determination algo
// Read from message port (how do we determine what the timeout is?)
BMessage* msg = MessageFromPort();
2005-11-23 15:17:58 +00:00
// Did we get a message?
if (msg) {
2005-11-23 15:17:58 +00:00
// Add to queue
fQueue->AddMessage(msg);
} else
continue;
// Get message count from port
int32 msgCount = port_count(fMsgPort);
for (int32 i = 0; i < msgCount; ++i) {
2005-11-23 15:17:58 +00:00
// Read 'count' messages from port (so we will not block)
// We use zero as our timeout since we know there is stuff there
msg = MessageFromPort(0);
2005-11-23 15:17:58 +00:00
// Add messages to queue
if (msg)
fQueue->AddMessage(msg);
}
2005-11-23 15:17:58 +00:00
// loop: As long as there are messages in the queue and the port is
// empty... and we are not terminating, of course.
bool dispatchNextMessage = true;
while (!fTerminating && dispatchNextMessage) {
// Get next message from queue (assign to fLastMessage)
fLastMessage = fQueue->NextMessage();
2005-11-23 15:17:58 +00:00
// Lock the looper
Lock();
if (!fLastMessage) {
// No more messages: Unlock the looper and terminate the
// dispatch loop.
dispatchNextMessage = false;
} else {
// Get the target handler
2005-11-21 16:58:36 +00:00
BHandler *handler = NULL;
// Use the private BMessage accessor to determine if we are
// using the preferred handler, or if a target has been
// specified
BMessage::Private messagePrivate(fLastMessage);
bool usePreferred = messagePrivate.UsePreferredTarget();
if (usePreferred) {
handler = PreferredHandler();
if (handler == NULL)
handler = this;
} else {
gDefaultTokens.GetToken(messagePrivate.GetTarget(),
B_HANDLER_TOKEN, (void **)&handler);
// if this handler doesn't belong to us, we drop the message
if (handler != NULL && handler->Looper() != this)
handler = NULL;
}
if (handler == NULL || usePreferred)
handler = _DetermineTarget(fLastMessage, handler);
2005-11-23 15:17:58 +00:00
unpack_cookie cookie;
while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
// if there is no target handler, the message is dropped
if (handler != NULL) {
// Is this a scripting message?
if (fLastMessage->HasSpecifiers()) {
int32 index = 0;
// Make sure the current specifier is kosher
if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
handler = resolve_specifier(handler, fLastMessage);
}
if (handler != NULL)
handler = _TopLevelFilter(fLastMessage, handler);
if (handler != NULL) {
_SanitizeMessage(fLastMessage, handler, usePreferred);
DispatchMessage(fLastMessage, handler);
2005-11-23 15:17:58 +00:00
}
}
2005-11-23 15:17:58 +00:00
// Delete the current message
delete fLastMessage;
fLastMessage = NULL;
}
}
if (fTerminating) {
// we leave the looper locked when we quit
return;
}
Unlock();
2005-11-23 15:17:58 +00:00
// Are any messages on the port?
if (port_count(fMsgPort) > 0) {
2005-11-23 15:17:58 +00:00
// Do outer loop
dispatchNextMessage = false;
}
}
}
2003-04-17 20:41:54 +00:00
}
2003-04-17 20:41:54 +00:00
window_type
BWindow::_ComposeType(window_look look, window_feel feel) const
{
switch (feel) {
case B_NORMAL_WINDOW_FEEL:
switch (look) {
case B_TITLED_WINDOW_LOOK:
return B_TITLED_WINDOW;
case B_DOCUMENT_WINDOW_LOOK:
return B_DOCUMENT_WINDOW;
case B_BORDERED_WINDOW_LOOK:
return B_BORDERED_WINDOW;
default:
return B_UNTYPED_WINDOW;
}
break;
case B_MODAL_APP_WINDOW_FEEL:
if (look == B_MODAL_WINDOW_LOOK)
return B_MODAL_WINDOW;
break;
case B_FLOATING_APP_WINDOW_FEEL:
if (look == B_FLOATING_WINDOW_LOOK)
return B_FLOATING_WINDOW;
break;
default:
return B_UNTYPED_WINDOW;
}
return B_UNTYPED_WINDOW;
}
void
BWindow::_DecomposeType(window_type type, window_look *_look,
window_feel *_feel) const
{
switch (type) {
case B_DOCUMENT_WINDOW:
*_look = B_DOCUMENT_WINDOW_LOOK;
*_feel = B_NORMAL_WINDOW_FEEL;
break;
case B_MODAL_WINDOW:
*_look = B_MODAL_WINDOW_LOOK;
*_feel = B_MODAL_APP_WINDOW_FEEL;
break;
case B_FLOATING_WINDOW:
*_look = B_FLOATING_WINDOW_LOOK;
*_feel = B_FLOATING_APP_WINDOW_FEEL;
break;
case B_BORDERED_WINDOW:
*_look = B_BORDERED_WINDOW_LOOK;
*_feel = B_NORMAL_WINDOW_FEEL;
break;
case B_TITLED_WINDOW:
case B_UNTYPED_WINDOW:
default:
*_look = B_TITLED_WINDOW_LOOK;
*_feel = B_NORMAL_WINDOW_FEEL;
break;
}
}
void
2005-11-21 16:58:36 +00:00
BWindow::_CreateTopView()
{
2005-11-21 16:58:36 +00:00
STRACE(("_CreateTopView(): enter\n"));
BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
fTopView = new BView(frame, "fTopView",
B_FOLLOW_ALL, B_WILL_DRAW);
2005-11-21 16:58:36 +00:00
fTopView->fTopLevelView = true;
//inhibit check_lock()
fLastViewToken = _get_object_token_(fTopView);
// set fTopView's owner, add it to window's eligible handler list
// and also set its next handler to be this window.
STRACE(("Calling setowner fTopView = %p this = %p.\n",
fTopView, this));
fTopView->_SetOwner(this);
2005-11-21 16:58:36 +00:00
// we can't use AddChild() because this is the top view
fTopView->_CreateSelf();
STRACE(("BuildTopView ended\n"));
}
void
2005-11-21 16:58:36 +00:00
BWindow::_SetFocus(BView *focusView, bool notifyInputServer)
{
if (fFocus == focusView)
return;
if (focusView)
focusView->MakeFocus(true);
// TODO: Notify the input server if we are passing focus
// from a view which has the B_INPUT_METHOD_AWARE to a one
// which does not, or vice-versa
if (notifyInputServer) {
// TODO: Send a message to input server using
// control_input_server()
}
}
/*!
2005-11-21 16:58:36 +00:00
\brief Determines the target of a message received for the
focus view.
*/
BHandler *
BWindow::_DetermineTarget(BMessage *message, BHandler *target)
{
switch (message->what) {
case B_KEY_DOWN:
case B_KEY_UP:
2005-11-21 16:58:36 +00:00
{
// if we have a default button, it might want to hear
// about pressing the <enter> key
int32 rawChar;
if (DefaultButton() != NULL
&& message->FindInt32("raw_char", &rawChar) == B_OK
&& rawChar == B_ENTER)
return DefaultButton();
// supposed to fall through
}
case B_UNMAPPED_KEY_DOWN:
case B_UNMAPPED_KEY_UP:
case B_MODIFIERS_CHANGED:
2005-11-23 15:17:58 +00:00
// these messages should be dispatched by the focus view
return CurrentFocus();
case B_MOUSE_DOWN:
case B_MOUSE_UP:
case B_MOUSE_MOVED:
case B_MOUSE_WHEEL_CHANGED:
2005-11-23 15:17:58 +00:00
// is there a token of the view that is currently under the mouse?
int32 token;
if (message->FindInt32("_view_token", &token) == B_OK) {
BHandler* handler;
if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN,
(void**)&handler) == B_OK
&& handler->Looper() == this) {
2005-11-23 15:17:58 +00:00
BView* view = dynamic_cast<BView*>(handler);
if (view != NULL)
return view;
}
}
// if there is no valid token in the message, we try our
// luck with the last target, if available
if (fLastMouseMovedView != NULL)
return fLastMouseMovedView;
break;
case B_PULSE:
case B_QUIT_REQUESTED:
// TODO: test wether R5 will let BView dispatch these messages
return this;
default:
break;
}
return target;
}
/*!
\brief Distributes the message to its intended targets. This is done for
all messages that should go to the preferred handler.
2005-11-21 16:58:36 +00:00
Returns \c true in case the message should still be dispatched
*/
bool
2005-11-23 15:17:58 +00:00
BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message, BHandler** _target,
bool* _usePreferred)
{
2005-11-23 15:17:58 +00:00
if (cookie.message == NULL)
return false;
if (cookie.index == 0 && !cookie.tokens_scanned) {
if (!*_usePreferred) {
// we only consider messages targeted at the preferred handler
cookie.message = NULL;
return true;
}
// initialize our cookie
cookie.message = *_message;
cookie.focus = *_target;
if (cookie.focus != NULL)
cookie.focus_token = _get_object_token_(*_target);
if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
*_usePreferred = false;
}
_DequeueAll();
2005-11-21 16:58:36 +00:00
2005-11-23 15:17:58 +00:00
// distribute the message to all targets specified in the
// message directly (but not to the focus view)
for (int32 token; !cookie.tokens_scanned
&& cookie.message->FindInt32("_token", cookie.index, &token) == B_OK;
cookie.index++) {
// focus view is preferred and should get its message directly
if (token == cookie.focus_token) {
cookie.found_focus = true;
continue;
}
if (token == cookie.last_view_token)
continue;
BView* target = _FindView(token);
if (target == NULL)
continue;
2005-11-23 15:17:58 +00:00
*_message = new BMessage(*cookie.message);
*_target = target;
cookie.index++;
return true;
}
2005-11-23 15:17:58 +00:00
cookie.tokens_scanned = true;
// if there is a last mouse moved view, and the new focus is
// different, the previous view wants to get its B_EXITED_VIEW
// message
if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != cookie.focus) {
*_message = new BMessage(*cookie.message);
*_target = fLastMouseMovedView;
cookie.last_view_token = B_NULL_TOKEN;
return true;
}
2005-11-23 15:17:58 +00:00
if (cookie.index > 0) {
// should this message still be dispatched by the focus view?
bool feedFocus;
2005-11-23 15:17:58 +00:00
if (!cookie.found_focus
&& (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
|| feedFocus == false)) {
delete cookie.message;
cookie.message = NULL;
return false;
2005-11-23 15:17:58 +00:00
}
}
2005-11-23 15:17:58 +00:00
*_message = cookie.message;
*_target = cookie.focus;
*_usePreferred = true;
cookie.message = NULL;
return true;
}
2005-11-23 15:17:58 +00:00
void
BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
{
if (target == NULL)
return;
switch (message->what) {
case B_MOUSE_MOVED:
case B_MOUSE_UP:
case B_MOUSE_DOWN:
BPoint where;
if (message->FindPoint("screen_where", &where) != B_OK)
break;
// add local window coordinates
message->AddPoint("where", ConvertFromScreen(where));
BView* view = dynamic_cast<BView*>(target);
if (view != NULL) {
// add local view coordinates
message->AddPoint("be:view_where", view->ConvertFromScreen(where));
if (message->what == B_MOUSE_MOVED) {
// is there a token of the view that is currently under the mouse?
BView* viewUnderMouse = NULL;
int32 token;
if (message->FindInt32("_view_token", &token) == B_OK) {
BHandler* handler;
if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN,
(void**)&handler) == B_OK
&& handler->Looper() == this)
viewUnderMouse = dynamic_cast<BView*>(handler);
}
// add transit information
int32 transit;
if (viewUnderMouse == view) {
// the mouse is over the target view
if (fLastMouseMovedView != view)
transit = B_ENTERED_VIEW;
else
transit = B_INSIDE_VIEW;
} else {
// the mouse is not over the target view
if (view == fLastMouseMovedView)
transit = B_EXITED_VIEW;
else
transit = B_OUTSIDE_VIEW;
2005-11-23 15:17:58 +00:00
}
message->AddInt32("be:transit", transit);
if (usePreferred || viewUnderMouse == NULL)
fLastMouseMovedView = viewUnderMouse;
2005-11-23 15:17:58 +00:00
}
}
break;
}
}
bool
BWindow::_HandleKeyDown(char key, uint32 modifiers)
{
// TODO: ask people if using 'raw_char' is OK ?
2003-04-17 20:41:54 +00:00
// handle BMenuBar key
if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0
&& fKeyMenuBar) {
// TODO: ask Marc about 'fWaitingForMenu' member!
2003-04-17 20:41:54 +00:00
// fWaitingForMenu = true;
2003-04-17 20:41:54 +00:00
fKeyMenuBar->StartMenuBar(0, true, false, NULL);
return true;
}
// Keyboard navigation through views
// (B_OPTION_KEY makes BTextViews and friends navigable, even in editing mode)
if (key == B_TAB && (modifiers & (B_COMMAND_KEY | B_OPTION_KEY)) != 0) {
_KeyboardNavigation();
2003-04-17 20:41:54 +00:00
return true;
}
// Handle shortcuts
if ((modifiers & B_COMMAND_KEY) != 0) {
// Command+q has been pressed, so, we will quit
// the shortcut mechanism doesn't allow handlers outside the window
if (key == 'Q' || key == 'q') {
be_app->PostMessage(B_QUIT_REQUESTED);
2003-04-17 20:41:54 +00:00
return true;
}
Shortcut* shortcut = _FindShortcut(key, modifiers);
if (shortcut != NULL) {
// TODO: would be nice to move this functionality to
// a Shortcut::Invoke() method - but since BMenu::InvokeItem()
// (and BMenuItem::Invoke()) are private, I didn't want
// to mess with them (BMenuItem::Invoke() is public in
// Dano/Zeta, though, maybe we should just follow their
// example)
if (shortcut->MenuItem() != NULL) {
BMenu* menu = shortcut->MenuItem()->Menu();
if (menu != NULL)
menu->InvokeItem(shortcut->MenuItem(), true);
} else {
BHandler* target = shortcut->Target();
if (target == NULL)
target = CurrentFocus();
2003-04-17 20:41:54 +00:00
BMessage* message = shortcut->Message();
if (message->ReplaceInt64("when", system_time()) != B_OK)
message->AddInt64("when", system_time());
PostMessage(message, target);
}
return true;
2003-04-17 20:41:54 +00:00
}
}
// TODO: convert keys to the encoding of the target view
2003-04-17 20:41:54 +00:00
return false;
}
void
BWindow::_KeyboardNavigation()
{
BMessage *message = CurrentMessage();
if (message == NULL)
return;
const char *bytes;
uint32 modifiers;
if (message->FindString("bytes", &bytes) != B_OK
|| bytes[0] != B_TAB)
return;
message->FindInt32("modifiers", (int32*)&modifiers);
BView *nextFocus;
int32 jumpGroups = modifiers & B_CONTROL_KEY ? B_NAVIGABLE_JUMP : B_NAVIGABLE;
if (modifiers & B_SHIFT_KEY)
nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
else
nextFocus = _FindNextNavigable(fFocus, jumpGroups);
if (nextFocus && nextFocus != fFocus)
2005-11-21 16:58:36 +00:00
_SetFocus(nextFocus, false);
}
BMessage *
BWindow::ConvertToMessage(void *raw, int32 code)
{
return BLooper::ConvertToMessage(raw, code);
}
BWindow::Shortcut *
BWindow::_FindShortcut(uint32 key, uint32 modifiers)
{
int32 count = fShortcuts.CountItems();
key = Shortcut::PrepareKey(key);
modifiers = Shortcut::PrepareModifiers(modifiers);
for (int32 index = 0; index < count; index++) {
Shortcut *shortcut = (Shortcut *)fShortcuts.ItemAt(index);
if (shortcut->Matches(key, modifiers))
return shortcut;
}
return NULL;
}
BView *
BWindow::_FindView(int32 token)
{
BHandler* handler;
if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN, (void**)&handler) != B_OK)
return NULL;
2003-04-17 20:41:54 +00:00
// the view must belong to us in order to be found by this method
BView* view = dynamic_cast<BView*>(handler);
if (view != NULL && view->Window() == this)
return view;
2003-04-17 20:41:54 +00:00
return NULL;
}
BView *
BWindow::_FindView(BView *view, BPoint point) const
{
// TODO: this is totally broken (bounds vs. frame) - since
// BView::Bounds() potentially queries the app_server
// anyway, we could just let the app_server answer this
// query directly.
2005-06-28 23:02:44 +00:00
if (view->Bounds().Contains(point) && !view->fFirstChild)
return view;
2005-06-28 23:02:44 +00:00
BView *child = view->fFirstChild;
while (child != NULL) {
if ((view = _FindView(child, point)) != NULL)
return view;
2005-06-28 23:02:44 +00:00
child = child->fNextSibling;
}
return NULL;
}
BView *
BWindow::_FindNextNavigable(BView *focus, uint32 flags)
{
if (focus == NULL)
focus = fTopView;
BView *nextFocus = focus;
// Search the tree for views that accept focus
while (true) {
2005-06-28 23:02:44 +00:00
if (nextFocus->fFirstChild)
nextFocus = nextFocus->fFirstChild;
else if (nextFocus->fNextSibling)
nextFocus = nextFocus->fNextSibling;
else {
2005-06-28 23:02:44 +00:00
while (!nextFocus->fNextSibling && nextFocus->fParent)
nextFocus = nextFocus->fParent;
if (nextFocus == fTopView)
2005-06-28 23:02:44 +00:00
nextFocus = nextFocus->fFirstChild;
else
2005-06-28 23:02:44 +00:00
nextFocus = nextFocus->fNextSibling;
}
// It means that the hole tree has been searched and there is no
// view with B_NAVIGABLE_JUMP flag set!
2003-04-17 20:41:54 +00:00
if (nextFocus == focus)
return NULL;
if (nextFocus->Flags() & flags)
return nextFocus;
}
}
BView *
BWindow::_FindPreviousNavigable(BView *focus, uint32 flags)
{
BView *prevFocus = focus;
// Search the tree for views that accept focus
while (true) {
BView *view;
if ((view = _LastViewChild(prevFocus)) != NULL)
prevFocus = view;
2005-06-28 23:02:44 +00:00
else if (prevFocus->fPreviousSibling)
prevFocus = prevFocus->fPreviousSibling;
else {
2005-06-28 23:02:44 +00:00
while (!prevFocus->fPreviousSibling && prevFocus->fParent)
prevFocus = prevFocus->fParent;
if (prevFocus == fTopView)
prevFocus = _LastViewChild(prevFocus);
else
2005-06-28 23:02:44 +00:00
prevFocus = prevFocus->fPreviousSibling;
}
2003-04-17 20:41:54 +00:00
// It means that the hole tree has been searched and there is no
// view with B_NAVIGABLE_JUMP flag set!
2003-04-17 20:41:54 +00:00
if (prevFocus == focus)
return NULL;
if (prevFocus->Flags() & flags)
return prevFocus;
}
2003-04-17 20:41:54 +00:00
}
BView *
BWindow::_LastViewChild(BView *parent)
{
2005-06-28 23:02:44 +00:00
BView *last = parent->fFirstChild;
if (last == NULL)
2003-04-17 20:41:54 +00:00
return NULL;
2005-06-28 23:02:44 +00:00
while (last->fNextSibling)
last = last->fNextSibling;
return last;
}
void
BWindow::SetIsFilePanel(bool yes)
{
// TODO: is this not enough?
fIsFilePanel = yes;
}
bool
BWindow::IsFilePanel() const
{
return fIsFilePanel;
}
//------------------------------------------------------------------------------
// Virtual reserved Functions
void BWindow::_ReservedWindow1() {}
void BWindow::_ReservedWindow2() {}
void BWindow::_ReservedWindow3() {}
void BWindow::_ReservedWindow4() {}
void BWindow::_ReservedWindow5() {}
void BWindow::_ReservedWindow6() {}
void BWindow::_ReservedWindow7() {}
void BWindow::_ReservedWindow8() {}
void
BWindow::PrintToStream() const
{
printf("BWindow '%s' data:\
Title = %s\
Token = %ld\
InTransaction = %s\
Active = %s\
fShowLevel = %d\
Flags = %lx\
send_port = %ld\
receive_port = %ld\
fTopView name = %s\
focus view name = %s\
lastMouseMoved = %s\
fLink = %p\
KeyMenuBar name = %s\
DefaultButton = %s\
2003-09-08 21:06:34 +00:00
# of shortcuts = %ld",
Name(), fTitle,
_get_object_token_(this),
fInTransaction == true ? "yes" : "no",
fActive == true ? "yes" : "no",
2003-09-08 21:06:34 +00:00
fShowLevel,
fFlags,
fLink->SenderPort(),
fLink->ReceiverPort(),
fTopView != NULL ? fTopView->Name() : "NULL",
fFocus != NULL ? fFocus->Name() : "NULL",
fLastMouseMovedView != NULL ? fLastMouseMovedView->Name() : "NULL",
fLink,
fKeyMenuBar != NULL ? fKeyMenuBar->Name() : "NULL",
fDefaultButton != NULL ? fDefaultButton->Name() : "NULL",
fShortcuts.CountItems());
2003-09-08 21:06:34 +00:00
/*
for( int32 i=0; i<accelList.CountItems(); i++){
_BCmdKey *key = (_BCmdKey*)accelList.ItemAt(i);
printf("\tShortCut %ld: char %s\n\t\t message: \n", i, (key->key > 127)?"ASCII":"UNICODE");
key->message->PrintToStream();
}
*/
printf("\
topViewToken = %ld\
pluseEnabled = %s\
isFilePanel = %s\
MaskActivated = %s\
pulseRate = %lld\
waitingForMenu = %s\
minimized = %s\
Menu semaphore = %ld\
maxZoomHeight = %f\
maxZoomWidth = %f\
minWindHeight = %f\
minWindWidth = %f\
maxWindHeight = %f\
maxWindWidth = %f\
frame = ( %f, %f, %f, %f )\
look = %d\
feel = %d\
lastViewToken = %ld\
2005-11-21 16:58:36 +00:00
pulseRunner = %s\n",
2003-09-08 21:06:34 +00:00
fTopViewToken,
fPulseEnabled==true?"Yes":"No",
fIsFilePanel==true?"Yes":"No",
fMaskActivated==true?"Yes":"No",
fPulseRate,
fWaitingForMenu==true?"Yes":"No",
fMinimized==true?"Yes":"No",
fMenuSem,
fMaxZoomHeight,
fMaxZoomWidth,
fMinHeight,
fMinWidth,
fMaxHeight,
fMaxWidth,
2003-09-08 21:06:34 +00:00
fFrame.left, fFrame.top, fFrame.right, fFrame.bottom,
(int16)fLook,
(int16)fFeel,
fLastViewToken,
2005-11-21 16:58:36 +00:00
fPulseRunner != NULL ? "In place" : "NULL");
2003-09-08 21:06:34 +00:00
}
/*
TODO list:
2003-04-17 20:41:54 +00:00
*) test arguments for SetWindowAligment
*) call hook functions: MenusBeginning, MenusEnded. Add menu activation code.
*/