tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / Window.cpp
blob141e4891bc448b8f511cdbb7b6618849d11a6a5c
1 /*
2 * Copyright 2001-2014 Haiku, Inc. All rights reserved
3 * Distributed under the terms of the MIT License.
5 * Authors:
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
13 #include <Window.h>
15 #include <ctype.h>
16 #include <math.h>
17 #include <stdio.h>
18 #include <stdlib.h>
20 #include <Application.h>
21 #include <Autolock.h>
22 #include <Bitmap.h>
23 #include <Button.h>
24 #include <FindDirectory.h>
25 #include <Layout.h>
26 #include <LayoutUtils.h>
27 #include <MenuBar.h>
28 #include <MenuItem.h>
29 #include <MessageQueue.h>
30 #include <MessageRunner.h>
31 #include <Path.h>
32 #include <PropertyInfo.h>
33 #include <Roster.h>
34 #include <Screen.h>
35 #include <String.h>
36 #include <UnicodeChar.h>
38 #include <AppMisc.h>
39 #include <AppServerLink.h>
40 #include <ApplicationPrivate.h>
41 #include <binary_compatibility/Interface.h>
42 #include <DirectMessageTarget.h>
43 #include <input_globals.h>
44 #include <InputServerTypes.h>
45 #include <MenuPrivate.h>
46 #include <MessagePrivate.h>
47 #include <PortLink.h>
48 #include <RosterPrivate.h>
49 #include <ServerProtocol.h>
50 #include <TokenSpace.h>
51 #include <ToolTipManager.h>
52 #include <ToolTipWindow.h>
53 #include <tracker_private.h>
54 #include <WindowPrivate.h>
57 //#define DEBUG_WIN
58 #ifdef DEBUG_WIN
59 # define STRACE(x) printf x
60 #else
61 # define STRACE(x) ;
62 #endif
64 #define B_HIDE_APPLICATION '_AHD'
65 // if we ever move this to a public namespace, we should also move the
66 // handling of this message into BApplication
68 #define _MINIMIZE_ '_WMZ'
69 #define _ZOOM_ '_WZO'
70 #define _SEND_BEHIND_ '_WSB'
71 #define _SEND_TO_FRONT_ '_WSF'
74 void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
77 struct BWindow::unpack_cookie {
78 unpack_cookie();
80 BMessage* message;
81 int32 index;
82 BHandler* focus;
83 int32 focus_token;
84 int32 last_view_token;
85 bool found_focus;
86 bool tokens_scanned;
90 class BWindow::Shortcut {
91 public:
92 Shortcut(uint32 key, uint32 modifiers,
93 BMenuItem* item);
94 Shortcut(uint32 key, uint32 modifiers,
95 BMessage* message, BHandler* target);
96 ~Shortcut();
98 bool Matches(uint32 key, uint32 modifiers) const;
100 BMenuItem* MenuItem() const { return fMenuItem; }
101 BMessage* Message() const { return fMessage; }
102 BHandler* Target() const { return fTarget; }
104 static uint32 AllowedModifiers();
105 static uint32 PrepareKey(uint32 key);
106 static uint32 PrepareModifiers(uint32 modifiers);
108 private:
109 uint32 fKey;
110 uint32 fModifiers;
111 BMenuItem* fMenuItem;
112 BMessage* fMessage;
113 BHandler* fTarget;
117 using BPrivate::gDefaultTokens;
118 using BPrivate::MenuPrivate;
120 static property_info sWindowPropInfo[] = {
122 "Active", { B_GET_PROPERTY, B_SET_PROPERTY },
123 { B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
127 "Feel", { B_GET_PROPERTY, B_SET_PROPERTY },
128 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
132 "Flags", { B_GET_PROPERTY, B_SET_PROPERTY },
133 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
137 "Frame", { B_GET_PROPERTY, B_SET_PROPERTY },
138 { B_DIRECT_SPECIFIER }, NULL, 0, { B_RECT_TYPE }
142 "Hidden", { B_GET_PROPERTY, B_SET_PROPERTY },
143 { B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
147 "Look", { B_GET_PROPERTY, B_SET_PROPERTY },
148 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
152 "Title", { B_GET_PROPERTY, B_SET_PROPERTY },
153 { B_DIRECT_SPECIFIER }, NULL, 0, { B_STRING_TYPE }
157 "Workspaces", { B_GET_PROPERTY, B_SET_PROPERTY },
158 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
162 "MenuBar", {},
163 { B_DIRECT_SPECIFIER }, NULL, 0, {}
167 "View", { B_COUNT_PROPERTIES },
168 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
172 "View", {}, {}, NULL, 0, {}
176 "Minimize", { B_GET_PROPERTY, B_SET_PROPERTY },
177 { B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
181 "TabFrame", { B_GET_PROPERTY },
182 { B_DIRECT_SPECIFIER }, NULL, 0, { B_RECT_TYPE }
188 static value_info sWindowValueInfo[] = {
190 "MoveTo", 'WDMT', B_COMMAND_KIND,
191 "Moves to the position in the BPoint data"
195 "MoveBy", 'WDMB', B_COMMAND_KIND,
196 "Moves by the offsets in the BPoint data"
200 "ResizeTo", 'WDRT', B_COMMAND_KIND,
201 "Resize to the size in the BPoint data"
205 "ResizeBy", 'WDRB', B_COMMAND_KIND,
206 "Resize by the offsets in the BPoint data"
213 void
214 _set_menu_sem_(BWindow* window, sem_id sem)
216 if (window != NULL)
217 window->fMenuSem = sem;
221 // #pragma mark -
224 BWindow::unpack_cookie::unpack_cookie()
226 message((BMessage*)~0UL),
227 // message == NULL is our exit condition
228 index(0),
229 focus_token(B_NULL_TOKEN),
230 last_view_token(B_NULL_TOKEN),
231 found_focus(false),
232 tokens_scanned(false)
237 // #pragma mark - BWindow::Shortcut
240 BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMenuItem* item)
242 fKey(PrepareKey(key)),
243 fModifiers(PrepareModifiers(modifiers)),
244 fMenuItem(item),
245 fMessage(NULL),
246 fTarget(NULL)
251 BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMessage* message,
252 BHandler* target)
254 fKey(PrepareKey(key)),
255 fModifiers(PrepareModifiers(modifiers)),
256 fMenuItem(NULL),
257 fMessage(message),
258 fTarget(target)
263 BWindow::Shortcut::~Shortcut()
265 // we own the message, if any
266 delete fMessage;
270 bool
271 BWindow::Shortcut::Matches(uint32 key, uint32 modifiers) const
273 return fKey == key && fModifiers == modifiers;
277 /*static*/
278 uint32
279 BWindow::Shortcut::AllowedModifiers()
281 return B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY | B_CONTROL_KEY
282 | B_MENU_KEY;
286 /*static*/
287 uint32
288 BWindow::Shortcut::PrepareModifiers(uint32 modifiers)
290 return (modifiers & AllowedModifiers()) | B_COMMAND_KEY;
294 /*static*/
295 uint32
296 BWindow::Shortcut::PrepareKey(uint32 key)
298 return BUnicodeChar::ToLower(key);
302 // #pragma mark - BWindow
305 BWindow::BWindow(BRect frame, const char* title, window_type type,
306 uint32 flags, uint32 workspace)
308 BLooper(title, B_DISPLAY_PRIORITY)
310 window_look look;
311 window_feel feel;
312 _DecomposeType(type, &look, &feel);
314 _InitData(frame, title, look, feel, flags, workspace);
318 BWindow::BWindow(BRect frame, const char* title, window_look look,
319 window_feel feel, uint32 flags, uint32 workspace)
321 BLooper(title, B_DISPLAY_PRIORITY)
323 _InitData(frame, title, look, feel, flags, workspace);
327 BWindow::BWindow(BMessage* data)
329 BLooper(data)
331 data->FindRect("_frame", &fFrame);
333 const char* title;
334 data->FindString("_title", &title);
336 window_look look;
337 data->FindInt32("_wlook", (int32*)&look);
339 window_feel feel;
340 data->FindInt32("_wfeel", (int32*)&feel);
342 if (data->FindInt32("_flags", (int32*)&fFlags) != B_OK)
343 fFlags = 0;
345 uint32 workspaces;
346 data->FindInt32("_wspace", (int32*)&workspaces);
348 uint32 type;
349 if (data->FindInt32("_type", (int32*)&type) == B_OK)
350 _DecomposeType((window_type)type, &fLook, &fFeel);
352 // connect to app_server and initialize data
353 _InitData(fFrame, title, look, feel, fFlags, workspaces);
355 if (data->FindFloat("_zoom", 0, &fMaxZoomWidth) == B_OK
356 && data->FindFloat("_zoom", 1, &fMaxZoomHeight) == B_OK)
357 SetZoomLimits(fMaxZoomWidth, fMaxZoomHeight);
359 if (data->FindFloat("_sizel", 0, &fMinWidth) == B_OK
360 && data->FindFloat("_sizel", 1, &fMinHeight) == B_OK
361 && data->FindFloat("_sizel", 2, &fMaxWidth) == B_OK
362 && data->FindFloat("_sizel", 3, &fMaxHeight) == B_OK)
363 SetSizeLimits(fMinWidth, fMaxWidth,
364 fMinHeight, fMaxHeight);
366 if (data->FindInt64("_pulse", &fPulseRate) == B_OK)
367 SetPulseRate(fPulseRate);
369 BMessage msg;
370 int32 i = 0;
371 while (data->FindMessage("_views", i++, &msg) == B_OK) {
372 BArchivable* obj = instantiate_object(&msg);
373 if (BView* child = dynamic_cast<BView*>(obj))
374 AddChild(child);
379 BWindow::BWindow(BRect frame, int32 bitmapToken)
381 BLooper("offscreen bitmap")
383 _DecomposeType(B_UNTYPED_WINDOW, &fLook, &fFeel);
384 _InitData(frame, "offscreen", fLook, fFeel, 0, 0, bitmapToken);
388 BWindow::~BWindow()
390 if (BMenu* menu = dynamic_cast<BMenu*>(fFocus)) {
391 MenuPrivate(menu).QuitTracking();
394 // The BWindow is locked when the destructor is called,
395 // we need to unlock because the menubar thread tries
396 // to post a message, which will deadlock otherwise.
397 // TODO: I replaced Unlock() with UnlockFully() because the window
398 // was kept locked after that in case it was closed using ALT-W.
399 // There might be an extra Lock() somewhere in the quitting path...
400 UnlockFully();
402 // Wait if a menu is still tracking
403 if (fMenuSem > 0) {
404 while (acquire_sem(fMenuSem) == B_INTERRUPTED)
408 Lock();
410 fTopView->RemoveSelf();
411 delete fTopView;
413 // remove all remaining shortcuts
414 int32 shortCutCount = fShortcuts.CountItems();
415 for (int32 i = 0; i < shortCutCount; i++) {
416 delete (Shortcut*)fShortcuts.ItemAtFast(i);
419 // TODO: release other dynamically-allocated objects
420 free(fTitle);
422 // disable pulsing
423 SetPulseRate(0);
425 // tell app_server about our demise
426 fLink->StartMessage(AS_DELETE_WINDOW);
427 // sync with the server so that for example
428 // a BBitmap can be sure that there are no
429 // more pending messages that are executed
430 // after the bitmap is deleted (which uses
431 // a different link and server side thread)
432 int32 code;
433 fLink->FlushWithReply(code);
435 // the sender port belongs to the app_server
436 delete_port(fLink->ReceiverPort());
437 delete fLink;
441 BArchivable*
442 BWindow::Instantiate(BMessage* data)
444 if (!validate_instantiation(data, "BWindow"))
445 return NULL;
447 return new(std::nothrow) BWindow(data);
451 status_t
452 BWindow::Archive(BMessage* data, bool deep) const
454 status_t ret = BLooper::Archive(data, deep);
456 if (ret == B_OK)
457 ret = data->AddRect("_frame", fFrame);
458 if (ret == B_OK)
459 ret = data->AddString("_title", fTitle);
460 if (ret == B_OK)
461 ret = data->AddInt32("_wlook", fLook);
462 if (ret == B_OK)
463 ret = data->AddInt32("_wfeel", fFeel);
464 if (ret == B_OK && fFlags != 0)
465 ret = data->AddInt32("_flags", fFlags);
466 if (ret == B_OK)
467 ret = data->AddInt32("_wspace", (uint32)Workspaces());
469 if (ret == B_OK && !_ComposeType(fLook, fFeel))
470 ret = data->AddInt32("_type", (uint32)Type());
472 if (fMaxZoomWidth != 32768.0 || fMaxZoomHeight != 32768.0) {
473 if (ret == B_OK)
474 ret = data->AddFloat("_zoom", fMaxZoomWidth);
475 if (ret == B_OK)
476 ret = data->AddFloat("_zoom", fMaxZoomHeight);
479 if (fMinWidth != 0.0 || fMinHeight != 0.0
480 || fMaxWidth != 32768.0 || fMaxHeight != 32768.0) {
481 if (ret == B_OK)
482 ret = data->AddFloat("_sizel", fMinWidth);
483 if (ret == B_OK)
484 ret = data->AddFloat("_sizel", fMinHeight);
485 if (ret == B_OK)
486 ret = data->AddFloat("_sizel", fMaxWidth);
487 if (ret == B_OK)
488 ret = data->AddFloat("_sizel", fMaxHeight);
491 if (ret == B_OK && fPulseRate != 500000)
492 data->AddInt64("_pulse", fPulseRate);
494 if (ret == B_OK && deep) {
495 int32 noOfViews = CountChildren();
496 for (int32 i = 0; i < noOfViews; i++){
497 BMessage childArchive;
498 ret = ChildAt(i)->Archive(&childArchive, true);
499 if (ret == B_OK)
500 ret = data->AddMessage("_views", &childArchive);
501 if (ret != B_OK)
502 break;
506 return ret;
510 void
511 BWindow::Quit()
513 if (!IsLocked()) {
514 const char* name = Name();
515 if (name == NULL)
516 name = "no-name";
518 printf("ERROR - you must Lock a looper before calling Quit(), "
519 "team=%" B_PRId32 ", looper=%s\n", Team(), name);
522 // Try to lock
523 if (!Lock()){
524 // We're toast already
525 return;
528 while (!IsHidden()) {
529 Hide();
532 if (fFlags & B_QUIT_ON_WINDOW_CLOSE)
533 be_app->PostMessage(B_QUIT_REQUESTED);
535 BLooper::Quit();
539 void
540 BWindow::AddChild(BView* child, BView* before)
542 BAutolock locker(this);
543 if (locker.IsLocked())
544 fTopView->AddChild(child, before);
548 void
549 BWindow::AddChild(BLayoutItem* child)
551 BAutolock locker(this);
552 if (locker.IsLocked())
553 fTopView->AddChild(child);
557 bool
558 BWindow::RemoveChild(BView* child)
560 BAutolock locker(this);
561 if (!locker.IsLocked())
562 return false;
564 return fTopView->RemoveChild(child);
568 int32
569 BWindow::CountChildren() const
571 BAutolock locker(const_cast<BWindow*>(this));
572 if (!locker.IsLocked())
573 return 0;
575 return fTopView->CountChildren();
579 BView*
580 BWindow::ChildAt(int32 index) const
582 BAutolock locker(const_cast<BWindow*>(this));
583 if (!locker.IsLocked())
584 return NULL;
586 return fTopView->ChildAt(index);
590 void
591 BWindow::Minimize(bool minimize)
593 if (IsModal() || IsFloating() || IsHidden() || fMinimized == minimize
594 || !Lock())
595 return;
597 fMinimized = minimize;
599 fLink->StartMessage(AS_MINIMIZE_WINDOW);
600 fLink->Attach<bool>(minimize);
601 fLink->Flush();
603 Unlock();
607 status_t
608 BWindow::SendBehind(const BWindow* window)
610 if (!Lock())
611 return B_ERROR;
613 fLink->StartMessage(AS_SEND_BEHIND);
614 fLink->Attach<int32>(window != NULL ? _get_object_token_(window) : -1);
615 fLink->Attach<team_id>(Team());
617 status_t status = B_ERROR;
618 fLink->FlushWithReply(status);
620 Unlock();
622 return status;
626 void
627 BWindow::Flush() const
629 if (const_cast<BWindow*>(this)->Lock()) {
630 fLink->Flush();
631 const_cast<BWindow*>(this)->Unlock();
636 void
637 BWindow::Sync() const
639 if (!const_cast<BWindow*>(this)->Lock())
640 return;
642 fLink->StartMessage(AS_SYNC);
644 // waiting for the reply is the actual syncing
645 int32 code;
646 fLink->FlushWithReply(code);
648 const_cast<BWindow*>(this)->Unlock();
652 void
653 BWindow::DisableUpdates()
655 if (Lock()) {
656 fLink->StartMessage(AS_DISABLE_UPDATES);
657 fLink->Flush();
658 Unlock();
663 void
664 BWindow::EnableUpdates()
666 if (Lock()) {
667 fLink->StartMessage(AS_ENABLE_UPDATES);
668 fLink->Flush();
669 Unlock();
674 void
675 BWindow::BeginViewTransaction()
677 if (Lock()) {
678 fInTransaction = true;
679 Unlock();
684 void
685 BWindow::EndViewTransaction()
687 if (Lock()) {
688 if (fInTransaction)
689 fLink->Flush();
690 fInTransaction = false;
691 Unlock();
696 bool
697 BWindow::InViewTransaction() const
699 BAutolock locker(const_cast<BWindow*>(this));
700 return fInTransaction;
704 bool
705 BWindow::IsFront() const
707 BAutolock locker(const_cast<BWindow*>(this));
708 if (!locker.IsLocked())
709 return false;
711 fLink->StartMessage(AS_IS_FRONT_WINDOW);
713 status_t status;
714 if (fLink->FlushWithReply(status) == B_OK)
715 return status >= B_OK;
717 return false;
721 void
722 BWindow::MessageReceived(BMessage* message)
724 if (!message->HasSpecifiers()) {
725 if (message->what == B_KEY_DOWN)
726 _KeyboardNavigation();
728 if (message->what == (int32)kMsgAppServerRestarted) {
729 fLink->SetSenderPort(
730 BApplication::Private::ServerLink()->SenderPort());
732 BPrivate::AppServerLink lockLink;
733 // we're talking to the server application using our own
734 // communication channel (fLink) - we better make sure no one
735 // interferes by locking that channel (which AppServerLink does
736 // implicetly)
738 fLink->StartMessage(AS_CREATE_WINDOW);
740 fLink->Attach<BRect>(fFrame);
741 fLink->Attach<uint32>((uint32)fLook);
742 fLink->Attach<uint32>((uint32)fFeel);
743 fLink->Attach<uint32>(fFlags);
744 fLink->Attach<uint32>(0);
745 fLink->Attach<int32>(_get_object_token_(this));
746 fLink->Attach<port_id>(fLink->ReceiverPort());
747 fLink->Attach<port_id>(fMsgPort);
748 fLink->AttachString(fTitle);
750 port_id sendPort;
751 int32 code;
752 if (fLink->FlushWithReply(code) == B_OK
753 && code == B_OK
754 && fLink->Read<port_id>(&sendPort) == B_OK) {
755 // read the frame size and its limits that were really
756 // enforced on the server side
758 fLink->Read<BRect>(&fFrame);
759 fLink->Read<float>(&fMinWidth);
760 fLink->Read<float>(&fMaxWidth);
761 fLink->Read<float>(&fMinHeight);
762 fLink->Read<float>(&fMaxHeight);
764 fMaxZoomWidth = fMaxWidth;
765 fMaxZoomHeight = fMaxHeight;
766 } else
767 sendPort = -1;
769 // Redirect our link to the new window connection
770 fLink->SetSenderPort(sendPort);
772 // connect all views to the server again
773 fTopView->_CreateSelf();
775 _SendShowOrHideMessage();
778 return BLooper::MessageReceived(message);
781 BMessage replyMsg(B_REPLY);
782 bool handled = false;
784 BMessage specifier;
785 int32 what;
786 const char* prop;
787 int32 index;
789 if (message->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
790 return BLooper::MessageReceived(message);
792 BPropertyInfo propertyInfo(sWindowPropInfo);
793 switch (propertyInfo.FindMatch(message, index, &specifier, what, prop)) {
794 case 0:
795 if (message->what == B_GET_PROPERTY) {
796 replyMsg.AddBool("result", IsActive());
797 handled = true;
798 } else if (message->what == B_SET_PROPERTY) {
799 bool newActive;
800 if (message->FindBool("data", &newActive) == B_OK) {
801 Activate(newActive);
802 handled = true;
805 break;
806 case 1:
807 if (message->what == B_GET_PROPERTY) {
808 replyMsg.AddInt32("result", (uint32)Feel());
809 handled = true;
810 } else {
811 uint32 newFeel;
812 if (message->FindInt32("data", (int32*)&newFeel) == B_OK) {
813 SetFeel((window_feel)newFeel);
814 handled = true;
817 break;
818 case 2:
819 if (message->what == B_GET_PROPERTY) {
820 replyMsg.AddInt32("result", Flags());
821 handled = true;
822 } else {
823 uint32 newFlags;
824 if (message->FindInt32("data", (int32*)&newFlags) == B_OK) {
825 SetFlags(newFlags);
826 handled = true;
829 break;
830 case 3:
831 if (message->what == B_GET_PROPERTY) {
832 replyMsg.AddRect("result", Frame());
833 handled = true;
834 } else {
835 BRect newFrame;
836 if (message->FindRect("data", &newFrame) == B_OK) {
837 MoveTo(newFrame.LeftTop());
838 ResizeTo(newFrame.Width(), newFrame.Height());
839 handled = true;
842 break;
843 case 4:
844 if (message->what == B_GET_PROPERTY) {
845 replyMsg.AddBool("result", IsHidden());
846 handled = true;
847 } else {
848 bool hide;
849 if (message->FindBool("data", &hide) == B_OK) {
850 if (hide) {
851 if (!IsHidden())
852 Hide();
853 } else if (IsHidden())
854 Show();
855 handled = true;
858 break;
859 case 5:
860 if (message->what == B_GET_PROPERTY) {
861 replyMsg.AddInt32("result", (uint32)Look());
862 handled = true;
863 } else {
864 uint32 newLook;
865 if (message->FindInt32("data", (int32*)&newLook) == B_OK) {
866 SetLook((window_look)newLook);
867 handled = true;
870 break;
871 case 6:
872 if (message->what == B_GET_PROPERTY) {
873 replyMsg.AddString("result", Title());
874 handled = true;
875 } else {
876 const char* newTitle = NULL;
877 if (message->FindString("data", &newTitle) == B_OK) {
878 SetTitle(newTitle);
879 handled = true;
882 break;
883 case 7:
884 if (message->what == B_GET_PROPERTY) {
885 replyMsg.AddInt32( "result", Workspaces());
886 handled = true;
887 } else {
888 uint32 newWorkspaces;
889 if (message->FindInt32("data", (int32*)&newWorkspaces) == B_OK) {
890 SetWorkspaces(newWorkspaces);
891 handled = true;
894 break;
895 case 11:
896 if (message->what == B_GET_PROPERTY) {
897 replyMsg.AddBool("result", IsMinimized());
898 handled = true;
899 } else {
900 bool minimize;
901 if (message->FindBool("data", &minimize) == B_OK) {
902 Minimize(minimize);
903 handled = true;
906 break;
907 case 12:
908 if (message->what == B_GET_PROPERTY) {
909 BMessage settings;
910 if (GetDecoratorSettings(&settings) == B_OK) {
911 BRect frame;
912 if (settings.FindRect("tab frame", &frame) == B_OK) {
913 replyMsg.AddRect("result", frame);
914 handled = true;
918 break;
919 default:
920 return BLooper::MessageReceived(message);
923 if (handled) {
924 if (message->what == B_SET_PROPERTY)
925 replyMsg.AddInt32("error", B_OK);
926 } else {
927 replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
928 replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
929 replyMsg.AddString("message", "Didn't understand the specifier(s)");
931 message->SendReply(&replyMsg);
935 void
936 BWindow::DispatchMessage(BMessage* message, BHandler* target)
938 if (message == NULL)
939 return;
941 switch (message->what) {
942 case B_ZOOM:
943 Zoom();
944 break;
946 case _MINIMIZE_:
947 // Used by the minimize shortcut
948 if ((Flags() & B_NOT_MINIMIZABLE) == 0)
949 Minimize(true);
950 break;
952 case _ZOOM_:
953 // Used by the zoom shortcut
954 if ((Flags() & B_NOT_ZOOMABLE) == 0)
955 Zoom();
956 break;
958 case _SEND_BEHIND_:
959 SendBehind(NULL);
960 break;
962 case _SEND_TO_FRONT_:
963 Activate();
964 break;
966 case B_MINIMIZE:
968 bool minimize;
969 if (message->FindBool("minimize", &minimize) == B_OK)
970 Minimize(minimize);
971 break;
974 case B_HIDE_APPLICATION:
976 // Hide all applications with the same signature
977 // (ie. those that are part of the same group to be consistent
978 // to what the Deskbar shows you).
979 app_info info;
980 be_app->GetAppInfo(&info);
982 BList list;
983 be_roster->GetAppList(info.signature, &list);
985 for (int32 i = 0; i < list.CountItems(); i++) {
986 do_minimize_team(BRect(), (team_id)(addr_t)list.ItemAt(i),
987 false);
989 break;
992 case B_WINDOW_RESIZED:
994 int32 width, height;
995 if (message->FindInt32("width", &width) == B_OK
996 && message->FindInt32("height", &height) == B_OK) {
997 // combine with pending resize notifications
998 BMessage* pendingMessage;
999 while ((pendingMessage
1000 = MessageQueue()->FindMessage(B_WINDOW_RESIZED, 0))) {
1001 int32 nextWidth;
1002 if (pendingMessage->FindInt32("width", &nextWidth) == B_OK)
1003 width = nextWidth;
1005 int32 nextHeight;
1006 if (pendingMessage->FindInt32("height", &nextHeight)
1007 == B_OK) {
1008 height = nextHeight;
1011 MessageQueue()->RemoveMessage(pendingMessage);
1012 delete pendingMessage;
1013 // this deletes the first *additional* message
1014 // fCurrentMessage is safe
1016 if (width != fFrame.Width() || height != fFrame.Height()) {
1017 // NOTE: we might have already handled the resize
1018 // in an _UPDATE_ message
1019 fFrame.right = fFrame.left + width;
1020 fFrame.bottom = fFrame.top + height;
1022 _AdoptResize();
1023 // FrameResized(width, height);
1025 // call hook function anyways
1026 // TODO: When a window is resized programmatically,
1027 // it receives this message, and maybe it is wise to
1028 // keep the asynchronous nature of this process to
1029 // not risk breaking any apps.
1030 FrameResized(width, height);
1032 break;
1035 case B_WINDOW_MOVED:
1037 BPoint origin;
1038 if (message->FindPoint("where", &origin) == B_OK) {
1039 if (fFrame.LeftTop() != origin) {
1040 // NOTE: we might have already handled the move
1041 // in an _UPDATE_ message
1042 fFrame.OffsetTo(origin);
1044 // FrameMoved(origin);
1046 // call hook function anyways
1047 // TODO: When a window is moved programmatically,
1048 // it receives this message, and maybe it is wise to
1049 // keep the asynchronous nature of this process to
1050 // not risk breaking any apps.
1051 FrameMoved(origin);
1053 break;
1056 case B_WINDOW_ACTIVATED:
1057 if (target != this) {
1058 target->MessageReceived(message);
1059 break;
1062 bool active;
1063 if (message->FindBool("active", &active) != B_OK)
1064 break;
1066 // find latest activation message
1068 while (true) {
1069 BMessage* pendingMessage = MessageQueue()->FindMessage(
1070 B_WINDOW_ACTIVATED, 0);
1071 if (pendingMessage == NULL)
1072 break;
1074 bool nextActive;
1075 if (pendingMessage->FindBool("active", &nextActive) == B_OK)
1076 active = nextActive;
1078 MessageQueue()->RemoveMessage(pendingMessage);
1079 delete pendingMessage;
1082 if (active != fActive) {
1083 fActive = active;
1085 WindowActivated(active);
1087 // call hook function 'WindowActivated(bool)' for all
1088 // views attached to this window.
1089 fTopView->_Activate(active);
1091 // we notify the input server if we are gaining or losing focus
1092 // from a view which has the B_INPUT_METHOD_AWARE on a window
1093 // activation
1094 if (!active)
1095 break;
1096 bool inputMethodAware = false;
1097 if (fFocus)
1098 inputMethodAware = fFocus->Flags() & B_INPUT_METHOD_AWARE;
1099 BMessage message(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
1100 BMessenger messenger(fFocus);
1101 BMessage reply;
1102 if (fFocus)
1103 message.AddMessenger("view", messenger);
1104 _control_input_server_(&message, &reply);
1106 break;
1108 case B_SCREEN_CHANGED:
1109 if (target == this) {
1110 BRect frame;
1111 uint32 mode;
1112 if (message->FindRect("frame", &frame) == B_OK
1113 && message->FindInt32("mode", (int32*)&mode) == B_OK)
1114 ScreenChanged(frame, (color_space)mode);
1115 } else
1116 target->MessageReceived(message);
1117 break;
1119 case B_WORKSPACE_ACTIVATED:
1120 if (target == this) {
1121 uint32 workspace;
1122 bool active;
1123 if (message->FindInt32("workspace", (int32*)&workspace) == B_OK
1124 && message->FindBool("active", &active) == B_OK)
1125 WorkspaceActivated(workspace, active);
1126 } else
1127 target->MessageReceived(message);
1128 break;
1130 case B_WORKSPACES_CHANGED:
1131 if (target == this) {
1132 uint32 oldWorkspace, newWorkspace;
1133 if (message->FindInt32("old", (int32*)&oldWorkspace) == B_OK
1134 && message->FindInt32("new", (int32*)&newWorkspace) == B_OK)
1135 WorkspacesChanged(oldWorkspace, newWorkspace);
1136 } else
1137 target->MessageReceived(message);
1138 break;
1140 case B_INVALIDATE:
1142 if (BView* view = dynamic_cast<BView*>(target)) {
1143 BRect rect;
1144 if (message->FindRect("be:area", &rect) == B_OK)
1145 view->Invalidate(rect);
1146 else
1147 view->Invalidate();
1148 } else
1149 target->MessageReceived(message);
1150 break;
1153 case B_KEY_DOWN:
1155 if (!_HandleKeyDown(message)) {
1156 if (BView* view = dynamic_cast<BView*>(target)) {
1157 // TODO: cannot use "string" here if we support having
1158 // different font encoding per view (it's supposed to be
1159 // converted by _HandleKeyDown() one day)
1160 const char* string;
1161 ssize_t bytes;
1162 if (message->FindData("bytes", B_STRING_TYPE,
1163 (const void**)&string, &bytes) == B_OK) {
1164 view->KeyDown(string, bytes - 1);
1166 } else
1167 target->MessageReceived(message);
1169 break;
1172 case B_KEY_UP:
1174 // TODO: same as above
1175 if (BView* view = dynamic_cast<BView*>(target)) {
1176 const char* string;
1177 ssize_t bytes;
1178 if (message->FindData("bytes", B_STRING_TYPE,
1179 (const void**)&string, &bytes) == B_OK) {
1180 view->KeyUp(string, bytes - 1);
1182 } else
1183 target->MessageReceived(message);
1184 break;
1187 case B_UNMAPPED_KEY_DOWN:
1189 if (!_HandleUnmappedKeyDown(message))
1190 target->MessageReceived(message);
1191 break;
1194 case B_MOUSE_DOWN:
1196 BView* view = dynamic_cast<BView*>(target);
1198 if (view != NULL) {
1199 BPoint where;
1200 message->FindPoint("be:view_where", &where);
1201 view->MouseDown(where);
1202 } else
1203 target->MessageReceived(message);
1205 break;
1208 case B_MOUSE_UP:
1210 if (BView* view = dynamic_cast<BView*>(target)) {
1211 BPoint where;
1212 message->FindPoint("be:view_where", &where);
1213 view->fMouseEventOptions = 0;
1214 view->MouseUp(where);
1215 } else
1216 target->MessageReceived(message);
1218 break;
1221 case B_MOUSE_MOVED:
1223 if (BView* view = dynamic_cast<BView*>(target)) {
1224 uint32 eventOptions = view->fEventOptions
1225 | view->fMouseEventOptions;
1226 bool noHistory = eventOptions & B_NO_POINTER_HISTORY;
1227 bool dropIfLate = !(eventOptions & B_FULL_POINTER_HISTORY);
1229 bigtime_t eventTime;
1230 if (message->FindInt64("when", (int64*)&eventTime) < B_OK)
1231 eventTime = system_time();
1233 uint32 transit;
1234 message->FindInt32("be:transit", (int32*)&transit);
1235 // don't drop late messages with these important transit values
1236 if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
1237 dropIfLate = false;
1239 // TODO: The dropping code may have the following problem:
1240 // On slower computers, 20ms may just be to abitious a delay.
1241 // There, we might constantly check the message queue for a
1242 // newer message, not find any, and still use the only but
1243 // later than 20ms message, which of course makes the whole
1244 // thing later than need be. An adaptive delay would be
1245 // kind of neat, but would probably use additional BWindow
1246 // members to count the successful versus fruitless queue
1247 // searches and the delay value itself or something similar.
1249 if (noHistory
1250 || (dropIfLate && (system_time() - eventTime > 20000))) {
1251 // filter out older mouse moved messages in the queue
1252 _DequeueAll();
1253 BMessageQueue* queue = MessageQueue();
1254 queue->Lock();
1256 BMessage* moved;
1257 for (int32 i = 0; (moved = queue->FindMessage(i)) != NULL;
1258 i++) {
1259 if (moved != message && moved->what == B_MOUSE_MOVED) {
1260 // there is a newer mouse moved message in the
1261 // queue, just ignore the current one, the newer one
1262 // will be handled here eventually
1263 queue->Unlock();
1264 return;
1267 queue->Unlock();
1270 BPoint where;
1271 uint32 buttons;
1272 message->FindPoint("be:view_where", &where);
1273 message->FindInt32("buttons", (int32*)&buttons);
1275 if (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW) {
1276 if (dynamic_cast<BPrivate::ToolTipWindow*>(this) == NULL)
1277 BToolTipManager::Manager()->HideTip();
1280 BMessage* dragMessage = NULL;
1281 if (message->HasMessage("be:drag_message")) {
1282 dragMessage = new BMessage();
1283 if (message->FindMessage("be:drag_message", dragMessage)
1284 != B_OK) {
1285 delete dragMessage;
1286 dragMessage = NULL;
1290 view->MouseMoved(where, transit, dragMessage);
1291 delete dragMessage;
1292 } else
1293 target->MessageReceived(message);
1295 break;
1298 case B_PULSE:
1299 if (target == this && fPulseRunner) {
1300 fTopView->_Pulse();
1301 fLink->Flush();
1302 } else
1303 target->MessageReceived(message);
1304 break;
1306 case _UPDATE_:
1308 //bigtime_t now = system_time();
1309 //bigtime_t drawTime = 0;
1310 STRACE(("info:BWindow handling _UPDATE_.\n"));
1312 fLink->StartMessage(AS_BEGIN_UPDATE);
1313 fInTransaction = true;
1315 int32 code;
1316 if (fLink->FlushWithReply(code) == B_OK
1317 && code == B_OK) {
1318 // read current window position and size first,
1319 // the update rect is in screen coordinates...
1320 // so we need to be up to date
1321 BPoint origin;
1322 fLink->Read<BPoint>(&origin);
1323 float width;
1324 float height;
1325 fLink->Read<float>(&width);
1326 fLink->Read<float>(&height);
1327 if (origin != fFrame.LeftTop()) {
1328 // TODO: remove code duplicatation with
1329 // B_WINDOW_MOVED case...
1330 //printf("window position was not up to date\n");
1331 fFrame.OffsetTo(origin);
1332 FrameMoved(origin);
1334 if (width != fFrame.Width() || height != fFrame.Height()) {
1335 // TODO: remove code duplicatation with
1336 // B_WINDOW_RESIZED case...
1337 //printf("window size was not up to date\n");
1338 fFrame.right = fFrame.left + width;
1339 fFrame.bottom = fFrame.top + height;
1341 _AdoptResize();
1342 FrameResized(width, height);
1345 // read tokens for views that need to be drawn
1346 // NOTE: we need to read the tokens completely
1347 // first, we cannot draw views in between reading
1348 // the tokens, since other communication would likely
1349 // mess up the data in the link.
1350 struct ViewUpdateInfo {
1351 int32 token;
1352 BRect updateRect;
1354 BList infos(20);
1355 while (true) {
1356 // read next token and create/add ViewUpdateInfo
1357 int32 token;
1358 status_t error = fLink->Read<int32>(&token);
1359 if (error < B_OK || token == B_NULL_TOKEN)
1360 break;
1361 ViewUpdateInfo* info = new(std::nothrow) ViewUpdateInfo;
1362 if (info == NULL || !infos.AddItem(info)) {
1363 delete info;
1364 break;
1366 info->token = token;
1367 // read culmulated update rect (is in screen coords)
1368 error = fLink->Read<BRect>(&(info->updateRect));
1369 if (error < B_OK)
1370 break;
1372 // draw
1373 int32 count = infos.CountItems();
1374 for (int32 i = 0; i < count; i++) {
1375 //bigtime_t drawStart = system_time();
1376 ViewUpdateInfo* info
1377 = (ViewUpdateInfo*)infos.ItemAtFast(i);
1378 if (BView* view = _FindView(info->token))
1379 view->_Draw(info->updateRect);
1380 else {
1381 printf("_UPDATE_ - didn't find view by token: %"
1382 B_PRId32 "\n", info->token);
1384 //drawTime += system_time() - drawStart;
1386 // NOTE: The tokens are actually hirachically sorted,
1387 // so traversing the list in revers and calling
1388 // child->_DrawAfterChildren() actually works like intended.
1389 for (int32 i = count - 1; i >= 0; i--) {
1390 ViewUpdateInfo* info
1391 = (ViewUpdateInfo*)infos.ItemAtFast(i);
1392 if (BView* view = _FindView(info->token))
1393 view->_DrawAfterChildren(info->updateRect);
1394 delete info;
1397 //printf(" %ld views drawn, total Draw() time: %lld\n", count, drawTime);
1400 fLink->StartMessage(AS_END_UPDATE);
1401 fLink->Flush();
1402 fInTransaction = false;
1403 fUpdateRequested = false;
1405 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now);
1406 break;
1409 case _MENUS_DONE_:
1410 MenusEnded();
1411 break;
1413 // These two are obviously some kind of old scripting messages
1414 // this is NOT an app_server message and we have to be cautious
1415 case B_WINDOW_MOVE_BY:
1417 BPoint offset;
1418 if (message->FindPoint("data", &offset) == B_OK)
1419 MoveBy(offset.x, offset.y);
1420 else
1421 message->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1422 break;
1425 // this is NOT an app_server message and we have to be cautious
1426 case B_WINDOW_MOVE_TO:
1428 BPoint origin;
1429 if (message->FindPoint("data", &origin) == B_OK)
1430 MoveTo(origin);
1431 else
1432 message->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1433 break;
1436 case B_LAYOUT_WINDOW:
1438 Layout(false);
1439 break;
1442 default:
1443 BLooper::DispatchMessage(message, target);
1444 break;
1449 void
1450 BWindow::FrameMoved(BPoint newPosition)
1452 // does nothing
1453 // Hook function
1457 void
1458 BWindow::FrameResized(float newWidth, float newHeight)
1460 // does nothing
1461 // Hook function
1465 void
1466 BWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1468 // does nothing
1469 // Hook function
1473 void
1474 BWindow::WorkspaceActivated(int32 workspace, bool state)
1476 // does nothing
1477 // Hook function
1481 void
1482 BWindow::MenusBeginning()
1484 // does nothing
1485 // Hook function
1489 void
1490 BWindow::MenusEnded()
1492 // does nothing
1493 // Hook function
1497 void
1498 BWindow::SetSizeLimits(float minWidth, float maxWidth,
1499 float minHeight, float maxHeight)
1501 if (minWidth > maxWidth || minHeight > maxHeight)
1502 return;
1504 if (!Lock())
1505 return;
1507 fLink->StartMessage(AS_SET_SIZE_LIMITS);
1508 fLink->Attach<float>(minWidth);
1509 fLink->Attach<float>(maxWidth);
1510 fLink->Attach<float>(minHeight);
1511 fLink->Attach<float>(maxHeight);
1513 int32 code;
1514 if (fLink->FlushWithReply(code) == B_OK
1515 && code == B_OK) {
1516 // read the values that were really enforced on
1517 // the server side (the window frame could have
1518 // been changed, too)
1519 fLink->Read<BRect>(&fFrame);
1520 fLink->Read<float>(&fMinWidth);
1521 fLink->Read<float>(&fMaxWidth);
1522 fLink->Read<float>(&fMinHeight);
1523 fLink->Read<float>(&fMaxHeight);
1525 _AdoptResize();
1526 // TODO: the same has to be done for SetLook() (that can alter
1527 // the size limits, and hence, the size of the window
1529 Unlock();
1533 void
1534 BWindow::GetSizeLimits(float* _minWidth, float* _maxWidth, float* _minHeight,
1535 float* _maxHeight)
1537 // TODO: What about locking?!?
1538 if (_minHeight != NULL)
1539 *_minHeight = fMinHeight;
1540 if (_minWidth != NULL)
1541 *_minWidth = fMinWidth;
1542 if (_maxHeight != NULL)
1543 *_maxHeight = fMaxHeight;
1544 if (_maxWidth != NULL)
1545 *_maxWidth = fMaxWidth;
1549 void
1550 BWindow::UpdateSizeLimits()
1552 if ((fFlags & B_AUTO_UPDATE_SIZE_LIMITS) != 0) {
1553 // Get min/max constraints of the top view and enforce window
1554 // size limits respectively.
1555 BSize minSize = fTopView->MinSize();
1556 BSize maxSize = fTopView->MaxSize();
1557 SetSizeLimits(minSize.width, maxSize.width,
1558 minSize.height, maxSize.height);
1563 status_t
1564 BWindow::SetDecoratorSettings(const BMessage& settings)
1566 // flatten the given settings into a buffer and send
1567 // it to the app_server to apply the settings to the
1568 // decorator
1570 int32 size = settings.FlattenedSize();
1571 char buffer[size];
1572 status_t status = settings.Flatten(buffer, size);
1573 if (status != B_OK)
1574 return status;
1576 if (!Lock())
1577 return B_ERROR;
1579 status = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS);
1581 if (status == B_OK)
1582 status = fLink->Attach<int32>(size);
1584 if (status == B_OK)
1585 status = fLink->Attach(buffer, size);
1587 if (status == B_OK)
1588 status = fLink->Flush();
1590 Unlock();
1592 return status;
1596 status_t
1597 BWindow::GetDecoratorSettings(BMessage* settings) const
1599 // read a flattened settings message from the app_server
1600 // and put it into settings
1602 if (!const_cast<BWindow*>(this)->Lock())
1603 return B_ERROR;
1605 status_t status = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS);
1607 if (status == B_OK) {
1608 int32 code;
1609 status = fLink->FlushWithReply(code);
1610 if (status == B_OK && code != B_OK)
1611 status = code;
1614 if (status == B_OK) {
1615 int32 size;
1616 status = fLink->Read<int32>(&size);
1617 if (status == B_OK) {
1618 char buffer[size];
1619 status = fLink->Read(buffer, size);
1620 if (status == B_OK) {
1621 status = settings->Unflatten(buffer);
1626 const_cast<BWindow*>(this)->Unlock();
1628 return status;
1632 void
1633 BWindow::SetZoomLimits(float maxWidth, float maxHeight)
1635 // TODO: What about locking?!?
1636 if (maxWidth > fMaxWidth)
1637 maxWidth = fMaxWidth;
1638 else
1639 fMaxZoomWidth = maxWidth;
1641 if (maxHeight > fMaxHeight)
1642 maxHeight = fMaxHeight;
1643 else
1644 fMaxZoomHeight = maxHeight;
1648 void
1649 BWindow::Zoom(BPoint origin, float width, float height)
1651 // the default implementation of this hook function
1652 // just does the obvious:
1653 MoveTo(origin);
1654 ResizeTo(width, height);
1658 void
1659 BWindow::Zoom()
1661 // TODO: What about locking?!?
1663 // From BeBook:
1664 // The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced
1665 // from the smallest of three rectangles:
1667 float borderWidth;
1668 float tabHeight;
1669 _GetDecoratorSize(&borderWidth, &tabHeight);
1671 // 1) the rectangle defined by SetZoomLimits(),
1672 float zoomedWidth = fMaxZoomWidth;
1673 float zoomedHeight = fMaxZoomHeight;
1675 // 2) the rectangle defined by SetSizeLimits()
1676 if (fMaxWidth < zoomedWidth)
1677 zoomedWidth = fMaxWidth;
1678 if (fMaxHeight < zoomedHeight)
1679 zoomedHeight = fMaxHeight;
1681 // 3) the screen rectangle
1682 BScreen screen(this);
1683 // TODO: Broken for tab on left side windows...
1684 float screenWidth = screen.Frame().Width() - 2 * borderWidth;
1685 float screenHeight = screen.Frame().Height() - (2 * borderWidth + tabHeight);
1686 if (screenWidth < zoomedWidth)
1687 zoomedWidth = screenWidth;
1688 if (screenHeight < zoomedHeight)
1689 zoomedHeight = screenHeight;
1691 BPoint zoomedLeftTop = screen.Frame().LeftTop() + BPoint(borderWidth,
1692 tabHeight + borderWidth);
1693 // Center if window cannot be made full screen
1694 if (screenWidth > zoomedWidth)
1695 zoomedLeftTop.x += (screenWidth - zoomedWidth) / 2;
1696 if (screenHeight > zoomedHeight)
1697 zoomedLeftTop.y += (screenHeight - zoomedHeight) / 2;
1699 // Un-Zoom
1701 if (fPreviousFrame.IsValid()
1702 // NOTE: don't check for fFrame.LeftTop() == zoomedLeftTop
1703 // -> makes it easier on the user to get a window back into place
1704 && fFrame.Width() == zoomedWidth && fFrame.Height() == zoomedHeight) {
1705 // already zoomed!
1706 Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(),
1707 fPreviousFrame.Height());
1708 return;
1711 // Zoom
1713 // remember fFrame for later "unzooming"
1714 fPreviousFrame = fFrame;
1716 Zoom(zoomedLeftTop, zoomedWidth, zoomedHeight);
1720 void
1721 BWindow::ScreenChanged(BRect screenSize, color_space depth)
1723 // Hook function
1727 void
1728 BWindow::SetPulseRate(bigtime_t rate)
1730 // TODO: What about locking?!?
1731 if (rate < 0
1732 || (rate == fPulseRate && !((rate == 0) ^ (fPulseRunner == NULL))))
1733 return;
1735 fPulseRate = rate;
1737 if (rate > 0) {
1738 if (fPulseRunner == NULL) {
1739 BMessage message(B_PULSE);
1740 fPulseRunner = new(std::nothrow) BMessageRunner(BMessenger(this),
1741 &message, rate);
1742 } else {
1743 fPulseRunner->SetInterval(rate);
1745 } else {
1746 // rate == 0
1747 delete fPulseRunner;
1748 fPulseRunner = NULL;
1753 bigtime_t
1754 BWindow::PulseRate() const
1756 return fPulseRate;
1760 void
1761 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem* item)
1763 Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, item);
1764 if (shortcut == NULL)
1765 return;
1767 // removes the shortcut if it already exists!
1768 RemoveShortcut(key, modifiers);
1770 fShortcuts.AddItem(shortcut);
1774 void
1775 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message)
1777 AddShortcut(key, modifiers, message, this);
1781 void
1782 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message,
1783 BHandler* target)
1785 if (message == NULL)
1786 return;
1788 Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, message,
1789 target);
1790 if (shortcut == NULL)
1791 return;
1793 // removes the shortcut if it already exists!
1794 RemoveShortcut(key, modifiers);
1796 fShortcuts.AddItem(shortcut);
1800 bool
1801 BWindow::HasShortcut(uint32 key, uint32 modifiers)
1803 return _FindShortcut(key, modifiers) != NULL;
1807 void
1808 BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
1810 Shortcut* shortcut = _FindShortcut(key, modifiers);
1811 if (shortcut != NULL) {
1812 fShortcuts.RemoveItem(shortcut);
1813 delete shortcut;
1814 } else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) {
1815 // the quit shortcut is a fake shortcut
1816 fNoQuitShortcut = true;
1821 BButton*
1822 BWindow::DefaultButton() const
1824 // TODO: What about locking?!?
1825 return fDefaultButton;
1829 void
1830 BWindow::SetDefaultButton(BButton* button)
1832 // TODO: What about locking?!?
1833 if (fDefaultButton == button)
1834 return;
1836 if (fDefaultButton != NULL) {
1837 // tell old button it's no longer the default one
1838 BButton* oldDefault = fDefaultButton;
1839 oldDefault->MakeDefault(false);
1840 oldDefault->Invalidate();
1843 fDefaultButton = button;
1845 if (button != NULL) {
1846 // notify new default button
1847 fDefaultButton->MakeDefault(true);
1848 fDefaultButton->Invalidate();
1853 bool
1854 BWindow::NeedsUpdate() const
1856 if (!const_cast<BWindow*>(this)->Lock())
1857 return false;
1859 fLink->StartMessage(AS_NEEDS_UPDATE);
1861 int32 code = B_ERROR;
1862 fLink->FlushWithReply(code);
1864 const_cast<BWindow*>(this)->Unlock();
1866 return code == B_OK;
1870 void
1871 BWindow::UpdateIfNeeded()
1873 // works only from the window thread
1874 if (find_thread(NULL) != Thread())
1875 return;
1877 // if the queue is already locked we are called recursivly
1878 // from our own dispatched update message
1879 if (((const BMessageQueue*)MessageQueue())->IsLocked())
1880 return;
1882 if (!Lock())
1883 return;
1885 // make sure all requests that would cause an update have
1886 // arrived at the server
1887 Sync();
1889 // Since we're blocking the event loop, we need to retrieve
1890 // all messages that are pending on the port.
1891 _DequeueAll();
1893 BMessageQueue* queue = MessageQueue();
1895 // First process and remove any _UPDATE_ message in the queue
1896 // With the current design, there can only be one at a time
1898 while (true) {
1899 queue->Lock();
1901 BMessage* message = queue->FindMessage(_UPDATE_, 0);
1902 queue->RemoveMessage(message);
1904 queue->Unlock();
1906 if (message == NULL)
1907 break;
1909 BWindow::DispatchMessage(message, this);
1910 delete message;
1913 Unlock();
1917 BView*
1918 BWindow::FindView(const char* viewName) const
1920 BAutolock locker(const_cast<BWindow*>(this));
1921 if (!locker.IsLocked())
1922 return NULL;
1924 return fTopView->FindView(viewName);
1928 BView*
1929 BWindow::FindView(BPoint point) const
1931 BAutolock locker(const_cast<BWindow*>(this));
1932 if (!locker.IsLocked())
1933 return NULL;
1935 // point is assumed to be in window coordinates,
1936 // fTopView has same bounds as window
1937 return _FindView(fTopView, point);
1941 BView*
1942 BWindow::CurrentFocus() const
1944 return fFocus;
1948 void
1949 BWindow::Activate(bool active)
1951 if (!Lock())
1952 return;
1954 if (!IsHidden()) {
1955 fMinimized = false;
1956 // activating a window will also unminimize it
1958 fLink->StartMessage(AS_ACTIVATE_WINDOW);
1959 fLink->Attach<bool>(active);
1960 fLink->Flush();
1963 Unlock();
1967 void
1968 BWindow::WindowActivated(bool focus)
1970 // hook function
1971 // does nothing
1975 void
1976 BWindow::ConvertToScreen(BPoint* point) const
1978 point->x += fFrame.left;
1979 point->y += fFrame.top;
1983 BPoint
1984 BWindow::ConvertToScreen(BPoint point) const
1986 return point + fFrame.LeftTop();
1990 void
1991 BWindow::ConvertFromScreen(BPoint* point) const
1993 point->x -= fFrame.left;
1994 point->y -= fFrame.top;
1998 BPoint
1999 BWindow::ConvertFromScreen(BPoint point) const
2001 return point - fFrame.LeftTop();
2005 void
2006 BWindow::ConvertToScreen(BRect* rect) const
2008 rect->OffsetBy(fFrame.LeftTop());
2012 BRect
2013 BWindow::ConvertToScreen(BRect rect) const
2015 return rect.OffsetByCopy(fFrame.LeftTop());
2019 void
2020 BWindow::ConvertFromScreen(BRect* rect) const
2022 rect->OffsetBy(-fFrame.left, -fFrame.top);
2026 BRect
2027 BWindow::ConvertFromScreen(BRect rect) const
2029 return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
2033 bool
2034 BWindow::IsMinimized() const
2036 BAutolock locker(const_cast<BWindow*>(this));
2037 if (!locker.IsLocked())
2038 return false;
2040 return fMinimized;
2044 BRect
2045 BWindow::Bounds() const
2047 return BRect(0, 0, fFrame.Width(), fFrame.Height());
2051 BRect
2052 BWindow::Frame() const
2054 return fFrame;
2058 BRect
2059 BWindow::DecoratorFrame() const
2061 BRect decoratorFrame(Frame());
2062 BRect tabRect(0, 0, 0, 0);
2064 float borderWidth = 5.0;
2066 BMessage settings;
2067 if (GetDecoratorSettings(&settings) == B_OK) {
2068 settings.FindRect("tab frame", &tabRect);
2069 settings.FindFloat("border width", &borderWidth);
2070 } else {
2071 // probably no-border window look
2072 if (fLook == B_NO_BORDER_WINDOW_LOOK)
2073 borderWidth = 0.f;
2074 else if (fLook == B_BORDERED_WINDOW_LOOK)
2075 borderWidth = 1.f;
2076 // else use fall-back values from above
2079 if (fLook == kLeftTitledWindowLook) {
2080 decoratorFrame.top -= borderWidth;
2081 decoratorFrame.left -= borderWidth + tabRect.Width();
2082 decoratorFrame.right += borderWidth;
2083 decoratorFrame.bottom += borderWidth;
2084 } else {
2085 decoratorFrame.top -= borderWidth + tabRect.Height();
2086 decoratorFrame.left -= borderWidth;
2087 decoratorFrame.right += borderWidth;
2088 decoratorFrame.bottom += borderWidth;
2091 return decoratorFrame;
2095 BSize
2096 BWindow::Size() const
2098 return BSize(fFrame.Width(), fFrame.Height());
2102 const char*
2103 BWindow::Title() const
2105 return fTitle;
2109 void
2110 BWindow::SetTitle(const char* title)
2112 if (title == NULL)
2113 title = "";
2115 free(fTitle);
2116 fTitle = strdup(title);
2118 _SetName(title);
2120 // we notify the app_server so we can actually see the change
2121 if (Lock()) {
2122 fLink->StartMessage(AS_SET_WINDOW_TITLE);
2123 fLink->AttachString(fTitle);
2124 fLink->Flush();
2125 Unlock();
2130 bool
2131 BWindow::IsActive() const
2133 return fActive;
2137 void
2138 BWindow::SetKeyMenuBar(BMenuBar* bar)
2140 fKeyMenuBar = bar;
2144 BMenuBar*
2145 BWindow::KeyMenuBar() const
2147 return fKeyMenuBar;
2151 bool
2152 BWindow::IsModal() const
2154 return fFeel == B_MODAL_SUBSET_WINDOW_FEEL
2155 || fFeel == B_MODAL_APP_WINDOW_FEEL
2156 || fFeel == B_MODAL_ALL_WINDOW_FEEL
2157 || fFeel == kMenuWindowFeel;
2161 bool
2162 BWindow::IsFloating() const
2164 return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL
2165 || fFeel == B_FLOATING_APP_WINDOW_FEEL
2166 || fFeel == B_FLOATING_ALL_WINDOW_FEEL;
2170 status_t
2171 BWindow::AddToSubset(BWindow* window)
2173 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
2174 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
2175 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
2176 return B_BAD_VALUE;
2178 if (!Lock())
2179 return B_ERROR;
2181 status_t status = B_ERROR;
2182 fLink->StartMessage(AS_ADD_TO_SUBSET);
2183 fLink->Attach<int32>(_get_object_token_(window));
2184 fLink->FlushWithReply(status);
2186 Unlock();
2188 return status;
2192 status_t
2193 BWindow::RemoveFromSubset(BWindow* window)
2195 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
2196 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
2197 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
2198 return B_BAD_VALUE;
2200 if (!Lock())
2201 return B_ERROR;
2203 status_t status = B_ERROR;
2204 fLink->StartMessage(AS_REMOVE_FROM_SUBSET);
2205 fLink->Attach<int32>(_get_object_token_(window));
2206 fLink->FlushWithReply(status);
2208 Unlock();
2210 return status;
2214 status_t
2215 BWindow::Perform(perform_code code, void* _data)
2217 switch (code) {
2218 case PERFORM_CODE_SET_LAYOUT:
2220 perform_data_set_layout* data = (perform_data_set_layout*)_data;
2221 BWindow::SetLayout(data->layout);
2222 return B_OK;
2226 return BLooper::Perform(code, _data);
2230 status_t
2231 BWindow::SetType(window_type type)
2233 window_look look;
2234 window_feel feel;
2235 _DecomposeType(type, &look, &feel);
2237 status_t status = SetLook(look);
2238 if (status == B_OK)
2239 status = SetFeel(feel);
2241 return status;
2245 window_type
2246 BWindow::Type() const
2248 return _ComposeType(fLook, fFeel);
2252 status_t
2253 BWindow::SetLook(window_look look)
2255 BAutolock locker(this);
2256 if (!locker.IsLocked())
2257 return B_BAD_VALUE;
2259 fLink->StartMessage(AS_SET_LOOK);
2260 fLink->Attach<int32>((int32)look);
2262 status_t status = B_ERROR;
2263 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2264 fLook = look;
2266 // TODO: this could have changed the window size, and thus, we
2267 // need to get it from the server (and call _AdoptResize()).
2269 return status;
2273 window_look
2274 BWindow::Look() const
2276 return fLook;
2280 status_t
2281 BWindow::SetFeel(window_feel feel)
2283 BAutolock locker(this);
2284 if (!locker.IsLocked())
2285 return B_BAD_VALUE;
2287 fLink->StartMessage(AS_SET_FEEL);
2288 fLink->Attach<int32>((int32)feel);
2290 status_t status = B_ERROR;
2291 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2292 fFeel = feel;
2294 return status;
2298 window_feel
2299 BWindow::Feel() const
2301 return fFeel;
2305 status_t
2306 BWindow::SetFlags(uint32 flags)
2308 BAutolock locker(this);
2309 if (!locker.IsLocked())
2310 return B_BAD_VALUE;
2312 fLink->StartMessage(AS_SET_FLAGS);
2313 fLink->Attach<uint32>(flags);
2315 int32 status = B_ERROR;
2316 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2317 fFlags = flags;
2319 return status;
2323 uint32
2324 BWindow::Flags() const
2326 return fFlags;
2330 status_t
2331 BWindow::SetWindowAlignment(window_alignment mode,
2332 int32 h, int32 hOffset, int32 width, int32 widthOffset,
2333 int32 v, int32 vOffset, int32 height, int32 heightOffset)
2335 if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0
2336 || (hOffset >= 0 && hOffset <= h)
2337 || (vOffset >= 0 && vOffset <= v)
2338 || (widthOffset >= 0 && widthOffset <= width)
2339 || (heightOffset >= 0 && heightOffset <= height))
2340 return B_BAD_VALUE;
2342 // TODO: test if hOffset = 0 and set it to 1 if true.
2344 if (!Lock())
2345 return B_ERROR;
2347 fLink->StartMessage(AS_SET_ALIGNMENT);
2348 fLink->Attach<int32>((int32)mode);
2349 fLink->Attach<int32>(h);
2350 fLink->Attach<int32>(hOffset);
2351 fLink->Attach<int32>(width);
2352 fLink->Attach<int32>(widthOffset);
2353 fLink->Attach<int32>(v);
2354 fLink->Attach<int32>(vOffset);
2355 fLink->Attach<int32>(height);
2356 fLink->Attach<int32>(heightOffset);
2358 status_t status = B_ERROR;
2359 fLink->FlushWithReply(status);
2361 Unlock();
2363 return status;
2367 status_t
2368 BWindow::GetWindowAlignment(window_alignment* mode,
2369 int32* h, int32* hOffset, int32* width, int32* widthOffset,
2370 int32* v, int32* vOffset, int32* height, int32* heightOffset) const
2372 if (!const_cast<BWindow*>(this)->Lock())
2373 return B_ERROR;
2375 fLink->StartMessage(AS_GET_ALIGNMENT);
2377 status_t status;
2378 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2379 fLink->Read<int32>((int32*)mode);
2380 fLink->Read<int32>(h);
2381 fLink->Read<int32>(hOffset);
2382 fLink->Read<int32>(width);
2383 fLink->Read<int32>(widthOffset);
2384 fLink->Read<int32>(v);
2385 fLink->Read<int32>(hOffset);
2386 fLink->Read<int32>(height);
2387 fLink->Read<int32>(heightOffset);
2390 const_cast<BWindow*>(this)->Unlock();
2391 return status;
2395 uint32
2396 BWindow::Workspaces() const
2398 if (!const_cast<BWindow*>(this)->Lock())
2399 return 0;
2401 uint32 workspaces = 0;
2403 fLink->StartMessage(AS_GET_WORKSPACES);
2405 status_t status;
2406 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2407 fLink->Read<uint32>(&workspaces);
2409 const_cast<BWindow*>(this)->Unlock();
2410 return workspaces;
2414 void
2415 BWindow::SetWorkspaces(uint32 workspaces)
2417 // TODO: don't forget about Tracker's background window.
2418 if (fFeel != B_NORMAL_WINDOW_FEEL)
2419 return;
2421 if (Lock()) {
2422 fLink->StartMessage(AS_SET_WORKSPACES);
2423 fLink->Attach<uint32>(workspaces);
2424 fLink->Flush();
2425 Unlock();
2430 BView*
2431 BWindow::LastMouseMovedView() const
2433 return fLastMouseMovedView;
2437 void
2438 BWindow::MoveBy(float dx, float dy)
2440 if ((dx != 0.0f || dy != 0.0f) && Lock()) {
2441 MoveTo(fFrame.left + dx, fFrame.top + dy);
2442 Unlock();
2447 void
2448 BWindow::MoveTo(BPoint point)
2450 MoveTo(point.x, point.y);
2454 void
2455 BWindow::MoveTo(float x, float y)
2457 if (!Lock())
2458 return;
2460 x = roundf(x);
2461 y = roundf(y);
2463 if (fFrame.left != x || fFrame.top != y) {
2464 fLink->StartMessage(AS_WINDOW_MOVE);
2465 fLink->Attach<float>(x);
2466 fLink->Attach<float>(y);
2468 status_t status;
2469 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2470 fFrame.OffsetTo(x, y);
2473 Unlock();
2477 void
2478 BWindow::ResizeBy(float dx, float dy)
2480 if (Lock()) {
2481 ResizeTo(fFrame.Width() + dx, fFrame.Height() + dy);
2482 Unlock();
2487 void
2488 BWindow::ResizeTo(float width, float height)
2490 if (!Lock())
2491 return;
2493 width = roundf(width);
2494 height = roundf(height);
2496 // stay in minimum & maximum frame limits
2497 if (width < fMinWidth)
2498 width = fMinWidth;
2499 else if (width > fMaxWidth)
2500 width = fMaxWidth;
2502 if (height < fMinHeight)
2503 height = fMinHeight;
2504 else if (height > fMaxHeight)
2505 height = fMaxHeight;
2507 if (width != fFrame.Width() || height != fFrame.Height()) {
2508 fLink->StartMessage(AS_WINDOW_RESIZE);
2509 fLink->Attach<float>(width);
2510 fLink->Attach<float>(height);
2512 status_t status;
2513 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2514 fFrame.right = fFrame.left + width;
2515 fFrame.bottom = fFrame.top + height;
2516 _AdoptResize();
2520 Unlock();
2524 void
2525 BWindow::ResizeToPreferred()
2527 Layout(false);
2529 float width = fTopView->PreferredSize().width;
2530 width = std::min(width, fTopView->MaxSize().width);
2531 width = std::max(width, fTopView->MinSize().width);
2533 float height = fTopView->PreferredSize().height;
2534 height = std::min(width, fTopView->MaxSize().height);
2535 height = std::max(width, fTopView->MinSize().height);
2537 if (GetLayout()->HasHeightForWidth())
2538 GetLayout()->GetHeightForWidth(width, NULL, NULL, &height);
2540 ResizeTo(width, height);
2544 void
2545 BWindow::CenterIn(const BRect& rect)
2547 // Set size limits now if needed
2548 UpdateSizeLimits();
2550 MoveTo(BLayoutUtils::AlignInFrame(rect, Size(),
2551 BAlignment(B_ALIGN_HORIZONTAL_CENTER,
2552 B_ALIGN_VERTICAL_CENTER)).LeftTop());
2556 void
2557 BWindow::CenterOnScreen()
2559 CenterIn(BScreen(this).Frame());
2563 // Centers the window on the screen with the passed in id.
2564 void
2565 BWindow::CenterOnScreen(screen_id id)
2567 CenterIn(BScreen(id).Frame());
2571 void
2572 BWindow::Show()
2574 bool runCalled = true;
2575 if (Lock()) {
2576 fShowLevel--;
2578 _SendShowOrHideMessage();
2580 runCalled = fRunCalled;
2582 Unlock();
2585 if (!runCalled) {
2586 // This is the fist time Show() is called, which implicitly runs the
2587 // looper. NOTE: The window is still locked if it has not been
2588 // run yet, so accessing members is safe.
2589 if (fLink->SenderPort() < B_OK) {
2590 // We don't have valid app_server connection; there is no point
2591 // in starting our looper
2592 fThread = B_ERROR;
2593 return;
2594 } else
2595 Run();
2600 void
2601 BWindow::Hide()
2603 if (Lock()) {
2604 // If we are minimized and are about to be hidden, unminimize
2605 if (IsMinimized() && fShowLevel == 0)
2606 Minimize(false);
2608 fShowLevel++;
2610 _SendShowOrHideMessage();
2612 Unlock();
2617 bool
2618 BWindow::IsHidden() const
2620 return fShowLevel > 0;
2624 bool
2625 BWindow::QuitRequested()
2627 return BLooper::QuitRequested();
2631 thread_id
2632 BWindow::Run()
2634 return BLooper::Run();
2638 void
2639 BWindow::SetLayout(BLayout* layout)
2641 fTopView->SetLayout(layout);
2645 BLayout*
2646 BWindow::GetLayout() const
2648 return fTopView->GetLayout();
2652 void
2653 BWindow::InvalidateLayout(bool descendants)
2655 fTopView->InvalidateLayout(descendants);
2659 void
2660 BWindow::Layout(bool force)
2662 UpdateSizeLimits();
2664 // Do the actual layout
2665 fTopView->Layout(force);
2669 status_t
2670 BWindow::GetSupportedSuites(BMessage* data)
2672 if (data == NULL)
2673 return B_BAD_VALUE;
2675 status_t status = data->AddString("suites", "suite/vnd.Be-window");
2676 if (status == B_OK) {
2677 BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo);
2679 status = data->AddFlat("messages", &propertyInfo);
2680 if (status == B_OK)
2681 status = BLooper::GetSupportedSuites(data);
2684 return status;
2688 BHandler*
2689 BWindow::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
2690 int32 what, const char* property)
2692 if (message->what == B_WINDOW_MOVE_BY
2693 || message->what == B_WINDOW_MOVE_TO)
2694 return this;
2696 BPropertyInfo propertyInfo(sWindowPropInfo);
2697 if (propertyInfo.FindMatch(message, index, specifier, what, property) >= 0) {
2698 if (strcmp(property, "View") == 0) {
2699 // we will NOT pop the current specifier
2700 return fTopView;
2701 } else if (strcmp(property, "MenuBar") == 0) {
2702 if (fKeyMenuBar) {
2703 message->PopSpecifier();
2704 return fKeyMenuBar;
2705 } else {
2706 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
2707 replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
2708 replyMsg.AddString("message",
2709 "This window doesn't have a main MenuBar");
2710 message->SendReply(&replyMsg);
2711 return NULL;
2713 } else
2714 return this;
2717 return BLooper::ResolveSpecifier(message, index, specifier, what, property);
2721 // #pragma mark - Private Methods
2724 void
2725 BWindow::_InitData(BRect frame, const char* title, window_look look,
2726 window_feel feel, uint32 flags, uint32 workspace, int32 bitmapToken)
2728 STRACE(("BWindow::InitData()\n"));
2730 if (be_app == NULL) {
2731 debugger("You need a valid BApplication object before interacting with "
2732 "the app_server");
2733 return;
2736 frame.left = roundf(frame.left);
2737 frame.top = roundf(frame.top);
2738 frame.right = roundf(frame.right);
2739 frame.bottom = roundf(frame.bottom);
2741 fFrame = frame;
2743 if (title == NULL)
2744 title = "";
2746 fTitle = strdup(title);
2748 _SetName(title);
2750 fFeel = feel;
2751 fLook = look;
2752 fFlags = flags | B_ASYNCHRONOUS_CONTROLS;
2754 fInTransaction = bitmapToken >= 0;
2755 fUpdateRequested = false;
2756 fActive = false;
2757 fShowLevel = 1;
2759 fTopView = NULL;
2760 fFocus = NULL;
2761 fLastMouseMovedView = NULL;
2762 fKeyMenuBar = NULL;
2763 fDefaultButton = NULL;
2765 // Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2766 // get sent to the application, and not one of our handlers.
2767 // It is only installed for non-modal windows, though.
2768 fNoQuitShortcut = IsModal();
2770 if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
2771 // Modal windows default to non-closable, but you can add the
2772 // shortcut manually, if a different behaviour is wanted
2773 AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2776 // Edit modifier keys
2778 AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
2779 AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
2780 AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
2781 AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
2783 // Window modifier keys
2785 AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
2786 new BMessage(_MINIMIZE_), NULL);
2787 AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
2788 new BMessage(_ZOOM_), NULL);
2789 AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
2790 new BMessage(B_HIDE_APPLICATION), NULL);
2791 AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY,
2792 new BMessage(_SEND_TO_FRONT_), NULL);
2793 AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY,
2794 new BMessage(_SEND_BEHIND_), NULL);
2796 // We set the default pulse rate, but we don't start the pulse
2797 fPulseRate = 500000;
2798 fPulseRunner = NULL;
2800 fIsFilePanel = false;
2802 fMenuSem = -1;
2804 fMinimized = false;
2806 fMaxZoomHeight = 32768.0;
2807 fMaxZoomWidth = 32768.0;
2808 fMinHeight = 0.0;
2809 fMinWidth = 0.0;
2810 fMaxHeight = 32768.0;
2811 fMaxWidth = 32768.0;
2813 fLastViewToken = B_NULL_TOKEN;
2815 // TODO: other initializations!
2816 fOffscreen = false;
2818 // Create the server-side window
2820 port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
2821 "w<app_server");
2822 if (receivePort < B_OK) {
2823 // TODO: huh?
2824 debugger("Could not create BWindow's receive port, used for "
2825 "interacting with the app_server!");
2826 delete this;
2827 return;
2830 STRACE(("BWindow::InitData(): contacting app_server...\n"));
2832 // let app_server know that a window has been created.
2833 fLink = new(std::nothrow) BPrivate::PortLink(
2834 BApplication::Private::ServerLink()->SenderPort(), receivePort);
2835 if (fLink == NULL) {
2836 // Zombie!
2837 return;
2841 BPrivate::AppServerLink lockLink;
2842 // we're talking to the server application using our own
2843 // communication channel (fLink) - we better make sure no one
2844 // interferes by locking that channel (which AppServerLink does
2845 // implicetly)
2847 if (bitmapToken < 0) {
2848 fLink->StartMessage(AS_CREATE_WINDOW);
2849 } else {
2850 fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2851 fLink->Attach<int32>(bitmapToken);
2852 fOffscreen = true;
2855 fLink->Attach<BRect>(fFrame);
2856 fLink->Attach<uint32>((uint32)fLook);
2857 fLink->Attach<uint32>((uint32)fFeel);
2858 fLink->Attach<uint32>(fFlags);
2859 fLink->Attach<uint32>(workspace);
2860 fLink->Attach<int32>(_get_object_token_(this));
2861 fLink->Attach<port_id>(receivePort);
2862 fLink->Attach<port_id>(fMsgPort);
2863 fLink->AttachString(title);
2865 port_id sendPort;
2866 int32 code;
2867 if (fLink->FlushWithReply(code) == B_OK
2868 && code == B_OK
2869 && fLink->Read<port_id>(&sendPort) == B_OK) {
2870 // read the frame size and its limits that were really
2871 // enforced on the server side
2873 fLink->Read<BRect>(&fFrame);
2874 fLink->Read<float>(&fMinWidth);
2875 fLink->Read<float>(&fMaxWidth);
2876 fLink->Read<float>(&fMinHeight);
2877 fLink->Read<float>(&fMaxHeight);
2879 fMaxZoomWidth = fMaxWidth;
2880 fMaxZoomHeight = fMaxHeight;
2881 } else
2882 sendPort = -1;
2884 // Redirect our link to the new window connection
2885 fLink->SetSenderPort(sendPort);
2888 STRACE(("Server says that our send port is %ld\n", sendPort));
2889 STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2891 _CreateTopView();
2895 //! Rename the handler and its thread
2896 void
2897 BWindow::_SetName(const char* title)
2899 if (title == NULL)
2900 title = "";
2902 // we will change BWindow's thread name to "w>window title"
2904 char threadName[B_OS_NAME_LENGTH];
2905 strcpy(threadName, "w>");
2906 #ifdef __HAIKU__
2907 strlcat(threadName, title, B_OS_NAME_LENGTH);
2908 #else
2909 int32 length = strlen(title);
2910 length = min_c(length, B_OS_NAME_LENGTH - 3);
2911 memcpy(threadName + 2, title, length);
2912 threadName[length + 2] = '\0';
2913 #endif
2915 // change the handler's name
2916 SetName(threadName);
2918 // if the message loop has been started...
2919 if (Thread() >= B_OK)
2920 rename_thread(Thread(), threadName);
2924 //! Reads all pending messages from the window port and put them into the queue.
2925 void
2926 BWindow::_DequeueAll()
2928 // Get message count from port
2929 int32 count = port_count(fMsgPort);
2931 for (int32 i = 0; i < count; i++) {
2932 BMessage* message = MessageFromPort(0);
2933 if (message != NULL)
2934 fDirectTarget->Queue()->AddMessage(message);
2939 /*! This here is an almost complete code duplication to BLooper::task_looper()
2940 but with some important differences:
2941 a) it uses the _DetermineTarget() method to tell what the later target of
2942 a message will be, if no explicit target is supplied.
2943 b) it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
2944 to all of its intended targets, and to add all fields the target would
2945 expect in such a message.
2947 This is important because the app_server sends all input events to the
2948 preferred handler, and expects them to be correctly distributed to their
2949 intended targets.
2951 void
2952 BWindow::task_looper()
2954 STRACE(("info: BWindow::task_looper() started.\n"));
2956 // Check that looper is locked (should be)
2957 AssertLocked();
2958 Unlock();
2960 if (IsLocked())
2961 debugger("window must not be locked!");
2963 while (!fTerminating) {
2964 // Did we get a message?
2965 BMessage* msg = MessageFromPort();
2966 if (msg)
2967 _AddMessagePriv(msg);
2969 // Get message count from port
2970 int32 msgCount = port_count(fMsgPort);
2971 for (int32 i = 0; i < msgCount; ++i) {
2972 // Read 'count' messages from port (so we will not block)
2973 // We use zero as our timeout since we know there is stuff there
2974 msg = MessageFromPort(0);
2975 // Add messages to queue
2976 if (msg)
2977 _AddMessagePriv(msg);
2980 bool dispatchNextMessage = true;
2981 while (!fTerminating && dispatchNextMessage) {
2982 // Get next message from queue (assign to fLastMessage after
2983 // locking)
2984 BMessage* message = fDirectTarget->Queue()->NextMessage();
2986 // Lock the looper
2987 if (!Lock()) {
2988 delete message;
2989 break;
2992 fLastMessage = message;
2994 if (fLastMessage == NULL) {
2995 // No more messages: Unlock the looper and terminate the
2996 // dispatch loop.
2997 dispatchNextMessage = false;
2998 } else {
2999 // Get the target handler
3000 BMessage::Private messagePrivate(fLastMessage);
3001 bool usePreferred = messagePrivate.UsePreferredTarget();
3002 BHandler* handler = NULL;
3003 bool dropMessage = false;
3005 if (usePreferred) {
3006 handler = PreferredHandler();
3007 if (handler == NULL)
3008 handler = this;
3009 } else {
3010 gDefaultTokens.GetToken(messagePrivate.GetTarget(),
3011 B_HANDLER_TOKEN, (void**)&handler);
3013 // if this handler doesn't belong to us, we drop the message
3014 if (handler != NULL && handler->Looper() != this) {
3015 dropMessage = true;
3016 handler = NULL;
3020 if ((handler == NULL && !dropMessage) || usePreferred)
3021 handler = _DetermineTarget(fLastMessage, handler);
3023 unpack_cookie cookie;
3024 while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
3025 // if there is no target handler, the message is dropped
3026 if (handler != NULL) {
3027 _SanitizeMessage(fLastMessage, handler, usePreferred);
3029 // Is this a scripting message?
3030 if (fLastMessage->HasSpecifiers()) {
3031 int32 index = 0;
3032 // Make sure the current specifier is kosher
3033 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
3034 handler = resolve_specifier(handler, fLastMessage);
3037 if (handler != NULL)
3038 handler = _TopLevelFilter(fLastMessage, handler);
3040 if (handler != NULL)
3041 DispatchMessage(fLastMessage, handler);
3044 // Delete the current message
3045 delete fLastMessage;
3046 fLastMessage = NULL;
3050 if (fTerminating) {
3051 // we leave the looper locked when we quit
3052 return;
3055 Unlock();
3057 // Are any messages on the port?
3058 if (port_count(fMsgPort) > 0) {
3059 // Do outer loop
3060 dispatchNextMessage = false;
3067 window_type
3068 BWindow::_ComposeType(window_look look, window_feel feel) const
3070 switch (feel) {
3071 case B_NORMAL_WINDOW_FEEL:
3072 switch (look) {
3073 case B_TITLED_WINDOW_LOOK:
3074 return B_TITLED_WINDOW;
3076 case B_DOCUMENT_WINDOW_LOOK:
3077 return B_DOCUMENT_WINDOW;
3079 case B_BORDERED_WINDOW_LOOK:
3080 return B_BORDERED_WINDOW;
3082 default:
3083 return B_UNTYPED_WINDOW;
3085 break;
3087 case B_MODAL_APP_WINDOW_FEEL:
3088 if (look == B_MODAL_WINDOW_LOOK)
3089 return B_MODAL_WINDOW;
3090 break;
3092 case B_FLOATING_APP_WINDOW_FEEL:
3093 if (look == B_FLOATING_WINDOW_LOOK)
3094 return B_FLOATING_WINDOW;
3095 break;
3097 default:
3098 return B_UNTYPED_WINDOW;
3101 return B_UNTYPED_WINDOW;
3105 void
3106 BWindow::_DecomposeType(window_type type, window_look* _look,
3107 window_feel* _feel) const
3109 switch (type) {
3110 case B_DOCUMENT_WINDOW:
3111 *_look = B_DOCUMENT_WINDOW_LOOK;
3112 *_feel = B_NORMAL_WINDOW_FEEL;
3113 break;
3115 case B_MODAL_WINDOW:
3116 *_look = B_MODAL_WINDOW_LOOK;
3117 *_feel = B_MODAL_APP_WINDOW_FEEL;
3118 break;
3120 case B_FLOATING_WINDOW:
3121 *_look = B_FLOATING_WINDOW_LOOK;
3122 *_feel = B_FLOATING_APP_WINDOW_FEEL;
3123 break;
3125 case B_BORDERED_WINDOW:
3126 *_look = B_BORDERED_WINDOW_LOOK;
3127 *_feel = B_NORMAL_WINDOW_FEEL;
3128 break;
3130 case B_TITLED_WINDOW:
3131 case B_UNTYPED_WINDOW:
3132 default:
3133 *_look = B_TITLED_WINDOW_LOOK;
3134 *_feel = B_NORMAL_WINDOW_FEEL;
3135 break;
3140 void
3141 BWindow::_CreateTopView()
3143 STRACE(("_CreateTopView(): enter\n"));
3145 BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
3146 // TODO: what to do here about std::nothrow?
3147 fTopView = new BView(frame, "fTopView",
3148 B_FOLLOW_ALL, B_WILL_DRAW);
3149 fTopView->fTopLevelView = true;
3151 //inhibit check_lock()
3152 fLastViewToken = _get_object_token_(fTopView);
3154 // set fTopView's owner, add it to window's eligible handler list
3155 // and also set its next handler to be this window.
3157 STRACE(("Calling setowner fTopView = %p this = %p.\n",
3158 fTopView, this));
3160 fTopView->_SetOwner(this);
3162 // we can't use AddChild() because this is the top view
3163 fTopView->_CreateSelf();
3165 STRACE(("BuildTopView ended\n"));
3170 Resizes the top view to match the window size. This will also
3171 adapt the size of all its child views as needed.
3172 This method has to be called whenever the frame of the window
3173 changes.
3175 void
3176 BWindow::_AdoptResize()
3178 // Resize views according to their resize modes - this
3179 // saves us some server communication, as the server
3180 // does the same with our views on its side.
3182 int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
3183 int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
3184 if (deltaWidth == 0 && deltaHeight == 0)
3185 return;
3187 fTopView->_ResizeBy(deltaWidth, deltaHeight);
3191 void
3192 BWindow::_SetFocus(BView* focusView, bool notifyInputServer)
3194 if (fFocus == focusView)
3195 return;
3197 // we notify the input server if we are passing focus
3198 // from a view which has the B_INPUT_METHOD_AWARE to a one
3199 // which does not, or vice-versa
3200 if (notifyInputServer && fActive) {
3201 bool inputMethodAware = false;
3202 if (focusView)
3203 inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
3204 BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
3205 BMessenger messenger(focusView);
3206 BMessage reply;
3207 if (focusView)
3208 msg.AddMessenger("view", messenger);
3209 _control_input_server_(&msg, &reply);
3212 fFocus = focusView;
3213 SetPreferredHandler(focusView);
3218 \brief Determines the target of a message received for the
3219 focus view.
3221 BHandler*
3222 BWindow::_DetermineTarget(BMessage* message, BHandler* target)
3224 if (target == NULL)
3225 target = this;
3227 switch (message->what) {
3228 case B_KEY_DOWN:
3229 case B_KEY_UP:
3231 // if we have a default button, it might want to hear
3232 // about pressing the <enter> key
3233 int32 rawChar;
3234 if (DefaultButton() != NULL
3235 && message->FindInt32("raw_char", &rawChar) == B_OK
3236 && rawChar == B_ENTER)
3237 return DefaultButton();
3239 // supposed to fall through
3241 case B_UNMAPPED_KEY_DOWN:
3242 case B_UNMAPPED_KEY_UP:
3243 case B_MODIFIERS_CHANGED:
3244 // these messages should be dispatched by the focus view
3245 if (CurrentFocus() != NULL)
3246 return CurrentFocus();
3247 break;
3249 case B_MOUSE_DOWN:
3250 case B_MOUSE_UP:
3251 case B_MOUSE_MOVED:
3252 case B_MOUSE_WHEEL_CHANGED:
3253 case B_MOUSE_IDLE:
3254 // is there a token of the view that is currently under the mouse?
3255 int32 token;
3256 if (message->FindInt32("_view_token", &token) == B_OK) {
3257 BView* view = _FindView(token);
3258 if (view != NULL)
3259 return view;
3262 // if there is no valid token in the message, we try our
3263 // luck with the last target, if available
3264 if (fLastMouseMovedView != NULL)
3265 return fLastMouseMovedView;
3266 break;
3268 case B_PULSE:
3269 case B_QUIT_REQUESTED:
3270 // TODO: test whether R5 will let BView dispatch these messages
3271 return this;
3273 case _MESSAGE_DROPPED_:
3274 if (fLastMouseMovedView != NULL)
3275 return fLastMouseMovedView;
3276 break;
3278 default:
3279 break;
3282 return target;
3286 /*! \brief Determines whether or not this message has targeted the focus view.
3288 This will return \c false only if the message did not go to the preferred
3289 handler, or if the packed message does not contain address the focus view
3290 at all.
3292 bool
3293 BWindow::_IsFocusMessage(BMessage* message)
3295 BMessage::Private messagePrivate(message);
3296 if (!messagePrivate.UsePreferredTarget())
3297 return false;
3299 bool feedFocus;
3300 if (message->HasInt32("_token")
3301 && (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus))
3302 return false;
3304 return true;
3308 /*! \brief Distributes the message to its intended targets. This is done for
3309 all messages that should go to the preferred handler.
3311 Returns \c true in case the message should still be dispatched
3313 bool
3314 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message,
3315 BHandler** _target, bool* _usePreferred)
3317 if (cookie.message == NULL)
3318 return false;
3320 if (cookie.index == 0 && !cookie.tokens_scanned) {
3321 // We were called the first time for this message
3323 if (!*_usePreferred) {
3324 // only consider messages targeted at the preferred handler
3325 cookie.message = NULL;
3326 return true;
3329 // initialize our cookie
3330 cookie.message = *_message;
3331 cookie.focus = *_target;
3333 if (cookie.focus != NULL)
3334 cookie.focus_token = _get_object_token_(*_target);
3336 if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
3337 cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
3339 *_usePreferred = false;
3342 _DequeueAll();
3344 // distribute the message to all targets specified in the
3345 // message directly (but not to the focus view)
3347 for (int32 token; !cookie.tokens_scanned
3348 && cookie.message->FindInt32("_token", cookie.index, &token)
3349 == B_OK;
3350 cookie.index++) {
3351 // focus view is preferred and should get its message directly
3352 if (token == cookie.focus_token) {
3353 cookie.found_focus = true;
3354 continue;
3356 if (token == cookie.last_view_token)
3357 continue;
3359 BView* target = _FindView(token);
3360 if (target == NULL)
3361 continue;
3363 *_message = new BMessage(*cookie.message);
3364 *_target = target;
3365 cookie.index++;
3366 return true;
3369 cookie.tokens_scanned = true;
3371 // if there is a last mouse moved view, and the new focus is
3372 // different, the previous view wants to get its B_EXITED_VIEW
3373 // message
3374 if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
3375 && fLastMouseMovedView != cookie.focus) {
3376 *_message = new BMessage(*cookie.message);
3377 *_target = fLastMouseMovedView;
3378 cookie.last_view_token = B_NULL_TOKEN;
3379 return true;
3382 bool dispatchToFocus = true;
3384 // check if the focus token is still valid (could have been removed in the mean time)
3385 BHandler* handler;
3386 if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
3387 || handler->Looper() != this)
3388 dispatchToFocus = false;
3390 if (dispatchToFocus && cookie.index > 0) {
3391 // should this message still be dispatched by the focus view?
3392 bool feedFocus;
3393 if (!cookie.found_focus
3394 && (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
3395 || feedFocus == false))
3396 dispatchToFocus = false;
3399 if (!dispatchToFocus) {
3400 delete cookie.message;
3401 cookie.message = NULL;
3402 return false;
3405 *_message = cookie.message;
3406 *_target = cookie.focus;
3407 *_usePreferred = true;
3408 cookie.message = NULL;
3409 return true;
3413 /*! Some messages don't get to the window in a shape an application should see.
3414 This method is supposed to give a message the last grinding before
3415 it's acceptable for the receiving application.
3417 void
3418 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
3420 if (target == NULL)
3421 return;
3423 switch (message->what) {
3424 case B_MOUSE_MOVED:
3425 case B_MOUSE_UP:
3426 case B_MOUSE_DOWN:
3428 BPoint where;
3429 if (message->FindPoint("screen_where", &where) != B_OK)
3430 break;
3432 BView* view = dynamic_cast<BView*>(target);
3434 if (view == NULL || message->what == B_MOUSE_MOVED) {
3435 // add local window coordinates, only
3436 // for regular mouse moved messages
3437 message->AddPoint("where", ConvertFromScreen(where));
3440 if (view != NULL) {
3441 // add local view coordinates
3442 BPoint viewWhere = view->ConvertFromScreen(where);
3443 if (message->what != B_MOUSE_MOVED) {
3444 // Yep, the meaning of "where" is different
3445 // for regular mouse moved messages versus
3446 // mouse up/down!
3447 message->AddPoint("where", viewWhere);
3449 message->AddPoint("be:view_where", viewWhere);
3451 if (message->what == B_MOUSE_MOVED) {
3452 // is there a token of the view that is currently under
3453 // the mouse?
3454 BView* viewUnderMouse = NULL;
3455 int32 token;
3456 if (message->FindInt32("_view_token", &token) == B_OK)
3457 viewUnderMouse = _FindView(token);
3459 // add transit information
3460 uint32 transit
3461 = _TransitForMouseMoved(view, viewUnderMouse);
3462 message->AddInt32("be:transit", transit);
3464 if (usePreferred)
3465 fLastMouseMovedView = viewUnderMouse;
3468 break;
3471 case B_MOUSE_IDLE:
3473 // App Server sends screen coordinates, convert the point to
3474 // local view coordinates, then add the point in be:view_where
3475 BPoint where;
3476 if (message->FindPoint("screen_where", &where) != B_OK)
3477 break;
3479 BView* view = dynamic_cast<BView*>(target);
3480 if (view != NULL) {
3481 // add local view coordinates
3482 message->AddPoint("be:view_where",
3483 view->ConvertFromScreen(where));
3485 break;
3488 case _MESSAGE_DROPPED_:
3490 uint32 originalWhat;
3491 if (message->FindInt32("_original_what",
3492 (int32*)&originalWhat) == B_OK) {
3493 message->what = originalWhat;
3494 message->RemoveName("_original_what");
3496 break;
3503 This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3504 is removed from the queue.
3505 It allows the window to update the last mouse moved view, and
3506 let it decide if this message should be kept. It will also remove
3507 the message from the queue.
3508 You need to hold the message queue lock when calling this method!
3510 \return true if this message can be used to get the mouse data from,
3511 \return false if this is not meant for the public.
3513 bool
3514 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3516 BMessage::Private messagePrivate(message);
3517 if (!messagePrivate.UsePreferredTarget()) {
3518 // this message is targeted at a specific handler, so we should
3519 // not steal it
3520 return false;
3523 int32 token;
3524 if (message->FindInt32("_token", 0, &token) == B_OK) {
3525 // This message has other targets, so we can't remove it;
3526 // just prevent it from being sent to the preferred handler
3527 // again (if it should have gotten it at all).
3528 bool feedFocus;
3529 if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)
3530 return false;
3532 message->RemoveName("_feed_focus");
3533 deleteMessage = false;
3534 } else {
3535 deleteMessage = true;
3537 if (message->what == B_MOUSE_MOVED) {
3538 // We need to update the last mouse moved view, as this message
3539 // won't make it to _SanitizeMessage() anymore.
3540 BView* viewUnderMouse = NULL;
3541 int32 token;
3542 if (message->FindInt32("_view_token", &token) == B_OK)
3543 viewUnderMouse = _FindView(token);
3545 // Don't remove important transit messages!
3546 uint32 transit = _TransitForMouseMoved(fLastMouseMovedView,
3547 viewUnderMouse);
3548 if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
3549 deleteMessage = false;
3552 if (deleteMessage) {
3553 // The message is only thought for the preferred handler, so we
3554 // can just remove it.
3555 MessageQueue()->RemoveMessage(message);
3559 return true;
3563 uint32
3564 BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const
3566 uint32 transit;
3567 if (viewUnderMouse == view) {
3568 // the mouse is over the target view
3569 if (fLastMouseMovedView != view)
3570 transit = B_ENTERED_VIEW;
3571 else
3572 transit = B_INSIDE_VIEW;
3573 } else {
3574 // the mouse is not over the target view
3575 if (view == fLastMouseMovedView)
3576 transit = B_EXITED_VIEW;
3577 else
3578 transit = B_OUTSIDE_VIEW;
3580 return transit;
3584 /*! Forwards the key to the switcher
3586 void
3587 BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat)
3589 // only send the first key press, no repeats
3590 if (repeat)
3591 return;
3593 BMessenger deskbar(kDeskbarSignature);
3594 if (!deskbar.IsValid()) {
3595 // TODO: have some kind of fallback-handling in case the Deskbar is
3596 // not available?
3597 return;
3600 BMessage message('TASK');
3601 message.AddInt32("key", rawKey);
3602 message.AddInt32("modifiers", modifiers);
3603 message.AddInt64("when", system_time());
3604 message.AddInt32("team", Team());
3605 deskbar.SendMessage(&message);
3609 /*! Handles keyboard input before it gets forwarded to the target handler.
3610 This includes shortcut evaluation, keyboard navigation, etc.
3612 \return handled if true, the event was already handled, and will not
3613 be forwarded to the target handler.
3615 TODO: must also convert the incoming key to the font encoding of the target
3617 bool
3618 BWindow::_HandleKeyDown(BMessage* event)
3620 // Only handle special functions when the event targeted the active focus
3621 // view
3622 if (!_IsFocusMessage(event))
3623 return false;
3625 const char* string = NULL;
3626 if (event->FindString("bytes", &string) != B_OK)
3627 return false;
3629 char key = string[0];
3631 uint32 modifiers;
3632 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3633 modifiers = 0;
3635 // handle BMenuBar key
3636 if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0 && fKeyMenuBar) {
3637 fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3638 return true;
3641 // Keyboard navigation through views
3642 // (B_OPTION_KEY makes BTextViews and friends navigable, even in editing
3643 // mode)
3644 if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) {
3645 _KeyboardNavigation();
3646 return true;
3649 int32 rawKey;
3650 event->FindInt32("key", &rawKey);
3652 // Deskbar's Switcher
3653 if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) {
3654 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3655 return true;
3658 // Optionally close window when the escape key is pressed
3659 if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) {
3660 BMessage message(B_QUIT_REQUESTED);
3661 message.AddBool("shortcut", true);
3663 PostMessage(&message);
3664 return true;
3667 // PrtScr key takes a screenshot
3668 if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) {
3669 // With no modifier keys the best way to get a screenshot is by
3670 // calling the screenshot CLI
3671 if (modifiers == 0) {
3672 be_roster->Launch("application/x-vnd.haiku-screenshot-cli");
3673 return true;
3676 // Prepare a message based on the modifier keys pressed and launch the
3677 // screenshot GUI
3678 BMessage message(B_ARGV_RECEIVED);
3679 int32 argc = 1;
3680 message.AddString("argv", "Screenshot");
3681 if ((modifiers & B_CONTROL_KEY) != 0) {
3682 argc++;
3683 message.AddString("argv", "--clipboard");
3685 if ((modifiers & B_SHIFT_KEY) != 0) {
3686 argc++;
3687 message.AddString("argv", "--silent");
3689 message.AddInt32("argc", argc);
3690 be_roster->Launch("application/x-vnd.haiku-screenshot", &message);
3691 return true;
3694 // Handle shortcuts
3695 if ((modifiers & B_COMMAND_KEY) != 0) {
3696 // Command+q has been pressed, so, we will quit
3697 // the shortcut mechanism doesn't allow handlers outside the window
3698 if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3699 BMessage message(B_QUIT_REQUESTED);
3700 message.AddBool("shortcut", true);
3702 be_app->PostMessage(&message);
3703 // eat the event
3704 return true;
3707 // Pretend that the user opened a menu, to give the subclass a
3708 // chance to update it's menus. This may install new shortcuts,
3709 // which is why we have to call it here, before trying to find
3710 // a shortcut for the given key.
3711 MenusBeginning();
3713 Shortcut* shortcut = _FindShortcut(key, modifiers);
3714 if (shortcut != NULL) {
3715 // TODO: would be nice to move this functionality to
3716 // a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3717 // (and BMenuItem::Invoke()) are private, I didn't want
3718 // to mess with them (BMenuItem::Invoke() is public in
3719 // Dano/Zeta, though, maybe we should just follow their
3720 // example)
3721 if (shortcut->MenuItem() != NULL) {
3722 BMenu* menu = shortcut->MenuItem()->Menu();
3723 if (menu != NULL)
3724 MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true);
3725 } else {
3726 BHandler* target = shortcut->Target();
3727 if (target == NULL)
3728 target = CurrentFocus();
3730 if (shortcut->Message() != NULL) {
3731 BMessage message(*shortcut->Message());
3733 if (message.ReplaceInt64("when", system_time()) != B_OK)
3734 message.AddInt64("when", system_time());
3735 if (message.ReplaceBool("shortcut", true) != B_OK)
3736 message.AddBool("shortcut", true);
3738 PostMessage(&message, target);
3743 MenusEnded();
3745 // we always eat the event if the command key was pressed
3746 return true;
3749 // TODO: convert keys to the encoding of the target view
3751 return false;
3755 bool
3756 BWindow::_HandleUnmappedKeyDown(BMessage* event)
3758 // Only handle special functions when the event targeted the active focus
3759 // view
3760 if (!_IsFocusMessage(event))
3761 return false;
3763 uint32 modifiers;
3764 int32 rawKey;
3765 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK
3766 || event->FindInt32("key", &rawKey))
3767 return false;
3769 // Deskbar's Switcher
3770 if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) {
3771 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3772 return true;
3775 return false;
3779 void
3780 BWindow::_KeyboardNavigation()
3782 BMessage* message = CurrentMessage();
3783 if (message == NULL)
3784 return;
3786 const char* bytes;
3787 uint32 modifiers;
3788 if (message->FindString("bytes", &bytes) != B_OK
3789 || bytes[0] != B_TAB)
3790 return;
3792 message->FindInt32("modifiers", (int32*)&modifiers);
3794 BView* nextFocus;
3795 int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0
3796 ? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3797 if (modifiers & B_SHIFT_KEY)
3798 nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3799 else
3800 nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3802 if (nextFocus && nextFocus != fFocus) {
3803 nextFocus->MakeFocus(true);
3809 \brief Return the position of the window centered horizontally to the passed
3810 in \a frame and vertically 3/4 from the top of \a frame.
3812 If the window is on the borders
3814 \param width The width of the window.
3815 \param height The height of the window.
3816 \param frame The \a frame to center the window in.
3818 \return The new window position.
3820 BPoint
3821 BWindow::AlertPosition(const BRect& frame)
3823 float width = Bounds().Width();
3824 float height = Bounds().Height();
3826 BPoint point(frame.left + (frame.Width() / 2.0f) - (width / 2.0f),
3827 frame.top + (frame.Height() / 4.0f) - ceil(height / 3.0f));
3829 BRect screenFrame = BScreen(this).Frame();
3830 if (frame == screenFrame) {
3831 // reference frame is screen frame, skip the below adjustments
3832 return point;
3835 float borderWidth;
3836 float tabHeight;
3837 _GetDecoratorSize(&borderWidth, &tabHeight);
3839 // clip the x position within the horizontal edges of the screen
3840 if (point.x < screenFrame.left + borderWidth)
3841 point.x = screenFrame.left + borderWidth;
3842 else if (point.x + width > screenFrame.right - borderWidth)
3843 point.x = screenFrame.right - borderWidth - width;
3845 // lower the window down if it is covering the window tab
3846 float tabPosition = frame.LeftTop().y + tabHeight + borderWidth;
3847 if (point.y < tabPosition)
3848 point.y = tabPosition;
3850 // clip the y position within the vertical edges of the screen
3851 if (point.y < screenFrame.top + borderWidth)
3852 point.y = screenFrame.top + borderWidth;
3853 else if (point.y + height > screenFrame.bottom - borderWidth)
3854 point.y = screenFrame.bottom - borderWidth - height;
3856 return point;
3860 BMessage*
3861 BWindow::ConvertToMessage(void* raw, int32 code)
3863 return BLooper::ConvertToMessage(raw, code);
3867 BWindow::Shortcut*
3868 BWindow::_FindShortcut(uint32 key, uint32 modifiers)
3870 int32 count = fShortcuts.CountItems();
3872 key = Shortcut::PrepareKey(key);
3873 modifiers = Shortcut::PrepareModifiers(modifiers);
3875 for (int32 index = 0; index < count; index++) {
3876 Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index);
3878 if (shortcut->Matches(key, modifiers))
3879 return shortcut;
3882 return NULL;
3886 BView*
3887 BWindow::_FindView(int32 token)
3889 BHandler* handler;
3890 if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN,
3891 (void**)&handler) != B_OK) {
3892 return NULL;
3895 // the view must belong to us in order to be found by this method
3896 BView* view = dynamic_cast<BView*>(handler);
3897 if (view != NULL && view->Window() == this)
3898 return view;
3900 return NULL;
3904 BView*
3905 BWindow::_FindView(BView* view, BPoint point) const
3907 // point is assumed to be already in view's coordinates
3908 if (!view->IsHidden() && view->Bounds().Contains(point)) {
3909 if (view->fFirstChild == NULL)
3910 return view;
3911 else {
3912 BView* child = view->fFirstChild;
3913 while (child != NULL) {
3914 BPoint childPoint = point - child->Frame().LeftTop();
3915 BView* subView = _FindView(child, childPoint);
3916 if (subView != NULL)
3917 return subView;
3919 child = child->fNextSibling;
3922 return view;
3924 return NULL;
3928 BView*
3929 BWindow::_FindNextNavigable(BView* focus, uint32 flags)
3931 if (focus == NULL)
3932 focus = fTopView;
3934 BView* nextFocus = focus;
3936 // Search the tree for views that accept focus (depth search)
3937 while (true) {
3938 if (nextFocus->fFirstChild)
3939 nextFocus = nextFocus->fFirstChild;
3940 else if (nextFocus->fNextSibling)
3941 nextFocus = nextFocus->fNextSibling;
3942 else {
3943 // go to the nearest parent with a next sibling
3944 while (!nextFocus->fNextSibling && nextFocus->fParent) {
3945 nextFocus = nextFocus->fParent;
3948 if (nextFocus == fTopView) {
3949 // if we started with the top view, we traversed the whole tree already
3950 if (nextFocus == focus)
3951 return NULL;
3953 nextFocus = nextFocus->fFirstChild;
3954 } else
3955 nextFocus = nextFocus->fNextSibling;
3958 if (nextFocus == focus || nextFocus == NULL) {
3959 // When we get here it means that the hole tree has been
3960 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3961 return NULL;
3964 if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
3965 return nextFocus;
3970 BView*
3971 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
3973 if (focus == NULL)
3974 focus = fTopView;
3976 BView* previousFocus = focus;
3978 // Search the tree for the previous view that accept focus
3979 while (true) {
3980 if (previousFocus->fPreviousSibling) {
3981 // find the last child in the previous sibling
3982 previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
3983 } else {
3984 previousFocus = previousFocus->fParent;
3985 if (previousFocus == fTopView)
3986 previousFocus = _LastViewChild(fTopView);
3989 if (previousFocus == focus || previousFocus == NULL) {
3990 // When we get here it means that the hole tree has been
3991 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3992 return NULL;
3995 if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
3996 return previousFocus;
4002 Returns the last child in a view hierarchy.
4003 Needed only by _FindPreviousNavigable().
4005 BView*
4006 BWindow::_LastViewChild(BView* parent)
4008 while (true) {
4009 BView* last = parent->fFirstChild;
4010 if (last == NULL)
4011 return parent;
4013 while (last->fNextSibling) {
4014 last = last->fNextSibling;
4017 parent = last;
4022 void
4023 BWindow::SetIsFilePanel(bool isFilePanel)
4025 fIsFilePanel = isFilePanel;
4029 bool
4030 BWindow::IsFilePanel() const
4032 return fIsFilePanel;
4036 void
4037 BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const
4039 // fallback in case retrieving the decorator settings fails
4040 // (highly unlikely)
4041 float borderWidth = 5.0;
4042 float tabHeight = 21.0;
4044 BMessage settings;
4045 if (GetDecoratorSettings(&settings) == B_OK) {
4046 BRect tabRect;
4047 if (settings.FindRect("tab frame", &tabRect) == B_OK)
4048 tabHeight = tabRect.Height();
4049 settings.FindFloat("border width", &borderWidth);
4050 } else {
4051 // probably no-border window look
4052 if (fLook == B_NO_BORDER_WINDOW_LOOK) {
4053 borderWidth = 0.0;
4054 tabHeight = 0.0;
4056 // else use fall-back values from above
4059 if (_borderWidth != NULL)
4060 *_borderWidth = borderWidth;
4061 if (_tabHeight != NULL)
4062 *_tabHeight = tabHeight;
4066 void
4067 BWindow::_SendShowOrHideMessage()
4069 fLink->StartMessage(AS_SHOW_OR_HIDE_WINDOW);
4070 fLink->Attach<int32>(fShowLevel);
4071 fLink->Flush();
4075 // #pragma mark - C++ binary compatibility kludge
4078 extern "C" void
4079 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout)
4081 // SetLayout()
4082 perform_data_set_layout data;
4083 data.layout = layout;
4084 window->Perform(PERFORM_CODE_SET_LAYOUT, &data);
4088 void BWindow::_ReservedWindow2() {}
4089 void BWindow::_ReservedWindow3() {}
4090 void BWindow::_ReservedWindow4() {}
4091 void BWindow::_ReservedWindow5() {}
4092 void BWindow::_ReservedWindow6() {}
4093 void BWindow::_ReservedWindow7() {}
4094 void BWindow::_ReservedWindow8() {}