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 102 BMenuItem* MenuItem() const { return fMenuItem; } 103 BMessage* Message() const { return fMessage; } 104 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 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 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 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 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 265 BWindow::Shortcut::~Shortcut() 266 { 267 // we own the message, if any 268 delete fMessage; 269 } 270 271 272 bool 273 BWindow::Shortcut::Matches(uint32 key, uint32 modifiers) const 274 { 275 return fKey == key && fModifiers == modifiers; 276 } 277 278 279 /*static*/ 280 uint32 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 290 BWindow::Shortcut::PrepareModifiers(uint32 modifiers) 291 { 292 return (modifiers & AllowedModifiers()) | B_COMMAND_KEY; 293 } 294 295 296 /*static*/ 297 uint32 298 BWindow::Shortcut::PrepareKey(uint32 key) 299 { 300 return BUnicodeChar::ToLower(key); 301 } 302 303 304 // #pragma mark - BWindow 305 306 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 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 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 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 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* 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 454 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 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 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 551 BWindow::AddChild(BLayoutItem* child) 552 { 553 BAutolock locker(this); 554 if (locker.IsLocked()) 555 fTopView->AddChild(child); 556 } 557 558 559 bool 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 571 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* 582 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 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 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 629 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 639 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 655 BWindow::DisableUpdates() 656 { 657 if (Lock()) { 658 fLink->StartMessage(AS_DISABLE_UPDATES); 659 fLink->Flush(); 660 Unlock(); 661 } 662 } 663 664 665 void 666 BWindow::EnableUpdates() 667 { 668 if (Lock()) { 669 fLink->StartMessage(AS_ENABLE_UPDATES); 670 fLink->Flush(); 671 Unlock(); 672 } 673 } 674 675 676 void 677 BWindow::BeginViewTransaction() 678 { 679 if (Lock()) { 680 fInTransaction = true; 681 Unlock(); 682 } 683 } 684 685 686 void 687 BWindow::EndViewTransaction() 688 { 689 if (Lock()) { 690 if (fInTransaction) 691 fLink->Flush(); 692 fInTransaction = false; 693 Unlock(); 694 } 695 } 696 697 698 bool 699 BWindow::InViewTransaction() const 700 { 701 BAutolock locker(const_cast<BWindow*>(this)); 702 return fInTransaction; 703 } 704 705 706 bool 707 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 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 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 1331 BWindow::FrameMoved(BPoint newPosition) 1332 { 1333 // does nothing 1334 // Hook function 1335 } 1336 1337 1338 void 1339 BWindow::FrameResized(float newWidth, float newHeight) 1340 { 1341 // does nothing 1342 // Hook function 1343 } 1344 1345 1346 void 1347 BWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 1348 { 1349 // does nothing 1350 // Hook function 1351 } 1352 1353 1354 void 1355 BWindow::WorkspaceActivated(int32 workspace, bool state) 1356 { 1357 // does nothing 1358 // Hook function 1359 } 1360 1361 1362 void 1363 BWindow::MenusBeginning() 1364 { 1365 // does nothing 1366 // Hook function 1367 } 1368 1369 1370 void 1371 BWindow::MenusEnded() 1372 { 1373 // does nothing 1374 // Hook function 1375 } 1376 1377 1378 void 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 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 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 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 1480 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 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 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 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 1645 BWindow::ScreenChanged(BRect screenSize, color_space depth) 1646 { 1647 // Hook function 1648 } 1649 1650 1651 void 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 1678 BWindow::PulseRate() const 1679 { 1680 return fPulseRate; 1681 } 1682 1683 1684 void 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 1699 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message) 1700 { 1701 AddShortcut(key, modifiers, message, this); 1702 } 1703 1704 1705 void 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 1725 BWindow::HasShortcut(uint32 key, uint32 modifiers) 1726 { 1727 return _FindShortcut(key, modifiers) != NULL; 1728 } 1729 1730 1731 void 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* 1746 BWindow::DefaultButton() const 1747 { 1748 // TODO: What about locking?!? 1749 return fDefaultButton; 1750 } 1751 1752 1753 void 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 1778 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 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* 1842 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* 1853 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* 1866 BWindow::CurrentFocus() const 1867 { 1868 return fFocus; 1869 } 1870 1871 1872 void 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 1892 BWindow::WindowActivated(bool focus) 1893 { 1894 // hook function 1895 // does nothing 1896 } 1897 1898 1899 void 1900 BWindow::ConvertToScreen(BPoint* point) const 1901 { 1902 point->x += fFrame.left; 1903 point->y += fFrame.top; 1904 } 1905 1906 1907 BPoint 1908 BWindow::ConvertToScreen(BPoint point) const 1909 { 1910 return point + fFrame.LeftTop(); 1911 } 1912 1913 1914 void 1915 BWindow::ConvertFromScreen(BPoint* point) const 1916 { 1917 point->x -= fFrame.left; 1918 point->y -= fFrame.top; 1919 } 1920 1921 1922 BPoint 1923 BWindow::ConvertFromScreen(BPoint point) const 1924 { 1925 return point - fFrame.LeftTop(); 1926 } 1927 1928 1929 void 1930 BWindow::ConvertToScreen(BRect* rect) const 1931 { 1932 rect->OffsetBy(fFrame.LeftTop()); 1933 } 1934 1935 1936 BRect 1937 BWindow::ConvertToScreen(BRect rect) const 1938 { 1939 return rect.OffsetByCopy(fFrame.LeftTop()); 1940 } 1941 1942 1943 void 1944 BWindow::ConvertFromScreen(BRect* rect) const 1945 { 1946 rect->OffsetBy(-fFrame.left, -fFrame.top); 1947 } 1948 1949 1950 BRect 1951 BWindow::ConvertFromScreen(BRect rect) const 1952 { 1953 return rect.OffsetByCopy(-fFrame.left, -fFrame.top); 1954 } 1955 1956 1957 bool 1958 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 1969 BWindow::Bounds() const 1970 { 1971 return BRect(0, 0, fFrame.Width(), fFrame.Height()); 1972 } 1973 1974 1975 BRect 1976 BWindow::Frame() const 1977 { 1978 return fFrame; 1979 } 1980 1981 1982 BRect 1983 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 2020 BWindow::Size() const 2021 { 2022 return BSize(fFrame.Width(), fFrame.Height()); 2023 } 2024 2025 2026 const char* 2027 BWindow::Title() const 2028 { 2029 return fTitle; 2030 } 2031 2032 2033 void 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 2055 BWindow::IsActive() const 2056 { 2057 return fActive; 2058 } 2059 2060 2061 void 2062 BWindow::SetKeyMenuBar(BMenuBar* bar) 2063 { 2064 fKeyMenuBar = bar; 2065 } 2066 2067 2068 BMenuBar* 2069 BWindow::KeyMenuBar() const 2070 { 2071 return fKeyMenuBar; 2072 } 2073 2074 2075 bool 2076 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 2086 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 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 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 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 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 2170 BWindow::Type() const 2171 { 2172 return _ComposeType(fLook, fFeel); 2173 } 2174 2175 2176 status_t 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 2198 BWindow::Look() const 2199 { 2200 return fLook; 2201 } 2202 2203 2204 status_t 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 2223 BWindow::Feel() const 2224 { 2225 return fFeel; 2226 } 2227 2228 2229 status_t 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 2248 BWindow::Flags() const 2249 { 2250 return fFlags; 2251 } 2252 2253 2254 status_t 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 2292 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 2320 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 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* 2355 BWindow::LastMouseMovedView() const 2356 { 2357 return fLastMouseMovedView; 2358 } 2359 2360 2361 void 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 2372 BWindow::MoveTo(BPoint point) 2373 { 2374 MoveTo(point.x, point.y); 2375 } 2376 2377 2378 void 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 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 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 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 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 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 2493 BWindow::CenterOnScreen(screen_id id) 2494 { 2495 CenterIn(BScreen(id).Frame()); 2496 } 2497 2498 2499 void 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 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 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 2602 BWindow::IsHidden() const 2603 { 2604 return fShowLevel > 0; 2605 } 2606 2607 2608 bool 2609 BWindow::QuitRequested() 2610 { 2611 return BLooper::QuitRequested(); 2612 } 2613 2614 2615 thread_id 2616 BWindow::Run() 2617 { 2618 EnableUpdates(); 2619 return BLooper::Run(); 2620 } 2621 2622 2623 void 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* 2635 BWindow::GetLayout() const 2636 { 2637 return fTopView->GetLayout(); 2638 } 2639 2640 2641 void 2642 BWindow::InvalidateLayout(bool descendants) 2643 { 2644 fTopView->InvalidateLayout(descendants); 2645 } 2646 2647 2648 void 2649 BWindow::Layout(bool force) 2650 { 2651 UpdateSizeLimits(); 2652 2653 // Do the actual layout 2654 fTopView->Layout(force); 2655 } 2656 2657 2658 bool 2659 BWindow::IsOffscreenWindow() const 2660 { 2661 return fOffscreen; 2662 } 2663 2664 2665 status_t 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* 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 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 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 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 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 3066 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 3104 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 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 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 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* 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 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 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 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 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 3567 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 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 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 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 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 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* 3874 BWindow::ConvertToMessage(void* raw, int32 code) 3875 { 3876 return BLooper::ConvertToMessage(raw, code); 3877 } 3878 3879 3880 BWindow::Shortcut* 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* 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* 3918 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* 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* 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* 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 4036 BWindow::SetIsFilePanel(bool isFilePanel) 4037 { 4038 fIsFilePanel = isFilePanel; 4039 } 4040 4041 4042 bool 4043 BWindow::IsFilePanel() const 4044 { 4045 return fIsFilePanel; 4046 } 4047 4048 4049 void 4050 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 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 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 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 4113 void BWindow::_ReservedWindow2() {} 4114 void BWindow::_ReservedWindow3() {} 4115 void BWindow::_ReservedWindow4() {} 4116 void BWindow::_ReservedWindow5() {} 4117 void BWindow::_ReservedWindow6() {} 4118 void BWindow::_ReservedWindow7() {} 4119 void BWindow::_ReservedWindow8() {} 4120 4121