1 /*
2 * Copyright 2001-2016 Haiku, Inc. All rights reserved
3 * Distributed under the terms of the MIT License.
4 *
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
10 */
11
12
13 #include <Window.h>
14
15 #include <ctype.h>
16 #include <math.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19
20 #include <Application.h>
21 #include <AppMisc.h>
22 #include <AppServerLink.h>
23 #include <ApplicationPrivate.h>
24 #include <Autolock.h>
25 #include <Bitmap.h>
26 #include <Button.h>
27 #include <Deskbar.h>
28 #include <DirectMessageTarget.h>
29 #include <FindDirectory.h>
30 #include <InputServerTypes.h>
31 #include <Layout.h>
32 #include <LayoutUtils.h>
33 #include <MenuBar.h>
34 #include <MenuItem.h>
35 #include <MenuPrivate.h>
36 #include <MessagePrivate.h>
37 #include <MessageQueue.h>
38 #include <MessageRunner.h>
39 #include <Path.h>
40 #include <PortLink.h>
41 #include <PropertyInfo.h>
42 #include <Roster.h>
43 #include <RosterPrivate.h>
44 #include <Screen.h>
45 #include <ServerProtocol.h>
46 #include <String.h>
47 #include <TextView.h>
48 #include <TokenSpace.h>
49 #include <ToolTipManager.h>
50 #include <ToolTipWindow.h>
51 #include <UnicodeChar.h>
52 #include <WindowPrivate.h>
53
54 #include <binary_compatibility/Interface.h>
55 #include <input_globals.h>
56 #include <tracker_private.h>
57
58
59 //#define DEBUG_WIN
60 #ifdef DEBUG_WIN
61 # define STRACE(x) printf x
62 #else
63 # define STRACE(x) ;
64 #endif
65
66 #define B_HIDE_APPLICATION '_AHD'
67 // if we ever move this to a public namespace, we should also move the
68 // handling of this message into BApplication
69
70 #define _MINIMIZE_ '_WMZ'
71 #define _ZOOM_ '_WZO'
72 #define _SEND_BEHIND_ '_WSB'
73 #define _SEND_TO_FRONT_ '_WSF'
74
75
76 void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
77
78
79 struct BWindow::unpack_cookie {
80 unpack_cookie();
81
82 BMessage* message;
83 int32 index;
84 BHandler* focus;
85 int32 focus_token;
86 int32 last_view_token;
87 bool found_focus;
88 bool tokens_scanned;
89 };
90
91
92 class BWindow::Shortcut {
93 public:
94 Shortcut(uint32 key, uint32 modifiers,
95 BMenuItem* item);
96 Shortcut(uint32 key, uint32 modifiers,
97 BMessage* message, BHandler* target);
98 ~Shortcut();
99
100 bool Matches(uint32 key, uint32 modifiers) const;
101
MenuItem() const102 BMenuItem* MenuItem() const { return fMenuItem; }
Message() const103 BMessage* Message() const { return fMessage; }
Target() const104 BHandler* Target() const { return fTarget; }
105
106 static uint32 AllowedModifiers();
107 static uint32 PrepareKey(uint32 key);
108 static uint32 PrepareModifiers(uint32 modifiers);
109
110 private:
111 uint32 fKey;
112 uint32 fModifiers;
113 BMenuItem* fMenuItem;
114 BMessage* fMessage;
115 BHandler* fTarget;
116 };
117
118
119 using BPrivate::gDefaultTokens;
120 using BPrivate::MenuPrivate;
121
122 static property_info sWindowPropInfo[] = {
123 {
124 "Active", { B_GET_PROPERTY, B_SET_PROPERTY },
125 { B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
126 },
127
128 {
129 "Feel", { B_GET_PROPERTY, B_SET_PROPERTY },
130 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
131 },
132
133 {
134 "Flags", { B_GET_PROPERTY, B_SET_PROPERTY },
135 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
136 },
137
138 {
139 "Frame", { B_GET_PROPERTY, B_SET_PROPERTY },
140 { B_DIRECT_SPECIFIER }, NULL, 0, { B_RECT_TYPE }
141 },
142
143 {
144 "Hidden", { B_GET_PROPERTY, B_SET_PROPERTY },
145 { B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
146 },
147
148 {
149 "Look", { B_GET_PROPERTY, B_SET_PROPERTY },
150 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
151 },
152
153 {
154 "Title", { B_GET_PROPERTY, B_SET_PROPERTY },
155 { B_DIRECT_SPECIFIER }, NULL, 0, { B_STRING_TYPE }
156 },
157
158 {
159 "Workspaces", { B_GET_PROPERTY, B_SET_PROPERTY },
160 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
161 },
162
163 {
164 "MenuBar", {},
165 { B_DIRECT_SPECIFIER }, NULL, 0, {}
166 },
167
168 {
169 "View", { B_COUNT_PROPERTIES },
170 { B_DIRECT_SPECIFIER }, NULL, 0, { B_INT32_TYPE }
171 },
172
173 {
174 "View", {}, {}, NULL, 0, {}
175 },
176
177 {
178 "Minimize", { B_GET_PROPERTY, B_SET_PROPERTY },
179 { B_DIRECT_SPECIFIER }, NULL, 0, { B_BOOL_TYPE }
180 },
181
182 {
183 "TabFrame", { B_GET_PROPERTY },
184 { B_DIRECT_SPECIFIER }, NULL, 0, { B_RECT_TYPE }
185 },
186
187 { 0 }
188 };
189
190 static value_info sWindowValueInfo[] = {
191 {
192 "MoveTo", 'WDMT', B_COMMAND_KIND,
193 "Moves to the position in the BPoint data"
194 },
195
196 {
197 "MoveBy", 'WDMB', B_COMMAND_KIND,
198 "Moves by the offsets in the BPoint data"
199 },
200
201 {
202 "ResizeTo", 'WDRT', B_COMMAND_KIND,
203 "Resize to the size in the BPoint data"
204 },
205
206 {
207 "ResizeBy", 'WDRB', B_COMMAND_KIND,
208 "Resize by the offsets in the BPoint data"
209 },
210
211 { 0 }
212 };
213
214
215 void
_set_menu_sem_(BWindow * window,sem_id sem)216 _set_menu_sem_(BWindow* window, sem_id sem)
217 {
218 if (window != NULL)
219 window->fMenuSem = sem;
220 }
221
222
223 // #pragma mark -
224
225
unpack_cookie()226 BWindow::unpack_cookie::unpack_cookie()
227 :
228 message((BMessage*)~0UL),
229 // message == NULL is our exit condition
230 index(0),
231 focus_token(B_NULL_TOKEN),
232 last_view_token(B_NULL_TOKEN),
233 found_focus(false),
234 tokens_scanned(false)
235 {
236 }
237
238
239 // #pragma mark - BWindow::Shortcut
240
241
Shortcut(uint32 key,uint32 modifiers,BMenuItem * item)242 BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMenuItem* item)
243 :
244 fKey(PrepareKey(key)),
245 fModifiers(PrepareModifiers(modifiers)),
246 fMenuItem(item),
247 fMessage(NULL),
248 fTarget(NULL)
249 {
250 }
251
252
Shortcut(uint32 key,uint32 modifiers,BMessage * message,BHandler * target)253 BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMessage* message,
254 BHandler* target)
255 :
256 fKey(PrepareKey(key)),
257 fModifiers(PrepareModifiers(modifiers)),
258 fMenuItem(NULL),
259 fMessage(message),
260 fTarget(target)
261 {
262 }
263
264
~Shortcut()265 BWindow::Shortcut::~Shortcut()
266 {
267 // we own the message, if any
268 delete fMessage;
269 }
270
271
272 bool
Matches(uint32 key,uint32 modifiers) const273 BWindow::Shortcut::Matches(uint32 key, uint32 modifiers) const
274 {
275 return fKey == key && fModifiers == modifiers;
276 }
277
278
279 /*static*/
280 uint32
AllowedModifiers()281 BWindow::Shortcut::AllowedModifiers()
282 {
283 return B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY | B_CONTROL_KEY
284 | B_MENU_KEY;
285 }
286
287
288 /*static*/
289 uint32
PrepareModifiers(uint32 modifiers)290 BWindow::Shortcut::PrepareModifiers(uint32 modifiers)
291 {
292 return (modifiers & AllowedModifiers()) | B_COMMAND_KEY;
293 }
294
295
296 /*static*/
297 uint32
PrepareKey(uint32 key)298 BWindow::Shortcut::PrepareKey(uint32 key)
299 {
300 return BUnicodeChar::ToLower(key);
301 }
302
303
304 // #pragma mark - BWindow
305
306
BWindow(BRect frame,const char * title,window_type type,uint32 flags,uint32 workspace)307 BWindow::BWindow(BRect frame, const char* title, window_type type,
308 uint32 flags, uint32 workspace)
309 :
310 BLooper(title, B_DISPLAY_PRIORITY)
311 {
312 window_look look;
313 window_feel feel;
314 _DecomposeType(type, &look, &feel);
315
316 _InitData(frame, title, look, feel, flags, workspace);
317 }
318
319
BWindow(BRect frame,const char * title,window_look look,window_feel feel,uint32 flags,uint32 workspace)320 BWindow::BWindow(BRect frame, const char* title, window_look look,
321 window_feel feel, uint32 flags, uint32 workspace)
322 :
323 BLooper(title, B_DISPLAY_PRIORITY)
324 {
325 _InitData(frame, title, look, feel, flags, workspace);
326 }
327
328
BWindow(BMessage * data)329 BWindow::BWindow(BMessage* data)
330 :
331 BLooper(data)
332 {
333 data->FindRect("_frame", &fFrame);
334
335 const char* title;
336 data->FindString("_title", &title);
337
338 window_look look;
339 data->FindInt32("_wlook", (int32*)&look);
340
341 window_feel feel;
342 data->FindInt32("_wfeel", (int32*)&feel);
343
344 if (data->FindInt32("_flags", (int32*)&fFlags) != B_OK)
345 fFlags = 0;
346
347 uint32 workspaces;
348 data->FindInt32("_wspace", (int32*)&workspaces);
349
350 uint32 type;
351 if (data->FindInt32("_type", (int32*)&type) == B_OK)
352 _DecomposeType((window_type)type, &fLook, &fFeel);
353
354 // connect to app_server and initialize data
355 _InitData(fFrame, title, look, feel, fFlags, workspaces);
356
357 if (data->FindFloat("_zoom", 0, &fMaxZoomWidth) == B_OK
358 && data->FindFloat("_zoom", 1, &fMaxZoomHeight) == B_OK)
359 SetZoomLimits(fMaxZoomWidth, fMaxZoomHeight);
360
361 if (data->FindFloat("_sizel", 0, &fMinWidth) == B_OK
362 && data->FindFloat("_sizel", 1, &fMinHeight) == B_OK
363 && data->FindFloat("_sizel", 2, &fMaxWidth) == B_OK
364 && data->FindFloat("_sizel", 3, &fMaxHeight) == B_OK)
365 SetSizeLimits(fMinWidth, fMaxWidth,
366 fMinHeight, fMaxHeight);
367
368 if (data->FindInt64("_pulse", &fPulseRate) == B_OK)
369 SetPulseRate(fPulseRate);
370
371 BMessage msg;
372 int32 i = 0;
373 while (data->FindMessage("_views", i++, &msg) == B_OK) {
374 BArchivable* obj = instantiate_object(&msg);
375 if (BView* child = dynamic_cast<BView*>(obj))
376 AddChild(child);
377 }
378 }
379
380
BWindow(BRect frame,int32 bitmapToken)381 BWindow::BWindow(BRect frame, int32 bitmapToken)
382 :
383 BLooper("offscreen bitmap")
384 {
385 _DecomposeType(B_UNTYPED_WINDOW, &fLook, &fFeel);
386 _InitData(frame, "offscreen", fLook, fFeel, 0, 0, bitmapToken);
387 }
388
389
~BWindow()390 BWindow::~BWindow()
391 {
392 if (BMenu* menu = dynamic_cast<BMenu*>(fFocus)) {
393 MenuPrivate(menu).QuitTracking();
394 }
395
396 // The BWindow is locked when the destructor is called,
397 // we need to unlock because the menubar thread tries
398 // to post a message, which will deadlock otherwise.
399 // TODO: I replaced Unlock() with UnlockFully() because the window
400 // was kept locked after that in case it was closed using ALT-W.
401 // There might be an extra Lock() somewhere in the quitting path...
402 UnlockFully();
403
404 // Wait if a menu is still tracking
405 if (fMenuSem > 0) {
406 while (acquire_sem(fMenuSem) == B_INTERRUPTED)
407 ;
408 }
409
410 Lock();
411
412 fTopView->RemoveSelf();
413 delete fTopView;
414
415 // remove all remaining shortcuts
416 int32 shortCutCount = fShortcuts.CountItems();
417 for (int32 i = 0; i < shortCutCount; i++) {
418 delete (Shortcut*)fShortcuts.ItemAtFast(i);
419 }
420
421 // TODO: release other dynamically-allocated objects
422 free(fTitle);
423
424 // disable pulsing
425 SetPulseRate(0);
426
427 // tell app_server about our demise
428 fLink->StartMessage(AS_DELETE_WINDOW);
429 // sync with the server so that for example
430 // a BBitmap can be sure that there are no
431 // more pending messages that are executed
432 // after the bitmap is deleted (which uses
433 // a different link and server side thread)
434 int32 code;
435 fLink->FlushWithReply(code);
436
437 // the sender port belongs to the app_server
438 delete_port(fLink->ReceiverPort());
439 delete fLink;
440 }
441
442
443 BArchivable*
Instantiate(BMessage * data)444 BWindow::Instantiate(BMessage* data)
445 {
446 if (!validate_instantiation(data, "BWindow"))
447 return NULL;
448
449 return new(std::nothrow) BWindow(data);
450 }
451
452
453 status_t
Archive(BMessage * data,bool deep) const454 BWindow::Archive(BMessage* data, bool deep) const
455 {
456 status_t ret = BLooper::Archive(data, deep);
457
458 if (ret == B_OK)
459 ret = data->AddRect("_frame", fFrame);
460 if (ret == B_OK)
461 ret = data->AddString("_title", fTitle);
462 if (ret == B_OK)
463 ret = data->AddInt32("_wlook", fLook);
464 if (ret == B_OK)
465 ret = data->AddInt32("_wfeel", fFeel);
466 if (ret == B_OK && fFlags != 0)
467 ret = data->AddInt32("_flags", fFlags);
468 if (ret == B_OK)
469 ret = data->AddInt32("_wspace", (uint32)Workspaces());
470
471 if (ret == B_OK && !_ComposeType(fLook, fFeel))
472 ret = data->AddInt32("_type", (uint32)Type());
473
474 if (fMaxZoomWidth != 32768.0 || fMaxZoomHeight != 32768.0) {
475 if (ret == B_OK)
476 ret = data->AddFloat("_zoom", fMaxZoomWidth);
477 if (ret == B_OK)
478 ret = data->AddFloat("_zoom", fMaxZoomHeight);
479 }
480
481 if (fMinWidth != 0.0 || fMinHeight != 0.0
482 || fMaxWidth != 32768.0 || fMaxHeight != 32768.0) {
483 if (ret == B_OK)
484 ret = data->AddFloat("_sizel", fMinWidth);
485 if (ret == B_OK)
486 ret = data->AddFloat("_sizel", fMinHeight);
487 if (ret == B_OK)
488 ret = data->AddFloat("_sizel", fMaxWidth);
489 if (ret == B_OK)
490 ret = data->AddFloat("_sizel", fMaxHeight);
491 }
492
493 if (ret == B_OK && fPulseRate != 500000)
494 data->AddInt64("_pulse", fPulseRate);
495
496 if (ret == B_OK && deep) {
497 int32 noOfViews = CountChildren();
498 for (int32 i = 0; i < noOfViews; i++){
499 BMessage childArchive;
500 ret = ChildAt(i)->Archive(&childArchive, true);
501 if (ret == B_OK)
502 ret = data->AddMessage("_views", &childArchive);
503 if (ret != B_OK)
504 break;
505 }
506 }
507
508 return ret;
509 }
510
511
512 void
Quit()513 BWindow::Quit()
514 {
515 if (!IsLocked()) {
516 const char* name = Name();
517 if (name == NULL)
518 name = "no-name";
519
520 printf("ERROR - you must Lock a looper before calling Quit(), "
521 "team=%" B_PRId32 ", looper=%s\n", Team(), name);
522 }
523
524 // Try to lock
525 if (!Lock()){
526 // We're toast already
527 return;
528 }
529
530 while (!IsHidden()) {
531 Hide();
532 }
533
534 if (fFlags & B_QUIT_ON_WINDOW_CLOSE)
535 be_app->PostMessage(B_QUIT_REQUESTED);
536
537 BLooper::Quit();
538 }
539
540
541 void
AddChild(BView * child,BView * before)542 BWindow::AddChild(BView* child, BView* before)
543 {
544 BAutolock locker(this);
545 if (locker.IsLocked())
546 fTopView->AddChild(child, before);
547 }
548
549
550 void
AddChild(BLayoutItem * child)551 BWindow::AddChild(BLayoutItem* child)
552 {
553 BAutolock locker(this);
554 if (locker.IsLocked())
555 fTopView->AddChild(child);
556 }
557
558
559 bool
RemoveChild(BView * child)560 BWindow::RemoveChild(BView* child)
561 {
562 BAutolock locker(this);
563 if (!locker.IsLocked())
564 return false;
565
566 return fTopView->RemoveChild(child);
567 }
568
569
570 int32
CountChildren() const571 BWindow::CountChildren() const
572 {
573 BAutolock locker(const_cast<BWindow*>(this));
574 if (!locker.IsLocked())
575 return 0;
576
577 return fTopView->CountChildren();
578 }
579
580
581 BView*
ChildAt(int32 index) const582 BWindow::ChildAt(int32 index) const
583 {
584 BAutolock locker(const_cast<BWindow*>(this));
585 if (!locker.IsLocked())
586 return NULL;
587
588 return fTopView->ChildAt(index);
589 }
590
591
592 void
Minimize(bool minimize)593 BWindow::Minimize(bool minimize)
594 {
595 if (IsModal() || IsFloating() || IsHidden() || fMinimized == minimize
596 || !Lock())
597 return;
598
599 fMinimized = minimize;
600
601 fLink->StartMessage(AS_MINIMIZE_WINDOW);
602 fLink->Attach<bool>(minimize);
603 fLink->Flush();
604
605 Unlock();
606 }
607
608
609 status_t
SendBehind(const BWindow * window)610 BWindow::SendBehind(const BWindow* window)
611 {
612 if (!Lock())
613 return B_ERROR;
614
615 fLink->StartMessage(AS_SEND_BEHIND);
616 fLink->Attach<int32>(window != NULL ? _get_object_token_(window) : -1);
617 fLink->Attach<team_id>(Team());
618
619 status_t status = B_ERROR;
620 fLink->FlushWithReply(status);
621
622 Unlock();
623
624 return status;
625 }
626
627
628 void
Flush() const629 BWindow::Flush() const
630 {
631 if (const_cast<BWindow*>(this)->Lock()) {
632 fLink->Flush();
633 const_cast<BWindow*>(this)->Unlock();
634 }
635 }
636
637
638 void
Sync() const639 BWindow::Sync() const
640 {
641 if (!const_cast<BWindow*>(this)->Lock())
642 return;
643
644 fLink->StartMessage(AS_SYNC);
645
646 // waiting for the reply is the actual syncing
647 int32 code;
648 fLink->FlushWithReply(code);
649
650 const_cast<BWindow*>(this)->Unlock();
651 }
652
653
654 void
DisableUpdates()655 BWindow::DisableUpdates()
656 {
657 if (Lock()) {
658 fLink->StartMessage(AS_DISABLE_UPDATES);
659 fLink->Flush();
660 Unlock();
661 }
662 }
663
664
665 void
EnableUpdates()666 BWindow::EnableUpdates()
667 {
668 if (Lock()) {
669 fLink->StartMessage(AS_ENABLE_UPDATES);
670 fLink->Flush();
671 Unlock();
672 }
673 }
674
675
676 void
BeginViewTransaction()677 BWindow::BeginViewTransaction()
678 {
679 if (Lock()) {
680 fInTransaction = true;
681 Unlock();
682 }
683 }
684
685
686 void
EndViewTransaction()687 BWindow::EndViewTransaction()
688 {
689 if (Lock()) {
690 if (fInTransaction)
691 fLink->Flush();
692 fInTransaction = false;
693 Unlock();
694 }
695 }
696
697
698 bool
InViewTransaction() const699 BWindow::InViewTransaction() const
700 {
701 BAutolock locker(const_cast<BWindow*>(this));
702 return fInTransaction;
703 }
704
705
706 bool
IsFront() const707 BWindow::IsFront() const
708 {
709 BAutolock locker(const_cast<BWindow*>(this));
710 if (!locker.IsLocked())
711 return false;
712
713 fLink->StartMessage(AS_IS_FRONT_WINDOW);
714
715 status_t status;
716 if (fLink->FlushWithReply(status) == B_OK)
717 return status >= B_OK;
718
719 return false;
720 }
721
722
723 void
MessageReceived(BMessage * message)724 BWindow::MessageReceived(BMessage* message)
725 {
726 if (!message->HasSpecifiers()) {
727 if (message->what == B_KEY_DOWN)
728 _KeyboardNavigation();
729
730 if (message->what == (int32)kMsgAppServerRestarted) {
731 fLink->SetSenderPort(
732 BApplication::Private::ServerLink()->SenderPort());
733
734 BPrivate::AppServerLink lockLink;
735 // we're talking to the server application using our own
736 // communication channel (fLink) - we better make sure no one
737 // interferes by locking that channel (which AppServerLink does
738 // implicitly)
739
740 fLink->StartMessage(AS_CREATE_WINDOW);
741
742 fLink->Attach<BRect>(fFrame);
743 fLink->Attach<uint32>((uint32)fLook);
744 fLink->Attach<uint32>((uint32)fFeel);
745 fLink->Attach<uint32>(fFlags);
746 fLink->Attach<uint32>(0);
747 fLink->Attach<int32>(_get_object_token_(this));
748 fLink->Attach<port_id>(fLink->ReceiverPort());
749 fLink->Attach<port_id>(fMsgPort);
750 fLink->AttachString(fTitle);
751
752 port_id sendPort;
753 int32 code;
754 if (fLink->FlushWithReply(code) == B_OK
755 && code == B_OK
756 && fLink->Read<port_id>(&sendPort) == B_OK) {
757 // read the frame size and its limits that were really
758 // enforced on the server side
759
760 fLink->Read<BRect>(&fFrame);
761 fLink->Read<float>(&fMinWidth);
762 fLink->Read<float>(&fMaxWidth);
763 fLink->Read<float>(&fMinHeight);
764 fLink->Read<float>(&fMaxHeight);
765
766 fMaxZoomWidth = fMaxWidth;
767 fMaxZoomHeight = fMaxHeight;
768 } else
769 sendPort = -1;
770
771 // Redirect our link to the new window connection
772 fLink->SetSenderPort(sendPort);
773
774 // connect all views to the server again
775 fTopView->_CreateSelf();
776
777 _SendShowOrHideMessage();
778 }
779
780 return BLooper::MessageReceived(message);
781 }
782
783 BMessage replyMsg(B_REPLY);
784 bool handled = false;
785
786 BMessage specifier;
787 int32 what;
788 const char* prop;
789 int32 index;
790
791 if (message->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
792 return BLooper::MessageReceived(message);
793
794 BPropertyInfo propertyInfo(sWindowPropInfo);
795 switch (propertyInfo.FindMatch(message, index, &specifier, what, prop)) {
796 case 0:
797 if (message->what == B_GET_PROPERTY) {
798 replyMsg.AddBool("result", IsActive());
799 handled = true;
800 } else if (message->what == B_SET_PROPERTY) {
801 bool newActive;
802 if (message->FindBool("data", &newActive) == B_OK) {
803 Activate(newActive);
804 handled = true;
805 }
806 }
807 break;
808 case 1:
809 if (message->what == B_GET_PROPERTY) {
810 replyMsg.AddInt32("result", (uint32)Feel());
811 handled = true;
812 } else {
813 uint32 newFeel;
814 if (message->FindInt32("data", (int32*)&newFeel) == B_OK) {
815 SetFeel((window_feel)newFeel);
816 handled = true;
817 }
818 }
819 break;
820 case 2:
821 if (message->what == B_GET_PROPERTY) {
822 replyMsg.AddInt32("result", Flags());
823 handled = true;
824 } else {
825 uint32 newFlags;
826 if (message->FindInt32("data", (int32*)&newFlags) == B_OK) {
827 SetFlags(newFlags);
828 handled = true;
829 }
830 }
831 break;
832 case 3:
833 if (message->what == B_GET_PROPERTY) {
834 replyMsg.AddRect("result", Frame());
835 handled = true;
836 } else {
837 BRect newFrame;
838 if (message->FindRect("data", &newFrame) == B_OK) {
839 MoveTo(newFrame.LeftTop());
840 ResizeTo(newFrame.Width(), newFrame.Height());
841 handled = true;
842 }
843 }
844 break;
845 case 4:
846 if (message->what == B_GET_PROPERTY) {
847 replyMsg.AddBool("result", IsHidden());
848 handled = true;
849 } else {
850 bool hide;
851 if (message->FindBool("data", &hide) == B_OK) {
852 if (hide) {
853 if (!IsHidden())
854 Hide();
855 } else if (IsHidden())
856 Show();
857 handled = true;
858 }
859 }
860 break;
861 case 5:
862 if (message->what == B_GET_PROPERTY) {
863 replyMsg.AddInt32("result", (uint32)Look());
864 handled = true;
865 } else {
866 uint32 newLook;
867 if (message->FindInt32("data", (int32*)&newLook) == B_OK) {
868 SetLook((window_look)newLook);
869 handled = true;
870 }
871 }
872 break;
873 case 6:
874 if (message->what == B_GET_PROPERTY) {
875 replyMsg.AddString("result", Title());
876 handled = true;
877 } else {
878 const char* newTitle = NULL;
879 if (message->FindString("data", &newTitle) == B_OK) {
880 SetTitle(newTitle);
881 handled = true;
882 }
883 }
884 break;
885 case 7:
886 if (message->what == B_GET_PROPERTY) {
887 replyMsg.AddInt32( "result", Workspaces());
888 handled = true;
889 } else {
890 uint32 newWorkspaces;
891 if (message->FindInt32("data", (int32*)&newWorkspaces) == B_OK) {
892 SetWorkspaces(newWorkspaces);
893 handled = true;
894 }
895 }
896 break;
897 case 11:
898 if (message->what == B_GET_PROPERTY) {
899 replyMsg.AddBool("result", IsMinimized());
900 handled = true;
901 } else {
902 bool minimize;
903 if (message->FindBool("data", &minimize) == B_OK) {
904 Minimize(minimize);
905 handled = true;
906 }
907 }
908 break;
909 case 12:
910 if (message->what == B_GET_PROPERTY) {
911 BMessage settings;
912 if (GetDecoratorSettings(&settings) == B_OK) {
913 BRect frame;
914 if (settings.FindRect("tab frame", &frame) == B_OK) {
915 replyMsg.AddRect("result", frame);
916 handled = true;
917 }
918 }
919 }
920 break;
921 default:
922 return BLooper::MessageReceived(message);
923 }
924
925 if (handled) {
926 if (message->what == B_SET_PROPERTY)
927 replyMsg.AddInt32("error", B_OK);
928 } else {
929 replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
930 replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
931 replyMsg.AddString("message", "Didn't understand the specifier(s)");
932 }
933 message->SendReply(&replyMsg);
934 }
935
936
937 void
DispatchMessage(BMessage * message,BHandler * target)938 BWindow::DispatchMessage(BMessage* message, BHandler* target)
939 {
940 if (message == NULL)
941 return;
942
943 switch (message->what) {
944 case B_ZOOM:
945 Zoom();
946 break;
947
948 case _MINIMIZE_:
949 // Used by the minimize shortcut
950 if ((Flags() & B_NOT_MINIMIZABLE) == 0)
951 Minimize(true);
952 break;
953
954 case _ZOOM_:
955 // Used by the zoom shortcut
956 if ((Flags() & B_NOT_ZOOMABLE) == 0)
957 Zoom();
958 break;
959
960 case _SEND_BEHIND_:
961 SendBehind(NULL);
962 break;
963
964 case _SEND_TO_FRONT_:
965 Activate();
966 break;
967
968 case B_MINIMIZE:
969 {
970 bool minimize;
971 if (message->FindBool("minimize", &minimize) == B_OK)
972 Minimize(minimize);
973 break;
974 }
975
976 case B_HIDE_APPLICATION:
977 {
978 // Hide all applications with the same signature
979 // (ie. those that are part of the same group to be consistent
980 // to what the Deskbar shows you).
981 app_info info;
982 be_app->GetAppInfo(&info);
983
984 BList list;
985 be_roster->GetAppList(info.signature, &list);
986
987 for (int32 i = 0; i < list.CountItems(); i++) {
988 do_minimize_team(BRect(), (team_id)(addr_t)list.ItemAt(i),
989 false);
990 }
991 break;
992 }
993
994 case B_WINDOW_RESIZED:
995 {
996 int32 width, height;
997 if (message->FindInt32("width", &width) == B_OK
998 && message->FindInt32("height", &height) == B_OK) {
999 // combine with pending resize notifications
1000 BMessage* pendingMessage;
1001 while ((pendingMessage
1002 = MessageQueue()->FindMessage(B_WINDOW_RESIZED, 0))) {
1003 int32 nextWidth;
1004 if (pendingMessage->FindInt32("width", &nextWidth) == B_OK)
1005 width = nextWidth;
1006
1007 int32 nextHeight;
1008 if (pendingMessage->FindInt32("height", &nextHeight)
1009 == B_OK) {
1010 height = nextHeight;
1011 }
1012
1013 MessageQueue()->RemoveMessage(pendingMessage);
1014 delete pendingMessage;
1015 // this deletes the first *additional* message
1016 // fCurrentMessage is safe
1017 }
1018 if (width != fFrame.Width() || height != fFrame.Height()) {
1019 // NOTE: we might have already handled the resize
1020 // in an _UPDATE_ message
1021 fFrame.right = fFrame.left + width;
1022 fFrame.bottom = fFrame.top + height;
1023
1024 _AdoptResize();
1025 // FrameResized(width, height);
1026 }
1027 // call hook function anyways
1028 // TODO: When a window is resized programmatically,
1029 // it receives this message, and maybe it is wise to
1030 // keep the asynchronous nature of this process to
1031 // not risk breaking any apps.
1032 FrameResized(width, height);
1033 }
1034 break;
1035 }
1036
1037 case B_WINDOW_MOVED:
1038 {
1039 BPoint origin;
1040 if (message->FindPoint("where", &origin) == B_OK) {
1041 if (fFrame.LeftTop() != origin) {
1042 // NOTE: we might have already handled the move
1043 // in an _UPDATE_ message
1044 fFrame.OffsetTo(origin);
1045
1046 // FrameMoved(origin);
1047 }
1048 // call hook function anyways
1049 // TODO: When a window is moved programmatically,
1050 // it receives this message, and maybe it is wise to
1051 // keep the asynchronous nature of this process to
1052 // not risk breaking any apps.
1053 FrameMoved(origin);
1054 }
1055 break;
1056 }
1057
1058 case B_WINDOW_ACTIVATED:
1059 if (target != this) {
1060 target->MessageReceived(message);
1061 break;
1062 }
1063
1064 bool active;
1065 if (message->FindBool("active", &active) != B_OK)
1066 break;
1067
1068 // find latest activation message
1069
1070 while (true) {
1071 BMessage* pendingMessage = MessageQueue()->FindMessage(
1072 B_WINDOW_ACTIVATED, 0);
1073 if (pendingMessage == NULL)
1074 break;
1075
1076 bool nextActive;
1077 if (pendingMessage->FindBool("active", &nextActive) == B_OK)
1078 active = nextActive;
1079
1080 MessageQueue()->RemoveMessage(pendingMessage);
1081 delete pendingMessage;
1082 }
1083
1084 if (active != fActive) {
1085 fActive = active;
1086
1087 WindowActivated(active);
1088
1089 // call hook function 'WindowActivated(bool)' for all
1090 // views attached to this window.
1091 fTopView->_Activate(active);
1092
1093 // we notify the input server if we are gaining or losing focus
1094 // from a view which has the B_INPUT_METHOD_AWARE on a window
1095 // activation
1096 if (!active)
1097 break;
1098 bool inputMethodAware = false;
1099 if (fFocus)
1100 inputMethodAware = fFocus->Flags() & B_INPUT_METHOD_AWARE;
1101 BMessage message(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
1102 BMessenger messenger(fFocus);
1103 BMessage reply;
1104 if (fFocus)
1105 message.AddMessenger("view", messenger);
1106 _control_input_server_(&message, &reply);
1107 }
1108 break;
1109
1110 case B_SCREEN_CHANGED:
1111 if (target == this) {
1112 BRect frame;
1113 uint32 mode;
1114 if (message->FindRect("frame", &frame) == B_OK
1115 && message->FindInt32("mode", (int32*)&mode) == B_OK) {
1116 _PropagateMessageToChildViews(message);
1117 // call hook method
1118 ScreenChanged(frame, (color_space)mode);
1119 }
1120 } else
1121 target->MessageReceived(message);
1122 break;
1123
1124 case B_WORKSPACE_ACTIVATED:
1125 if (target == this) {
1126 uint32 workspace;
1127 bool active;
1128 if (message->FindInt32("workspace", (int32*)&workspace) == B_OK
1129 && message->FindBool("active", &active) == B_OK) {
1130 _PropagateMessageToChildViews(message);
1131 // call hook method
1132 WorkspaceActivated(workspace, active);
1133 }
1134 } else
1135 target->MessageReceived(message);
1136 break;
1137
1138 case B_WORKSPACES_CHANGED:
1139 if (target == this) {
1140 uint32 oldWorkspace;
1141 uint32 newWorkspace;
1142 if (message->FindInt32("old", (int32*)&oldWorkspace) == B_OK
1143 && message->FindInt32("new", (int32*)&newWorkspace) == B_OK) {
1144 _PropagateMessageToChildViews(message);
1145 // call hook method
1146 WorkspacesChanged(oldWorkspace, newWorkspace);
1147 }
1148 } else
1149 target->MessageReceived(message);
1150 break;
1151
1152 case B_KEY_DOWN:
1153 if (!_HandleKeyDown(message))
1154 target->MessageReceived(message);
1155 break;
1156
1157 case B_UNMAPPED_KEY_DOWN:
1158 if (!_HandleUnmappedKeyDown(message))
1159 target->MessageReceived(message);
1160 break;
1161
1162 case B_PULSE:
1163 if (target == this && fPulseRunner) {
1164 fTopView->_Pulse();
1165 fLink->Flush();
1166 } else
1167 target->MessageReceived(message);
1168 break;
1169
1170 case _UPDATE_:
1171 {
1172 //bigtime_t now = system_time();
1173 //bigtime_t drawTime = 0;
1174 STRACE(("info:BWindow handling _UPDATE_.\n"));
1175
1176 fLink->StartMessage(AS_BEGIN_UPDATE);
1177 fInTransaction = true;
1178
1179 int32 code;
1180 if (fLink->FlushWithReply(code) == B_OK
1181 && code == B_OK) {
1182 // read current window position and size first,
1183 // the update rect is in screen coordinates...
1184 // so we need to be up to date
1185 BPoint origin;
1186 fLink->Read<BPoint>(&origin);
1187 float width;
1188 float height;
1189 fLink->Read<float>(&width);
1190 fLink->Read<float>(&height);
1191
1192 // read tokens for views that need to be drawn
1193 // NOTE: we need to read the tokens completely
1194 // first, we cannot draw views in between reading
1195 // the tokens, since other communication would likely
1196 // mess up the data in the link.
1197 struct ViewUpdateInfo {
1198 int32 token;
1199 BRect updateRect;
1200 };
1201 BList infos(20);
1202 while (true) {
1203 // read next token and create/add ViewUpdateInfo
1204 int32 token;
1205 status_t error = fLink->Read<int32>(&token);
1206 if (error < B_OK || token == B_NULL_TOKEN)
1207 break;
1208 ViewUpdateInfo* info = new(std::nothrow) ViewUpdateInfo;
1209 if (info == NULL || !infos.AddItem(info)) {
1210 delete info;
1211 break;
1212 }
1213 info->token = token;
1214 // read culmulated update rect (is in screen coords)
1215 error = fLink->Read<BRect>(&(info->updateRect));
1216 if (error < B_OK)
1217 break;
1218 }
1219 // Hooks should be called after finishing reading reply because
1220 // they can access fLink.
1221 if (origin != fFrame.LeftTop()) {
1222 // TODO: remove code duplicatation with
1223 // B_WINDOW_MOVED case...
1224 //printf("window position was not up to date\n");
1225 fFrame.OffsetTo(origin);
1226 FrameMoved(origin);
1227 }
1228 if (width != fFrame.Width() || height != fFrame.Height()) {
1229 // TODO: remove code duplicatation with
1230 // B_WINDOW_RESIZED case...
1231 //printf("window size was not up to date\n");
1232 fFrame.right = fFrame.left + width;
1233 fFrame.bottom = fFrame.top + height;
1234
1235 _AdoptResize();
1236 FrameResized(width, height);
1237 }
1238
1239 // draw
1240 int32 count = infos.CountItems();
1241 for (int32 i = 0; i < count; i++) {
1242 //bigtime_t drawStart = system_time();
1243 ViewUpdateInfo* info
1244 = (ViewUpdateInfo*)infos.ItemAtFast(i);
1245 if (BView* view = _FindView(info->token))
1246 view->_Draw(info->updateRect);
1247 else {
1248 printf("_UPDATE_ - didn't find view by token: %"
1249 B_PRId32 "\n", info->token);
1250 }
1251 //drawTime += system_time() - drawStart;
1252 }
1253 // NOTE: The tokens are actually hirachically sorted,
1254 // so traversing the list in revers and calling
1255 // child->_DrawAfterChildren() actually works like intended.
1256 for (int32 i = count - 1; i >= 0; i--) {
1257 ViewUpdateInfo* info
1258 = (ViewUpdateInfo*)infos.ItemAtFast(i);
1259 if (BView* view = _FindView(info->token))
1260 view->_DrawAfterChildren(info->updateRect);
1261 delete info;
1262 }
1263
1264 //printf(" %ld views drawn, total Draw() time: %lld\n", count, drawTime);
1265 }
1266
1267 fLink->StartMessage(AS_END_UPDATE);
1268 fLink->Flush();
1269 fInTransaction = false;
1270 fUpdateRequested = false;
1271
1272 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now);
1273 break;
1274 }
1275
1276 case _MENUS_DONE_:
1277 MenusEnded();
1278 break;
1279
1280 // These two are obviously some kind of old scripting messages
1281 // this is NOT an app_server message and we have to be cautious
1282 case B_WINDOW_MOVE_BY:
1283 {
1284 BPoint offset;
1285 if (message->FindPoint("data", &offset) == B_OK)
1286 MoveBy(offset.x, offset.y);
1287 else
1288 message->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1289 break;
1290 }
1291
1292 // this is NOT an app_server message and we have to be cautious
1293 case B_WINDOW_MOVE_TO:
1294 {
1295 BPoint origin;
1296 if (message->FindPoint("data", &origin) == B_OK)
1297 MoveTo(origin);
1298 else
1299 message->SendReply(B_MESSAGE_NOT_UNDERSTOOD);
1300 break;
1301 }
1302
1303 case B_LAYOUT_WINDOW:
1304 {
1305 Layout(false);
1306 break;
1307 }
1308
1309 case B_COLORS_UPDATED:
1310 {
1311 fTopView->_ColorsUpdated(message);
1312 target->MessageReceived(message);
1313 break;
1314 }
1315
1316 case B_FONTS_UPDATED:
1317 {
1318 fTopView->_FontsUpdated(message);
1319 target->MessageReceived(message);
1320 break;
1321 }
1322
1323 default:
1324 BLooper::DispatchMessage(message, target);
1325 break;
1326 }
1327 }
1328
1329
1330 void
FrameMoved(BPoint newPosition)1331 BWindow::FrameMoved(BPoint newPosition)
1332 {
1333 // does nothing
1334 // Hook function
1335 }
1336
1337
1338 void
FrameResized(float newWidth,float newHeight)1339 BWindow::FrameResized(float newWidth, float newHeight)
1340 {
1341 // does nothing
1342 // Hook function
1343 }
1344
1345
1346 void
WorkspacesChanged(uint32 oldWorkspaces,uint32 newWorkspaces)1347 BWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1348 {
1349 // does nothing
1350 // Hook function
1351 }
1352
1353
1354 void
WorkspaceActivated(int32 workspace,bool state)1355 BWindow::WorkspaceActivated(int32 workspace, bool state)
1356 {
1357 // does nothing
1358 // Hook function
1359 }
1360
1361
1362 void
MenusBeginning()1363 BWindow::MenusBeginning()
1364 {
1365 // does nothing
1366 // Hook function
1367 }
1368
1369
1370 void
MenusEnded()1371 BWindow::MenusEnded()
1372 {
1373 // does nothing
1374 // Hook function
1375 }
1376
1377
1378 void
SetSizeLimits(float minWidth,float maxWidth,float minHeight,float maxHeight)1379 BWindow::SetSizeLimits(float minWidth, float maxWidth,
1380 float minHeight, float maxHeight)
1381 {
1382 if (minWidth > maxWidth || minHeight > maxHeight)
1383 return;
1384
1385 if (!Lock())
1386 return;
1387
1388 fLink->StartMessage(AS_SET_SIZE_LIMITS);
1389 fLink->Attach<float>(minWidth);
1390 fLink->Attach<float>(maxWidth);
1391 fLink->Attach<float>(minHeight);
1392 fLink->Attach<float>(maxHeight);
1393
1394 int32 code;
1395 if (fLink->FlushWithReply(code) == B_OK
1396 && code == B_OK) {
1397 // read the values that were really enforced on
1398 // the server side (the window frame could have
1399 // been changed, too)
1400 fLink->Read<BRect>(&fFrame);
1401 fLink->Read<float>(&fMinWidth);
1402 fLink->Read<float>(&fMaxWidth);
1403 fLink->Read<float>(&fMinHeight);
1404 fLink->Read<float>(&fMaxHeight);
1405
1406 _AdoptResize();
1407 // TODO: the same has to be done for SetLook() (that can alter
1408 // the size limits, and hence, the size of the window
1409 }
1410 Unlock();
1411 }
1412
1413
1414 void
GetSizeLimits(float * _minWidth,float * _maxWidth,float * _minHeight,float * _maxHeight)1415 BWindow::GetSizeLimits(float* _minWidth, float* _maxWidth, float* _minHeight,
1416 float* _maxHeight)
1417 {
1418 // TODO: What about locking?!?
1419 if (_minHeight != NULL)
1420 *_minHeight = fMinHeight;
1421 if (_minWidth != NULL)
1422 *_minWidth = fMinWidth;
1423 if (_maxHeight != NULL)
1424 *_maxHeight = fMaxHeight;
1425 if (_maxWidth != NULL)
1426 *_maxWidth = fMaxWidth;
1427 }
1428
1429
1430 void
UpdateSizeLimits()1431 BWindow::UpdateSizeLimits()
1432 {
1433 BAutolock locker(this);
1434
1435 if ((fFlags & B_AUTO_UPDATE_SIZE_LIMITS) != 0) {
1436 // Get min/max constraints of the top view and enforce window
1437 // size limits respectively.
1438 BSize minSize = fTopView->MinSize();
1439 BSize maxSize = fTopView->MaxSize();
1440 SetSizeLimits(minSize.width, maxSize.width,
1441 minSize.height, maxSize.height);
1442 }
1443 }
1444
1445
1446 status_t
SetDecoratorSettings(const BMessage & settings)1447 BWindow::SetDecoratorSettings(const BMessage& settings)
1448 {
1449 // flatten the given settings into a buffer and send
1450 // it to the app_server to apply the settings to the
1451 // decorator
1452
1453 int32 size = settings.FlattenedSize();
1454 char buffer[size];
1455 status_t status = settings.Flatten(buffer, size);
1456 if (status != B_OK)
1457 return status;
1458
1459 if (!Lock())
1460 return B_ERROR;
1461
1462 status = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS);
1463
1464 if (status == B_OK)
1465 status = fLink->Attach<int32>(size);
1466
1467 if (status == B_OK)
1468 status = fLink->Attach(buffer, size);
1469
1470 if (status == B_OK)
1471 status = fLink->Flush();
1472
1473 Unlock();
1474
1475 return status;
1476 }
1477
1478
1479 status_t
GetDecoratorSettings(BMessage * settings) const1480 BWindow::GetDecoratorSettings(BMessage* settings) const
1481 {
1482 // read a flattened settings message from the app_server
1483 // and put it into settings
1484
1485 if (!const_cast<BWindow*>(this)->Lock())
1486 return B_ERROR;
1487
1488 status_t status = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS);
1489
1490 if (status == B_OK) {
1491 int32 code;
1492 status = fLink->FlushWithReply(code);
1493 if (status == B_OK && code != B_OK)
1494 status = code;
1495 }
1496
1497 if (status == B_OK) {
1498 int32 size;
1499 status = fLink->Read<int32>(&size);
1500 if (status == B_OK) {
1501 char buffer[size];
1502 status = fLink->Read(buffer, size);
1503 if (status == B_OK) {
1504 status = settings->Unflatten(buffer);
1505 }
1506 }
1507 }
1508
1509 const_cast<BWindow*>(this)->Unlock();
1510
1511 return status;
1512 }
1513
1514
1515 void
SetZoomLimits(float maxWidth,float maxHeight)1516 BWindow::SetZoomLimits(float maxWidth, float maxHeight)
1517 {
1518 // TODO: What about locking?!?
1519 if (maxWidth > fMaxWidth)
1520 maxWidth = fMaxWidth;
1521 fMaxZoomWidth = maxWidth;
1522
1523 if (maxHeight > fMaxHeight)
1524 maxHeight = fMaxHeight;
1525 fMaxZoomHeight = maxHeight;
1526 }
1527
1528
1529 void
Zoom(BPoint origin,float width,float height)1530 BWindow::Zoom(BPoint origin, float width, float height)
1531 {
1532 // the default implementation of this hook function
1533 // just does the obvious:
1534 MoveTo(origin);
1535 ResizeTo(width, height);
1536 }
1537
1538
1539 void
Zoom()1540 BWindow::Zoom()
1541 {
1542 // TODO: What about locking?!?
1543
1544 // From BeBook:
1545 // The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced
1546 // from the smallest of three rectangles:
1547
1548 // 1) the rectangle defined by SetZoomLimits() and,
1549 // 2) the rectangle defined by SetSizeLimits()
1550 float maxZoomWidth = std::min(fMaxZoomWidth, fMaxWidth);
1551 float maxZoomHeight = std::min(fMaxZoomHeight, fMaxHeight);
1552
1553 // 3) the screen rectangle
1554 BRect screenFrame = (BScreen(this)).Frame();
1555 maxZoomWidth = std::min(maxZoomWidth, screenFrame.Width());
1556 maxZoomHeight = std::min(maxZoomHeight, screenFrame.Height());
1557
1558 BRect zoomArea = screenFrame; // starts at screen size
1559
1560 BDeskbar deskbar;
1561 BRect deskbarFrame = deskbar.Frame();
1562 bool isShiftDown = (modifiers() & B_SHIFT_KEY) != 0;
1563 if (!isShiftDown && !deskbar.IsAutoHide()) {
1564 // remove area taken up by Deskbar unless hidden or shift is held down
1565 switch (deskbar.Location()) {
1566 case B_DESKBAR_TOP:
1567 zoomArea.top = deskbarFrame.bottom + 2;
1568 break;
1569
1570 case B_DESKBAR_BOTTOM:
1571 case B_DESKBAR_LEFT_BOTTOM:
1572 case B_DESKBAR_RIGHT_BOTTOM:
1573 zoomArea.bottom = deskbarFrame.top - 2;
1574 break;
1575
1576 // in vertical expando mode only if not always-on-top or auto-raise
1577 case B_DESKBAR_LEFT_TOP:
1578 if (!deskbar.IsExpanded())
1579 zoomArea.top = deskbarFrame.bottom + 2;
1580 else if (!deskbar.IsAlwaysOnTop() && !deskbar.IsAutoRaise())
1581 zoomArea.left = deskbarFrame.right + 2;
1582 break;
1583
1584 default:
1585 case B_DESKBAR_RIGHT_TOP:
1586 if (!deskbar.IsExpanded())
1587 break;
1588 else if (!deskbar.IsAlwaysOnTop() && !deskbar.IsAutoRaise())
1589 zoomArea.right = deskbarFrame.left - 2;
1590 break;
1591 }
1592 }
1593
1594 // TODO: Broken for tab on left side windows...
1595 float borderWidth;
1596 float tabHeight;
1597 _GetDecoratorSize(&borderWidth, &tabHeight);
1598
1599 // remove the area taken up by the tab and border
1600 zoomArea.left += borderWidth;
1601 zoomArea.top += borderWidth + tabHeight;
1602 zoomArea.right -= borderWidth;
1603 zoomArea.bottom -= borderWidth;
1604
1605 // inset towards center vertically first to see if there will be room
1606 // above or below Deskbar
1607 if (zoomArea.Height() > maxZoomHeight)
1608 zoomArea.InsetBy(0, roundf((zoomArea.Height() - maxZoomHeight) / 2));
1609
1610 if (zoomArea.top > deskbarFrame.bottom
1611 || zoomArea.bottom < deskbarFrame.top) {
1612 // there is room above or below Deskbar, start from screen width
1613 // minus borders instead of desktop width minus borders
1614 zoomArea.left = screenFrame.left + borderWidth;
1615 zoomArea.right = screenFrame.right - borderWidth;
1616 }
1617
1618 // inset towards center
1619 if (zoomArea.Width() > maxZoomWidth)
1620 zoomArea.InsetBy(roundf((zoomArea.Width() - maxZoomWidth) / 2), 0);
1621
1622 // Un-Zoom
1623
1624 if (fPreviousFrame.IsValid()
1625 // NOTE: don't check for fFrame.LeftTop() == zoomArea.LeftTop()
1626 // -> makes it easier on the user to get a window back into place
1627 && fFrame.Width() == zoomArea.Width()
1628 && fFrame.Height() == zoomArea.Height()) {
1629 // already zoomed!
1630 Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(),
1631 fPreviousFrame.Height());
1632 return;
1633 }
1634
1635 // Zoom
1636
1637 // remember fFrame for later "unzooming"
1638 fPreviousFrame = fFrame;
1639
1640 Zoom(zoomArea.LeftTop(), zoomArea.Width(), zoomArea.Height());
1641 }
1642
1643
1644 void
ScreenChanged(BRect screenSize,color_space depth)1645 BWindow::ScreenChanged(BRect screenSize, color_space depth)
1646 {
1647 // Hook function
1648 }
1649
1650
1651 void
SetPulseRate(bigtime_t rate)1652 BWindow::SetPulseRate(bigtime_t rate)
1653 {
1654 // TODO: What about locking?!?
1655 if (rate < 0
1656 || (rate == fPulseRate && !((rate == 0) ^ (fPulseRunner == NULL))))
1657 return;
1658
1659 fPulseRate = rate;
1660
1661 if (rate > 0) {
1662 if (fPulseRunner == NULL) {
1663 BMessage message(B_PULSE);
1664 fPulseRunner = new(std::nothrow) BMessageRunner(BMessenger(this),
1665 &message, rate);
1666 } else {
1667 fPulseRunner->SetInterval(rate);
1668 }
1669 } else {
1670 // rate == 0
1671 delete fPulseRunner;
1672 fPulseRunner = NULL;
1673 }
1674 }
1675
1676
1677 bigtime_t
PulseRate() const1678 BWindow::PulseRate() const
1679 {
1680 return fPulseRate;
1681 }
1682
1683
1684 void
AddShortcut(uint32 key,uint32 modifiers,BMenuItem * item)1685 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem* item)
1686 {
1687 Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, item);
1688 if (shortcut == NULL)
1689 return;
1690
1691 // removes the shortcut if it already exists!
1692 RemoveShortcut(key, modifiers);
1693
1694 fShortcuts.AddItem(shortcut);
1695 }
1696
1697
1698 void
AddShortcut(uint32 key,uint32 modifiers,BMessage * message)1699 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message)
1700 {
1701 AddShortcut(key, modifiers, message, this);
1702 }
1703
1704
1705 void
AddShortcut(uint32 key,uint32 modifiers,BMessage * message,BHandler * target)1706 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message,
1707 BHandler* target)
1708 {
1709 if (message == NULL)
1710 return;
1711
1712 Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, message,
1713 target);
1714 if (shortcut == NULL)
1715 return;
1716
1717 // removes the shortcut if it already exists!
1718 RemoveShortcut(key, modifiers);
1719
1720 fShortcuts.AddItem(shortcut);
1721 }
1722
1723
1724 bool
HasShortcut(uint32 key,uint32 modifiers)1725 BWindow::HasShortcut(uint32 key, uint32 modifiers)
1726 {
1727 return _FindShortcut(key, modifiers) != NULL;
1728 }
1729
1730
1731 void
RemoveShortcut(uint32 key,uint32 modifiers)1732 BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
1733 {
1734 Shortcut* shortcut = _FindShortcut(key, modifiers);
1735 if (shortcut != NULL) {
1736 fShortcuts.RemoveItem(shortcut);
1737 delete shortcut;
1738 } else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) {
1739 // the quit shortcut is a fake shortcut
1740 fNoQuitShortcut = true;
1741 }
1742 }
1743
1744
1745 BButton*
DefaultButton() const1746 BWindow::DefaultButton() const
1747 {
1748 // TODO: What about locking?!?
1749 return fDefaultButton;
1750 }
1751
1752
1753 void
SetDefaultButton(BButton * button)1754 BWindow::SetDefaultButton(BButton* button)
1755 {
1756 // TODO: What about locking?!?
1757 if (fDefaultButton == button)
1758 return;
1759
1760 if (fDefaultButton != NULL) {
1761 // tell old button it's no longer the default one
1762 BButton* oldDefault = fDefaultButton;
1763 oldDefault->MakeDefault(false);
1764 oldDefault->Invalidate();
1765 }
1766
1767 fDefaultButton = button;
1768
1769 if (button != NULL) {
1770 // notify new default button
1771 fDefaultButton->MakeDefault(true);
1772 fDefaultButton->Invalidate();
1773 }
1774 }
1775
1776
1777 bool
NeedsUpdate() const1778 BWindow::NeedsUpdate() const
1779 {
1780 if (!const_cast<BWindow*>(this)->Lock())
1781 return false;
1782
1783 fLink->StartMessage(AS_NEEDS_UPDATE);
1784
1785 int32 code = B_ERROR;
1786 fLink->FlushWithReply(code);
1787
1788 const_cast<BWindow*>(this)->Unlock();
1789
1790 return code == B_OK;
1791 }
1792
1793
1794 void
UpdateIfNeeded()1795 BWindow::UpdateIfNeeded()
1796 {
1797 // works only from the window thread
1798 if (find_thread(NULL) != Thread())
1799 return;
1800
1801 // if the queue is already locked we are called recursivly
1802 // from our own dispatched update message
1803 if (((const BMessageQueue*)MessageQueue())->IsLocked())
1804 return;
1805
1806 if (!Lock())
1807 return;
1808
1809 // make sure all requests that would cause an update have
1810 // arrived at the server
1811 Sync();
1812
1813 // Since we're blocking the event loop, we need to retrieve
1814 // all messages that are pending on the port.
1815 _DequeueAll();
1816
1817 BMessageQueue* queue = MessageQueue();
1818
1819 // First process and remove any _UPDATE_ message in the queue
1820 // With the current design, there can only be one at a time
1821
1822 while (true) {
1823 queue->Lock();
1824
1825 BMessage* message = queue->FindMessage(_UPDATE_, 0);
1826 queue->RemoveMessage(message);
1827
1828 queue->Unlock();
1829
1830 if (message == NULL)
1831 break;
1832
1833 BWindow::DispatchMessage(message, this);
1834 delete message;
1835 }
1836
1837 Unlock();
1838 }
1839
1840
1841 BView*
FindView(const char * viewName) const1842 BWindow::FindView(const char* viewName) const
1843 {
1844 BAutolock locker(const_cast<BWindow*>(this));
1845 if (!locker.IsLocked())
1846 return NULL;
1847
1848 return fTopView->FindView(viewName);
1849 }
1850
1851
1852 BView*
FindView(BPoint point) const1853 BWindow::FindView(BPoint point) const
1854 {
1855 BAutolock locker(const_cast<BWindow*>(this));
1856 if (!locker.IsLocked())
1857 return NULL;
1858
1859 // point is assumed to be in window coordinates,
1860 // fTopView has same bounds as window
1861 return _FindView(fTopView, point);
1862 }
1863
1864
1865 BView*
CurrentFocus() const1866 BWindow::CurrentFocus() const
1867 {
1868 return fFocus;
1869 }
1870
1871
1872 void
Activate(bool active)1873 BWindow::Activate(bool active)
1874 {
1875 if (!Lock())
1876 return;
1877
1878 if (!IsHidden()) {
1879 fMinimized = false;
1880 // activating a window will also unminimize it
1881
1882 fLink->StartMessage(AS_ACTIVATE_WINDOW);
1883 fLink->Attach<bool>(active);
1884 fLink->Flush();
1885 }
1886
1887 Unlock();
1888 }
1889
1890
1891 void
WindowActivated(bool focus)1892 BWindow::WindowActivated(bool focus)
1893 {
1894 // hook function
1895 // does nothing
1896 }
1897
1898
1899 void
ConvertToScreen(BPoint * point) const1900 BWindow::ConvertToScreen(BPoint* point) const
1901 {
1902 point->x += fFrame.left;
1903 point->y += fFrame.top;
1904 }
1905
1906
1907 BPoint
ConvertToScreen(BPoint point) const1908 BWindow::ConvertToScreen(BPoint point) const
1909 {
1910 return point + fFrame.LeftTop();
1911 }
1912
1913
1914 void
ConvertFromScreen(BPoint * point) const1915 BWindow::ConvertFromScreen(BPoint* point) const
1916 {
1917 point->x -= fFrame.left;
1918 point->y -= fFrame.top;
1919 }
1920
1921
1922 BPoint
ConvertFromScreen(BPoint point) const1923 BWindow::ConvertFromScreen(BPoint point) const
1924 {
1925 return point - fFrame.LeftTop();
1926 }
1927
1928
1929 void
ConvertToScreen(BRect * rect) const1930 BWindow::ConvertToScreen(BRect* rect) const
1931 {
1932 rect->OffsetBy(fFrame.LeftTop());
1933 }
1934
1935
1936 BRect
ConvertToScreen(BRect rect) const1937 BWindow::ConvertToScreen(BRect rect) const
1938 {
1939 return rect.OffsetByCopy(fFrame.LeftTop());
1940 }
1941
1942
1943 void
ConvertFromScreen(BRect * rect) const1944 BWindow::ConvertFromScreen(BRect* rect) const
1945 {
1946 rect->OffsetBy(-fFrame.left, -fFrame.top);
1947 }
1948
1949
1950 BRect
ConvertFromScreen(BRect rect) const1951 BWindow::ConvertFromScreen(BRect rect) const
1952 {
1953 return rect.OffsetByCopy(-fFrame.left, -fFrame.top);
1954 }
1955
1956
1957 bool
IsMinimized() const1958 BWindow::IsMinimized() const
1959 {
1960 BAutolock locker(const_cast<BWindow*>(this));
1961 if (!locker.IsLocked())
1962 return false;
1963
1964 return fMinimized;
1965 }
1966
1967
1968 BRect
Bounds() const1969 BWindow::Bounds() const
1970 {
1971 return BRect(0, 0, fFrame.Width(), fFrame.Height());
1972 }
1973
1974
1975 BRect
Frame() const1976 BWindow::Frame() const
1977 {
1978 return fFrame;
1979 }
1980
1981
1982 BRect
DecoratorFrame() const1983 BWindow::DecoratorFrame() const
1984 {
1985 BRect decoratorFrame(Frame());
1986 BRect tabRect(0, 0, 0, 0);
1987
1988 float borderWidth = 5.0;
1989
1990 BMessage settings;
1991 if (GetDecoratorSettings(&settings) == B_OK) {
1992 settings.FindRect("tab frame", &tabRect);
1993 settings.FindFloat("border width", &borderWidth);
1994 } else {
1995 // probably no-border window look
1996 if (fLook == B_NO_BORDER_WINDOW_LOOK)
1997 borderWidth = 0.f;
1998 else if (fLook == B_BORDERED_WINDOW_LOOK)
1999 borderWidth = 1.f;
2000 // else use fall-back values from above
2001 }
2002
2003 if (fLook == kLeftTitledWindowLook) {
2004 decoratorFrame.top -= borderWidth;
2005 decoratorFrame.left -= borderWidth + tabRect.Width();
2006 decoratorFrame.right += borderWidth;
2007 decoratorFrame.bottom += borderWidth;
2008 } else {
2009 decoratorFrame.top -= borderWidth + tabRect.Height();
2010 decoratorFrame.left -= borderWidth;
2011 decoratorFrame.right += borderWidth;
2012 decoratorFrame.bottom += borderWidth;
2013 }
2014
2015 return decoratorFrame;
2016 }
2017
2018
2019 BSize
Size() const2020 BWindow::Size() const
2021 {
2022 return BSize(fFrame.Width(), fFrame.Height());
2023 }
2024
2025
2026 const char*
Title() const2027 BWindow::Title() const
2028 {
2029 return fTitle;
2030 }
2031
2032
2033 void
SetTitle(const char * title)2034 BWindow::SetTitle(const char* title)
2035 {
2036 if (title == NULL)
2037 title = "";
2038
2039 free(fTitle);
2040 fTitle = strdup(title);
2041
2042 _SetName(title);
2043
2044 // we notify the app_server so we can actually see the change
2045 if (Lock()) {
2046 fLink->StartMessage(AS_SET_WINDOW_TITLE);
2047 fLink->AttachString(fTitle);
2048 fLink->Flush();
2049 Unlock();
2050 }
2051 }
2052
2053
2054 bool
IsActive() const2055 BWindow::IsActive() const
2056 {
2057 return fActive;
2058 }
2059
2060
2061 void
SetKeyMenuBar(BMenuBar * bar)2062 BWindow::SetKeyMenuBar(BMenuBar* bar)
2063 {
2064 fKeyMenuBar = bar;
2065 }
2066
2067
2068 BMenuBar*
KeyMenuBar() const2069 BWindow::KeyMenuBar() const
2070 {
2071 return fKeyMenuBar;
2072 }
2073
2074
2075 bool
IsModal() const2076 BWindow::IsModal() const
2077 {
2078 return fFeel == B_MODAL_SUBSET_WINDOW_FEEL
2079 || fFeel == B_MODAL_APP_WINDOW_FEEL
2080 || fFeel == B_MODAL_ALL_WINDOW_FEEL
2081 || fFeel == kMenuWindowFeel;
2082 }
2083
2084
2085 bool
IsFloating() const2086 BWindow::IsFloating() const
2087 {
2088 return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL
2089 || fFeel == B_FLOATING_APP_WINDOW_FEEL
2090 || fFeel == B_FLOATING_ALL_WINDOW_FEEL;
2091 }
2092
2093
2094 status_t
AddToSubset(BWindow * window)2095 BWindow::AddToSubset(BWindow* window)
2096 {
2097 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
2098 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
2099 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
2100 return B_BAD_VALUE;
2101
2102 if (!Lock())
2103 return B_ERROR;
2104
2105 status_t status = B_ERROR;
2106 fLink->StartMessage(AS_ADD_TO_SUBSET);
2107 fLink->Attach<int32>(_get_object_token_(window));
2108 fLink->FlushWithReply(status);
2109
2110 Unlock();
2111
2112 return status;
2113 }
2114
2115
2116 status_t
RemoveFromSubset(BWindow * window)2117 BWindow::RemoveFromSubset(BWindow* window)
2118 {
2119 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL
2120 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL
2121 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL))
2122 return B_BAD_VALUE;
2123
2124 if (!Lock())
2125 return B_ERROR;
2126
2127 status_t status = B_ERROR;
2128 fLink->StartMessage(AS_REMOVE_FROM_SUBSET);
2129 fLink->Attach<int32>(_get_object_token_(window));
2130 fLink->FlushWithReply(status);
2131
2132 Unlock();
2133
2134 return status;
2135 }
2136
2137
2138 status_t
Perform(perform_code code,void * _data)2139 BWindow::Perform(perform_code code, void* _data)
2140 {
2141 switch (code) {
2142 case PERFORM_CODE_SET_LAYOUT:
2143 {
2144 perform_data_set_layout* data = (perform_data_set_layout*)_data;
2145 BWindow::SetLayout(data->layout);
2146 return B_OK;
2147 }
2148 }
2149
2150 return BLooper::Perform(code, _data);
2151 }
2152
2153
2154 status_t
SetType(window_type type)2155 BWindow::SetType(window_type type)
2156 {
2157 window_look look;
2158 window_feel feel;
2159 _DecomposeType(type, &look, &feel);
2160
2161 status_t status = SetLook(look);
2162 if (status == B_OK)
2163 status = SetFeel(feel);
2164
2165 return status;
2166 }
2167
2168
2169 window_type
Type() const2170 BWindow::Type() const
2171 {
2172 return _ComposeType(fLook, fFeel);
2173 }
2174
2175
2176 status_t
SetLook(window_look look)2177 BWindow::SetLook(window_look look)
2178 {
2179 BAutolock locker(this);
2180 if (!locker.IsLocked())
2181 return B_BAD_VALUE;
2182
2183 fLink->StartMessage(AS_SET_LOOK);
2184 fLink->Attach<int32>((int32)look);
2185
2186 status_t status = B_ERROR;
2187 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2188 fLook = look;
2189
2190 // TODO: this could have changed the window size, and thus, we
2191 // need to get it from the server (and call _AdoptResize()).
2192
2193 return status;
2194 }
2195
2196
2197 window_look
Look() const2198 BWindow::Look() const
2199 {
2200 return fLook;
2201 }
2202
2203
2204 status_t
SetFeel(window_feel feel)2205 BWindow::SetFeel(window_feel feel)
2206 {
2207 BAutolock locker(this);
2208 if (!locker.IsLocked())
2209 return B_BAD_VALUE;
2210
2211 fLink->StartMessage(AS_SET_FEEL);
2212 fLink->Attach<int32>((int32)feel);
2213
2214 status_t status = B_ERROR;
2215 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2216 fFeel = feel;
2217
2218 return status;
2219 }
2220
2221
2222 window_feel
Feel() const2223 BWindow::Feel() const
2224 {
2225 return fFeel;
2226 }
2227
2228
2229 status_t
SetFlags(uint32 flags)2230 BWindow::SetFlags(uint32 flags)
2231 {
2232 BAutolock locker(this);
2233 if (!locker.IsLocked())
2234 return B_BAD_VALUE;
2235
2236 fLink->StartMessage(AS_SET_FLAGS);
2237 fLink->Attach<uint32>(flags);
2238
2239 int32 status = B_ERROR;
2240 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2241 fFlags = flags;
2242
2243 return status;
2244 }
2245
2246
2247 uint32
Flags() const2248 BWindow::Flags() const
2249 {
2250 return fFlags;
2251 }
2252
2253
2254 status_t
SetWindowAlignment(window_alignment mode,int32 h,int32 hOffset,int32 width,int32 widthOffset,int32 v,int32 vOffset,int32 height,int32 heightOffset)2255 BWindow::SetWindowAlignment(window_alignment mode,
2256 int32 h, int32 hOffset, int32 width, int32 widthOffset,
2257 int32 v, int32 vOffset, int32 height, int32 heightOffset)
2258 {
2259 if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0
2260 || (hOffset >= 0 && hOffset <= h)
2261 || (vOffset >= 0 && vOffset <= v)
2262 || (widthOffset >= 0 && widthOffset <= width)
2263 || (heightOffset >= 0 && heightOffset <= height))
2264 return B_BAD_VALUE;
2265
2266 // TODO: test if hOffset = 0 and set it to 1 if true.
2267
2268 if (!Lock())
2269 return B_ERROR;
2270
2271 fLink->StartMessage(AS_SET_ALIGNMENT);
2272 fLink->Attach<int32>((int32)mode);
2273 fLink->Attach<int32>(h);
2274 fLink->Attach<int32>(hOffset);
2275 fLink->Attach<int32>(width);
2276 fLink->Attach<int32>(widthOffset);
2277 fLink->Attach<int32>(v);
2278 fLink->Attach<int32>(vOffset);
2279 fLink->Attach<int32>(height);
2280 fLink->Attach<int32>(heightOffset);
2281
2282 status_t status = B_ERROR;
2283 fLink->FlushWithReply(status);
2284
2285 Unlock();
2286
2287 return status;
2288 }
2289
2290
2291 status_t
GetWindowAlignment(window_alignment * mode,int32 * h,int32 * hOffset,int32 * width,int32 * widthOffset,int32 * v,int32 * vOffset,int32 * height,int32 * heightOffset) const2292 BWindow::GetWindowAlignment(window_alignment* mode,
2293 int32* h, int32* hOffset, int32* width, int32* widthOffset,
2294 int32* v, int32* vOffset, int32* height, int32* heightOffset) const
2295 {
2296 if (!const_cast<BWindow*>(this)->Lock())
2297 return B_ERROR;
2298
2299 fLink->StartMessage(AS_GET_ALIGNMENT);
2300
2301 status_t status;
2302 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2303 fLink->Read<int32>((int32*)mode);
2304 fLink->Read<int32>(h);
2305 fLink->Read<int32>(hOffset);
2306 fLink->Read<int32>(width);
2307 fLink->Read<int32>(widthOffset);
2308 fLink->Read<int32>(v);
2309 fLink->Read<int32>(hOffset);
2310 fLink->Read<int32>(height);
2311 fLink->Read<int32>(heightOffset);
2312 }
2313
2314 const_cast<BWindow*>(this)->Unlock();
2315 return status;
2316 }
2317
2318
2319 uint32
Workspaces() const2320 BWindow::Workspaces() const
2321 {
2322 if (!const_cast<BWindow*>(this)->Lock())
2323 return 0;
2324
2325 uint32 workspaces = 0;
2326
2327 fLink->StartMessage(AS_GET_WORKSPACES);
2328
2329 status_t status;
2330 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2331 fLink->Read<uint32>(&workspaces);
2332
2333 const_cast<BWindow*>(this)->Unlock();
2334 return workspaces;
2335 }
2336
2337
2338 void
SetWorkspaces(uint32 workspaces)2339 BWindow::SetWorkspaces(uint32 workspaces)
2340 {
2341 // TODO: don't forget about Tracker's background window.
2342 if (fFeel != B_NORMAL_WINDOW_FEEL)
2343 return;
2344
2345 if (Lock()) {
2346 fLink->StartMessage(AS_SET_WORKSPACES);
2347 fLink->Attach<uint32>(workspaces);
2348 fLink->Flush();
2349 Unlock();
2350 }
2351 }
2352
2353
2354 BView*
LastMouseMovedView() const2355 BWindow::LastMouseMovedView() const
2356 {
2357 return fLastMouseMovedView;
2358 }
2359
2360
2361 void
MoveBy(float dx,float dy)2362 BWindow::MoveBy(float dx, float dy)
2363 {
2364 if ((dx != 0.0f || dy != 0.0f) && Lock()) {
2365 MoveTo(fFrame.left + dx, fFrame.top + dy);
2366 Unlock();
2367 }
2368 }
2369
2370
2371 void
MoveTo(BPoint point)2372 BWindow::MoveTo(BPoint point)
2373 {
2374 MoveTo(point.x, point.y);
2375 }
2376
2377
2378 void
MoveTo(float x,float y)2379 BWindow::MoveTo(float x, float y)
2380 {
2381 if (!Lock())
2382 return;
2383
2384 x = roundf(x);
2385 y = roundf(y);
2386
2387 if (fFrame.left != x || fFrame.top != y) {
2388 fLink->StartMessage(AS_WINDOW_MOVE);
2389 fLink->Attach<float>(x);
2390 fLink->Attach<float>(y);
2391
2392 status_t status;
2393 if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
2394 fFrame.OffsetTo(x, y);
2395 }
2396
2397 Unlock();
2398 }
2399
2400
2401 void
ResizeBy(float dx,float dy)2402 BWindow::ResizeBy(float dx, float dy)
2403 {
2404 if (Lock()) {
2405 ResizeTo(fFrame.Width() + dx, fFrame.Height() + dy);
2406 Unlock();
2407 }
2408 }
2409
2410
2411 void
ResizeTo(float width,float height)2412 BWindow::ResizeTo(float width, float height)
2413 {
2414 if (!Lock())
2415 return;
2416
2417 width = roundf(width);
2418 height = roundf(height);
2419
2420 // stay in minimum & maximum frame limits
2421 if (width < fMinWidth)
2422 width = fMinWidth;
2423 else if (width > fMaxWidth)
2424 width = fMaxWidth;
2425
2426 if (height < fMinHeight)
2427 height = fMinHeight;
2428 else if (height > fMaxHeight)
2429 height = fMaxHeight;
2430
2431 if (width != fFrame.Width() || height != fFrame.Height()) {
2432 fLink->StartMessage(AS_WINDOW_RESIZE);
2433 fLink->Attach<float>(width);
2434 fLink->Attach<float>(height);
2435
2436 status_t status;
2437 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) {
2438 fFrame.right = fFrame.left + width;
2439 fFrame.bottom = fFrame.top + height;
2440 _AdoptResize();
2441 }
2442 }
2443
2444 Unlock();
2445 }
2446
2447
2448 void
ResizeToPreferred()2449 BWindow::ResizeToPreferred()
2450 {
2451 BAutolock locker(this);
2452 Layout(false);
2453
2454 float width = fTopView->PreferredSize().width;
2455 width = std::min(width, fTopView->MaxSize().width);
2456 width = std::max(width, fTopView->MinSize().width);
2457
2458 float height = fTopView->PreferredSize().height;
2459 height = std::min(height, fTopView->MaxSize().height);
2460 height = std::max(height, fTopView->MinSize().height);
2461
2462 if (GetLayout()->HasHeightForWidth())
2463 GetLayout()->GetHeightForWidth(width, NULL, NULL, &height);
2464
2465 ResizeTo(width, height);
2466 }
2467
2468
2469 void
CenterIn(const BRect & rect)2470 BWindow::CenterIn(const BRect& rect)
2471 {
2472 BAutolock locker(this);
2473
2474 // Set size limits now if needed
2475 UpdateSizeLimits();
2476
2477 MoveTo(BLayoutUtils::AlignInFrame(rect, Size(),
2478 BAlignment(B_ALIGN_HORIZONTAL_CENTER,
2479 B_ALIGN_VERTICAL_CENTER)).LeftTop());
2480 MoveOnScreen(B_DO_NOT_RESIZE_TO_FIT | B_MOVE_IF_PARTIALLY_OFFSCREEN);
2481 }
2482
2483
2484 void
CenterOnScreen()2485 BWindow::CenterOnScreen()
2486 {
2487 CenterIn(BScreen(this).Frame());
2488 }
2489
2490
2491 // Centers the window on the screen with the passed in id.
2492 void
CenterOnScreen(screen_id id)2493 BWindow::CenterOnScreen(screen_id id)
2494 {
2495 CenterIn(BScreen(id).Frame());
2496 }
2497
2498
2499 void
MoveOnScreen(uint32 flags)2500 BWindow::MoveOnScreen(uint32 flags)
2501 {
2502 // Set size limits now if needed
2503 UpdateSizeLimits();
2504
2505 BRect screenFrame = BScreen(this).Frame();
2506 BRect frame = Frame();
2507
2508 float borderWidth;
2509 float tabHeight;
2510 _GetDecoratorSize(&borderWidth, &tabHeight);
2511
2512 frame.InsetBy(-borderWidth, -borderWidth);
2513 frame.top -= tabHeight;
2514
2515 if ((flags & B_DO_NOT_RESIZE_TO_FIT) == 0) {
2516 // Make sure the window fits on the screen
2517 if (frame.Width() > screenFrame.Width())
2518 frame.right -= frame.Width() - screenFrame.Width();
2519 if (frame.Height() > screenFrame.Height())
2520 frame.bottom -= frame.Height() - screenFrame.Height();
2521
2522 BRect innerFrame = frame;
2523 innerFrame.top += tabHeight;
2524 innerFrame.InsetBy(borderWidth, borderWidth);
2525 ResizeTo(innerFrame.Width(), innerFrame.Height());
2526 }
2527
2528 if (((flags & B_MOVE_IF_PARTIALLY_OFFSCREEN) == 0
2529 && !screenFrame.Contains(frame))
2530 || !frame.Intersects(screenFrame)) {
2531 // Off and away
2532 CenterOnScreen();
2533 return;
2534 }
2535
2536 // Move such that the upper left corner, and most of the window
2537 // will be visible.
2538 float left = frame.left;
2539 if (left < screenFrame.left)
2540 left = screenFrame.left;
2541 else if (frame.right > screenFrame.right)
2542 left = std::max(0.f, screenFrame.right - frame.Width());
2543
2544 float top = frame.top;
2545 if (top < screenFrame.top)
2546 top = screenFrame.top;
2547 else if (frame.bottom > screenFrame.bottom)
2548 top = std::max(0.f, screenFrame.bottom - frame.Height());
2549
2550 if (top != frame.top || left != frame.left)
2551 MoveTo(left + borderWidth, top + tabHeight + borderWidth);
2552 }
2553
2554
2555 void
Show()2556 BWindow::Show()
2557 {
2558 bool runCalled = true;
2559 if (Lock()) {
2560 fShowLevel--;
2561
2562 _SendShowOrHideMessage();
2563
2564 runCalled = fRunCalled;
2565
2566 Unlock();
2567 }
2568
2569 if (!runCalled) {
2570 // This is the fist time Show() is called, which implicitly runs the
2571 // looper. NOTE: The window is still locked if it has not been
2572 // run yet, so accessing members is safe.
2573 if (fLink->SenderPort() < B_OK) {
2574 // We don't have valid app_server connection; there is no point
2575 // in starting our looper
2576 fThread = B_ERROR;
2577 return;
2578 } else
2579 Run();
2580 }
2581 }
2582
2583
2584 void
Hide()2585 BWindow::Hide()
2586 {
2587 if (Lock()) {
2588 // If we are minimized and are about to be hidden, unminimize
2589 if (IsMinimized() && fShowLevel == 0)
2590 Minimize(false);
2591
2592 fShowLevel++;
2593
2594 _SendShowOrHideMessage();
2595
2596 Unlock();
2597 }
2598 }
2599
2600
2601 bool
IsHidden() const2602 BWindow::IsHidden() const
2603 {
2604 return fShowLevel > 0;
2605 }
2606
2607
2608 bool
QuitRequested()2609 BWindow::QuitRequested()
2610 {
2611 return BLooper::QuitRequested();
2612 }
2613
2614
2615 thread_id
Run()2616 BWindow::Run()
2617 {
2618 EnableUpdates();
2619 return BLooper::Run();
2620 }
2621
2622
2623 void
SetLayout(BLayout * layout)2624 BWindow::SetLayout(BLayout* layout)
2625 {
2626 // Adopt layout's colors for fTopView
2627 if (layout != NULL)
2628 fTopView->AdoptViewColors(layout->View());
2629
2630 fTopView->SetLayout(layout);
2631 }
2632
2633
2634 BLayout*
GetLayout() const2635 BWindow::GetLayout() const
2636 {
2637 return fTopView->GetLayout();
2638 }
2639
2640
2641 void
InvalidateLayout(bool descendants)2642 BWindow::InvalidateLayout(bool descendants)
2643 {
2644 fTopView->InvalidateLayout(descendants);
2645 }
2646
2647
2648 void
Layout(bool force)2649 BWindow::Layout(bool force)
2650 {
2651 UpdateSizeLimits();
2652
2653 // Do the actual layout
2654 fTopView->Layout(force);
2655 }
2656
2657
2658 bool
IsOffscreenWindow() const2659 BWindow::IsOffscreenWindow() const
2660 {
2661 return fOffscreen;
2662 }
2663
2664
2665 status_t
GetSupportedSuites(BMessage * data)2666 BWindow::GetSupportedSuites(BMessage* data)
2667 {
2668 if (data == NULL)
2669 return B_BAD_VALUE;
2670
2671 status_t status = data->AddString("suites", "suite/vnd.Be-window");
2672 if (status == B_OK) {
2673 BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo);
2674
2675 status = data->AddFlat("messages", &propertyInfo);
2676 if (status == B_OK)
2677 status = BLooper::GetSupportedSuites(data);
2678 }
2679
2680 return status;
2681 }
2682
2683
2684 BHandler*
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 what,const char * property)2685 BWindow::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
2686 int32 what, const char* property)
2687 {
2688 if (message->what == B_WINDOW_MOVE_BY
2689 || message->what == B_WINDOW_MOVE_TO)
2690 return this;
2691
2692 BPropertyInfo propertyInfo(sWindowPropInfo);
2693 if (propertyInfo.FindMatch(message, index, specifier, what, property) >= 0) {
2694 if (strcmp(property, "View") == 0) {
2695 // we will NOT pop the current specifier
2696 return fTopView;
2697 } else if (strcmp(property, "MenuBar") == 0) {
2698 if (fKeyMenuBar) {
2699 message->PopSpecifier();
2700 return fKeyMenuBar;
2701 } else {
2702 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
2703 replyMsg.AddInt32("error", B_NAME_NOT_FOUND);
2704 replyMsg.AddString("message",
2705 "This window doesn't have a main MenuBar");
2706 message->SendReply(&replyMsg);
2707 return NULL;
2708 }
2709 } else
2710 return this;
2711 }
2712
2713 return BLooper::ResolveSpecifier(message, index, specifier, what, property);
2714 }
2715
2716
2717 // #pragma mark - Private Methods
2718
2719
2720 void
_InitData(BRect frame,const char * title,window_look look,window_feel feel,uint32 flags,uint32 workspace,int32 bitmapToken)2721 BWindow::_InitData(BRect frame, const char* title, window_look look,
2722 window_feel feel, uint32 flags, uint32 workspace, int32 bitmapToken)
2723 {
2724 STRACE(("BWindow::InitData()\n"));
2725
2726 if (be_app == NULL) {
2727 debugger("You need a valid BApplication object before interacting with "
2728 "the app_server");
2729 return;
2730 }
2731
2732 frame.left = roundf(frame.left);
2733 frame.top = roundf(frame.top);
2734 frame.right = roundf(frame.right);
2735 frame.bottom = roundf(frame.bottom);
2736
2737 fFrame = frame;
2738
2739 if (title == NULL)
2740 title = "";
2741
2742 fTitle = strdup(title);
2743
2744 _SetName(title);
2745
2746 fFeel = feel;
2747 fLook = look;
2748 fFlags = flags | B_ASYNCHRONOUS_CONTROLS;
2749
2750 fInTransaction = bitmapToken >= 0;
2751 fUpdateRequested = false;
2752 fActive = false;
2753 fShowLevel = 1;
2754
2755 fTopView = NULL;
2756 fFocus = NULL;
2757 fLastMouseMovedView = NULL;
2758 fKeyMenuBar = NULL;
2759 fDefaultButton = NULL;
2760
2761 // Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
2762 // get sent to the application, and not one of our handlers.
2763 // It is only installed for non-modal windows, though.
2764 fNoQuitShortcut = IsModal();
2765
2766 if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
2767 // Modal windows default to non-closable, but you can add the
2768 // shortcut manually, if a different behaviour is wanted
2769 AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
2770 }
2771
2772 // Edit modifier keys
2773
2774 AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
2775 AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
2776 AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
2777 AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);
2778
2779 // Window modifier keys
2780
2781 AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
2782 new BMessage(_MINIMIZE_), NULL);
2783 AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
2784 new BMessage(_ZOOM_), NULL);
2785 AddShortcut('Z', B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY,
2786 new BMessage(_ZOOM_), NULL);
2787 AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
2788 new BMessage(B_HIDE_APPLICATION), NULL);
2789 AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY,
2790 new BMessage(_SEND_TO_FRONT_), NULL);
2791 AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY,
2792 new BMessage(_SEND_BEHIND_), NULL);
2793
2794 // We set the default pulse rate, but we don't start the pulse
2795 fPulseRate = 500000;
2796 fPulseRunner = NULL;
2797
2798 fIsFilePanel = false;
2799
2800 fMenuSem = -1;
2801
2802 fMinimized = false;
2803
2804 fMaxZoomHeight = 32768.0;
2805 fMaxZoomWidth = 32768.0;
2806 fMinHeight = 0.0;
2807 fMinWidth = 0.0;
2808 fMaxHeight = 32768.0;
2809 fMaxWidth = 32768.0;
2810
2811 fLastViewToken = B_NULL_TOKEN;
2812
2813 // TODO: other initializations!
2814 fOffscreen = false;
2815
2816 // Create the server-side window
2817
2818 port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
2819 "w<app_server");
2820 if (receivePort < B_OK) {
2821 // TODO: huh?
2822 debugger("Could not create BWindow's receive port, used for "
2823 "interacting with the app_server!");
2824 delete this;
2825 return;
2826 }
2827
2828 STRACE(("BWindow::InitData(): contacting app_server...\n"));
2829
2830 // let app_server know that a window has been created.
2831 fLink = new(std::nothrow) BPrivate::PortLink(
2832 BApplication::Private::ServerLink()->SenderPort(), receivePort);
2833 if (fLink == NULL) {
2834 // Zombie!
2835 return;
2836 }
2837
2838 {
2839 BPrivate::AppServerLink lockLink;
2840 // we're talking to the server application using our own
2841 // communication channel (fLink) - we better make sure no one
2842 // interferes by locking that channel (which AppServerLink does
2843 // implicetly)
2844
2845 if (bitmapToken < 0) {
2846 fLink->StartMessage(AS_CREATE_WINDOW);
2847 } else {
2848 fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
2849 fLink->Attach<int32>(bitmapToken);
2850 fOffscreen = true;
2851 }
2852
2853 fLink->Attach<BRect>(fFrame);
2854 fLink->Attach<uint32>((uint32)fLook);
2855 fLink->Attach<uint32>((uint32)fFeel);
2856 fLink->Attach<uint32>(fFlags);
2857 fLink->Attach<uint32>(workspace);
2858 fLink->Attach<int32>(_get_object_token_(this));
2859 fLink->Attach<port_id>(receivePort);
2860 fLink->Attach<port_id>(fMsgPort);
2861 fLink->AttachString(title);
2862
2863 port_id sendPort;
2864 int32 code;
2865 if (fLink->FlushWithReply(code) == B_OK
2866 && code == B_OK
2867 && fLink->Read<port_id>(&sendPort) == B_OK) {
2868 // read the frame size and its limits that were really
2869 // enforced on the server side
2870
2871 fLink->Read<BRect>(&fFrame);
2872 fLink->Read<float>(&fMinWidth);
2873 fLink->Read<float>(&fMaxWidth);
2874 fLink->Read<float>(&fMinHeight);
2875 fLink->Read<float>(&fMaxHeight);
2876
2877 fMaxZoomWidth = fMaxWidth;
2878 fMaxZoomHeight = fMaxHeight;
2879 } else
2880 sendPort = -1;
2881
2882 // Redirect our link to the new window connection
2883 fLink->SetSenderPort(sendPort);
2884 STRACE(("Server says that our send port is %ld\n", sendPort));
2885 }
2886
2887 STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));
2888
2889 _CreateTopView();
2890 }
2891
2892
2893 //! Rename the handler and its thread
2894 void
_SetName(const char * title)2895 BWindow::_SetName(const char* title)
2896 {
2897 if (title == NULL)
2898 title = "";
2899
2900 // we will change BWindow's thread name to "w>window title"
2901
2902 char threadName[B_OS_NAME_LENGTH];
2903 strcpy(threadName, "w>");
2904 #ifdef __HAIKU__
2905 strlcat(threadName, title, B_OS_NAME_LENGTH);
2906 #else
2907 int32 length = strlen(title);
2908 length = min_c(length, B_OS_NAME_LENGTH - 3);
2909 memcpy(threadName + 2, title, length);
2910 threadName[length + 2] = '\0';
2911 #endif
2912
2913 // change the handler's name
2914 SetName(threadName);
2915
2916 // if the message loop has been started...
2917 if (Thread() >= B_OK)
2918 rename_thread(Thread(), threadName);
2919 }
2920
2921
2922 //! Reads all pending messages from the window port and put them into the queue.
2923 void
_DequeueAll()2924 BWindow::_DequeueAll()
2925 {
2926 // Get message count from port
2927 int32 count = port_count(fMsgPort);
2928
2929 for (int32 i = 0; i < count; i++) {
2930 BMessage* message = MessageFromPort(0);
2931 if (message != NULL)
2932 fDirectTarget->Queue()->AddMessage(message);
2933 }
2934 }
2935
2936
2937 /*! This here is an almost complete code duplication to BLooper::task_looper()
2938 but with some important differences:
2939 a) it uses the _DetermineTarget() method to tell what the later target of
2940 a message will be, if no explicit target is supplied.
2941 b) it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message
2942 to all of its intended targets, and to add all fields the target would
2943 expect in such a message.
2944
2945 This is important because the app_server sends all input events to the
2946 preferred handler, and expects them to be correctly distributed to their
2947 intended targets.
2948 */
2949 void
task_looper()2950 BWindow::task_looper()
2951 {
2952 STRACE(("info: BWindow::task_looper() started.\n"));
2953
2954 // Check that looper is locked (should be)
2955 AssertLocked();
2956 Unlock();
2957
2958 if (IsLocked())
2959 debugger("window must not be locked!");
2960
2961 while (!fTerminating) {
2962 // Did we get a message?
2963 BMessage* msg = MessageFromPort();
2964 if (msg)
2965 _AddMessagePriv(msg);
2966
2967 // Get message count from port
2968 int32 msgCount = port_count(fMsgPort);
2969 for (int32 i = 0; i < msgCount; ++i) {
2970 // Read 'count' messages from port (so we will not block)
2971 // We use zero as our timeout since we know there is stuff there
2972 msg = MessageFromPort(0);
2973 // Add messages to queue
2974 if (msg)
2975 _AddMessagePriv(msg);
2976 }
2977
2978 bool dispatchNextMessage = true;
2979 while (!fTerminating && dispatchNextMessage) {
2980 // Get next message from queue (assign to fLastMessage after
2981 // locking)
2982 BMessage* message = fDirectTarget->Queue()->NextMessage();
2983
2984 // Lock the looper
2985 if (!Lock()) {
2986 delete message;
2987 break;
2988 }
2989
2990 fLastMessage = message;
2991
2992 if (fLastMessage == NULL) {
2993 // No more messages: Unlock the looper and terminate the
2994 // dispatch loop.
2995 dispatchNextMessage = false;
2996 } else {
2997 // Get the target handler
2998 BMessage::Private messagePrivate(fLastMessage);
2999 bool usePreferred = messagePrivate.UsePreferredTarget();
3000 BHandler* handler = NULL;
3001 bool dropMessage = false;
3002
3003 if (usePreferred) {
3004 handler = PreferredHandler();
3005 if (handler == NULL)
3006 handler = this;
3007 } else {
3008 gDefaultTokens.GetToken(messagePrivate.GetTarget(),
3009 B_HANDLER_TOKEN, (void**)&handler);
3010
3011 // if this handler doesn't belong to us, we drop the message
3012 if (handler != NULL && handler->Looper() != this) {
3013 dropMessage = true;
3014 handler = NULL;
3015 }
3016 }
3017
3018 if ((handler == NULL && !dropMessage) || usePreferred)
3019 handler = _DetermineTarget(fLastMessage, handler);
3020
3021 unpack_cookie cookie;
3022 while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) {
3023 // if there is no target handler, the message is dropped
3024 if (handler != NULL) {
3025 _SanitizeMessage(fLastMessage, handler, usePreferred);
3026
3027 // Is this a scripting message?
3028 if (fLastMessage->HasSpecifiers()) {
3029 int32 index = 0;
3030 // Make sure the current specifier is kosher
3031 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
3032 handler = resolve_specifier(handler, fLastMessage);
3033 }
3034
3035 if (handler != NULL)
3036 handler = _TopLevelFilter(fLastMessage, handler);
3037
3038 if (handler != NULL)
3039 DispatchMessage(fLastMessage, handler);
3040 }
3041
3042 // Delete the current message
3043 delete fLastMessage;
3044 fLastMessage = NULL;
3045 }
3046 }
3047
3048 if (fTerminating) {
3049 // we leave the looper locked when we quit
3050 return;
3051 }
3052
3053 Unlock();
3054
3055 // Are any messages on the port?
3056 if (port_count(fMsgPort) > 0) {
3057 // Do outer loop
3058 dispatchNextMessage = false;
3059 }
3060 }
3061 }
3062 }
3063
3064
3065 window_type
_ComposeType(window_look look,window_feel feel) const3066 BWindow::_ComposeType(window_look look, window_feel feel) const
3067 {
3068 switch (feel) {
3069 case B_NORMAL_WINDOW_FEEL:
3070 switch (look) {
3071 case B_TITLED_WINDOW_LOOK:
3072 return B_TITLED_WINDOW;
3073
3074 case B_DOCUMENT_WINDOW_LOOK:
3075 return B_DOCUMENT_WINDOW;
3076
3077 case B_BORDERED_WINDOW_LOOK:
3078 return B_BORDERED_WINDOW;
3079
3080 default:
3081 return B_UNTYPED_WINDOW;
3082 }
3083 break;
3084
3085 case B_MODAL_APP_WINDOW_FEEL:
3086 if (look == B_MODAL_WINDOW_LOOK)
3087 return B_MODAL_WINDOW;
3088 break;
3089
3090 case B_FLOATING_APP_WINDOW_FEEL:
3091 if (look == B_FLOATING_WINDOW_LOOK)
3092 return B_FLOATING_WINDOW;
3093 break;
3094
3095 default:
3096 return B_UNTYPED_WINDOW;
3097 }
3098
3099 return B_UNTYPED_WINDOW;
3100 }
3101
3102
3103 void
_DecomposeType(window_type type,window_look * _look,window_feel * _feel) const3104 BWindow::_DecomposeType(window_type type, window_look* _look,
3105 window_feel* _feel) const
3106 {
3107 switch (type) {
3108 case B_DOCUMENT_WINDOW:
3109 *_look = B_DOCUMENT_WINDOW_LOOK;
3110 *_feel = B_NORMAL_WINDOW_FEEL;
3111 break;
3112
3113 case B_MODAL_WINDOW:
3114 *_look = B_MODAL_WINDOW_LOOK;
3115 *_feel = B_MODAL_APP_WINDOW_FEEL;
3116 break;
3117
3118 case B_FLOATING_WINDOW:
3119 *_look = B_FLOATING_WINDOW_LOOK;
3120 *_feel = B_FLOATING_APP_WINDOW_FEEL;
3121 break;
3122
3123 case B_BORDERED_WINDOW:
3124 *_look = B_BORDERED_WINDOW_LOOK;
3125 *_feel = B_NORMAL_WINDOW_FEEL;
3126 break;
3127
3128 case B_TITLED_WINDOW:
3129 case B_UNTYPED_WINDOW:
3130 default:
3131 *_look = B_TITLED_WINDOW_LOOK;
3132 *_feel = B_NORMAL_WINDOW_FEEL;
3133 break;
3134 }
3135 }
3136
3137
3138 void
_CreateTopView()3139 BWindow::_CreateTopView()
3140 {
3141 STRACE(("_CreateTopView(): enter\n"));
3142
3143 BRect frame = fFrame.OffsetToCopy(B_ORIGIN);
3144 // TODO: what to do here about std::nothrow?
3145 fTopView = new BView(frame, "fTopView", B_FOLLOW_ALL, B_WILL_DRAW);
3146 fTopView->fTopLevelView = true;
3147
3148 //inhibit check_lock()
3149 fLastViewToken = _get_object_token_(fTopView);
3150
3151 // set fTopView's owner, add it to window's eligible handler list
3152 // and also set its next handler to be this window.
3153
3154 STRACE(("Calling setowner fTopView = %p this = %p.\n",
3155 fTopView, this));
3156
3157 fTopView->_SetOwner(this);
3158
3159 // we can't use AddChild() because this is the top view
3160 fTopView->_CreateSelf();
3161 STRACE(("BuildTopView ended\n"));
3162 }
3163
3164
3165 /*!
3166 Resizes the top view to match the window size. This will also
3167 adapt the size of all its child views as needed.
3168 This method has to be called whenever the frame of the window
3169 changes.
3170 */
3171 void
_AdoptResize()3172 BWindow::_AdoptResize()
3173 {
3174 // Resize views according to their resize modes - this
3175 // saves us some server communication, as the server
3176 // does the same with our views on its side.
3177
3178 int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width());
3179 int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height());
3180 if (deltaWidth == 0 && deltaHeight == 0)
3181 return;
3182
3183 fTopView->_ResizeBy(deltaWidth, deltaHeight);
3184 }
3185
3186
3187 void
_SetFocus(BView * focusView,bool notifyInputServer)3188 BWindow::_SetFocus(BView* focusView, bool notifyInputServer)
3189 {
3190 if (fFocus == focusView)
3191 return;
3192
3193 // we notify the input server if we are passing focus
3194 // from a view which has the B_INPUT_METHOD_AWARE to a one
3195 // which does not, or vice-versa
3196 if (notifyInputServer && fActive) {
3197 bool inputMethodAware = false;
3198 if (focusView)
3199 inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE;
3200 BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW);
3201 BMessenger messenger(focusView);
3202 BMessage reply;
3203 if (focusView)
3204 msg.AddMessenger("view", messenger);
3205 _control_input_server_(&msg, &reply);
3206 }
3207
3208 fFocus = focusView;
3209 SetPreferredHandler(focusView);
3210 }
3211
3212
3213 /*!
3214 \brief Determines the target of a message received for the
3215 focus view.
3216 */
3217 BHandler*
_DetermineTarget(BMessage * message,BHandler * target)3218 BWindow::_DetermineTarget(BMessage* message, BHandler* target)
3219 {
3220 if (target == NULL)
3221 target = this;
3222
3223 switch (message->what) {
3224 case B_KEY_DOWN:
3225 case B_KEY_UP:
3226 {
3227 // if we have a default button, it might want to hear
3228 // about pressing the <enter> key
3229 const int32 kNonLockModifierKeys = B_SHIFT_KEY | B_COMMAND_KEY
3230 | B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY;
3231 int32 rawChar;
3232 if (DefaultButton() != NULL
3233 && message->FindInt32("raw_char", &rawChar) == B_OK
3234 && rawChar == B_ENTER
3235 && (modifiers() & kNonLockModifierKeys) == 0)
3236 return DefaultButton();
3237
3238 // supposed to fall through
3239 }
3240 case B_UNMAPPED_KEY_DOWN:
3241 case B_UNMAPPED_KEY_UP:
3242 case B_MODIFIERS_CHANGED:
3243 // these messages should be dispatched by the focus view
3244 if (CurrentFocus() != NULL)
3245 return CurrentFocus();
3246 break;
3247
3248 case B_MOUSE_DOWN:
3249 case B_MOUSE_UP:
3250 case B_MOUSE_MOVED:
3251 case B_MOUSE_WHEEL_CHANGED:
3252 case B_MOUSE_IDLE:
3253 // is there a token of the view that is currently under the mouse?
3254 int32 token;
3255 if (message->FindInt32("_view_token", &token) == B_OK) {
3256 BView* view = _FindView(token);
3257 if (view != NULL)
3258 return view;
3259 }
3260
3261 // if there is no valid token in the message, we try our
3262 // luck with the last target, if available
3263 if (fLastMouseMovedView != NULL)
3264 return fLastMouseMovedView;
3265 break;
3266
3267 case B_PULSE:
3268 case B_QUIT_REQUESTED:
3269 // TODO: test whether R5 will let BView dispatch these messages
3270 return this;
3271
3272 case _MESSAGE_DROPPED_:
3273 if (fLastMouseMovedView != NULL)
3274 return fLastMouseMovedView;
3275 break;
3276
3277 default:
3278 break;
3279 }
3280
3281 return target;
3282 }
3283
3284
3285 /*! \brief Determines whether or not this message has targeted the focus view.
3286
3287 This will return \c false only if the message did not go to the preferred
3288 handler, or if the packed message does not contain address the focus view
3289 at all.
3290 */
3291 bool
_IsFocusMessage(BMessage * message)3292 BWindow::_IsFocusMessage(BMessage* message)
3293 {
3294 BMessage::Private messagePrivate(message);
3295 if (!messagePrivate.UsePreferredTarget())
3296 return false;
3297
3298 bool feedFocus;
3299 if (message->HasInt32("_token")
3300 && (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus))
3301 return false;
3302
3303 return true;
3304 }
3305
3306
3307 /*! \brief Distributes the message to its intended targets. This is done for
3308 all messages that should go to the preferred handler.
3309
3310 Returns \c true in case the message should still be dispatched
3311 */
3312 bool
_UnpackMessage(unpack_cookie & cookie,BMessage ** _message,BHandler ** _target,bool * _usePreferred)3313 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message,
3314 BHandler** _target, bool* _usePreferred)
3315 {
3316 if (cookie.message == NULL)
3317 return false;
3318
3319 if (cookie.index == 0 && !cookie.tokens_scanned) {
3320 // We were called the first time for this message
3321
3322 if (!*_usePreferred) {
3323 // only consider messages targeted at the preferred handler
3324 cookie.message = NULL;
3325 return true;
3326 }
3327
3328 // initialize our cookie
3329 cookie.message = *_message;
3330 cookie.focus = *_target;
3331
3332 if (cookie.focus != NULL)
3333 cookie.focus_token = _get_object_token_(*_target);
3334
3335 if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED)
3336 cookie.last_view_token = _get_object_token_(fLastMouseMovedView);
3337
3338 *_usePreferred = false;
3339 }
3340
3341 _DequeueAll();
3342
3343 // distribute the message to all targets specified in the
3344 // message directly (but not to the focus view)
3345
3346 for (int32 token; !cookie.tokens_scanned
3347 && cookie.message->FindInt32("_token", cookie.index, &token)
3348 == B_OK;
3349 cookie.index++) {
3350 // focus view is preferred and should get its message directly
3351 if (token == cookie.focus_token) {
3352 cookie.found_focus = true;
3353 continue;
3354 }
3355 if (token == cookie.last_view_token)
3356 continue;
3357
3358 BView* target = _FindView(token);
3359 if (target == NULL)
3360 continue;
3361
3362 *_message = new BMessage(*cookie.message);
3363 // the secondary copies of the message should not be treated as focus
3364 // messages, otherwise there will be unintended side effects, i.e.
3365 // keyboard shortcuts getting processed multiple times.
3366 (*_message)->RemoveName("_feed_focus");
3367 *_target = target;
3368 cookie.index++;
3369 return true;
3370 }
3371
3372 cookie.tokens_scanned = true;
3373
3374 // if there is a last mouse moved view, and the new focus is
3375 // different, the previous view wants to get its B_EXITED_VIEW
3376 // message
3377 if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL
3378 && fLastMouseMovedView != cookie.focus) {
3379 *_message = new BMessage(*cookie.message);
3380 *_target = fLastMouseMovedView;
3381 cookie.last_view_token = B_NULL_TOKEN;
3382 return true;
3383 }
3384
3385 bool dispatchToFocus = true;
3386
3387 // check if the focus token is still valid (could have been removed in the mean time)
3388 BHandler* handler;
3389 if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK
3390 || handler->Looper() != this)
3391 dispatchToFocus = false;
3392
3393 if (dispatchToFocus && cookie.index > 0) {
3394 // should this message still be dispatched by the focus view?
3395 bool feedFocus;
3396 if (!cookie.found_focus
3397 && (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK
3398 || feedFocus == false))
3399 dispatchToFocus = false;
3400 }
3401
3402 if (!dispatchToFocus) {
3403 delete cookie.message;
3404 cookie.message = NULL;
3405 return false;
3406 }
3407
3408 *_message = cookie.message;
3409 *_target = cookie.focus;
3410 *_usePreferred = true;
3411 cookie.message = NULL;
3412 return true;
3413 }
3414
3415
3416 /*! Some messages don't get to the window in a shape an application should see.
3417 This method is supposed to give a message the last grinding before
3418 it's acceptable for the receiving application.
3419 */
3420 void
_SanitizeMessage(BMessage * message,BHandler * target,bool usePreferred)3421 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred)
3422 {
3423 if (target == NULL)
3424 return;
3425
3426 switch (message->what) {
3427 case B_MOUSE_MOVED:
3428 case B_MOUSE_UP:
3429 case B_MOUSE_DOWN:
3430 {
3431 BPoint where;
3432 if (message->FindPoint("screen_where", &where) != B_OK)
3433 break;
3434
3435 BView* view = dynamic_cast<BView*>(target);
3436
3437 if (view == NULL || message->what == B_MOUSE_MOVED) {
3438 // add local window coordinates, only
3439 // for regular mouse moved messages
3440 message->AddPoint("where", ConvertFromScreen(where));
3441 }
3442
3443 if (view != NULL) {
3444 // add local view coordinates
3445 BPoint viewWhere = view->ConvertFromScreen(where);
3446 if (message->what != B_MOUSE_MOVED) {
3447 // Yep, the meaning of "where" is different
3448 // for regular mouse moved messages versus
3449 // mouse up/down!
3450 message->AddPoint("where", viewWhere);
3451 }
3452 message->AddPoint("be:view_where", viewWhere);
3453
3454 if (message->what == B_MOUSE_MOVED) {
3455 // is there a token of the view that is currently under
3456 // the mouse?
3457 BView* viewUnderMouse = NULL;
3458 int32 token;
3459 if (message->FindInt32("_view_token", &token) == B_OK)
3460 viewUnderMouse = _FindView(token);
3461
3462 // add transit information
3463 uint32 transit
3464 = _TransitForMouseMoved(view, viewUnderMouse);
3465 message->AddInt32("be:transit", transit);
3466
3467 if (usePreferred)
3468 fLastMouseMovedView = viewUnderMouse;
3469 }
3470 }
3471 break;
3472 }
3473
3474 case B_MOUSE_IDLE:
3475 {
3476 // App Server sends screen coordinates, convert the point to
3477 // local view coordinates, then add the point in be:view_where
3478 BPoint where;
3479 if (message->FindPoint("screen_where", &where) != B_OK)
3480 break;
3481
3482 BView* view = dynamic_cast<BView*>(target);
3483 if (view != NULL) {
3484 // add local view coordinates
3485 message->AddPoint("be:view_where",
3486 view->ConvertFromScreen(where));
3487 }
3488 break;
3489 }
3490
3491 case _MESSAGE_DROPPED_:
3492 {
3493 uint32 originalWhat;
3494 if (message->FindInt32("_original_what",
3495 (int32*)&originalWhat) == B_OK) {
3496 message->what = originalWhat;
3497 message->RemoveName("_original_what");
3498 }
3499 break;
3500 }
3501 }
3502 }
3503
3504
3505 /*!
3506 This is called by BView::GetMouse() when a B_MOUSE_MOVED message
3507 is removed from the queue.
3508 It allows the window to update the last mouse moved view, and
3509 let it decide if this message should be kept. It will also remove
3510 the message from the queue.
3511 You need to hold the message queue lock when calling this method!
3512
3513 \return true if this message can be used to get the mouse data from,
3514 \return false if this is not meant for the public.
3515 */
3516 bool
_StealMouseMessage(BMessage * message,bool & deleteMessage)3517 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage)
3518 {
3519 BMessage::Private messagePrivate(message);
3520 if (!messagePrivate.UsePreferredTarget()) {
3521 // this message is targeted at a specific handler, so we should
3522 // not steal it
3523 return false;
3524 }
3525
3526 int32 token;
3527 if (message->FindInt32("_token", 0, &token) == B_OK) {
3528 // This message has other targets, so we can't remove it;
3529 // just prevent it from being sent to the preferred handler
3530 // again (if it should have gotten it at all).
3531 bool feedFocus;
3532 if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)
3533 return false;
3534
3535 message->RemoveName("_feed_focus");
3536 deleteMessage = false;
3537 } else {
3538 deleteMessage = true;
3539
3540 if (message->what == B_MOUSE_MOVED) {
3541 // We need to update the last mouse moved view, as this message
3542 // won't make it to _SanitizeMessage() anymore.
3543 BView* viewUnderMouse = NULL;
3544 int32 token;
3545 if (message->FindInt32("_view_token", &token) == B_OK)
3546 viewUnderMouse = _FindView(token);
3547
3548 // Don't remove important transit messages!
3549 uint32 transit = _TransitForMouseMoved(fLastMouseMovedView,
3550 viewUnderMouse);
3551 if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
3552 deleteMessage = false;
3553 }
3554
3555 if (deleteMessage) {
3556 // The message is only thought for the preferred handler, so we
3557 // can just remove it.
3558 MessageQueue()->RemoveMessage(message);
3559 }
3560 }
3561
3562 return true;
3563 }
3564
3565
3566 uint32
_TransitForMouseMoved(BView * view,BView * viewUnderMouse) const3567 BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const
3568 {
3569 uint32 transit;
3570 if (viewUnderMouse == view) {
3571 // the mouse is over the target view
3572 if (fLastMouseMovedView != view)
3573 transit = B_ENTERED_VIEW;
3574 else
3575 transit = B_INSIDE_VIEW;
3576 } else {
3577 // the mouse is not over the target view
3578 if (view == fLastMouseMovedView)
3579 transit = B_EXITED_VIEW;
3580 else
3581 transit = B_OUTSIDE_VIEW;
3582 }
3583 return transit;
3584 }
3585
3586
3587 /*! Forwards the key to the switcher
3588 */
3589 void
_Switcher(int32 rawKey,uint32 modifiers,bool repeat)3590 BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat)
3591 {
3592 // only send the first key press, no repeats
3593 if (repeat)
3594 return;
3595
3596 BMessenger deskbar(kDeskbarSignature);
3597 if (!deskbar.IsValid()) {
3598 // TODO: have some kind of fallback-handling in case the Deskbar is
3599 // not available?
3600 return;
3601 }
3602
3603 BMessage message('TASK');
3604 message.AddInt32("key", rawKey);
3605 message.AddInt32("modifiers", modifiers);
3606 message.AddInt64("when", system_time());
3607 message.AddInt32("team", Team());
3608 deskbar.SendMessage(&message);
3609 }
3610
3611
3612 /*! Handles keyboard input before it gets forwarded to the target handler.
3613 This includes shortcut evaluation, keyboard navigation, etc.
3614
3615 \return handled if true, the event was already handled, and will not
3616 be forwarded to the target handler.
3617
3618 TODO: must also convert the incoming key to the font encoding of the target
3619 */
3620 bool
_HandleKeyDown(BMessage * event)3621 BWindow::_HandleKeyDown(BMessage* event)
3622 {
3623 // Only handle special functions when the event targeted the active focus
3624 // view
3625 if (!_IsFocusMessage(event))
3626 return false;
3627
3628 const char* bytes = NULL;
3629 if (event->FindString("bytes", &bytes) != B_OK)
3630 return false;
3631
3632 char key = bytes[0];
3633
3634 uint32 modifiers;
3635 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK)
3636 modifiers = 0;
3637
3638 // handle BMenuBar key
3639 if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0
3640 && fKeyMenuBar != NULL) {
3641 fKeyMenuBar->StartMenuBar(0, true, false, NULL);
3642 return true;
3643 }
3644
3645 // Keyboard navigation through views
3646 // (B_OPTION_KEY makes BTextViews and friends navigable, even in editing
3647 // mode)
3648 if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) {
3649 _KeyboardNavigation();
3650 return true;
3651 }
3652
3653 int32 rawKey;
3654 event->FindInt32("key", &rawKey);
3655
3656 // Deskbar's Switcher
3657 if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) {
3658 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3659 return true;
3660 }
3661
3662 // Optionally close window when the escape key is pressed
3663 if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) {
3664 BMessage message(B_QUIT_REQUESTED);
3665 message.AddBool("shortcut", true);
3666
3667 PostMessage(&message);
3668 return true;
3669 }
3670
3671 // PrtScr key takes a screenshot
3672 if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) {
3673 // With no modifier keys the best way to get a screenshot is by
3674 // calling the screenshot CLI
3675 if (modifiers == 0) {
3676 be_roster->Launch("application/x-vnd.haiku-screenshot-cli");
3677 return true;
3678 }
3679
3680 // Prepare a message based on the modifier keys pressed and launch the
3681 // screenshot GUI
3682 BMessage message(B_ARGV_RECEIVED);
3683 int32 argc = 1;
3684 message.AddString("argv", "Screenshot");
3685 if ((modifiers & B_CONTROL_KEY) != 0) {
3686 argc++;
3687 message.AddString("argv", "--clipboard");
3688 }
3689 if ((modifiers & B_SHIFT_KEY) != 0) {
3690 argc++;
3691 message.AddString("argv", "--silent");
3692 }
3693 message.AddInt32("argc", argc);
3694 be_roster->Launch("application/x-vnd.haiku-screenshot", &message);
3695 return true;
3696 }
3697
3698 // Handle shortcuts
3699 if ((modifiers & B_COMMAND_KEY) != 0) {
3700 // Command+q has been pressed, so, we will quit
3701 // the shortcut mechanism doesn't allow handlers outside the window
3702 if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) {
3703 BMessage message(B_QUIT_REQUESTED);
3704 message.AddBool("shortcut", true);
3705
3706 be_app->PostMessage(&message);
3707 // eat the event
3708 return true;
3709 }
3710
3711 // Send Command+Left and Command+Right to textview if it has focus
3712 if (key == B_LEFT_ARROW || key == B_RIGHT_ARROW) {
3713 // check key before doing expensive dynamic_cast
3714 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
3715 if (textView != NULL) {
3716 textView->KeyDown(bytes, modifiers);
3717 // eat the event
3718 return true;
3719 }
3720 }
3721
3722 // Pretend that the user opened a menu, to give the subclass a
3723 // chance to update it's menus. This may install new shortcuts,
3724 // which is why we have to call it here, before trying to find
3725 // a shortcut for the given key.
3726 MenusBeginning();
3727
3728 Shortcut* shortcut = _FindShortcut(key, modifiers);
3729 if (shortcut != NULL) {
3730 // TODO: would be nice to move this functionality to
3731 // a Shortcut::Invoke() method - but since BMenu::InvokeItem()
3732 // (and BMenuItem::Invoke()) are private, I didn't want
3733 // to mess with them (BMenuItem::Invoke() is public in
3734 // Dano/Zeta, though, maybe we should just follow their
3735 // example)
3736 if (shortcut->MenuItem() != NULL) {
3737 BMenu* menu = shortcut->MenuItem()->Menu();
3738 if (menu != NULL)
3739 MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true);
3740 } else {
3741 BHandler* target = shortcut->Target();
3742 if (target == NULL)
3743 target = CurrentFocus();
3744
3745 if (shortcut->Message() != NULL) {
3746 BMessage message(*shortcut->Message());
3747
3748 if (message.ReplaceInt64("when", system_time()) != B_OK)
3749 message.AddInt64("when", system_time());
3750 if (message.ReplaceBool("shortcut", true) != B_OK)
3751 message.AddBool("shortcut", true);
3752
3753 PostMessage(&message, target);
3754 }
3755 }
3756 }
3757
3758 MenusEnded();
3759
3760 // we always eat the event if the command key was pressed
3761 return true;
3762 }
3763
3764 // TODO: convert keys to the encoding of the target view
3765
3766 return false;
3767 }
3768
3769
3770 bool
_HandleUnmappedKeyDown(BMessage * event)3771 BWindow::_HandleUnmappedKeyDown(BMessage* event)
3772 {
3773 // Only handle special functions when the event targeted the active focus
3774 // view
3775 if (!_IsFocusMessage(event))
3776 return false;
3777
3778 uint32 modifiers;
3779 int32 rawKey;
3780 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK
3781 || event->FindInt32("key", &rawKey))
3782 return false;
3783
3784 // Deskbar's Switcher
3785 if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) {
3786 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat"));
3787 return true;
3788 }
3789
3790 return false;
3791 }
3792
3793
3794 void
_KeyboardNavigation()3795 BWindow::_KeyboardNavigation()
3796 {
3797 BMessage* message = CurrentMessage();
3798 if (message == NULL)
3799 return;
3800
3801 const char* bytes;
3802 uint32 modifiers;
3803 if (message->FindString("bytes", &bytes) != B_OK || bytes[0] != B_TAB)
3804 return;
3805
3806 message->FindInt32("modifiers", (int32*)&modifiers);
3807
3808 BView* nextFocus;
3809 int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0
3810 ? B_NAVIGABLE_JUMP : B_NAVIGABLE;
3811 if (modifiers & B_SHIFT_KEY)
3812 nextFocus = _FindPreviousNavigable(fFocus, jumpGroups);
3813 else
3814 nextFocus = _FindNextNavigable(fFocus, jumpGroups);
3815
3816 if (nextFocus != NULL && nextFocus != fFocus)
3817 nextFocus->MakeFocus(true);
3818 }
3819
3820
3821 /*!
3822 \brief Return the position of the window centered horizontally to the passed
3823 in \a frame and vertically 3/4 from the top of \a frame.
3824
3825 If the window is on the borders
3826
3827 \param width The width of the window.
3828 \param height The height of the window.
3829 \param frame The \a frame to center the window in.
3830
3831 \return The new window position.
3832 */
3833 BPoint
AlertPosition(const BRect & frame)3834 BWindow::AlertPosition(const BRect& frame)
3835 {
3836 float width = Bounds().Width();
3837 float height = Bounds().Height();
3838
3839 BPoint point(frame.left + (frame.Width() / 2.0f) - (width / 2.0f),
3840 frame.top + (frame.Height() / 4.0f) - ceil(height / 3.0f));
3841
3842 BRect screenFrame = BScreen(this).Frame();
3843 if (frame == screenFrame) {
3844 // reference frame is screen frame, skip the below adjustments
3845 return point;
3846 }
3847
3848 float borderWidth;
3849 float tabHeight;
3850 _GetDecoratorSize(&borderWidth, &tabHeight);
3851
3852 // clip the x position within the horizontal edges of the screen
3853 if (point.x < screenFrame.left + borderWidth)
3854 point.x = screenFrame.left + borderWidth;
3855 else if (point.x + width > screenFrame.right - borderWidth)
3856 point.x = screenFrame.right - borderWidth - width;
3857
3858 // lower the window down if it is covering the window tab
3859 float tabPosition = frame.LeftTop().y + tabHeight + borderWidth;
3860 if (point.y < tabPosition)
3861 point.y = tabPosition;
3862
3863 // clip the y position within the vertical edges of the screen
3864 if (point.y < screenFrame.top + borderWidth)
3865 point.y = screenFrame.top + borderWidth;
3866 else if (point.y + height > screenFrame.bottom - borderWidth)
3867 point.y = screenFrame.bottom - borderWidth - height;
3868
3869 return point;
3870 }
3871
3872
3873 BMessage*
ConvertToMessage(void * raw,int32 code)3874 BWindow::ConvertToMessage(void* raw, int32 code)
3875 {
3876 return BLooper::ConvertToMessage(raw, code);
3877 }
3878
3879
3880 BWindow::Shortcut*
_FindShortcut(uint32 key,uint32 modifiers)3881 BWindow::_FindShortcut(uint32 key, uint32 modifiers)
3882 {
3883 int32 count = fShortcuts.CountItems();
3884
3885 key = Shortcut::PrepareKey(key);
3886 modifiers = Shortcut::PrepareModifiers(modifiers);
3887
3888 for (int32 index = 0; index < count; index++) {
3889 Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index);
3890
3891 if (shortcut->Matches(key, modifiers))
3892 return shortcut;
3893 }
3894
3895 return NULL;
3896 }
3897
3898
3899 BView*
_FindView(int32 token)3900 BWindow::_FindView(int32 token)
3901 {
3902 BHandler* handler;
3903 if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN,
3904 (void**)&handler) != B_OK) {
3905 return NULL;
3906 }
3907
3908 // the view must belong to us in order to be found by this method
3909 BView* view = dynamic_cast<BView*>(handler);
3910 if (view != NULL && view->Window() == this)
3911 return view;
3912
3913 return NULL;
3914 }
3915
3916
3917 BView*
_FindView(BView * view,BPoint point) const3918 BWindow::_FindView(BView* view, BPoint point) const
3919 {
3920 // point is assumed to be already in view's coordinates
3921 if (!view->IsHidden(view) && view->Bounds().Contains(point)) {
3922 if (view->fFirstChild == NULL)
3923 return view;
3924 else {
3925 BView* child = view->fFirstChild;
3926 while (child != NULL) {
3927 BPoint childPoint = point - child->Frame().LeftTop();
3928 BView* subView = _FindView(child, childPoint);
3929 if (subView != NULL)
3930 return subView;
3931
3932 child = child->fNextSibling;
3933 }
3934 }
3935 return view;
3936 }
3937 return NULL;
3938 }
3939
3940
3941 BView*
_FindNextNavigable(BView * focus,uint32 flags)3942 BWindow::_FindNextNavigable(BView* focus, uint32 flags)
3943 {
3944 if (focus == NULL)
3945 focus = fTopView;
3946
3947 BView* nextFocus = focus;
3948
3949 // Search the tree for views that accept focus (depth search)
3950 while (true) {
3951 if (nextFocus->fFirstChild)
3952 nextFocus = nextFocus->fFirstChild;
3953 else if (nextFocus->fNextSibling)
3954 nextFocus = nextFocus->fNextSibling;
3955 else {
3956 // go to the nearest parent with a next sibling
3957 while (!nextFocus->fNextSibling && nextFocus->fParent) {
3958 nextFocus = nextFocus->fParent;
3959 }
3960
3961 if (nextFocus == fTopView) {
3962 // if we started with the top view, we traversed the whole tree already
3963 if (nextFocus == focus)
3964 return NULL;
3965
3966 nextFocus = nextFocus->fFirstChild;
3967 } else
3968 nextFocus = nextFocus->fNextSibling;
3969 }
3970
3971 if (nextFocus == focus || nextFocus == NULL) {
3972 // When we get here it means that the hole tree has been
3973 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
3974 return NULL;
3975 }
3976
3977 if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0)
3978 return nextFocus;
3979 }
3980 }
3981
3982
3983 BView*
_FindPreviousNavigable(BView * focus,uint32 flags)3984 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags)
3985 {
3986 if (focus == NULL)
3987 focus = fTopView;
3988
3989 BView* previousFocus = focus;
3990
3991 // Search the tree for the previous view that accept focus
3992 while (true) {
3993 if (previousFocus->fPreviousSibling) {
3994 // find the last child in the previous sibling
3995 previousFocus = _LastViewChild(previousFocus->fPreviousSibling);
3996 } else {
3997 previousFocus = previousFocus->fParent;
3998 if (previousFocus == fTopView)
3999 previousFocus = _LastViewChild(fTopView);
4000 }
4001
4002 if (previousFocus == focus || previousFocus == NULL) {
4003 // When we get here it means that the hole tree has been
4004 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set!
4005 return NULL;
4006 }
4007
4008 if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0)
4009 return previousFocus;
4010 }
4011 }
4012
4013
4014 /*!
4015 Returns the last child in a view hierarchy.
4016 Needed only by _FindPreviousNavigable().
4017 */
4018 BView*
_LastViewChild(BView * parent)4019 BWindow::_LastViewChild(BView* parent)
4020 {
4021 while (true) {
4022 BView* last = parent->fFirstChild;
4023 if (last == NULL)
4024 return parent;
4025
4026 while (last->fNextSibling) {
4027 last = last->fNextSibling;
4028 }
4029
4030 parent = last;
4031 }
4032 }
4033
4034
4035 void
SetIsFilePanel(bool isFilePanel)4036 BWindow::SetIsFilePanel(bool isFilePanel)
4037 {
4038 fIsFilePanel = isFilePanel;
4039 }
4040
4041
4042 bool
IsFilePanel() const4043 BWindow::IsFilePanel() const
4044 {
4045 return fIsFilePanel;
4046 }
4047
4048
4049 void
_GetDecoratorSize(float * _borderWidth,float * _tabHeight) const4050 BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const
4051 {
4052 // fallback in case retrieving the decorator settings fails
4053 // (highly unlikely)
4054 float borderWidth = 5.0;
4055 float tabHeight = 21.0;
4056
4057 BMessage settings;
4058 if (GetDecoratorSettings(&settings) == B_OK) {
4059 BRect tabRect;
4060 if (settings.FindRect("tab frame", &tabRect) == B_OK)
4061 tabHeight = tabRect.Height();
4062 settings.FindFloat("border width", &borderWidth);
4063 } else {
4064 // probably no-border window look
4065 if (fLook == B_NO_BORDER_WINDOW_LOOK) {
4066 borderWidth = 0.0;
4067 tabHeight = 0.0;
4068 }
4069 // else use fall-back values from above
4070 }
4071
4072 if (_borderWidth != NULL)
4073 *_borderWidth = borderWidth;
4074 if (_tabHeight != NULL)
4075 *_tabHeight = tabHeight;
4076 }
4077
4078
4079 void
_SendShowOrHideMessage()4080 BWindow::_SendShowOrHideMessage()
4081 {
4082 fLink->StartMessage(AS_SHOW_OR_HIDE_WINDOW);
4083 fLink->Attach<int32>(fShowLevel);
4084 fLink->Flush();
4085 }
4086
4087
4088 void
_PropagateMessageToChildViews(BMessage * message)4089 BWindow::_PropagateMessageToChildViews(BMessage* message)
4090 {
4091 int32 childrenCount = CountChildren();
4092 for (int32 index = 0; index < childrenCount; index++) {
4093 BView* view = ChildAt(index);
4094 if (view != NULL)
4095 PostMessage(message, view);
4096 }
4097 }
4098
4099
4100 // #pragma mark - C++ binary compatibility kludge
4101
4102
4103 extern "C" void
_ReservedWindow1__7BWindow(BWindow * window,BLayout * layout)4104 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout)
4105 {
4106 // SetLayout()
4107 perform_data_set_layout data;
4108 data.layout = layout;
4109 window->Perform(PERFORM_CODE_SET_LAYOUT, &data);
4110 }
4111
4112
_ReservedWindow2()4113 void BWindow::_ReservedWindow2() {}
_ReservedWindow3()4114 void BWindow::_ReservedWindow3() {}
_ReservedWindow4()4115 void BWindow::_ReservedWindow4() {}
_ReservedWindow5()4116 void BWindow::_ReservedWindow5() {}
_ReservedWindow6()4117 void BWindow::_ReservedWindow6() {}
_ReservedWindow7()4118 void BWindow::_ReservedWindow7() {}
_ReservedWindow8()4119 void BWindow::_ReservedWindow8() {}
4120
4121