2 * Copyright 2001-2016 Haiku, Inc. All rights reserved
3 * Distributed under the terms of the MIT License.
6 * Stephan Aßmus, superstippi@gmx.de
7 * Axel Dörfler, axeld@pinc-software.de
8 * Adrian Oanca, adioanca@cotty.iren.ro
9 * John Scipione, jscipione@gmail.com
20 #include <Application.h>
22 #include <AppServerLink.h>
23 #include <ApplicationPrivate.h>
28 #include <DirectMessageTarget.h>
29 #include <FindDirectory.h>
30 #include <InputServerTypes.h>
32 #include <LayoutUtils.h>
35 #include <MenuPrivate.h>
36 #include <MessagePrivate.h>
37 #include <MessageQueue.h>
38 #include <MessageRunner.h>
41 #include <PropertyInfo.h>
43 #include <RosterPrivate.h>
45 #include <ServerProtocol.h>
48 #include <TokenSpace.h>
49 #include <ToolTipManager.h>
50 #include <ToolTipWindow.h>
51 #include <UnicodeChar.h>
52 #include <WindowPrivate.h>
54 #include <binary_compatibility/Interface.h>
55 #include <input_globals.h>
56 #include <tracker_private.h>
61 # define STRACE(x) printf x
66 #define B_HIDE_APPLICATION '_AHD'
67 // if we ever move this to a public namespace, we should also move the
68 // handling of this message into BApplication
70 #define _MINIMIZE_ '_WMZ'
72 #define _SEND_BEHIND_ '_WSB'
73 #define _SEND_TO_FRONT_ '_WSF'
76 void do_minimize_team(BRect zoomRect
, team_id team
, bool zoom
);
79 struct BWindow::unpack_cookie
{
86 int32 last_view_token
;
92 class BWindow::Shortcut
{
94 Shortcut(uint32 key
, uint32 modifiers
,
96 Shortcut(uint32 key
, uint32 modifiers
,
97 BMessage
* message
, BHandler
* target
);
100 bool Matches(uint32 key
, uint32 modifiers
) const;
102 BMenuItem
* MenuItem() const { return fMenuItem
; }
103 BMessage
* Message() const { return fMessage
; }
104 BHandler
* Target() const { return fTarget
; }
106 static uint32
AllowedModifiers();
107 static uint32
PrepareKey(uint32 key
);
108 static uint32
PrepareModifiers(uint32 modifiers
);
113 BMenuItem
* fMenuItem
;
119 using BPrivate::gDefaultTokens
;
120 using BPrivate::MenuPrivate
;
122 static property_info sWindowPropInfo
[] = {
124 "Active", { B_GET_PROPERTY
, B_SET_PROPERTY
},
125 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_BOOL_TYPE
}
129 "Feel", { B_GET_PROPERTY
, B_SET_PROPERTY
},
130 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_INT32_TYPE
}
134 "Flags", { B_GET_PROPERTY
, B_SET_PROPERTY
},
135 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_INT32_TYPE
}
139 "Frame", { B_GET_PROPERTY
, B_SET_PROPERTY
},
140 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_RECT_TYPE
}
144 "Hidden", { B_GET_PROPERTY
, B_SET_PROPERTY
},
145 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_BOOL_TYPE
}
149 "Look", { B_GET_PROPERTY
, B_SET_PROPERTY
},
150 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_INT32_TYPE
}
154 "Title", { B_GET_PROPERTY
, B_SET_PROPERTY
},
155 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_STRING_TYPE
}
159 "Workspaces", { B_GET_PROPERTY
, B_SET_PROPERTY
},
160 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_INT32_TYPE
}
165 { B_DIRECT_SPECIFIER
}, NULL
, 0, {}
169 "View", { B_COUNT_PROPERTIES
},
170 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_INT32_TYPE
}
174 "View", {}, {}, NULL
, 0, {}
178 "Minimize", { B_GET_PROPERTY
, B_SET_PROPERTY
},
179 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_BOOL_TYPE
}
183 "TabFrame", { B_GET_PROPERTY
},
184 { B_DIRECT_SPECIFIER
}, NULL
, 0, { B_RECT_TYPE
}
190 static value_info sWindowValueInfo
[] = {
192 "MoveTo", 'WDMT', B_COMMAND_KIND
,
193 "Moves to the position in the BPoint data"
197 "MoveBy", 'WDMB', B_COMMAND_KIND
,
198 "Moves by the offsets in the BPoint data"
202 "ResizeTo", 'WDRT', B_COMMAND_KIND
,
203 "Resize to the size in the BPoint data"
207 "ResizeBy", 'WDRB', B_COMMAND_KIND
,
208 "Resize by the offsets in the BPoint data"
216 _set_menu_sem_(BWindow
* window
, sem_id sem
)
219 window
->fMenuSem
= sem
;
226 BWindow::unpack_cookie::unpack_cookie()
228 message((BMessage
*)~0UL),
229 // message == NULL is our exit condition
231 focus_token(B_NULL_TOKEN
),
232 last_view_token(B_NULL_TOKEN
),
234 tokens_scanned(false)
239 // #pragma mark - BWindow::Shortcut
242 BWindow::Shortcut::Shortcut(uint32 key
, uint32 modifiers
, BMenuItem
* item
)
244 fKey(PrepareKey(key
)),
245 fModifiers(PrepareModifiers(modifiers
)),
253 BWindow::Shortcut::Shortcut(uint32 key
, uint32 modifiers
, BMessage
* message
,
256 fKey(PrepareKey(key
)),
257 fModifiers(PrepareModifiers(modifiers
)),
265 BWindow::Shortcut::~Shortcut()
267 // we own the message, if any
273 BWindow::Shortcut::Matches(uint32 key
, uint32 modifiers
) const
275 return fKey
== key
&& fModifiers
== modifiers
;
281 BWindow::Shortcut::AllowedModifiers()
283 return B_COMMAND_KEY
| B_OPTION_KEY
| B_SHIFT_KEY
| B_CONTROL_KEY
290 BWindow::Shortcut::PrepareModifiers(uint32 modifiers
)
292 return (modifiers
& AllowedModifiers()) | B_COMMAND_KEY
;
298 BWindow::Shortcut::PrepareKey(uint32 key
)
300 return BUnicodeChar::ToLower(key
);
304 // #pragma mark - BWindow
307 BWindow::BWindow(BRect frame
, const char* title
, window_type type
,
308 uint32 flags
, uint32 workspace
)
310 BLooper(title
, B_DISPLAY_PRIORITY
)
314 _DecomposeType(type
, &look
, &feel
);
316 _InitData(frame
, title
, look
, feel
, flags
, workspace
);
320 BWindow::BWindow(BRect frame
, const char* title
, window_look look
,
321 window_feel feel
, uint32 flags
, uint32 workspace
)
323 BLooper(title
, B_DISPLAY_PRIORITY
)
325 _InitData(frame
, title
, look
, feel
, flags
, workspace
);
329 BWindow::BWindow(BMessage
* data
)
333 data
->FindRect("_frame", &fFrame
);
336 data
->FindString("_title", &title
);
339 data
->FindInt32("_wlook", (int32
*)&look
);
342 data
->FindInt32("_wfeel", (int32
*)&feel
);
344 if (data
->FindInt32("_flags", (int32
*)&fFlags
) != B_OK
)
348 data
->FindInt32("_wspace", (int32
*)&workspaces
);
351 if (data
->FindInt32("_type", (int32
*)&type
) == B_OK
)
352 _DecomposeType((window_type
)type
, &fLook
, &fFeel
);
354 // connect to app_server and initialize data
355 _InitData(fFrame
, title
, look
, feel
, fFlags
, workspaces
);
357 if (data
->FindFloat("_zoom", 0, &fMaxZoomWidth
) == B_OK
358 && data
->FindFloat("_zoom", 1, &fMaxZoomHeight
) == B_OK
)
359 SetZoomLimits(fMaxZoomWidth
, fMaxZoomHeight
);
361 if (data
->FindFloat("_sizel", 0, &fMinWidth
) == B_OK
362 && data
->FindFloat("_sizel", 1, &fMinHeight
) == B_OK
363 && data
->FindFloat("_sizel", 2, &fMaxWidth
) == B_OK
364 && data
->FindFloat("_sizel", 3, &fMaxHeight
) == B_OK
)
365 SetSizeLimits(fMinWidth
, fMaxWidth
,
366 fMinHeight
, fMaxHeight
);
368 if (data
->FindInt64("_pulse", &fPulseRate
) == B_OK
)
369 SetPulseRate(fPulseRate
);
373 while (data
->FindMessage("_views", i
++, &msg
) == B_OK
) {
374 BArchivable
* obj
= instantiate_object(&msg
);
375 if (BView
* child
= dynamic_cast<BView
*>(obj
))
381 BWindow::BWindow(BRect frame
, int32 bitmapToken
)
383 BLooper("offscreen bitmap")
385 _DecomposeType(B_UNTYPED_WINDOW
, &fLook
, &fFeel
);
386 _InitData(frame
, "offscreen", fLook
, fFeel
, 0, 0, bitmapToken
);
392 if (BMenu
* menu
= dynamic_cast<BMenu
*>(fFocus
)) {
393 MenuPrivate(menu
).QuitTracking();
396 // The BWindow is locked when the destructor is called,
397 // we need to unlock because the menubar thread tries
398 // to post a message, which will deadlock otherwise.
399 // TODO: I replaced Unlock() with UnlockFully() because the window
400 // was kept locked after that in case it was closed using ALT-W.
401 // There might be an extra Lock() somewhere in the quitting path...
404 // Wait if a menu is still tracking
406 while (acquire_sem(fMenuSem
) == B_INTERRUPTED
)
412 fTopView
->RemoveSelf();
415 // remove all remaining shortcuts
416 int32 shortCutCount
= fShortcuts
.CountItems();
417 for (int32 i
= 0; i
< shortCutCount
; i
++) {
418 delete (Shortcut
*)fShortcuts
.ItemAtFast(i
);
421 // TODO: release other dynamically-allocated objects
427 // tell app_server about our demise
428 fLink
->StartMessage(AS_DELETE_WINDOW
);
429 // sync with the server so that for example
430 // a BBitmap can be sure that there are no
431 // more pending messages that are executed
432 // after the bitmap is deleted (which uses
433 // a different link and server side thread)
435 fLink
->FlushWithReply(code
);
437 // the sender port belongs to the app_server
438 delete_port(fLink
->ReceiverPort());
444 BWindow::Instantiate(BMessage
* data
)
446 if (!validate_instantiation(data
, "BWindow"))
449 return new(std::nothrow
) BWindow(data
);
454 BWindow::Archive(BMessage
* data
, bool deep
) const
456 status_t ret
= BLooper::Archive(data
, deep
);
459 ret
= data
->AddRect("_frame", fFrame
);
461 ret
= data
->AddString("_title", fTitle
);
463 ret
= data
->AddInt32("_wlook", fLook
);
465 ret
= data
->AddInt32("_wfeel", fFeel
);
466 if (ret
== B_OK
&& fFlags
!= 0)
467 ret
= data
->AddInt32("_flags", fFlags
);
469 ret
= data
->AddInt32("_wspace", (uint32
)Workspaces());
471 if (ret
== B_OK
&& !_ComposeType(fLook
, fFeel
))
472 ret
= data
->AddInt32("_type", (uint32
)Type());
474 if (fMaxZoomWidth
!= 32768.0 || fMaxZoomHeight
!= 32768.0) {
476 ret
= data
->AddFloat("_zoom", fMaxZoomWidth
);
478 ret
= data
->AddFloat("_zoom", fMaxZoomHeight
);
481 if (fMinWidth
!= 0.0 || fMinHeight
!= 0.0
482 || fMaxWidth
!= 32768.0 || fMaxHeight
!= 32768.0) {
484 ret
= data
->AddFloat("_sizel", fMinWidth
);
486 ret
= data
->AddFloat("_sizel", fMinHeight
);
488 ret
= data
->AddFloat("_sizel", fMaxWidth
);
490 ret
= data
->AddFloat("_sizel", fMaxHeight
);
493 if (ret
== B_OK
&& fPulseRate
!= 500000)
494 data
->AddInt64("_pulse", fPulseRate
);
496 if (ret
== B_OK
&& deep
) {
497 int32 noOfViews
= CountChildren();
498 for (int32 i
= 0; i
< noOfViews
; i
++){
499 BMessage childArchive
;
500 ret
= ChildAt(i
)->Archive(&childArchive
, true);
502 ret
= data
->AddMessage("_views", &childArchive
);
516 const char* name
= Name();
520 printf("ERROR - you must Lock a looper before calling Quit(), "
521 "team=%" B_PRId32
", looper=%s\n", Team(), name
);
526 // We're toast already
530 while (!IsHidden()) {
534 if (fFlags
& B_QUIT_ON_WINDOW_CLOSE
)
535 be_app
->PostMessage(B_QUIT_REQUESTED
);
542 BWindow::AddChild(BView
* child
, BView
* before
)
544 BAutolock
locker(this);
545 if (locker
.IsLocked())
546 fTopView
->AddChild(child
, before
);
551 BWindow::AddChild(BLayoutItem
* child
)
553 BAutolock
locker(this);
554 if (locker
.IsLocked())
555 fTopView
->AddChild(child
);
560 BWindow::RemoveChild(BView
* child
)
562 BAutolock
locker(this);
563 if (!locker
.IsLocked())
566 return fTopView
->RemoveChild(child
);
571 BWindow::CountChildren() const
573 BAutolock
locker(const_cast<BWindow
*>(this));
574 if (!locker
.IsLocked())
577 return fTopView
->CountChildren();
582 BWindow::ChildAt(int32 index
) const
584 BAutolock
locker(const_cast<BWindow
*>(this));
585 if (!locker
.IsLocked())
588 return fTopView
->ChildAt(index
);
593 BWindow::Minimize(bool minimize
)
595 if (IsModal() || IsFloating() || IsHidden() || fMinimized
== minimize
599 fMinimized
= minimize
;
601 fLink
->StartMessage(AS_MINIMIZE_WINDOW
);
602 fLink
->Attach
<bool>(minimize
);
610 BWindow::SendBehind(const BWindow
* window
)
615 fLink
->StartMessage(AS_SEND_BEHIND
);
616 fLink
->Attach
<int32
>(window
!= NULL
? _get_object_token_(window
) : -1);
617 fLink
->Attach
<team_id
>(Team());
619 status_t status
= B_ERROR
;
620 fLink
->FlushWithReply(status
);
629 BWindow::Flush() const
631 if (const_cast<BWindow
*>(this)->Lock()) {
633 const_cast<BWindow
*>(this)->Unlock();
639 BWindow::Sync() const
641 if (!const_cast<BWindow
*>(this)->Lock())
644 fLink
->StartMessage(AS_SYNC
);
646 // waiting for the reply is the actual syncing
648 fLink
->FlushWithReply(code
);
650 const_cast<BWindow
*>(this)->Unlock();
655 BWindow::DisableUpdates()
658 fLink
->StartMessage(AS_DISABLE_UPDATES
);
666 BWindow::EnableUpdates()
669 fLink
->StartMessage(AS_ENABLE_UPDATES
);
677 BWindow::BeginViewTransaction()
680 fInTransaction
= true;
687 BWindow::EndViewTransaction()
692 fInTransaction
= false;
699 BWindow::InViewTransaction() const
701 BAutolock
locker(const_cast<BWindow
*>(this));
702 return fInTransaction
;
707 BWindow::IsFront() const
709 BAutolock
locker(const_cast<BWindow
*>(this));
710 if (!locker
.IsLocked())
713 fLink
->StartMessage(AS_IS_FRONT_WINDOW
);
716 if (fLink
->FlushWithReply(status
) == B_OK
)
717 return status
>= B_OK
;
724 BWindow::MessageReceived(BMessage
* message
)
726 if (!message
->HasSpecifiers()) {
727 if (message
->what
== B_KEY_DOWN
)
728 _KeyboardNavigation();
730 if (message
->what
== (int32
)kMsgAppServerRestarted
) {
731 fLink
->SetSenderPort(
732 BApplication::Private::ServerLink()->SenderPort());
734 BPrivate::AppServerLink lockLink
;
735 // we're talking to the server application using our own
736 // communication channel (fLink) - we better make sure no one
737 // interferes by locking that channel (which AppServerLink does
740 fLink
->StartMessage(AS_CREATE_WINDOW
);
742 fLink
->Attach
<BRect
>(fFrame
);
743 fLink
->Attach
<uint32
>((uint32
)fLook
);
744 fLink
->Attach
<uint32
>((uint32
)fFeel
);
745 fLink
->Attach
<uint32
>(fFlags
);
746 fLink
->Attach
<uint32
>(0);
747 fLink
->Attach
<int32
>(_get_object_token_(this));
748 fLink
->Attach
<port_id
>(fLink
->ReceiverPort());
749 fLink
->Attach
<port_id
>(fMsgPort
);
750 fLink
->AttachString(fTitle
);
754 if (fLink
->FlushWithReply(code
) == B_OK
756 && fLink
->Read
<port_id
>(&sendPort
) == B_OK
) {
757 // read the frame size and its limits that were really
758 // enforced on the server side
760 fLink
->Read
<BRect
>(&fFrame
);
761 fLink
->Read
<float>(&fMinWidth
);
762 fLink
->Read
<float>(&fMaxWidth
);
763 fLink
->Read
<float>(&fMinHeight
);
764 fLink
->Read
<float>(&fMaxHeight
);
766 fMaxZoomWidth
= fMaxWidth
;
767 fMaxZoomHeight
= fMaxHeight
;
771 // Redirect our link to the new window connection
772 fLink
->SetSenderPort(sendPort
);
774 // connect all views to the server again
775 fTopView
->_CreateSelf();
777 _SendShowOrHideMessage();
780 return BLooper::MessageReceived(message
);
783 BMessage
replyMsg(B_REPLY
);
784 bool handled
= false;
791 if (message
->GetCurrentSpecifier(&index
, &specifier
, &what
, &prop
) != B_OK
)
792 return BLooper::MessageReceived(message
);
794 BPropertyInfo
propertyInfo(sWindowPropInfo
);
795 switch (propertyInfo
.FindMatch(message
, index
, &specifier
, what
, prop
)) {
797 if (message
->what
== B_GET_PROPERTY
) {
798 replyMsg
.AddBool("result", IsActive());
800 } else if (message
->what
== B_SET_PROPERTY
) {
802 if (message
->FindBool("data", &newActive
) == B_OK
) {
809 if (message
->what
== B_GET_PROPERTY
) {
810 replyMsg
.AddInt32("result", (uint32
)Feel());
814 if (message
->FindInt32("data", (int32
*)&newFeel
) == B_OK
) {
815 SetFeel((window_feel
)newFeel
);
821 if (message
->what
== B_GET_PROPERTY
) {
822 replyMsg
.AddInt32("result", Flags());
826 if (message
->FindInt32("data", (int32
*)&newFlags
) == B_OK
) {
833 if (message
->what
== B_GET_PROPERTY
) {
834 replyMsg
.AddRect("result", Frame());
838 if (message
->FindRect("data", &newFrame
) == B_OK
) {
839 MoveTo(newFrame
.LeftTop());
840 ResizeTo(newFrame
.Width(), newFrame
.Height());
846 if (message
->what
== B_GET_PROPERTY
) {
847 replyMsg
.AddBool("result", IsHidden());
851 if (message
->FindBool("data", &hide
) == B_OK
) {
855 } else if (IsHidden())
862 if (message
->what
== B_GET_PROPERTY
) {
863 replyMsg
.AddInt32("result", (uint32
)Look());
867 if (message
->FindInt32("data", (int32
*)&newLook
) == B_OK
) {
868 SetLook((window_look
)newLook
);
874 if (message
->what
== B_GET_PROPERTY
) {
875 replyMsg
.AddString("result", Title());
878 const char* newTitle
= NULL
;
879 if (message
->FindString("data", &newTitle
) == B_OK
) {
886 if (message
->what
== B_GET_PROPERTY
) {
887 replyMsg
.AddInt32( "result", Workspaces());
890 uint32 newWorkspaces
;
891 if (message
->FindInt32("data", (int32
*)&newWorkspaces
) == B_OK
) {
892 SetWorkspaces(newWorkspaces
);
898 if (message
->what
== B_GET_PROPERTY
) {
899 replyMsg
.AddBool("result", IsMinimized());
903 if (message
->FindBool("data", &minimize
) == B_OK
) {
910 if (message
->what
== B_GET_PROPERTY
) {
912 if (GetDecoratorSettings(&settings
) == B_OK
) {
914 if (settings
.FindRect("tab frame", &frame
) == B_OK
) {
915 replyMsg
.AddRect("result", frame
);
922 return BLooper::MessageReceived(message
);
926 if (message
->what
== B_SET_PROPERTY
)
927 replyMsg
.AddInt32("error", B_OK
);
929 replyMsg
.what
= B_MESSAGE_NOT_UNDERSTOOD
;
930 replyMsg
.AddInt32("error", B_BAD_SCRIPT_SYNTAX
);
931 replyMsg
.AddString("message", "Didn't understand the specifier(s)");
933 message
->SendReply(&replyMsg
);
938 BWindow::DispatchMessage(BMessage
* message
, BHandler
* target
)
943 switch (message
->what
) {
949 // Used by the minimize shortcut
950 if ((Flags() & B_NOT_MINIMIZABLE
) == 0)
955 // Used by the zoom shortcut
956 if ((Flags() & B_NOT_ZOOMABLE
) == 0)
964 case _SEND_TO_FRONT_
:
971 if (message
->FindBool("minimize", &minimize
) == B_OK
)
976 case B_HIDE_APPLICATION
:
978 // Hide all applications with the same signature
979 // (ie. those that are part of the same group to be consistent
980 // to what the Deskbar shows you).
982 be_app
->GetAppInfo(&info
);
985 be_roster
->GetAppList(info
.signature
, &list
);
987 for (int32 i
= 0; i
< list
.CountItems(); i
++) {
988 do_minimize_team(BRect(), (team_id
)(addr_t
)list
.ItemAt(i
),
994 case B_WINDOW_RESIZED
:
997 if (message
->FindInt32("width", &width
) == B_OK
998 && message
->FindInt32("height", &height
) == B_OK
) {
999 // combine with pending resize notifications
1000 BMessage
* pendingMessage
;
1001 while ((pendingMessage
1002 = MessageQueue()->FindMessage(B_WINDOW_RESIZED
, 0))) {
1004 if (pendingMessage
->FindInt32("width", &nextWidth
) == B_OK
)
1008 if (pendingMessage
->FindInt32("height", &nextHeight
)
1010 height
= nextHeight
;
1013 MessageQueue()->RemoveMessage(pendingMessage
);
1014 delete pendingMessage
;
1015 // this deletes the first *additional* message
1016 // fCurrentMessage is safe
1018 if (width
!= fFrame
.Width() || height
!= fFrame
.Height()) {
1019 // NOTE: we might have already handled the resize
1020 // in an _UPDATE_ message
1021 fFrame
.right
= fFrame
.left
+ width
;
1022 fFrame
.bottom
= fFrame
.top
+ height
;
1025 // FrameResized(width, height);
1027 // call hook function anyways
1028 // TODO: When a window is resized programmatically,
1029 // it receives this message, and maybe it is wise to
1030 // keep the asynchronous nature of this process to
1031 // not risk breaking any apps.
1032 FrameResized(width
, height
);
1037 case B_WINDOW_MOVED
:
1040 if (message
->FindPoint("where", &origin
) == B_OK
) {
1041 if (fFrame
.LeftTop() != origin
) {
1042 // NOTE: we might have already handled the move
1043 // in an _UPDATE_ message
1044 fFrame
.OffsetTo(origin
);
1046 // FrameMoved(origin);
1048 // call hook function anyways
1049 // TODO: When a window is moved programmatically,
1050 // it receives this message, and maybe it is wise to
1051 // keep the asynchronous nature of this process to
1052 // not risk breaking any apps.
1058 case B_WINDOW_ACTIVATED
:
1059 if (target
!= this) {
1060 target
->MessageReceived(message
);
1065 if (message
->FindBool("active", &active
) != B_OK
)
1068 // find latest activation message
1071 BMessage
* pendingMessage
= MessageQueue()->FindMessage(
1072 B_WINDOW_ACTIVATED
, 0);
1073 if (pendingMessage
== NULL
)
1077 if (pendingMessage
->FindBool("active", &nextActive
) == B_OK
)
1078 active
= nextActive
;
1080 MessageQueue()->RemoveMessage(pendingMessage
);
1081 delete pendingMessage
;
1084 if (active
!= fActive
) {
1087 WindowActivated(active
);
1089 // call hook function 'WindowActivated(bool)' for all
1090 // views attached to this window.
1091 fTopView
->_Activate(active
);
1093 // we notify the input server if we are gaining or losing focus
1094 // from a view which has the B_INPUT_METHOD_AWARE on a window
1098 bool inputMethodAware
= false;
1100 inputMethodAware
= fFocus
->Flags() & B_INPUT_METHOD_AWARE
;
1101 BMessage
message(inputMethodAware
? IS_FOCUS_IM_AWARE_VIEW
: IS_UNFOCUS_IM_AWARE_VIEW
);
1102 BMessenger
messenger(fFocus
);
1105 message
.AddMessenger("view", messenger
);
1106 _control_input_server_(&message
, &reply
);
1110 case B_SCREEN_CHANGED
:
1111 if (target
== this) {
1114 if (message
->FindRect("frame", &frame
) == B_OK
1115 && message
->FindInt32("mode", (int32
*)&mode
) == B_OK
) {
1116 // propegate message to child views
1117 int32 childCount
= CountChildren();
1118 for (int32 i
= 0; i
< childCount
; i
++) {
1119 BView
* view
= ChildAt(i
);
1121 view
->MessageReceived(message
);
1124 ScreenChanged(frame
, (color_space
)mode
);
1127 target
->MessageReceived(message
);
1130 case B_WORKSPACE_ACTIVATED
:
1131 if (target
== this) {
1134 if (message
->FindInt32("workspace", (int32
*)&workspace
) == B_OK
1135 && message
->FindBool("active", &active
) == B_OK
)
1136 WorkspaceActivated(workspace
, active
);
1138 target
->MessageReceived(message
);
1141 case B_WORKSPACES_CHANGED
:
1142 if (target
== this) {
1143 uint32 oldWorkspace
, newWorkspace
;
1144 if (message
->FindInt32("old", (int32
*)&oldWorkspace
) == B_OK
1145 && message
->FindInt32("new", (int32
*)&newWorkspace
) == B_OK
)
1146 WorkspacesChanged(oldWorkspace
, newWorkspace
);
1148 target
->MessageReceived(message
);
1153 if (BView
* view
= dynamic_cast<BView
*>(target
)) {
1155 if (message
->FindRect("be:area", &rect
) == B_OK
)
1156 view
->Invalidate(rect
);
1160 target
->MessageReceived(message
);
1166 if (!_HandleKeyDown(message
)) {
1167 if (BView
* view
= dynamic_cast<BView
*>(target
)) {
1168 // TODO: cannot use "string" here if we support having
1169 // different font encoding per view (it's supposed to be
1170 // converted by _HandleKeyDown() one day)
1173 if (message
->FindData("bytes", B_STRING_TYPE
,
1174 (const void**)&string
, &bytes
) == B_OK
) {
1175 view
->KeyDown(string
, bytes
- 1);
1178 target
->MessageReceived(message
);
1185 // TODO: same as above
1186 if (BView
* view
= dynamic_cast<BView
*>(target
)) {
1189 if (message
->FindData("bytes", B_STRING_TYPE
,
1190 (const void**)&string
, &bytes
) == B_OK
) {
1191 view
->KeyUp(string
, bytes
- 1);
1194 target
->MessageReceived(message
);
1198 case B_UNMAPPED_KEY_DOWN
:
1200 if (!_HandleUnmappedKeyDown(message
))
1201 target
->MessageReceived(message
);
1207 BView
* view
= dynamic_cast<BView
*>(target
);
1211 message
->FindPoint("be:view_where", &where
);
1212 view
->MouseDown(where
);
1214 target
->MessageReceived(message
);
1221 if (BView
* view
= dynamic_cast<BView
*>(target
)) {
1223 message
->FindPoint("be:view_where", &where
);
1224 view
->fMouseEventOptions
= 0;
1225 view
->MouseUp(where
);
1227 target
->MessageReceived(message
);
1234 if (BView
* view
= dynamic_cast<BView
*>(target
)) {
1235 uint32 eventOptions
= view
->fEventOptions
1236 | view
->fMouseEventOptions
;
1237 bool noHistory
= eventOptions
& B_NO_POINTER_HISTORY
;
1238 bool dropIfLate
= !(eventOptions
& B_FULL_POINTER_HISTORY
);
1240 bigtime_t eventTime
;
1241 if (message
->FindInt64("when", (int64
*)&eventTime
) < B_OK
)
1242 eventTime
= system_time();
1245 message
->FindInt32("be:transit", (int32
*)&transit
);
1246 // don't drop late messages with these important transit values
1247 if (transit
== B_ENTERED_VIEW
|| transit
== B_EXITED_VIEW
)
1250 // TODO: The dropping code may have the following problem:
1251 // On slower computers, 20ms may just be to abitious a delay.
1252 // There, we might constantly check the message queue for a
1253 // newer message, not find any, and still use the only but
1254 // later than 20ms message, which of course makes the whole
1255 // thing later than need be. An adaptive delay would be
1256 // kind of neat, but would probably use additional BWindow
1257 // members to count the successful versus fruitless queue
1258 // searches and the delay value itself or something similar.
1261 || (dropIfLate
&& (system_time() - eventTime
> 20000))) {
1262 // filter out older mouse moved messages in the queue
1264 BMessageQueue
* queue
= MessageQueue();
1268 for (int32 i
= 0; (moved
= queue
->FindMessage(i
)) != NULL
;
1270 if (moved
!= message
&& moved
->what
== B_MOUSE_MOVED
) {
1271 // there is a newer mouse moved message in the
1272 // queue, just ignore the current one, the newer one
1273 // will be handled here eventually
1283 message
->FindPoint("be:view_where", &where
);
1284 message
->FindInt32("buttons", (int32
*)&buttons
);
1286 if (transit
== B_EXITED_VIEW
|| transit
== B_OUTSIDE_VIEW
) {
1287 if (dynamic_cast<BPrivate::ToolTipWindow
*>(this) == NULL
)
1288 BToolTipManager::Manager()->HideTip();
1291 BMessage
* dragMessage
= NULL
;
1292 if (message
->HasMessage("be:drag_message")) {
1293 dragMessage
= new BMessage();
1294 if (message
->FindMessage("be:drag_message", dragMessage
)
1301 view
->MouseMoved(where
, transit
, dragMessage
);
1304 target
->MessageReceived(message
);
1310 if (target
== this && fPulseRunner
) {
1314 target
->MessageReceived(message
);
1319 //bigtime_t now = system_time();
1320 //bigtime_t drawTime = 0;
1321 STRACE(("info:BWindow handling _UPDATE_.\n"));
1323 fLink
->StartMessage(AS_BEGIN_UPDATE
);
1324 fInTransaction
= true;
1327 if (fLink
->FlushWithReply(code
) == B_OK
1329 // read current window position and size first,
1330 // the update rect is in screen coordinates...
1331 // so we need to be up to date
1333 fLink
->Read
<BPoint
>(&origin
);
1336 fLink
->Read
<float>(&width
);
1337 fLink
->Read
<float>(&height
);
1338 if (origin
!= fFrame
.LeftTop()) {
1339 // TODO: remove code duplicatation with
1340 // B_WINDOW_MOVED case...
1341 //printf("window position was not up to date\n");
1342 fFrame
.OffsetTo(origin
);
1345 if (width
!= fFrame
.Width() || height
!= fFrame
.Height()) {
1346 // TODO: remove code duplicatation with
1347 // B_WINDOW_RESIZED case...
1348 //printf("window size was not up to date\n");
1349 fFrame
.right
= fFrame
.left
+ width
;
1350 fFrame
.bottom
= fFrame
.top
+ height
;
1353 FrameResized(width
, height
);
1356 // read tokens for views that need to be drawn
1357 // NOTE: we need to read the tokens completely
1358 // first, we cannot draw views in between reading
1359 // the tokens, since other communication would likely
1360 // mess up the data in the link.
1361 struct ViewUpdateInfo
{
1367 // read next token and create/add ViewUpdateInfo
1369 status_t error
= fLink
->Read
<int32
>(&token
);
1370 if (error
< B_OK
|| token
== B_NULL_TOKEN
)
1372 ViewUpdateInfo
* info
= new(std::nothrow
) ViewUpdateInfo
;
1373 if (info
== NULL
|| !infos
.AddItem(info
)) {
1377 info
->token
= token
;
1378 // read culmulated update rect (is in screen coords)
1379 error
= fLink
->Read
<BRect
>(&(info
->updateRect
));
1384 int32 count
= infos
.CountItems();
1385 for (int32 i
= 0; i
< count
; i
++) {
1386 //bigtime_t drawStart = system_time();
1387 ViewUpdateInfo
* info
1388 = (ViewUpdateInfo
*)infos
.ItemAtFast(i
);
1389 if (BView
* view
= _FindView(info
->token
))
1390 view
->_Draw(info
->updateRect
);
1392 printf("_UPDATE_ - didn't find view by token: %"
1393 B_PRId32
"\n", info
->token
);
1395 //drawTime += system_time() - drawStart;
1397 // NOTE: The tokens are actually hirachically sorted,
1398 // so traversing the list in revers and calling
1399 // child->_DrawAfterChildren() actually works like intended.
1400 for (int32 i
= count
- 1; i
>= 0; i
--) {
1401 ViewUpdateInfo
* info
1402 = (ViewUpdateInfo
*)infos
.ItemAtFast(i
);
1403 if (BView
* view
= _FindView(info
->token
))
1404 view
->_DrawAfterChildren(info
->updateRect
);
1408 //printf(" %ld views drawn, total Draw() time: %lld\n", count, drawTime);
1411 fLink
->StartMessage(AS_END_UPDATE
);
1413 fInTransaction
= false;
1414 fUpdateRequested
= false;
1416 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now);
1424 // These two are obviously some kind of old scripting messages
1425 // this is NOT an app_server message and we have to be cautious
1426 case B_WINDOW_MOVE_BY
:
1429 if (message
->FindPoint("data", &offset
) == B_OK
)
1430 MoveBy(offset
.x
, offset
.y
);
1432 message
->SendReply(B_MESSAGE_NOT_UNDERSTOOD
);
1436 // this is NOT an app_server message and we have to be cautious
1437 case B_WINDOW_MOVE_TO
:
1440 if (message
->FindPoint("data", &origin
) == B_OK
)
1443 message
->SendReply(B_MESSAGE_NOT_UNDERSTOOD
);
1447 case B_LAYOUT_WINDOW
:
1453 case B_COLORS_UPDATED
:
1455 fTopView
->_ColorsUpdated(message
);
1456 target
->MessageReceived(message
);
1460 case B_FONTS_UPDATED
:
1462 fTopView
->_FontsUpdated(message
);
1463 target
->MessageReceived(message
);
1468 BLooper::DispatchMessage(message
, target
);
1475 BWindow::FrameMoved(BPoint newPosition
)
1483 BWindow::FrameResized(float newWidth
, float newHeight
)
1491 BWindow::WorkspacesChanged(uint32 oldWorkspaces
, uint32 newWorkspaces
)
1499 BWindow::WorkspaceActivated(int32 workspace
, bool state
)
1507 BWindow::MenusBeginning()
1515 BWindow::MenusEnded()
1523 BWindow::SetSizeLimits(float minWidth
, float maxWidth
,
1524 float minHeight
, float maxHeight
)
1526 if (minWidth
> maxWidth
|| minHeight
> maxHeight
)
1532 fLink
->StartMessage(AS_SET_SIZE_LIMITS
);
1533 fLink
->Attach
<float>(minWidth
);
1534 fLink
->Attach
<float>(maxWidth
);
1535 fLink
->Attach
<float>(minHeight
);
1536 fLink
->Attach
<float>(maxHeight
);
1539 if (fLink
->FlushWithReply(code
) == B_OK
1541 // read the values that were really enforced on
1542 // the server side (the window frame could have
1543 // been changed, too)
1544 fLink
->Read
<BRect
>(&fFrame
);
1545 fLink
->Read
<float>(&fMinWidth
);
1546 fLink
->Read
<float>(&fMaxWidth
);
1547 fLink
->Read
<float>(&fMinHeight
);
1548 fLink
->Read
<float>(&fMaxHeight
);
1551 // TODO: the same has to be done for SetLook() (that can alter
1552 // the size limits, and hence, the size of the window
1559 BWindow::GetSizeLimits(float* _minWidth
, float* _maxWidth
, float* _minHeight
,
1562 // TODO: What about locking?!?
1563 if (_minHeight
!= NULL
)
1564 *_minHeight
= fMinHeight
;
1565 if (_minWidth
!= NULL
)
1566 *_minWidth
= fMinWidth
;
1567 if (_maxHeight
!= NULL
)
1568 *_maxHeight
= fMaxHeight
;
1569 if (_maxWidth
!= NULL
)
1570 *_maxWidth
= fMaxWidth
;
1575 BWindow::UpdateSizeLimits()
1577 BAutolock
locker(this);
1579 if ((fFlags
& B_AUTO_UPDATE_SIZE_LIMITS
) != 0) {
1580 // Get min/max constraints of the top view and enforce window
1581 // size limits respectively.
1582 BSize minSize
= fTopView
->MinSize();
1583 BSize maxSize
= fTopView
->MaxSize();
1584 SetSizeLimits(minSize
.width
, maxSize
.width
,
1585 minSize
.height
, maxSize
.height
);
1591 BWindow::SetDecoratorSettings(const BMessage
& settings
)
1593 // flatten the given settings into a buffer and send
1594 // it to the app_server to apply the settings to the
1597 int32 size
= settings
.FlattenedSize();
1599 status_t status
= settings
.Flatten(buffer
, size
);
1606 status
= fLink
->StartMessage(AS_SET_DECORATOR_SETTINGS
);
1609 status
= fLink
->Attach
<int32
>(size
);
1612 status
= fLink
->Attach(buffer
, size
);
1615 status
= fLink
->Flush();
1624 BWindow::GetDecoratorSettings(BMessage
* settings
) const
1626 // read a flattened settings message from the app_server
1627 // and put it into settings
1629 if (!const_cast<BWindow
*>(this)->Lock())
1632 status_t status
= fLink
->StartMessage(AS_GET_DECORATOR_SETTINGS
);
1634 if (status
== B_OK
) {
1636 status
= fLink
->FlushWithReply(code
);
1637 if (status
== B_OK
&& code
!= B_OK
)
1641 if (status
== B_OK
) {
1643 status
= fLink
->Read
<int32
>(&size
);
1644 if (status
== B_OK
) {
1646 status
= fLink
->Read(buffer
, size
);
1647 if (status
== B_OK
) {
1648 status
= settings
->Unflatten(buffer
);
1653 const_cast<BWindow
*>(this)->Unlock();
1660 BWindow::SetZoomLimits(float maxWidth
, float maxHeight
)
1662 // TODO: What about locking?!?
1663 if (maxWidth
> fMaxWidth
)
1664 maxWidth
= fMaxWidth
;
1666 fMaxZoomWidth
= maxWidth
;
1668 if (maxHeight
> fMaxHeight
)
1669 maxHeight
= fMaxHeight
;
1671 fMaxZoomHeight
= maxHeight
;
1676 BWindow::Zoom(BPoint origin
, float width
, float height
)
1678 // the default implementation of this hook function
1679 // just does the obvious:
1681 ResizeTo(width
, height
);
1688 // TODO: What about locking?!?
1691 // The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced
1692 // from the smallest of three rectangles:
1694 // 1) the rectangle defined by SetZoomLimits() and,
1695 // 2) the rectangle defined by SetSizeLimits()
1696 float maxZoomWidth
= std::min(fMaxZoomWidth
, fMaxWidth
);
1697 float maxZoomHeight
= std::min(fMaxZoomHeight
, fMaxHeight
);
1699 // 3) the screen rectangle
1700 BRect screenFrame
= (BScreen(this)).Frame();
1701 maxZoomWidth
= std::min(maxZoomWidth
, screenFrame
.Width());
1702 maxZoomHeight
= std::min(maxZoomHeight
, screenFrame
.Height());
1704 BRect zoomArea
= screenFrame
; // starts at screen size
1707 BRect deskbarFrame
= deskbar
.Frame();
1708 if (!deskbar
.IsAutoHide()) {
1709 // remove area taken up by Deskbar (if not auto-hidden)
1710 switch (deskbar
.Location()) {
1712 zoomArea
.top
= deskbarFrame
.bottom
+ 2;
1715 case B_DESKBAR_BOTTOM
:
1716 zoomArea
.bottom
= deskbarFrame
.top
- 2;
1719 // in vertical mode, only if not always on top and not auto-raise
1720 case B_DESKBAR_LEFT_TOP
:
1721 case B_DESKBAR_LEFT_BOTTOM
:
1722 if (!deskbar
.IsAlwaysOnTop() && !deskbar
.IsAutoRaise())
1723 zoomArea
.left
= deskbarFrame
.right
+ 2;
1727 case B_DESKBAR_RIGHT_TOP
:
1728 case B_DESKBAR_RIGHT_BOTTOM
:
1729 if (!deskbar
.IsAlwaysOnTop() && !deskbar
.IsAutoRaise())
1730 zoomArea
.right
= deskbarFrame
.left
- 2;
1735 // TODO: Broken for tab on left side windows...
1738 _GetDecoratorSize(&borderWidth
, &tabHeight
);
1740 // remove the area taken up by the tab and border
1741 zoomArea
.left
+= borderWidth
;
1742 zoomArea
.top
+= borderWidth
+ tabHeight
;
1743 zoomArea
.right
-= borderWidth
;
1744 zoomArea
.bottom
-= borderWidth
;
1746 // inset towards center vertically first to see if there will be room
1747 // above or below Deskbar
1748 if (zoomArea
.Height() > maxZoomHeight
)
1749 zoomArea
.InsetBy(0, roundf((zoomArea
.Height() - maxZoomHeight
) / 2));
1751 if (zoomArea
.top
> deskbarFrame
.bottom
1752 || zoomArea
.bottom
< deskbarFrame
.top
) {
1753 // there is room above or below Deskbar, start from screen width
1754 // minus borders instead of desktop width minus borders
1755 zoomArea
.left
= screenFrame
.left
+ borderWidth
;
1756 zoomArea
.right
= screenFrame
.right
- borderWidth
;
1759 // inset towards center
1760 if (zoomArea
.Width() > maxZoomWidth
)
1761 zoomArea
.InsetBy(roundf((zoomArea
.Width() - maxZoomWidth
) / 2), 0);
1765 if (fPreviousFrame
.IsValid()
1766 // NOTE: don't check for fFrame.LeftTop() == zoomArea.LeftTop()
1767 // -> makes it easier on the user to get a window back into place
1768 && fFrame
.Width() == zoomArea
.Width()
1769 && fFrame
.Height() == zoomArea
.Height()) {
1771 Zoom(fPreviousFrame
.LeftTop(), fPreviousFrame
.Width(),
1772 fPreviousFrame
.Height());
1778 // remember fFrame for later "unzooming"
1779 fPreviousFrame
= fFrame
;
1781 Zoom(zoomArea
.LeftTop(), zoomArea
.Width(), zoomArea
.Height());
1786 BWindow::ScreenChanged(BRect screenSize
, color_space depth
)
1793 BWindow::SetPulseRate(bigtime_t rate
)
1795 // TODO: What about locking?!?
1797 || (rate
== fPulseRate
&& !((rate
== 0) ^ (fPulseRunner
== NULL
))))
1803 if (fPulseRunner
== NULL
) {
1804 BMessage
message(B_PULSE
);
1805 fPulseRunner
= new(std::nothrow
) BMessageRunner(BMessenger(this),
1808 fPulseRunner
->SetInterval(rate
);
1812 delete fPulseRunner
;
1813 fPulseRunner
= NULL
;
1819 BWindow::PulseRate() const
1826 BWindow::AddShortcut(uint32 key
, uint32 modifiers
, BMenuItem
* item
)
1828 Shortcut
* shortcut
= new(std::nothrow
) Shortcut(key
, modifiers
, item
);
1829 if (shortcut
== NULL
)
1832 // removes the shortcut if it already exists!
1833 RemoveShortcut(key
, modifiers
);
1835 fShortcuts
.AddItem(shortcut
);
1840 BWindow::AddShortcut(uint32 key
, uint32 modifiers
, BMessage
* message
)
1842 AddShortcut(key
, modifiers
, message
, this);
1847 BWindow::AddShortcut(uint32 key
, uint32 modifiers
, BMessage
* message
,
1850 if (message
== NULL
)
1853 Shortcut
* shortcut
= new(std::nothrow
) Shortcut(key
, modifiers
, message
,
1855 if (shortcut
== NULL
)
1858 // removes the shortcut if it already exists!
1859 RemoveShortcut(key
, modifiers
);
1861 fShortcuts
.AddItem(shortcut
);
1866 BWindow::HasShortcut(uint32 key
, uint32 modifiers
)
1868 return _FindShortcut(key
, modifiers
) != NULL
;
1873 BWindow::RemoveShortcut(uint32 key
, uint32 modifiers
)
1875 Shortcut
* shortcut
= _FindShortcut(key
, modifiers
);
1876 if (shortcut
!= NULL
) {
1877 fShortcuts
.RemoveItem(shortcut
);
1879 } else if ((key
== 'q' || key
== 'Q') && modifiers
== B_COMMAND_KEY
) {
1880 // the quit shortcut is a fake shortcut
1881 fNoQuitShortcut
= true;
1887 BWindow::DefaultButton() const
1889 // TODO: What about locking?!?
1890 return fDefaultButton
;
1895 BWindow::SetDefaultButton(BButton
* button
)
1897 // TODO: What about locking?!?
1898 if (fDefaultButton
== button
)
1901 if (fDefaultButton
!= NULL
) {
1902 // tell old button it's no longer the default one
1903 BButton
* oldDefault
= fDefaultButton
;
1904 oldDefault
->MakeDefault(false);
1905 oldDefault
->Invalidate();
1908 fDefaultButton
= button
;
1910 if (button
!= NULL
) {
1911 // notify new default button
1912 fDefaultButton
->MakeDefault(true);
1913 fDefaultButton
->Invalidate();
1919 BWindow::NeedsUpdate() const
1921 if (!const_cast<BWindow
*>(this)->Lock())
1924 fLink
->StartMessage(AS_NEEDS_UPDATE
);
1926 int32 code
= B_ERROR
;
1927 fLink
->FlushWithReply(code
);
1929 const_cast<BWindow
*>(this)->Unlock();
1931 return code
== B_OK
;
1936 BWindow::UpdateIfNeeded()
1938 // works only from the window thread
1939 if (find_thread(NULL
) != Thread())
1942 // if the queue is already locked we are called recursivly
1943 // from our own dispatched update message
1944 if (((const BMessageQueue
*)MessageQueue())->IsLocked())
1950 // make sure all requests that would cause an update have
1951 // arrived at the server
1954 // Since we're blocking the event loop, we need to retrieve
1955 // all messages that are pending on the port.
1958 BMessageQueue
* queue
= MessageQueue();
1960 // First process and remove any _UPDATE_ message in the queue
1961 // With the current design, there can only be one at a time
1966 BMessage
* message
= queue
->FindMessage(_UPDATE_
, 0);
1967 queue
->RemoveMessage(message
);
1971 if (message
== NULL
)
1974 BWindow::DispatchMessage(message
, this);
1983 BWindow::FindView(const char* viewName
) const
1985 BAutolock
locker(const_cast<BWindow
*>(this));
1986 if (!locker
.IsLocked())
1989 return fTopView
->FindView(viewName
);
1994 BWindow::FindView(BPoint point
) const
1996 BAutolock
locker(const_cast<BWindow
*>(this));
1997 if (!locker
.IsLocked())
2000 // point is assumed to be in window coordinates,
2001 // fTopView has same bounds as window
2002 return _FindView(fTopView
, point
);
2007 BWindow::CurrentFocus() const
2014 BWindow::Activate(bool active
)
2021 // activating a window will also unminimize it
2023 fLink
->StartMessage(AS_ACTIVATE_WINDOW
);
2024 fLink
->Attach
<bool>(active
);
2033 BWindow::WindowActivated(bool focus
)
2041 BWindow::ConvertToScreen(BPoint
* point
) const
2043 point
->x
+= fFrame
.left
;
2044 point
->y
+= fFrame
.top
;
2049 BWindow::ConvertToScreen(BPoint point
) const
2051 return point
+ fFrame
.LeftTop();
2056 BWindow::ConvertFromScreen(BPoint
* point
) const
2058 point
->x
-= fFrame
.left
;
2059 point
->y
-= fFrame
.top
;
2064 BWindow::ConvertFromScreen(BPoint point
) const
2066 return point
- fFrame
.LeftTop();
2071 BWindow::ConvertToScreen(BRect
* rect
) const
2073 rect
->OffsetBy(fFrame
.LeftTop());
2078 BWindow::ConvertToScreen(BRect rect
) const
2080 return rect
.OffsetByCopy(fFrame
.LeftTop());
2085 BWindow::ConvertFromScreen(BRect
* rect
) const
2087 rect
->OffsetBy(-fFrame
.left
, -fFrame
.top
);
2092 BWindow::ConvertFromScreen(BRect rect
) const
2094 return rect
.OffsetByCopy(-fFrame
.left
, -fFrame
.top
);
2099 BWindow::IsMinimized() const
2101 BAutolock
locker(const_cast<BWindow
*>(this));
2102 if (!locker
.IsLocked())
2110 BWindow::Bounds() const
2112 return BRect(0, 0, fFrame
.Width(), fFrame
.Height());
2117 BWindow::Frame() const
2124 BWindow::DecoratorFrame() const
2126 BRect
decoratorFrame(Frame());
2127 BRect
tabRect(0, 0, 0, 0);
2129 float borderWidth
= 5.0;
2132 if (GetDecoratorSettings(&settings
) == B_OK
) {
2133 settings
.FindRect("tab frame", &tabRect
);
2134 settings
.FindFloat("border width", &borderWidth
);
2136 // probably no-border window look
2137 if (fLook
== B_NO_BORDER_WINDOW_LOOK
)
2139 else if (fLook
== B_BORDERED_WINDOW_LOOK
)
2141 // else use fall-back values from above
2144 if (fLook
== kLeftTitledWindowLook
) {
2145 decoratorFrame
.top
-= borderWidth
;
2146 decoratorFrame
.left
-= borderWidth
+ tabRect
.Width();
2147 decoratorFrame
.right
+= borderWidth
;
2148 decoratorFrame
.bottom
+= borderWidth
;
2150 decoratorFrame
.top
-= borderWidth
+ tabRect
.Height();
2151 decoratorFrame
.left
-= borderWidth
;
2152 decoratorFrame
.right
+= borderWidth
;
2153 decoratorFrame
.bottom
+= borderWidth
;
2156 return decoratorFrame
;
2161 BWindow::Size() const
2163 return BSize(fFrame
.Width(), fFrame
.Height());
2168 BWindow::Title() const
2175 BWindow::SetTitle(const char* title
)
2181 fTitle
= strdup(title
);
2185 // we notify the app_server so we can actually see the change
2187 fLink
->StartMessage(AS_SET_WINDOW_TITLE
);
2188 fLink
->AttachString(fTitle
);
2196 BWindow::IsActive() const
2203 BWindow::SetKeyMenuBar(BMenuBar
* bar
)
2210 BWindow::KeyMenuBar() const
2217 BWindow::IsModal() const
2219 return fFeel
== B_MODAL_SUBSET_WINDOW_FEEL
2220 || fFeel
== B_MODAL_APP_WINDOW_FEEL
2221 || fFeel
== B_MODAL_ALL_WINDOW_FEEL
2222 || fFeel
== kMenuWindowFeel
;
2227 BWindow::IsFloating() const
2229 return fFeel
== B_FLOATING_SUBSET_WINDOW_FEEL
2230 || fFeel
== B_FLOATING_APP_WINDOW_FEEL
2231 || fFeel
== B_FLOATING_ALL_WINDOW_FEEL
;
2236 BWindow::AddToSubset(BWindow
* window
)
2238 if (window
== NULL
|| window
->Feel() != B_NORMAL_WINDOW_FEEL
2239 || (fFeel
!= B_MODAL_SUBSET_WINDOW_FEEL
2240 && fFeel
!= B_FLOATING_SUBSET_WINDOW_FEEL
))
2246 status_t status
= B_ERROR
;
2247 fLink
->StartMessage(AS_ADD_TO_SUBSET
);
2248 fLink
->Attach
<int32
>(_get_object_token_(window
));
2249 fLink
->FlushWithReply(status
);
2258 BWindow::RemoveFromSubset(BWindow
* window
)
2260 if (window
== NULL
|| window
->Feel() != B_NORMAL_WINDOW_FEEL
2261 || (fFeel
!= B_MODAL_SUBSET_WINDOW_FEEL
2262 && fFeel
!= B_FLOATING_SUBSET_WINDOW_FEEL
))
2268 status_t status
= B_ERROR
;
2269 fLink
->StartMessage(AS_REMOVE_FROM_SUBSET
);
2270 fLink
->Attach
<int32
>(_get_object_token_(window
));
2271 fLink
->FlushWithReply(status
);
2280 BWindow::Perform(perform_code code
, void* _data
)
2283 case PERFORM_CODE_SET_LAYOUT
:
2285 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
2286 BWindow::SetLayout(data
->layout
);
2291 return BLooper::Perform(code
, _data
);
2296 BWindow::SetType(window_type type
)
2300 _DecomposeType(type
, &look
, &feel
);
2302 status_t status
= SetLook(look
);
2304 status
= SetFeel(feel
);
2311 BWindow::Type() const
2313 return _ComposeType(fLook
, fFeel
);
2318 BWindow::SetLook(window_look look
)
2320 BAutolock
locker(this);
2321 if (!locker
.IsLocked())
2324 fLink
->StartMessage(AS_SET_LOOK
);
2325 fLink
->Attach
<int32
>((int32
)look
);
2327 status_t status
= B_ERROR
;
2328 if (fLink
->FlushWithReply(status
) == B_OK
&& status
== B_OK
)
2331 // TODO: this could have changed the window size, and thus, we
2332 // need to get it from the server (and call _AdoptResize()).
2339 BWindow::Look() const
2346 BWindow::SetFeel(window_feel feel
)
2348 BAutolock
locker(this);
2349 if (!locker
.IsLocked())
2352 fLink
->StartMessage(AS_SET_FEEL
);
2353 fLink
->Attach
<int32
>((int32
)feel
);
2355 status_t status
= B_ERROR
;
2356 if (fLink
->FlushWithReply(status
) == B_OK
&& status
== B_OK
)
2364 BWindow::Feel() const
2371 BWindow::SetFlags(uint32 flags
)
2373 BAutolock
locker(this);
2374 if (!locker
.IsLocked())
2377 fLink
->StartMessage(AS_SET_FLAGS
);
2378 fLink
->Attach
<uint32
>(flags
);
2380 int32 status
= B_ERROR
;
2381 if (fLink
->FlushWithReply(status
) == B_OK
&& status
== B_OK
)
2389 BWindow::Flags() const
2396 BWindow::SetWindowAlignment(window_alignment mode
,
2397 int32 h
, int32 hOffset
, int32 width
, int32 widthOffset
,
2398 int32 v
, int32 vOffset
, int32 height
, int32 heightOffset
)
2400 if ((mode
& (B_BYTE_ALIGNMENT
| B_PIXEL_ALIGNMENT
)) == 0
2401 || (hOffset
>= 0 && hOffset
<= h
)
2402 || (vOffset
>= 0 && vOffset
<= v
)
2403 || (widthOffset
>= 0 && widthOffset
<= width
)
2404 || (heightOffset
>= 0 && heightOffset
<= height
))
2407 // TODO: test if hOffset = 0 and set it to 1 if true.
2412 fLink
->StartMessage(AS_SET_ALIGNMENT
);
2413 fLink
->Attach
<int32
>((int32
)mode
);
2414 fLink
->Attach
<int32
>(h
);
2415 fLink
->Attach
<int32
>(hOffset
);
2416 fLink
->Attach
<int32
>(width
);
2417 fLink
->Attach
<int32
>(widthOffset
);
2418 fLink
->Attach
<int32
>(v
);
2419 fLink
->Attach
<int32
>(vOffset
);
2420 fLink
->Attach
<int32
>(height
);
2421 fLink
->Attach
<int32
>(heightOffset
);
2423 status_t status
= B_ERROR
;
2424 fLink
->FlushWithReply(status
);
2433 BWindow::GetWindowAlignment(window_alignment
* mode
,
2434 int32
* h
, int32
* hOffset
, int32
* width
, int32
* widthOffset
,
2435 int32
* v
, int32
* vOffset
, int32
* height
, int32
* heightOffset
) const
2437 if (!const_cast<BWindow
*>(this)->Lock())
2440 fLink
->StartMessage(AS_GET_ALIGNMENT
);
2443 if (fLink
->FlushWithReply(status
) == B_OK
&& status
== B_OK
) {
2444 fLink
->Read
<int32
>((int32
*)mode
);
2445 fLink
->Read
<int32
>(h
);
2446 fLink
->Read
<int32
>(hOffset
);
2447 fLink
->Read
<int32
>(width
);
2448 fLink
->Read
<int32
>(widthOffset
);
2449 fLink
->Read
<int32
>(v
);
2450 fLink
->Read
<int32
>(hOffset
);
2451 fLink
->Read
<int32
>(height
);
2452 fLink
->Read
<int32
>(heightOffset
);
2455 const_cast<BWindow
*>(this)->Unlock();
2461 BWindow::Workspaces() const
2463 if (!const_cast<BWindow
*>(this)->Lock())
2466 uint32 workspaces
= 0;
2468 fLink
->StartMessage(AS_GET_WORKSPACES
);
2471 if (fLink
->FlushWithReply(status
) == B_OK
&& status
== B_OK
)
2472 fLink
->Read
<uint32
>(&workspaces
);
2474 const_cast<BWindow
*>(this)->Unlock();
2480 BWindow::SetWorkspaces(uint32 workspaces
)
2482 // TODO: don't forget about Tracker's background window.
2483 if (fFeel
!= B_NORMAL_WINDOW_FEEL
)
2487 fLink
->StartMessage(AS_SET_WORKSPACES
);
2488 fLink
->Attach
<uint32
>(workspaces
);
2496 BWindow::LastMouseMovedView() const
2498 return fLastMouseMovedView
;
2503 BWindow::MoveBy(float dx
, float dy
)
2505 if ((dx
!= 0.0f
|| dy
!= 0.0f
) && Lock()) {
2506 MoveTo(fFrame
.left
+ dx
, fFrame
.top
+ dy
);
2513 BWindow::MoveTo(BPoint point
)
2515 MoveTo(point
.x
, point
.y
);
2520 BWindow::MoveTo(float x
, float y
)
2528 if (fFrame
.left
!= x
|| fFrame
.top
!= y
) {
2529 fLink
->StartMessage(AS_WINDOW_MOVE
);
2530 fLink
->Attach
<float>(x
);
2531 fLink
->Attach
<float>(y
);
2534 if (fLink
->FlushWithReply(status
) == B_OK
&& status
== B_OK
)
2535 fFrame
.OffsetTo(x
, y
);
2543 BWindow::ResizeBy(float dx
, float dy
)
2546 ResizeTo(fFrame
.Width() + dx
, fFrame
.Height() + dy
);
2553 BWindow::ResizeTo(float width
, float height
)
2558 width
= roundf(width
);
2559 height
= roundf(height
);
2561 // stay in minimum & maximum frame limits
2562 if (width
< fMinWidth
)
2564 else if (width
> fMaxWidth
)
2567 if (height
< fMinHeight
)
2568 height
= fMinHeight
;
2569 else if (height
> fMaxHeight
)
2570 height
= fMaxHeight
;
2572 if (width
!= fFrame
.Width() || height
!= fFrame
.Height()) {
2573 fLink
->StartMessage(AS_WINDOW_RESIZE
);
2574 fLink
->Attach
<float>(width
);
2575 fLink
->Attach
<float>(height
);
2578 if (fLink
->FlushWithReply(status
) == B_OK
&& status
== B_OK
) {
2579 fFrame
.right
= fFrame
.left
+ width
;
2580 fFrame
.bottom
= fFrame
.top
+ height
;
2590 BWindow::ResizeToPreferred()
2592 BAutolock
locker(this);
2595 float width
= fTopView
->PreferredSize().width
;
2596 width
= std::min(width
, fTopView
->MaxSize().width
);
2597 width
= std::max(width
, fTopView
->MinSize().width
);
2599 float height
= fTopView
->PreferredSize().height
;
2600 height
= std::min(width
, fTopView
->MaxSize().height
);
2601 height
= std::max(width
, fTopView
->MinSize().height
);
2603 if (GetLayout()->HasHeightForWidth())
2604 GetLayout()->GetHeightForWidth(width
, NULL
, NULL
, &height
);
2606 ResizeTo(width
, height
);
2611 BWindow::CenterIn(const BRect
& rect
)
2613 BAutolock
locker(this);
2615 // Set size limits now if needed
2618 MoveTo(BLayoutUtils::AlignInFrame(rect
, Size(),
2619 BAlignment(B_ALIGN_HORIZONTAL_CENTER
,
2620 B_ALIGN_VERTICAL_CENTER
)).LeftTop());
2625 BWindow::CenterOnScreen()
2627 CenterIn(BScreen(this).Frame());
2631 // Centers the window on the screen with the passed in id.
2633 BWindow::CenterOnScreen(screen_id id
)
2635 CenterIn(BScreen(id
).Frame());
2640 BWindow::MoveOnScreen(uint32 flags
)
2642 // Set size limits now if needed
2645 BRect screenFrame
= BScreen(this).Frame();
2646 BRect frame
= Frame();
2650 _GetDecoratorSize(&borderWidth
, &tabHeight
);
2652 frame
.InsetBy(-borderWidth
, -borderWidth
);
2653 frame
.top
-= tabHeight
;
2655 if ((flags
& B_DO_NOT_RESIZE_TO_FIT
) == 0) {
2656 // Make sure the window fits on the screen
2657 if (frame
.Width() > screenFrame
.Width())
2658 frame
.right
-= frame
.Width() - screenFrame
.Width();
2659 if (frame
.Height() > screenFrame
.Height())
2660 frame
.bottom
-= frame
.Height() - screenFrame
.Height();
2662 BRect innerFrame
= frame
;
2663 innerFrame
.top
+= tabHeight
;
2664 innerFrame
.InsetBy(borderWidth
, borderWidth
);
2665 ResizeTo(innerFrame
.Width(), innerFrame
.Height());
2668 if (((flags
& B_MOVE_IF_PARTIALLY_OFFSCREEN
) == 0
2669 && !screenFrame
.Contains(frame
))
2670 || !frame
.Intersects(screenFrame
)) {
2676 // Move such that the upper left corner, and most of the window
2678 float left
= frame
.left
;
2679 if (left
< screenFrame
.left
)
2680 left
= screenFrame
.left
;
2681 else if (frame
.right
> screenFrame
.right
)
2682 left
= std::max(0.f
, screenFrame
.right
- frame
.Width());
2684 float top
= frame
.top
;
2685 if (top
< screenFrame
.top
)
2686 top
= screenFrame
.top
;
2687 else if (frame
.bottom
> screenFrame
.bottom
)
2688 top
= std::max(0.f
, screenFrame
.bottom
- frame
.Height());
2690 if (top
!= frame
.top
|| left
!= frame
.left
)
2691 MoveTo(left
+ borderWidth
, top
+ tabHeight
+ borderWidth
);
2698 bool runCalled
= true;
2702 _SendShowOrHideMessage();
2704 runCalled
= fRunCalled
;
2710 // This is the fist time Show() is called, which implicitly runs the
2711 // looper. NOTE: The window is still locked if it has not been
2712 // run yet, so accessing members is safe.
2713 if (fLink
->SenderPort() < B_OK
) {
2714 // We don't have valid app_server connection; there is no point
2715 // in starting our looper
2728 // If we are minimized and are about to be hidden, unminimize
2729 if (IsMinimized() && fShowLevel
== 0)
2734 _SendShowOrHideMessage();
2742 BWindow::IsHidden() const
2744 return fShowLevel
> 0;
2749 BWindow::QuitRequested()
2751 return BLooper::QuitRequested();
2758 return BLooper::Run();
2763 BWindow::SetLayout(BLayout
* layout
)
2765 // Adopt layout's colors for fTopView
2767 fTopView
->AdoptViewColors(layout
->View());
2769 fTopView
->SetLayout(layout
);
2774 BWindow::GetLayout() const
2776 return fTopView
->GetLayout();
2781 BWindow::InvalidateLayout(bool descendants
)
2783 fTopView
->InvalidateLayout(descendants
);
2788 BWindow::Layout(bool force
)
2792 // Do the actual layout
2793 fTopView
->Layout(force
);
2798 BWindow::IsOffscreenWindow() const
2805 BWindow::GetSupportedSuites(BMessage
* data
)
2810 status_t status
= data
->AddString("suites", "suite/vnd.Be-window");
2811 if (status
== B_OK
) {
2812 BPropertyInfo
propertyInfo(sWindowPropInfo
, sWindowValueInfo
);
2814 status
= data
->AddFlat("messages", &propertyInfo
);
2816 status
= BLooper::GetSupportedSuites(data
);
2824 BWindow::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
2825 int32 what
, const char* property
)
2827 if (message
->what
== B_WINDOW_MOVE_BY
2828 || message
->what
== B_WINDOW_MOVE_TO
)
2831 BPropertyInfo
propertyInfo(sWindowPropInfo
);
2832 if (propertyInfo
.FindMatch(message
, index
, specifier
, what
, property
) >= 0) {
2833 if (strcmp(property
, "View") == 0) {
2834 // we will NOT pop the current specifier
2836 } else if (strcmp(property
, "MenuBar") == 0) {
2838 message
->PopSpecifier();
2841 BMessage
replyMsg(B_MESSAGE_NOT_UNDERSTOOD
);
2842 replyMsg
.AddInt32("error", B_NAME_NOT_FOUND
);
2843 replyMsg
.AddString("message",
2844 "This window doesn't have a main MenuBar");
2845 message
->SendReply(&replyMsg
);
2852 return BLooper::ResolveSpecifier(message
, index
, specifier
, what
, property
);
2856 // #pragma mark - Private Methods
2860 BWindow::_InitData(BRect frame
, const char* title
, window_look look
,
2861 window_feel feel
, uint32 flags
, uint32 workspace
, int32 bitmapToken
)
2863 STRACE(("BWindow::InitData()\n"));
2865 if (be_app
== NULL
) {
2866 debugger("You need a valid BApplication object before interacting with "
2871 frame
.left
= roundf(frame
.left
);
2872 frame
.top
= roundf(frame
.top
);
2873 frame
.right
= roundf(frame
.right
);
2874 frame
.bottom
= roundf(frame
.bottom
);
2881 fTitle
= strdup(title
);
2887 fFlags
= flags
| B_ASYNCHRONOUS_CONTROLS
;
2889 fInTransaction
= bitmapToken
>= 0;
2890 fUpdateRequested
= false;
2896 fLastMouseMovedView
= NULL
;
2898 fDefaultButton
= NULL
;
2900 // Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2901 // get sent to the application, and not one of our handlers.
2902 // It is only installed for non-modal windows, though.
2903 fNoQuitShortcut
= IsModal();
2905 if ((fFlags
& B_NOT_CLOSABLE
) == 0 && !IsModal()) {
2906 // Modal windows default to non-closable, but you can add the
2907 // shortcut manually, if a different behaviour is wanted
2908 AddShortcut('W', B_COMMAND_KEY
, new BMessage(B_QUIT_REQUESTED
));
2911 // Edit modifier keys
2913 AddShortcut('X', B_COMMAND_KEY
, new BMessage(B_CUT
), NULL
);
2914 AddShortcut('C', B_COMMAND_KEY
, new BMessage(B_COPY
), NULL
);
2915 AddShortcut('V', B_COMMAND_KEY
, new BMessage(B_PASTE
), NULL
);
2916 AddShortcut('A', B_COMMAND_KEY
, new BMessage(B_SELECT_ALL
), NULL
);
2918 // Window modifier keys
2920 AddShortcut('M', B_COMMAND_KEY
| B_CONTROL_KEY
,
2921 new BMessage(_MINIMIZE_
), NULL
);
2922 AddShortcut('Z', B_COMMAND_KEY
| B_CONTROL_KEY
,
2923 new BMessage(_ZOOM_
), NULL
);
2924 AddShortcut('H', B_COMMAND_KEY
| B_CONTROL_KEY
,
2925 new BMessage(B_HIDE_APPLICATION
), NULL
);
2926 AddShortcut('F', B_COMMAND_KEY
| B_CONTROL_KEY
,
2927 new BMessage(_SEND_TO_FRONT_
), NULL
);
2928 AddShortcut('B', B_COMMAND_KEY
| B_CONTROL_KEY
,
2929 new BMessage(_SEND_BEHIND_
), NULL
);
2931 // We set the default pulse rate, but we don't start the pulse
2932 fPulseRate
= 500000;
2933 fPulseRunner
= NULL
;
2935 fIsFilePanel
= false;
2941 fMaxZoomHeight
= 32768.0;
2942 fMaxZoomWidth
= 32768.0;
2945 fMaxHeight
= 32768.0;
2946 fMaxWidth
= 32768.0;
2948 fLastViewToken
= B_NULL_TOKEN
;
2950 // TODO: other initializations!
2953 // Create the server-side window
2955 port_id receivePort
= create_port(B_LOOPER_PORT_DEFAULT_CAPACITY
,
2957 if (receivePort
< B_OK
) {
2959 debugger("Could not create BWindow's receive port, used for "
2960 "interacting with the app_server!");
2965 STRACE(("BWindow::InitData(): contacting app_server...\n"));
2967 // let app_server know that a window has been created.
2968 fLink
= new(std::nothrow
) BPrivate::PortLink(
2969 BApplication::Private::ServerLink()->SenderPort(), receivePort
);
2970 if (fLink
== NULL
) {
2976 BPrivate::AppServerLink lockLink
;
2977 // we're talking to the server application using our own
2978 // communication channel (fLink) - we better make sure no one
2979 // interferes by locking that channel (which AppServerLink does
2982 if (bitmapToken
< 0) {
2983 fLink
->StartMessage(AS_CREATE_WINDOW
);
2985 fLink
->StartMessage(AS_CREATE_OFFSCREEN_WINDOW
);
2986 fLink
->Attach
<int32
>(bitmapToken
);
2990 fLink
->Attach
<BRect
>(fFrame
);
2991 fLink
->Attach
<uint32
>((uint32
)fLook
);
2992 fLink
->Attach
<uint32
>((uint32
)fFeel
);
2993 fLink
->Attach
<uint32
>(fFlags
);
2994 fLink
->Attach
<uint32
>(workspace
);
2995 fLink
->Attach
<int32
>(_get_object_token_(this));
2996 fLink
->Attach
<port_id
>(receivePort
);
2997 fLink
->Attach
<port_id
>(fMsgPort
);
2998 fLink
->AttachString(title
);
3002 if (fLink
->FlushWithReply(code
) == B_OK
3004 && fLink
->Read
<port_id
>(&sendPort
) == B_OK
) {
3005 // read the frame size and its limits that were really
3006 // enforced on the server side
3008 fLink
->Read
<BRect
>(&fFrame
);
3009 fLink
->Read
<float>(&fMinWidth
);
3010 fLink
->Read
<float>(&fMaxWidth
);
3011 fLink
->Read
<float>(&fMinHeight
);
3012 fLink
->Read
<float>(&fMaxHeight
);
3014 fMaxZoomWidth
= fMaxWidth
;
3015 fMaxZoomHeight
= fMaxHeight
;
3019 // Redirect our link to the new window connection
3020 fLink
->SetSenderPort(sendPort
);
3023 STRACE(("Server says that our send port is %ld\n", sendPort
));
3024 STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
3030 //! Rename the handler and its thread
3032 BWindow::_SetName(const char* title
)
3037 // we will change BWindow's thread name to "w>window title"
3039 char threadName
[B_OS_NAME_LENGTH
];
3040 strcpy(threadName
, "w>");
3042 strlcat(threadName
, title
, B_OS_NAME_LENGTH
);
3044 int32 length
= strlen(title
);
3045 length
= min_c(length
, B_OS_NAME_LENGTH
- 3);
3046 memcpy(threadName
+ 2, title
, length
);
3047 threadName
[length
+ 2] = '\0';
3050 // change the handler's name
3051 SetName(threadName
);
3053 // if the message loop has been started...
3054 if (Thread() >= B_OK
)
3055 rename_thread(Thread(), threadName
);
3059 //! Reads all pending messages from the window port and put them into the queue.
3061 BWindow::_DequeueAll()
3063 // Get message count from port
3064 int32 count
= port_count(fMsgPort
);
3066 for (int32 i
= 0; i
< count
; i
++) {
3067 BMessage
* message
= MessageFromPort(0);
3068 if (message
!= NULL
)
3069 fDirectTarget
->Queue()->AddMessage(message
);
3074 /*! This here is an almost complete code duplication to BLooper::task_looper()
3075 but with some important differences:
3076 a) it uses the _DetermineTarget() method to tell what the later target of
3077 a message will be, if no explicit target is supplied.
3078 b) it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
3079 to all of its intended targets, and to add all fields the target would
3080 expect in such a message.
3082 This is important because the app_server sends all input events to the
3083 preferred handler, and expects them to be correctly distributed to their
3087 BWindow::task_looper()
3089 STRACE(("info: BWindow::task_looper() started.\n"));
3091 // Check that looper is locked (should be)
3096 debugger("window must not be locked!");
3098 while (!fTerminating
) {
3099 // Did we get a message?
3100 BMessage
* msg
= MessageFromPort();
3102 _AddMessagePriv(msg
);
3104 // Get message count from port
3105 int32 msgCount
= port_count(fMsgPort
);
3106 for (int32 i
= 0; i
< msgCount
; ++i
) {
3107 // Read 'count' messages from port (so we will not block)
3108 // We use zero as our timeout since we know there is stuff there
3109 msg
= MessageFromPort(0);
3110 // Add messages to queue
3112 _AddMessagePriv(msg
);
3115 bool dispatchNextMessage
= true;
3116 while (!fTerminating
&& dispatchNextMessage
) {
3117 // Get next message from queue (assign to fLastMessage after
3119 BMessage
* message
= fDirectTarget
->Queue()->NextMessage();
3127 fLastMessage
= message
;
3129 if (fLastMessage
== NULL
) {
3130 // No more messages: Unlock the looper and terminate the
3132 dispatchNextMessage
= false;
3134 // Get the target handler
3135 BMessage::Private
messagePrivate(fLastMessage
);
3136 bool usePreferred
= messagePrivate
.UsePreferredTarget();
3137 BHandler
* handler
= NULL
;
3138 bool dropMessage
= false;
3141 handler
= PreferredHandler();
3142 if (handler
== NULL
)
3145 gDefaultTokens
.GetToken(messagePrivate
.GetTarget(),
3146 B_HANDLER_TOKEN
, (void**)&handler
);
3148 // if this handler doesn't belong to us, we drop the message
3149 if (handler
!= NULL
&& handler
->Looper() != this) {
3155 if ((handler
== NULL
&& !dropMessage
) || usePreferred
)
3156 handler
= _DetermineTarget(fLastMessage
, handler
);
3158 unpack_cookie cookie
;
3159 while (_UnpackMessage(cookie
, &fLastMessage
, &handler
, &usePreferred
)) {
3160 // if there is no target handler, the message is dropped
3161 if (handler
!= NULL
) {
3162 _SanitizeMessage(fLastMessage
, handler
, usePreferred
);
3164 // Is this a scripting message?
3165 if (fLastMessage
->HasSpecifiers()) {
3167 // Make sure the current specifier is kosher
3168 if (fLastMessage
->GetCurrentSpecifier(&index
) == B_OK
)
3169 handler
= resolve_specifier(handler
, fLastMessage
);
3172 if (handler
!= NULL
)
3173 handler
= _TopLevelFilter(fLastMessage
, handler
);
3175 if (handler
!= NULL
)
3176 DispatchMessage(fLastMessage
, handler
);
3179 // Delete the current message
3180 delete fLastMessage
;
3181 fLastMessage
= NULL
;
3186 // we leave the looper locked when we quit
3192 // Are any messages on the port?
3193 if (port_count(fMsgPort
) > 0) {
3195 dispatchNextMessage
= false;
3203 BWindow::_ComposeType(window_look look
, window_feel feel
) const
3206 case B_NORMAL_WINDOW_FEEL
:
3208 case B_TITLED_WINDOW_LOOK
:
3209 return B_TITLED_WINDOW
;
3211 case B_DOCUMENT_WINDOW_LOOK
:
3212 return B_DOCUMENT_WINDOW
;
3214 case B_BORDERED_WINDOW_LOOK
:
3215 return B_BORDERED_WINDOW
;
3218 return B_UNTYPED_WINDOW
;
3222 case B_MODAL_APP_WINDOW_FEEL
:
3223 if (look
== B_MODAL_WINDOW_LOOK
)
3224 return B_MODAL_WINDOW
;
3227 case B_FLOATING_APP_WINDOW_FEEL
:
3228 if (look
== B_FLOATING_WINDOW_LOOK
)
3229 return B_FLOATING_WINDOW
;
3233 return B_UNTYPED_WINDOW
;
3236 return B_UNTYPED_WINDOW
;
3241 BWindow::_DecomposeType(window_type type
, window_look
* _look
,
3242 window_feel
* _feel
) const
3245 case B_DOCUMENT_WINDOW
:
3246 *_look
= B_DOCUMENT_WINDOW_LOOK
;
3247 *_feel
= B_NORMAL_WINDOW_FEEL
;
3250 case B_MODAL_WINDOW
:
3251 *_look
= B_MODAL_WINDOW_LOOK
;
3252 *_feel
= B_MODAL_APP_WINDOW_FEEL
;
3255 case B_FLOATING_WINDOW
:
3256 *_look
= B_FLOATING_WINDOW_LOOK
;
3257 *_feel
= B_FLOATING_APP_WINDOW_FEEL
;
3260 case B_BORDERED_WINDOW
:
3261 *_look
= B_BORDERED_WINDOW_LOOK
;
3262 *_feel
= B_NORMAL_WINDOW_FEEL
;
3265 case B_TITLED_WINDOW
:
3266 case B_UNTYPED_WINDOW
:
3268 *_look
= B_TITLED_WINDOW_LOOK
;
3269 *_feel
= B_NORMAL_WINDOW_FEEL
;
3276 BWindow::_CreateTopView()
3278 STRACE(("_CreateTopView(): enter\n"));
3280 BRect frame
= fFrame
.OffsetToCopy(B_ORIGIN
);
3281 // TODO: what to do here about std::nothrow?
3282 fTopView
= new BView(frame
, "fTopView", B_FOLLOW_ALL
, B_WILL_DRAW
);
3283 fTopView
->fTopLevelView
= true;
3285 //inhibit check_lock()
3286 fLastViewToken
= _get_object_token_(fTopView
);
3288 // set fTopView's owner, add it to window's eligible handler list
3289 // and also set its next handler to be this window.
3291 STRACE(("Calling setowner fTopView = %p this = %p.\n",
3294 fTopView
->_SetOwner(this);
3296 // we can't use AddChild() because this is the top view
3297 fTopView
->_CreateSelf();
3298 STRACE(("BuildTopView ended\n"));
3303 Resizes the top view to match the window size. This will also
3304 adapt the size of all its child views as needed.
3305 This method has to be called whenever the frame of the window
3309 BWindow::_AdoptResize()
3311 // Resize views according to their resize modes - this
3312 // saves us some server communication, as the server
3313 // does the same with our views on its side.
3315 int32 deltaWidth
= (int32
)(fFrame
.Width() - fTopView
->Bounds().Width());
3316 int32 deltaHeight
= (int32
)(fFrame
.Height() - fTopView
->Bounds().Height());
3317 if (deltaWidth
== 0 && deltaHeight
== 0)
3320 fTopView
->_ResizeBy(deltaWidth
, deltaHeight
);
3325 BWindow::_SetFocus(BView
* focusView
, bool notifyInputServer
)
3327 if (fFocus
== focusView
)
3330 // we notify the input server if we are passing focus
3331 // from a view which has the B_INPUT_METHOD_AWARE to a one
3332 // which does not, or vice-versa
3333 if (notifyInputServer
&& fActive
) {
3334 bool inputMethodAware
= false;
3336 inputMethodAware
= focusView
->Flags() & B_INPUT_METHOD_AWARE
;
3337 BMessage
msg(inputMethodAware
? IS_FOCUS_IM_AWARE_VIEW
: IS_UNFOCUS_IM_AWARE_VIEW
);
3338 BMessenger
messenger(focusView
);
3341 msg
.AddMessenger("view", messenger
);
3342 _control_input_server_(&msg
, &reply
);
3346 SetPreferredHandler(focusView
);
3351 \brief Determines the target of a message received for the
3355 BWindow::_DetermineTarget(BMessage
* message
, BHandler
* target
)
3360 switch (message
->what
) {
3364 // if we have a default button, it might want to hear
3365 // about pressing the <enter> key
3366 const int32 kNonLockModifierKeys
= B_SHIFT_KEY
| B_COMMAND_KEY
3367 | B_CONTROL_KEY
| B_OPTION_KEY
| B_MENU_KEY
;
3369 if (DefaultButton() != NULL
3370 && message
->FindInt32("raw_char", &rawChar
) == B_OK
3371 && rawChar
== B_ENTER
3372 && (modifiers() & kNonLockModifierKeys
) == 0)
3373 return DefaultButton();
3375 // supposed to fall through
3377 case B_UNMAPPED_KEY_DOWN
:
3378 case B_UNMAPPED_KEY_UP
:
3379 case B_MODIFIERS_CHANGED
:
3380 // these messages should be dispatched by the focus view
3381 if (CurrentFocus() != NULL
)
3382 return CurrentFocus();
3388 case B_MOUSE_WHEEL_CHANGED
:
3390 // is there a token of the view that is currently under the mouse?
3392 if (message
->FindInt32("_view_token", &token
) == B_OK
) {
3393 BView
* view
= _FindView(token
);
3398 // if there is no valid token in the message, we try our
3399 // luck with the last target, if available
3400 if (fLastMouseMovedView
!= NULL
)
3401 return fLastMouseMovedView
;
3405 case B_QUIT_REQUESTED
:
3406 // TODO: test whether R5 will let BView dispatch these messages
3409 case _MESSAGE_DROPPED_
:
3410 if (fLastMouseMovedView
!= NULL
)
3411 return fLastMouseMovedView
;
3422 /*! \brief Determines whether or not this message has targeted the focus view.
3424 This will return \c false only if the message did not go to the preferred
3425 handler, or if the packed message does not contain address the focus view
3429 BWindow::_IsFocusMessage(BMessage
* message
)
3431 BMessage::Private
messagePrivate(message
);
3432 if (!messagePrivate
.UsePreferredTarget())
3436 if (message
->HasInt32("_token")
3437 && (message
->FindBool("_feed_focus", &feedFocus
) != B_OK
|| !feedFocus
))
3444 /*! \brief Distributes the message to its intended targets. This is done for
3445 all messages that should go to the preferred handler.
3447 Returns \c true in case the message should still be dispatched
3450 BWindow::_UnpackMessage(unpack_cookie
& cookie
, BMessage
** _message
,
3451 BHandler
** _target
, bool* _usePreferred
)
3453 if (cookie
.message
== NULL
)
3456 if (cookie
.index
== 0 && !cookie
.tokens_scanned
) {
3457 // We were called the first time for this message
3459 if (!*_usePreferred
) {
3460 // only consider messages targeted at the preferred handler
3461 cookie
.message
= NULL
;
3465 // initialize our cookie
3466 cookie
.message
= *_message
;
3467 cookie
.focus
= *_target
;
3469 if (cookie
.focus
!= NULL
)
3470 cookie
.focus_token
= _get_object_token_(*_target
);
3472 if (fLastMouseMovedView
!= NULL
&& cookie
.message
->what
== B_MOUSE_MOVED
)
3473 cookie
.last_view_token
= _get_object_token_(fLastMouseMovedView
);
3475 *_usePreferred
= false;
3480 // distribute the message to all targets specified in the
3481 // message directly (but not to the focus view)
3483 for (int32 token
; !cookie
.tokens_scanned
3484 && cookie
.message
->FindInt32("_token", cookie
.index
, &token
)
3487 // focus view is preferred and should get its message directly
3488 if (token
== cookie
.focus_token
) {
3489 cookie
.found_focus
= true;
3492 if (token
== cookie
.last_view_token
)
3495 BView
* target
= _FindView(token
);
3499 *_message
= new BMessage(*cookie
.message
);
3500 // the secondary copies of the message should not be treated as focus
3501 // messages, otherwise there will be unintended side effects, i.e.
3502 // keyboard shortcuts getting processed multiple times.
3503 (*_message
)->RemoveName("_feed_focus");
3509 cookie
.tokens_scanned
= true;
3511 // if there is a last mouse moved view, and the new focus is
3512 // different, the previous view wants to get its B_EXITED_VIEW
3514 if (cookie
.last_view_token
!= B_NULL_TOKEN
&& fLastMouseMovedView
!= NULL
3515 && fLastMouseMovedView
!= cookie
.focus
) {
3516 *_message
= new BMessage(*cookie
.message
);
3517 *_target
= fLastMouseMovedView
;
3518 cookie
.last_view_token
= B_NULL_TOKEN
;
3522 bool dispatchToFocus
= true;
3524 // check if the focus token is still valid (could have been removed in the mean time)
3526 if (gDefaultTokens
.GetToken(cookie
.focus_token
, B_HANDLER_TOKEN
, (void**)&handler
) != B_OK
3527 || handler
->Looper() != this)
3528 dispatchToFocus
= false;
3530 if (dispatchToFocus
&& cookie
.index
> 0) {
3531 // should this message still be dispatched by the focus view?
3533 if (!cookie
.found_focus
3534 && (cookie
.message
->FindBool("_feed_focus", &feedFocus
) != B_OK
3535 || feedFocus
== false))
3536 dispatchToFocus
= false;
3539 if (!dispatchToFocus
) {
3540 delete cookie
.message
;
3541 cookie
.message
= NULL
;
3545 *_message
= cookie
.message
;
3546 *_target
= cookie
.focus
;
3547 *_usePreferred
= true;
3548 cookie
.message
= NULL
;
3553 /*! Some messages don't get to the window in a shape an application should see.
3554 This method is supposed to give a message the last grinding before
3555 it's acceptable for the receiving application.
3558 BWindow::_SanitizeMessage(BMessage
* message
, BHandler
* target
, bool usePreferred
)
3563 switch (message
->what
) {
3569 if (message
->FindPoint("screen_where", &where
) != B_OK
)
3572 BView
* view
= dynamic_cast<BView
*>(target
);
3574 if (view
== NULL
|| message
->what
== B_MOUSE_MOVED
) {
3575 // add local window coordinates, only
3576 // for regular mouse moved messages
3577 message
->AddPoint("where", ConvertFromScreen(where
));
3581 // add local view coordinates
3582 BPoint viewWhere
= view
->ConvertFromScreen(where
);
3583 if (message
->what
!= B_MOUSE_MOVED
) {
3584 // Yep, the meaning of "where" is different
3585 // for regular mouse moved messages versus
3587 message
->AddPoint("where", viewWhere
);
3589 message
->AddPoint("be:view_where", viewWhere
);
3591 if (message
->what
== B_MOUSE_MOVED
) {
3592 // is there a token of the view that is currently under
3594 BView
* viewUnderMouse
= NULL
;
3596 if (message
->FindInt32("_view_token", &token
) == B_OK
)
3597 viewUnderMouse
= _FindView(token
);
3599 // add transit information
3601 = _TransitForMouseMoved(view
, viewUnderMouse
);
3602 message
->AddInt32("be:transit", transit
);
3605 fLastMouseMovedView
= viewUnderMouse
;
3613 // App Server sends screen coordinates, convert the point to
3614 // local view coordinates, then add the point in be:view_where
3616 if (message
->FindPoint("screen_where", &where
) != B_OK
)
3619 BView
* view
= dynamic_cast<BView
*>(target
);
3621 // add local view coordinates
3622 message
->AddPoint("be:view_where",
3623 view
->ConvertFromScreen(where
));
3628 case _MESSAGE_DROPPED_
:
3630 uint32 originalWhat
;
3631 if (message
->FindInt32("_original_what",
3632 (int32
*)&originalWhat
) == B_OK
) {
3633 message
->what
= originalWhat
;
3634 message
->RemoveName("_original_what");
3643 This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3644 is removed from the queue.
3645 It allows the window to update the last mouse moved view, and
3646 let it decide if this message should be kept. It will also remove
3647 the message from the queue.
3648 You need to hold the message queue lock when calling this method!
3650 \return true if this message can be used to get the mouse data from,
3651 \return false if this is not meant for the public.
3654 BWindow::_StealMouseMessage(BMessage
* message
, bool& deleteMessage
)
3656 BMessage::Private
messagePrivate(message
);
3657 if (!messagePrivate
.UsePreferredTarget()) {
3658 // this message is targeted at a specific handler, so we should
3664 if (message
->FindInt32("_token", 0, &token
) == B_OK
) {
3665 // This message has other targets, so we can't remove it;
3666 // just prevent it from being sent to the preferred handler
3667 // again (if it should have gotten it at all).
3669 if (message
->FindBool("_feed_focus", &feedFocus
) != B_OK
|| !feedFocus
)
3672 message
->RemoveName("_feed_focus");
3673 deleteMessage
= false;
3675 deleteMessage
= true;
3677 if (message
->what
== B_MOUSE_MOVED
) {
3678 // We need to update the last mouse moved view, as this message
3679 // won't make it to _SanitizeMessage() anymore.
3680 BView
* viewUnderMouse
= NULL
;
3682 if (message
->FindInt32("_view_token", &token
) == B_OK
)
3683 viewUnderMouse
= _FindView(token
);
3685 // Don't remove important transit messages!
3686 uint32 transit
= _TransitForMouseMoved(fLastMouseMovedView
,
3688 if (transit
== B_ENTERED_VIEW
|| transit
== B_EXITED_VIEW
)
3689 deleteMessage
= false;
3692 if (deleteMessage
) {
3693 // The message is only thought for the preferred handler, so we
3694 // can just remove it.
3695 MessageQueue()->RemoveMessage(message
);
3704 BWindow::_TransitForMouseMoved(BView
* view
, BView
* viewUnderMouse
) const
3707 if (viewUnderMouse
== view
) {
3708 // the mouse is over the target view
3709 if (fLastMouseMovedView
!= view
)
3710 transit
= B_ENTERED_VIEW
;
3712 transit
= B_INSIDE_VIEW
;
3714 // the mouse is not over the target view
3715 if (view
== fLastMouseMovedView
)
3716 transit
= B_EXITED_VIEW
;
3718 transit
= B_OUTSIDE_VIEW
;
3724 /*! Forwards the key to the switcher
3727 BWindow::_Switcher(int32 rawKey
, uint32 modifiers
, bool repeat
)
3729 // only send the first key press, no repeats
3733 BMessenger
deskbar(kDeskbarSignature
);
3734 if (!deskbar
.IsValid()) {
3735 // TODO: have some kind of fallback-handling in case the Deskbar is
3740 BMessage
message('TASK');
3741 message
.AddInt32("key", rawKey
);
3742 message
.AddInt32("modifiers", modifiers
);
3743 message
.AddInt64("when", system_time());
3744 message
.AddInt32("team", Team());
3745 deskbar
.SendMessage(&message
);
3749 /*! Handles keyboard input before it gets forwarded to the target handler.
3750 This includes shortcut evaluation, keyboard navigation, etc.
3752 \return handled if true, the event was already handled, and will not
3753 be forwarded to the target handler.
3755 TODO: must also convert the incoming key to the font encoding of the target
3758 BWindow::_HandleKeyDown(BMessage
* event
)
3760 // Only handle special functions when the event targeted the active focus
3762 if (!_IsFocusMessage(event
))
3765 const char* bytes
= NULL
;
3766 if (event
->FindString("bytes", &bytes
) != B_OK
)
3769 char key
= bytes
[0];
3772 if (event
->FindInt32("modifiers", (int32
*)&modifiers
) != B_OK
)
3775 // handle BMenuBar key
3776 if (key
== B_ESCAPE
&& (modifiers
& B_COMMAND_KEY
) != 0
3777 && fKeyMenuBar
!= NULL
) {
3778 fKeyMenuBar
->StartMenuBar(0, true, false, NULL
);
3782 // Keyboard navigation through views
3783 // (B_OPTION_KEY makes BTextViews and friends navigable, even in editing
3785 if (key
== B_TAB
&& (modifiers
& B_OPTION_KEY
) != 0) {
3786 _KeyboardNavigation();
3791 event
->FindInt32("key", &rawKey
);
3793 // Deskbar's Switcher
3794 if ((key
== B_TAB
|| rawKey
== 0x11) && (modifiers
& B_CONTROL_KEY
) != 0) {
3795 _Switcher(rawKey
, modifiers
, event
->HasInt32("be:key_repeat"));
3799 // Optionally close window when the escape key is pressed
3800 if (key
== B_ESCAPE
&& (Flags() & B_CLOSE_ON_ESCAPE
) != 0) {
3801 BMessage
message(B_QUIT_REQUESTED
);
3802 message
.AddBool("shortcut", true);
3804 PostMessage(&message
);
3808 // PrtScr key takes a screenshot
3809 if (key
== B_FUNCTION_KEY
&& rawKey
== B_PRINT_KEY
) {
3810 // With no modifier keys the best way to get a screenshot is by
3811 // calling the screenshot CLI
3812 if (modifiers
== 0) {
3813 be_roster
->Launch("application/x-vnd.haiku-screenshot-cli");
3817 // Prepare a message based on the modifier keys pressed and launch the
3819 BMessage
message(B_ARGV_RECEIVED
);
3821 message
.AddString("argv", "Screenshot");
3822 if ((modifiers
& B_CONTROL_KEY
) != 0) {
3824 message
.AddString("argv", "--clipboard");
3826 if ((modifiers
& B_SHIFT_KEY
) != 0) {
3828 message
.AddString("argv", "--silent");
3830 message
.AddInt32("argc", argc
);
3831 be_roster
->Launch("application/x-vnd.haiku-screenshot", &message
);
3836 if ((modifiers
& B_COMMAND_KEY
) != 0) {
3837 // Command+q has been pressed, so, we will quit
3838 // the shortcut mechanism doesn't allow handlers outside the window
3839 if (!fNoQuitShortcut
&& (key
== 'Q' || key
== 'q')) {
3840 BMessage
message(B_QUIT_REQUESTED
);
3841 message
.AddBool("shortcut", true);
3843 be_app
->PostMessage(&message
);
3848 // Send Command+Left and Command+Right to textview if it has focus
3849 if (key
== B_LEFT_ARROW
|| key
== B_RIGHT_ARROW
) {
3850 // check key before doing expensive dynamic_cast
3851 BTextView
* textView
= dynamic_cast<BTextView
*>(CurrentFocus());
3852 if (textView
!= NULL
) {
3853 textView
->KeyDown(bytes
, modifiers
);
3859 // Pretend that the user opened a menu, to give the subclass a
3860 // chance to update it's menus. This may install new shortcuts,
3861 // which is why we have to call it here, before trying to find
3862 // a shortcut for the given key.
3865 Shortcut
* shortcut
= _FindShortcut(key
, modifiers
);
3866 if (shortcut
!= NULL
) {
3867 // TODO: would be nice to move this functionality to
3868 // a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3869 // (and BMenuItem::Invoke()) are private, I didn't want
3870 // to mess with them (BMenuItem::Invoke() is public in
3871 // Dano/Zeta, though, maybe we should just follow their
3873 if (shortcut
->MenuItem() != NULL
) {
3874 BMenu
* menu
= shortcut
->MenuItem()->Menu();
3876 MenuPrivate(menu
).InvokeItem(shortcut
->MenuItem(), true);
3878 BHandler
* target
= shortcut
->Target();
3880 target
= CurrentFocus();
3882 if (shortcut
->Message() != NULL
) {
3883 BMessage
message(*shortcut
->Message());
3885 if (message
.ReplaceInt64("when", system_time()) != B_OK
)
3886 message
.AddInt64("when", system_time());
3887 if (message
.ReplaceBool("shortcut", true) != B_OK
)
3888 message
.AddBool("shortcut", true);
3890 PostMessage(&message
, target
);
3897 // we always eat the event if the command key was pressed
3901 // TODO: convert keys to the encoding of the target view
3908 BWindow::_HandleUnmappedKeyDown(BMessage
* event
)
3910 // Only handle special functions when the event targeted the active focus
3912 if (!_IsFocusMessage(event
))
3917 if (event
->FindInt32("modifiers", (int32
*)&modifiers
) != B_OK
3918 || event
->FindInt32("key", &rawKey
))
3921 // Deskbar's Switcher
3922 if (rawKey
== 0x11 && (modifiers
& B_CONTROL_KEY
) != 0) {
3923 _Switcher(rawKey
, modifiers
, event
->HasInt32("be:key_repeat"));
3932 BWindow::_KeyboardNavigation()
3934 BMessage
* message
= CurrentMessage();
3935 if (message
== NULL
)
3940 if (message
->FindString("bytes", &bytes
) != B_OK
|| bytes
[0] != B_TAB
)
3943 message
->FindInt32("modifiers", (int32
*)&modifiers
);
3946 int32 jumpGroups
= (modifiers
& B_OPTION_KEY
) != 0
3947 ? B_NAVIGABLE_JUMP
: B_NAVIGABLE
;
3948 if (modifiers
& B_SHIFT_KEY
)
3949 nextFocus
= _FindPreviousNavigable(fFocus
, jumpGroups
);
3951 nextFocus
= _FindNextNavigable(fFocus
, jumpGroups
);
3953 if (nextFocus
!= NULL
&& nextFocus
!= fFocus
)
3954 nextFocus
->MakeFocus(true);
3959 \brief Return the position of the window centered horizontally to the passed
3960 in \a frame and vertically 3/4 from the top of \a frame.
3962 If the window is on the borders
3964 \param width The width of the window.
3965 \param height The height of the window.
3966 \param frame The \a frame to center the window in.
3968 \return The new window position.
3971 BWindow::AlertPosition(const BRect
& frame
)
3973 float width
= Bounds().Width();
3974 float height
= Bounds().Height();
3976 BPoint
point(frame
.left
+ (frame
.Width() / 2.0f
) - (width
/ 2.0f
),
3977 frame
.top
+ (frame
.Height() / 4.0f
) - ceil(height
/ 3.0f
));
3979 BRect screenFrame
= BScreen(this).Frame();
3980 if (frame
== screenFrame
) {
3981 // reference frame is screen frame, skip the below adjustments
3987 _GetDecoratorSize(&borderWidth
, &tabHeight
);
3989 // clip the x position within the horizontal edges of the screen
3990 if (point
.x
< screenFrame
.left
+ borderWidth
)
3991 point
.x
= screenFrame
.left
+ borderWidth
;
3992 else if (point
.x
+ width
> screenFrame
.right
- borderWidth
)
3993 point
.x
= screenFrame
.right
- borderWidth
- width
;
3995 // lower the window down if it is covering the window tab
3996 float tabPosition
= frame
.LeftTop().y
+ tabHeight
+ borderWidth
;
3997 if (point
.y
< tabPosition
)
3998 point
.y
= tabPosition
;
4000 // clip the y position within the vertical edges of the screen
4001 if (point
.y
< screenFrame
.top
+ borderWidth
)
4002 point
.y
= screenFrame
.top
+ borderWidth
;
4003 else if (point
.y
+ height
> screenFrame
.bottom
- borderWidth
)
4004 point
.y
= screenFrame
.bottom
- borderWidth
- height
;
4011 BWindow::ConvertToMessage(void* raw
, int32 code
)
4013 return BLooper::ConvertToMessage(raw
, code
);
4018 BWindow::_FindShortcut(uint32 key
, uint32 modifiers
)
4020 int32 count
= fShortcuts
.CountItems();
4022 key
= Shortcut::PrepareKey(key
);
4023 modifiers
= Shortcut::PrepareModifiers(modifiers
);
4025 for (int32 index
= 0; index
< count
; index
++) {
4026 Shortcut
* shortcut
= (Shortcut
*)fShortcuts
.ItemAt(index
);
4028 if (shortcut
->Matches(key
, modifiers
))
4037 BWindow::_FindView(int32 token
)
4040 if (gDefaultTokens
.GetToken(token
, B_HANDLER_TOKEN
,
4041 (void**)&handler
) != B_OK
) {
4045 // the view must belong to us in order to be found by this method
4046 BView
* view
= dynamic_cast<BView
*>(handler
);
4047 if (view
!= NULL
&& view
->Window() == this)
4055 BWindow::_FindView(BView
* view
, BPoint point
) const
4057 // point is assumed to be already in view's coordinates
4058 if (!view
->IsHidden(view
) && view
->Bounds().Contains(point
)) {
4059 if (view
->fFirstChild
== NULL
)
4062 BView
* child
= view
->fFirstChild
;
4063 while (child
!= NULL
) {
4064 BPoint childPoint
= point
- child
->Frame().LeftTop();
4065 BView
* subView
= _FindView(child
, childPoint
);
4066 if (subView
!= NULL
)
4069 child
= child
->fNextSibling
;
4079 BWindow::_FindNextNavigable(BView
* focus
, uint32 flags
)
4084 BView
* nextFocus
= focus
;
4086 // Search the tree for views that accept focus (depth search)
4088 if (nextFocus
->fFirstChild
)
4089 nextFocus
= nextFocus
->fFirstChild
;
4090 else if (nextFocus
->fNextSibling
)
4091 nextFocus
= nextFocus
->fNextSibling
;
4093 // go to the nearest parent with a next sibling
4094 while (!nextFocus
->fNextSibling
&& nextFocus
->fParent
) {
4095 nextFocus
= nextFocus
->fParent
;
4098 if (nextFocus
== fTopView
) {
4099 // if we started with the top view, we traversed the whole tree already
4100 if (nextFocus
== focus
)
4103 nextFocus
= nextFocus
->fFirstChild
;
4105 nextFocus
= nextFocus
->fNextSibling
;
4108 if (nextFocus
== focus
|| nextFocus
== NULL
) {
4109 // When we get here it means that the hole tree has been
4110 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
4114 if (!nextFocus
->IsHidden() && (nextFocus
->Flags() & flags
) != 0)
4121 BWindow::_FindPreviousNavigable(BView
* focus
, uint32 flags
)
4126 BView
* previousFocus
= focus
;
4128 // Search the tree for the previous view that accept focus
4130 if (previousFocus
->fPreviousSibling
) {
4131 // find the last child in the previous sibling
4132 previousFocus
= _LastViewChild(previousFocus
->fPreviousSibling
);
4134 previousFocus
= previousFocus
->fParent
;
4135 if (previousFocus
== fTopView
)
4136 previousFocus
= _LastViewChild(fTopView
);
4139 if (previousFocus
== focus
|| previousFocus
== NULL
) {
4140 // When we get here it means that the hole tree has been
4141 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
4145 if (!previousFocus
->IsHidden() && (previousFocus
->Flags() & flags
) != 0)
4146 return previousFocus
;
4152 Returns the last child in a view hierarchy.
4153 Needed only by _FindPreviousNavigable().
4156 BWindow::_LastViewChild(BView
* parent
)
4159 BView
* last
= parent
->fFirstChild
;
4163 while (last
->fNextSibling
) {
4164 last
= last
->fNextSibling
;
4173 BWindow::SetIsFilePanel(bool isFilePanel
)
4175 fIsFilePanel
= isFilePanel
;
4180 BWindow::IsFilePanel() const
4182 return fIsFilePanel
;
4187 BWindow::_GetDecoratorSize(float* _borderWidth
, float* _tabHeight
) const
4189 // fallback in case retrieving the decorator settings fails
4190 // (highly unlikely)
4191 float borderWidth
= 5.0;
4192 float tabHeight
= 21.0;
4195 if (GetDecoratorSettings(&settings
) == B_OK
) {
4197 if (settings
.FindRect("tab frame", &tabRect
) == B_OK
)
4198 tabHeight
= tabRect
.Height();
4199 settings
.FindFloat("border width", &borderWidth
);
4201 // probably no-border window look
4202 if (fLook
== B_NO_BORDER_WINDOW_LOOK
) {
4206 // else use fall-back values from above
4209 if (_borderWidth
!= NULL
)
4210 *_borderWidth
= borderWidth
;
4211 if (_tabHeight
!= NULL
)
4212 *_tabHeight
= tabHeight
;
4217 BWindow::_SendShowOrHideMessage()
4219 fLink
->StartMessage(AS_SHOW_OR_HIDE_WINDOW
);
4220 fLink
->Attach
<int32
>(fShowLevel
);
4225 // #pragma mark - C++ binary compatibility kludge
4229 _ReservedWindow1__7BWindow(BWindow
* window
, BLayout
* layout
)
4232 perform_data_set_layout data
;
4233 data
.layout
= layout
;
4234 window
->Perform(PERFORM_CODE_SET_LAYOUT
, &data
);
4238 void BWindow::_ReservedWindow2() {}
4239 void BWindow::_ReservedWindow3() {}
4240 void BWindow::_ReservedWindow4() {}
4241 void BWindow::_ReservedWindow5() {}
4242 void BWindow::_ReservedWindow6() {}
4243 void BWindow::_ReservedWindow7() {}
4244 void BWindow::_ReservedWindow8() {}