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 // propegate message to child views 1117 int32 childCount = CountChildren(); 1118 for (int32 i = 0; i < childCount; i++) { 1119 BView* view = ChildAt(i); 1120 if (view != NULL) 1121 view->MessageReceived(message); 1122 } 1123 // call hook method 1124 ScreenChanged(frame, (color_space)mode); 1125 } 1126 } else 1127 target->MessageReceived(message); 1128 break; 1129 1130 case B_WORKSPACE_ACTIVATED: 1131 if (target == this) { 1132 uint32 workspace; 1133 bool active; 1134 if (message->FindInt32("workspace", (int32*)&workspace) == B_OK 1135 && message->FindBool("active", &active) == B_OK) 1136 WorkspaceActivated(workspace, active); 1137 } else 1138 target->MessageReceived(message); 1139 break; 1140 1141 case B_WORKSPACES_CHANGED: 1142 if (target == this) { 1143 uint32 oldWorkspace, newWorkspace; 1144 if (message->FindInt32("old", (int32*)&oldWorkspace) == B_OK 1145 && message->FindInt32("new", (int32*)&newWorkspace) == B_OK) 1146 WorkspacesChanged(oldWorkspace, newWorkspace); 1147 } else 1148 target->MessageReceived(message); 1149 break; 1150 1151 case B_KEY_DOWN: 1152 if (!_HandleKeyDown(message)) 1153 target->MessageReceived(message); 1154 break; 1155 1156 case B_UNMAPPED_KEY_DOWN: 1157 if (!_HandleUnmappedKeyDown(message)) 1158 target->MessageReceived(message); 1159 break; 1160 1161 case B_PULSE: 1162 if (target == this && fPulseRunner) { 1163 fTopView->_Pulse(); 1164 fLink->Flush(); 1165 } else 1166 target->MessageReceived(message); 1167 break; 1168 1169 case _UPDATE_: 1170 { 1171 //bigtime_t now = system_time(); 1172 //bigtime_t drawTime = 0; 1173 STRACE(("info:BWindow handling _UPDATE_.\n")); 1174 1175 fLink->StartMessage(AS_BEGIN_UPDATE); 1176 fInTransaction = true; 1177 1178 int32 code; 1179 if (fLink->FlushWithReply(code) == B_OK 1180 && code == B_OK) { 1181 // read current window position and size first, 1182 // the update rect is in screen coordinates... 1183 // so we need to be up to date 1184 BPoint origin; 1185 fLink->Read<BPoint>(&origin); 1186 float width; 1187 float height; 1188 fLink->Read<float>(&width); 1189 fLink->Read<float>(&height); 1190 1191 // read tokens for views that need to be drawn 1192 // NOTE: we need to read the tokens completely 1193 // first, we cannot draw views in between reading 1194 // the tokens, since other communication would likely 1195 // mess up the data in the link. 1196 struct ViewUpdateInfo { 1197 int32 token; 1198 BRect updateRect; 1199 }; 1200 BList infos(20); 1201 while (true) { 1202 // read next token and create/add ViewUpdateInfo 1203 int32 token; 1204 status_t error = fLink->Read<int32>(&token); 1205 if (error < B_OK || token == B_NULL_TOKEN) 1206 break; 1207 ViewUpdateInfo* info = new(std::nothrow) ViewUpdateInfo; 1208 if (info == NULL || !infos.AddItem(info)) { 1209 delete info; 1210 break; 1211 } 1212 info->token = token; 1213 // read culmulated update rect (is in screen coords) 1214 error = fLink->Read<BRect>(&(info->updateRect)); 1215 if (error < B_OK) 1216 break; 1217 } 1218 // Hooks should be called after finishing reading reply because 1219 // they can access fLink. 1220 if (origin != fFrame.LeftTop()) { 1221 // TODO: remove code duplicatation with 1222 // B_WINDOW_MOVED case... 1223 //printf("window position was not up to date\n"); 1224 fFrame.OffsetTo(origin); 1225 FrameMoved(origin); 1226 } 1227 if (width != fFrame.Width() || height != fFrame.Height()) { 1228 // TODO: remove code duplicatation with 1229 // B_WINDOW_RESIZED case... 1230 //printf("window size was not up to date\n"); 1231 fFrame.right = fFrame.left + width; 1232 fFrame.bottom = fFrame.top + height; 1233 1234 _AdoptResize(); 1235 FrameResized(width, height); 1236 } 1237 1238 // draw 1239 int32 count = infos.CountItems(); 1240 for (int32 i = 0; i < count; i++) { 1241 //bigtime_t drawStart = system_time(); 1242 ViewUpdateInfo* info 1243 = (ViewUpdateInfo*)infos.ItemAtFast(i); 1244 if (BView* view = _FindView(info->token)) 1245 view->_Draw(info->updateRect); 1246 else { 1247 printf("_UPDATE_ - didn't find view by token: %" 1248 B_PRId32 "\n", info->token); 1249 } 1250 //drawTime += system_time() - drawStart; 1251 } 1252 // NOTE: The tokens are actually hirachically sorted, 1253 // so traversing the list in revers and calling 1254 // child->_DrawAfterChildren() actually works like intended. 1255 for (int32 i = count - 1; i >= 0; i--) { 1256 ViewUpdateInfo* info 1257 = (ViewUpdateInfo*)infos.ItemAtFast(i); 1258 if (BView* view = _FindView(info->token)) 1259 view->_DrawAfterChildren(info->updateRect); 1260 delete info; 1261 } 1262 1263 //printf(" %ld views drawn, total Draw() time: %lld\n", count, drawTime); 1264 } 1265 1266 fLink->StartMessage(AS_END_UPDATE); 1267 fLink->Flush(); 1268 fInTransaction = false; 1269 fUpdateRequested = false; 1270 1271 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now); 1272 break; 1273 } 1274 1275 case _MENUS_DONE_: 1276 MenusEnded(); 1277 break; 1278 1279 // These two are obviously some kind of old scripting messages 1280 // this is NOT an app_server message and we have to be cautious 1281 case B_WINDOW_MOVE_BY: 1282 { 1283 BPoint offset; 1284 if (message->FindPoint("data", &offset) == B_OK) 1285 MoveBy(offset.x, offset.y); 1286 else 1287 message->SendReply(B_MESSAGE_NOT_UNDERSTOOD); 1288 break; 1289 } 1290 1291 // this is NOT an app_server message and we have to be cautious 1292 case B_WINDOW_MOVE_TO: 1293 { 1294 BPoint origin; 1295 if (message->FindPoint("data", &origin) == B_OK) 1296 MoveTo(origin); 1297 else 1298 message->SendReply(B_MESSAGE_NOT_UNDERSTOOD); 1299 break; 1300 } 1301 1302 case B_LAYOUT_WINDOW: 1303 { 1304 Layout(false); 1305 break; 1306 } 1307 1308 case B_COLORS_UPDATED: 1309 { 1310 fTopView->_ColorsUpdated(message); 1311 target->MessageReceived(message); 1312 break; 1313 } 1314 1315 case B_FONTS_UPDATED: 1316 { 1317 fTopView->_FontsUpdated(message); 1318 target->MessageReceived(message); 1319 break; 1320 } 1321 1322 default: 1323 BLooper::DispatchMessage(message, target); 1324 break; 1325 } 1326 } 1327 1328 1329 void 1330 BWindow::FrameMoved(BPoint newPosition) 1331 { 1332 // does nothing 1333 // Hook function 1334 } 1335 1336 1337 void 1338 BWindow::FrameResized(float newWidth, float newHeight) 1339 { 1340 // does nothing 1341 // Hook function 1342 } 1343 1344 1345 void 1346 BWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 1347 { 1348 // does nothing 1349 // Hook function 1350 } 1351 1352 1353 void 1354 BWindow::WorkspaceActivated(int32 workspace, bool state) 1355 { 1356 // does nothing 1357 // Hook function 1358 } 1359 1360 1361 void 1362 BWindow::MenusBeginning() 1363 { 1364 // does nothing 1365 // Hook function 1366 } 1367 1368 1369 void 1370 BWindow::MenusEnded() 1371 { 1372 // does nothing 1373 // Hook function 1374 } 1375 1376 1377 void 1378 BWindow::SetSizeLimits(float minWidth, float maxWidth, 1379 float minHeight, float maxHeight) 1380 { 1381 if (minWidth > maxWidth || minHeight > maxHeight) 1382 return; 1383 1384 if (!Lock()) 1385 return; 1386 1387 fLink->StartMessage(AS_SET_SIZE_LIMITS); 1388 fLink->Attach<float>(minWidth); 1389 fLink->Attach<float>(maxWidth); 1390 fLink->Attach<float>(minHeight); 1391 fLink->Attach<float>(maxHeight); 1392 1393 int32 code; 1394 if (fLink->FlushWithReply(code) == B_OK 1395 && code == B_OK) { 1396 // read the values that were really enforced on 1397 // the server side (the window frame could have 1398 // been changed, too) 1399 fLink->Read<BRect>(&fFrame); 1400 fLink->Read<float>(&fMinWidth); 1401 fLink->Read<float>(&fMaxWidth); 1402 fLink->Read<float>(&fMinHeight); 1403 fLink->Read<float>(&fMaxHeight); 1404 1405 _AdoptResize(); 1406 // TODO: the same has to be done for SetLook() (that can alter 1407 // the size limits, and hence, the size of the window 1408 } 1409 Unlock(); 1410 } 1411 1412 1413 void 1414 BWindow::GetSizeLimits(float* _minWidth, float* _maxWidth, float* _minHeight, 1415 float* _maxHeight) 1416 { 1417 // TODO: What about locking?!? 1418 if (_minHeight != NULL) 1419 *_minHeight = fMinHeight; 1420 if (_minWidth != NULL) 1421 *_minWidth = fMinWidth; 1422 if (_maxHeight != NULL) 1423 *_maxHeight = fMaxHeight; 1424 if (_maxWidth != NULL) 1425 *_maxWidth = fMaxWidth; 1426 } 1427 1428 1429 void 1430 BWindow::UpdateSizeLimits() 1431 { 1432 BAutolock locker(this); 1433 1434 if ((fFlags & B_AUTO_UPDATE_SIZE_LIMITS) != 0) { 1435 // Get min/max constraints of the top view and enforce window 1436 // size limits respectively. 1437 BSize minSize = fTopView->MinSize(); 1438 BSize maxSize = fTopView->MaxSize(); 1439 SetSizeLimits(minSize.width, maxSize.width, 1440 minSize.height, maxSize.height); 1441 } 1442 } 1443 1444 1445 status_t 1446 BWindow::SetDecoratorSettings(const BMessage& settings) 1447 { 1448 // flatten the given settings into a buffer and send 1449 // it to the app_server to apply the settings to the 1450 // decorator 1451 1452 int32 size = settings.FlattenedSize(); 1453 char buffer[size]; 1454 status_t status = settings.Flatten(buffer, size); 1455 if (status != B_OK) 1456 return status; 1457 1458 if (!Lock()) 1459 return B_ERROR; 1460 1461 status = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS); 1462 1463 if (status == B_OK) 1464 status = fLink->Attach<int32>(size); 1465 1466 if (status == B_OK) 1467 status = fLink->Attach(buffer, size); 1468 1469 if (status == B_OK) 1470 status = fLink->Flush(); 1471 1472 Unlock(); 1473 1474 return status; 1475 } 1476 1477 1478 status_t 1479 BWindow::GetDecoratorSettings(BMessage* settings) const 1480 { 1481 // read a flattened settings message from the app_server 1482 // and put it into settings 1483 1484 if (!const_cast<BWindow*>(this)->Lock()) 1485 return B_ERROR; 1486 1487 status_t status = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS); 1488 1489 if (status == B_OK) { 1490 int32 code; 1491 status = fLink->FlushWithReply(code); 1492 if (status == B_OK && code != B_OK) 1493 status = code; 1494 } 1495 1496 if (status == B_OK) { 1497 int32 size; 1498 status = fLink->Read<int32>(&size); 1499 if (status == B_OK) { 1500 char buffer[size]; 1501 status = fLink->Read(buffer, size); 1502 if (status == B_OK) { 1503 status = settings->Unflatten(buffer); 1504 } 1505 } 1506 } 1507 1508 const_cast<BWindow*>(this)->Unlock(); 1509 1510 return status; 1511 } 1512 1513 1514 void 1515 BWindow::SetZoomLimits(float maxWidth, float maxHeight) 1516 { 1517 // TODO: What about locking?!? 1518 if (maxWidth > fMaxWidth) 1519 maxWidth = fMaxWidth; 1520 else 1521 fMaxZoomWidth = maxWidth; 1522 1523 if (maxHeight > fMaxHeight) 1524 maxHeight = fMaxHeight; 1525 else 1526 fMaxZoomHeight = maxHeight; 1527 } 1528 1529 1530 void 1531 BWindow::Zoom(BPoint origin, float width, float height) 1532 { 1533 // the default implementation of this hook function 1534 // just does the obvious: 1535 MoveTo(origin); 1536 ResizeTo(width, height); 1537 } 1538 1539 1540 void 1541 BWindow::Zoom() 1542 { 1543 // TODO: What about locking?!? 1544 1545 // From BeBook: 1546 // The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced 1547 // from the smallest of three rectangles: 1548 1549 // 1) the rectangle defined by SetZoomLimits() and, 1550 // 2) the rectangle defined by SetSizeLimits() 1551 float maxZoomWidth = std::min(fMaxZoomWidth, fMaxWidth); 1552 float maxZoomHeight = std::min(fMaxZoomHeight, fMaxHeight); 1553 1554 // 3) the screen rectangle 1555 BRect screenFrame = (BScreen(this)).Frame(); 1556 maxZoomWidth = std::min(maxZoomWidth, screenFrame.Width()); 1557 maxZoomHeight = std::min(maxZoomHeight, screenFrame.Height()); 1558 1559 BRect zoomArea = screenFrame; // starts at screen size 1560 1561 BDeskbar deskbar; 1562 BRect deskbarFrame = deskbar.Frame(); 1563 bool isShiftDown = (modifiers() & B_SHIFT_KEY) != 0; 1564 if (!isShiftDown && !deskbar.IsAutoHide()) { 1565 // remove area taken up by Deskbar unless hidden or shift is held down 1566 switch (deskbar.Location()) { 1567 case B_DESKBAR_TOP: 1568 zoomArea.top = deskbarFrame.bottom + 2; 1569 break; 1570 1571 case B_DESKBAR_BOTTOM: 1572 case B_DESKBAR_LEFT_BOTTOM: 1573 case B_DESKBAR_RIGHT_BOTTOM: 1574 zoomArea.bottom = deskbarFrame.top - 2; 1575 break; 1576 1577 // in vertical expando mode only if not always-on-top or auto-raise 1578 case B_DESKBAR_LEFT_TOP: 1579 if (!deskbar.IsExpanded()) 1580 zoomArea.top = deskbarFrame.bottom + 2; 1581 else if (!deskbar.IsAlwaysOnTop() && !deskbar.IsAutoRaise()) 1582 zoomArea.left = deskbarFrame.right + 2; 1583 break; 1584 1585 default: 1586 case B_DESKBAR_RIGHT_TOP: 1587 if (!deskbar.IsExpanded()) 1588 break; 1589 else if (!deskbar.IsAlwaysOnTop() && !deskbar.IsAutoRaise()) 1590 zoomArea.right = deskbarFrame.left - 2; 1591 break; 1592 } 1593 } 1594 1595 // TODO: Broken for tab on left side windows... 1596 float borderWidth; 1597 float tabHeight; 1598 _GetDecoratorSize(&borderWidth, &tabHeight); 1599 1600 // remove the area taken up by the tab and border 1601 zoomArea.left += borderWidth; 1602 zoomArea.top += borderWidth + tabHeight; 1603 zoomArea.right -= borderWidth; 1604 zoomArea.bottom -= borderWidth; 1605 1606 // inset towards center vertically first to see if there will be room 1607 // above or below Deskbar 1608 if (zoomArea.Height() > maxZoomHeight) 1609 zoomArea.InsetBy(0, roundf((zoomArea.Height() - maxZoomHeight) / 2)); 1610 1611 if (zoomArea.top > deskbarFrame.bottom 1612 || zoomArea.bottom < deskbarFrame.top) { 1613 // there is room above or below Deskbar, start from screen width 1614 // minus borders instead of desktop width minus borders 1615 zoomArea.left = screenFrame.left + borderWidth; 1616 zoomArea.right = screenFrame.right - borderWidth; 1617 } 1618 1619 // inset towards center 1620 if (zoomArea.Width() > maxZoomWidth) 1621 zoomArea.InsetBy(roundf((zoomArea.Width() - maxZoomWidth) / 2), 0); 1622 1623 // Un-Zoom 1624 1625 if (fPreviousFrame.IsValid() 1626 // NOTE: don't check for fFrame.LeftTop() == zoomArea.LeftTop() 1627 // -> makes it easier on the user to get a window back into place 1628 && fFrame.Width() == zoomArea.Width() 1629 && fFrame.Height() == zoomArea.Height()) { 1630 // already zoomed! 1631 Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(), 1632 fPreviousFrame.Height()); 1633 return; 1634 } 1635 1636 // Zoom 1637 1638 // remember fFrame for later "unzooming" 1639 fPreviousFrame = fFrame; 1640 1641 Zoom(zoomArea.LeftTop(), zoomArea.Width(), zoomArea.Height()); 1642 } 1643 1644 1645 void 1646 BWindow::ScreenChanged(BRect screenSize, color_space depth) 1647 { 1648 // Hook function 1649 } 1650 1651 1652 void 1653 BWindow::SetPulseRate(bigtime_t rate) 1654 { 1655 // TODO: What about locking?!? 1656 if (rate < 0 1657 || (rate == fPulseRate && !((rate == 0) ^ (fPulseRunner == NULL)))) 1658 return; 1659 1660 fPulseRate = rate; 1661 1662 if (rate > 0) { 1663 if (fPulseRunner == NULL) { 1664 BMessage message(B_PULSE); 1665 fPulseRunner = new(std::nothrow) BMessageRunner(BMessenger(this), 1666 &message, rate); 1667 } else { 1668 fPulseRunner->SetInterval(rate); 1669 } 1670 } else { 1671 // rate == 0 1672 delete fPulseRunner; 1673 fPulseRunner = NULL; 1674 } 1675 } 1676 1677 1678 bigtime_t 1679 BWindow::PulseRate() const 1680 { 1681 return fPulseRate; 1682 } 1683 1684 1685 void 1686 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem* item) 1687 { 1688 Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, item); 1689 if (shortcut == NULL) 1690 return; 1691 1692 // removes the shortcut if it already exists! 1693 RemoveShortcut(key, modifiers); 1694 1695 fShortcuts.AddItem(shortcut); 1696 } 1697 1698 1699 void 1700 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message) 1701 { 1702 AddShortcut(key, modifiers, message, this); 1703 } 1704 1705 1706 void 1707 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message, 1708 BHandler* target) 1709 { 1710 if (message == NULL) 1711 return; 1712 1713 Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, message, 1714 target); 1715 if (shortcut == NULL) 1716 return; 1717 1718 // removes the shortcut if it already exists! 1719 RemoveShortcut(key, modifiers); 1720 1721 fShortcuts.AddItem(shortcut); 1722 } 1723 1724 1725 bool 1726 BWindow::HasShortcut(uint32 key, uint32 modifiers) 1727 { 1728 return _FindShortcut(key, modifiers) != NULL; 1729 } 1730 1731 1732 void 1733 BWindow::RemoveShortcut(uint32 key, uint32 modifiers) 1734 { 1735 Shortcut* shortcut = _FindShortcut(key, modifiers); 1736 if (shortcut != NULL) { 1737 fShortcuts.RemoveItem(shortcut); 1738 delete shortcut; 1739 } else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) { 1740 // the quit shortcut is a fake shortcut 1741 fNoQuitShortcut = true; 1742 } 1743 } 1744 1745 1746 BButton* 1747 BWindow::DefaultButton() const 1748 { 1749 // TODO: What about locking?!? 1750 return fDefaultButton; 1751 } 1752 1753 1754 void 1755 BWindow::SetDefaultButton(BButton* button) 1756 { 1757 // TODO: What about locking?!? 1758 if (fDefaultButton == button) 1759 return; 1760 1761 if (fDefaultButton != NULL) { 1762 // tell old button it's no longer the default one 1763 BButton* oldDefault = fDefaultButton; 1764 oldDefault->MakeDefault(false); 1765 oldDefault->Invalidate(); 1766 } 1767 1768 fDefaultButton = button; 1769 1770 if (button != NULL) { 1771 // notify new default button 1772 fDefaultButton->MakeDefault(true); 1773 fDefaultButton->Invalidate(); 1774 } 1775 } 1776 1777 1778 bool 1779 BWindow::NeedsUpdate() const 1780 { 1781 if (!const_cast<BWindow*>(this)->Lock()) 1782 return false; 1783 1784 fLink->StartMessage(AS_NEEDS_UPDATE); 1785 1786 int32 code = B_ERROR; 1787 fLink->FlushWithReply(code); 1788 1789 const_cast<BWindow*>(this)->Unlock(); 1790 1791 return code == B_OK; 1792 } 1793 1794 1795 void 1796 BWindow::UpdateIfNeeded() 1797 { 1798 // works only from the window thread 1799 if (find_thread(NULL) != Thread()) 1800 return; 1801 1802 // if the queue is already locked we are called recursivly 1803 // from our own dispatched update message 1804 if (((const BMessageQueue*)MessageQueue())->IsLocked()) 1805 return; 1806 1807 if (!Lock()) 1808 return; 1809 1810 // make sure all requests that would cause an update have 1811 // arrived at the server 1812 Sync(); 1813 1814 // Since we're blocking the event loop, we need to retrieve 1815 // all messages that are pending on the port. 1816 _DequeueAll(); 1817 1818 BMessageQueue* queue = MessageQueue(); 1819 1820 // First process and remove any _UPDATE_ message in the queue 1821 // With the current design, there can only be one at a time 1822 1823 while (true) { 1824 queue->Lock(); 1825 1826 BMessage* message = queue->FindMessage(_UPDATE_, 0); 1827 queue->RemoveMessage(message); 1828 1829 queue->Unlock(); 1830 1831 if (message == NULL) 1832 break; 1833 1834 BWindow::DispatchMessage(message, this); 1835 delete message; 1836 } 1837 1838 Unlock(); 1839 } 1840 1841 1842 BView* 1843 BWindow::FindView(const char* viewName) const 1844 { 1845 BAutolock locker(const_cast<BWindow*>(this)); 1846 if (!locker.IsLocked()) 1847 return NULL; 1848 1849 return fTopView->FindView(viewName); 1850 } 1851 1852 1853 BView* 1854 BWindow::FindView(BPoint point) const 1855 { 1856 BAutolock locker(const_cast<BWindow*>(this)); 1857 if (!locker.IsLocked()) 1858 return NULL; 1859 1860 // point is assumed to be in window coordinates, 1861 // fTopView has same bounds as window 1862 return _FindView(fTopView, point); 1863 } 1864 1865 1866 BView* 1867 BWindow::CurrentFocus() const 1868 { 1869 return fFocus; 1870 } 1871 1872 1873 void 1874 BWindow::Activate(bool active) 1875 { 1876 if (!Lock()) 1877 return; 1878 1879 if (!IsHidden()) { 1880 fMinimized = false; 1881 // activating a window will also unminimize it 1882 1883 fLink->StartMessage(AS_ACTIVATE_WINDOW); 1884 fLink->Attach<bool>(active); 1885 fLink->Flush(); 1886 } 1887 1888 Unlock(); 1889 } 1890 1891 1892 void 1893 BWindow::WindowActivated(bool focus) 1894 { 1895 // hook function 1896 // does nothing 1897 } 1898 1899 1900 void 1901 BWindow::ConvertToScreen(BPoint* point) const 1902 { 1903 point->x += fFrame.left; 1904 point->y += fFrame.top; 1905 } 1906 1907 1908 BPoint 1909 BWindow::ConvertToScreen(BPoint point) const 1910 { 1911 return point + fFrame.LeftTop(); 1912 } 1913 1914 1915 void 1916 BWindow::ConvertFromScreen(BPoint* point) const 1917 { 1918 point->x -= fFrame.left; 1919 point->y -= fFrame.top; 1920 } 1921 1922 1923 BPoint 1924 BWindow::ConvertFromScreen(BPoint point) const 1925 { 1926 return point - fFrame.LeftTop(); 1927 } 1928 1929 1930 void 1931 BWindow::ConvertToScreen(BRect* rect) const 1932 { 1933 rect->OffsetBy(fFrame.LeftTop()); 1934 } 1935 1936 1937 BRect 1938 BWindow::ConvertToScreen(BRect rect) const 1939 { 1940 return rect.OffsetByCopy(fFrame.LeftTop()); 1941 } 1942 1943 1944 void 1945 BWindow::ConvertFromScreen(BRect* rect) const 1946 { 1947 rect->OffsetBy(-fFrame.left, -fFrame.top); 1948 } 1949 1950 1951 BRect 1952 BWindow::ConvertFromScreen(BRect rect) const 1953 { 1954 return rect.OffsetByCopy(-fFrame.left, -fFrame.top); 1955 } 1956 1957 1958 bool 1959 BWindow::IsMinimized() const 1960 { 1961 BAutolock locker(const_cast<BWindow*>(this)); 1962 if (!locker.IsLocked()) 1963 return false; 1964 1965 return fMinimized; 1966 } 1967 1968 1969 BRect 1970 BWindow::Bounds() const 1971 { 1972 return BRect(0, 0, fFrame.Width(), fFrame.Height()); 1973 } 1974 1975 1976 BRect 1977 BWindow::Frame() const 1978 { 1979 return fFrame; 1980 } 1981 1982 1983 BRect 1984 BWindow::DecoratorFrame() const 1985 { 1986 BRect decoratorFrame(Frame()); 1987 BRect tabRect(0, 0, 0, 0); 1988 1989 float borderWidth = 5.0; 1990 1991 BMessage settings; 1992 if (GetDecoratorSettings(&settings) == B_OK) { 1993 settings.FindRect("tab frame", &tabRect); 1994 settings.FindFloat("border width", &borderWidth); 1995 } else { 1996 // probably no-border window look 1997 if (fLook == B_NO_BORDER_WINDOW_LOOK) 1998 borderWidth = 0.f; 1999 else if (fLook == B_BORDERED_WINDOW_LOOK) 2000 borderWidth = 1.f; 2001 // else use fall-back values from above 2002 } 2003 2004 if (fLook == kLeftTitledWindowLook) { 2005 decoratorFrame.top -= borderWidth; 2006 decoratorFrame.left -= borderWidth + tabRect.Width(); 2007 decoratorFrame.right += borderWidth; 2008 decoratorFrame.bottom += borderWidth; 2009 } else { 2010 decoratorFrame.top -= borderWidth + tabRect.Height(); 2011 decoratorFrame.left -= borderWidth; 2012 decoratorFrame.right += borderWidth; 2013 decoratorFrame.bottom += borderWidth; 2014 } 2015 2016 return decoratorFrame; 2017 } 2018 2019 2020 BSize 2021 BWindow::Size() const 2022 { 2023 return BSize(fFrame.Width(), fFrame.Height()); 2024 } 2025 2026 2027 const char* 2028 BWindow::Title() const 2029 { 2030 return fTitle; 2031 } 2032 2033 2034 void 2035 BWindow::SetTitle(const char* title) 2036 { 2037 if (title == NULL) 2038 title = ""; 2039 2040 free(fTitle); 2041 fTitle = strdup(title); 2042 2043 _SetName(title); 2044 2045 // we notify the app_server so we can actually see the change 2046 if (Lock()) { 2047 fLink->StartMessage(AS_SET_WINDOW_TITLE); 2048 fLink->AttachString(fTitle); 2049 fLink->Flush(); 2050 Unlock(); 2051 } 2052 } 2053 2054 2055 bool 2056 BWindow::IsActive() const 2057 { 2058 return fActive; 2059 } 2060 2061 2062 void 2063 BWindow::SetKeyMenuBar(BMenuBar* bar) 2064 { 2065 fKeyMenuBar = bar; 2066 } 2067 2068 2069 BMenuBar* 2070 BWindow::KeyMenuBar() const 2071 { 2072 return fKeyMenuBar; 2073 } 2074 2075 2076 bool 2077 BWindow::IsModal() const 2078 { 2079 return fFeel == B_MODAL_SUBSET_WINDOW_FEEL 2080 || fFeel == B_MODAL_APP_WINDOW_FEEL 2081 || fFeel == B_MODAL_ALL_WINDOW_FEEL 2082 || fFeel == kMenuWindowFeel; 2083 } 2084 2085 2086 bool 2087 BWindow::IsFloating() const 2088 { 2089 return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL 2090 || fFeel == B_FLOATING_APP_WINDOW_FEEL 2091 || fFeel == B_FLOATING_ALL_WINDOW_FEEL; 2092 } 2093 2094 2095 status_t 2096 BWindow::AddToSubset(BWindow* window) 2097 { 2098 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL 2099 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL 2100 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL)) 2101 return B_BAD_VALUE; 2102 2103 if (!Lock()) 2104 return B_ERROR; 2105 2106 status_t status = B_ERROR; 2107 fLink->StartMessage(AS_ADD_TO_SUBSET); 2108 fLink->Attach<int32>(_get_object_token_(window)); 2109 fLink->FlushWithReply(status); 2110 2111 Unlock(); 2112 2113 return status; 2114 } 2115 2116 2117 status_t 2118 BWindow::RemoveFromSubset(BWindow* window) 2119 { 2120 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL 2121 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL 2122 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL)) 2123 return B_BAD_VALUE; 2124 2125 if (!Lock()) 2126 return B_ERROR; 2127 2128 status_t status = B_ERROR; 2129 fLink->StartMessage(AS_REMOVE_FROM_SUBSET); 2130 fLink->Attach<int32>(_get_object_token_(window)); 2131 fLink->FlushWithReply(status); 2132 2133 Unlock(); 2134 2135 return status; 2136 } 2137 2138 2139 status_t 2140 BWindow::Perform(perform_code code, void* _data) 2141 { 2142 switch (code) { 2143 case PERFORM_CODE_SET_LAYOUT: 2144 { 2145 perform_data_set_layout* data = (perform_data_set_layout*)_data; 2146 BWindow::SetLayout(data->layout); 2147 return B_OK; 2148 } 2149 } 2150 2151 return BLooper::Perform(code, _data); 2152 } 2153 2154 2155 status_t 2156 BWindow::SetType(window_type type) 2157 { 2158 window_look look; 2159 window_feel feel; 2160 _DecomposeType(type, &look, &feel); 2161 2162 status_t status = SetLook(look); 2163 if (status == B_OK) 2164 status = SetFeel(feel); 2165 2166 return status; 2167 } 2168 2169 2170 window_type 2171 BWindow::Type() const 2172 { 2173 return _ComposeType(fLook, fFeel); 2174 } 2175 2176 2177 status_t 2178 BWindow::SetLook(window_look look) 2179 { 2180 BAutolock locker(this); 2181 if (!locker.IsLocked()) 2182 return B_BAD_VALUE; 2183 2184 fLink->StartMessage(AS_SET_LOOK); 2185 fLink->Attach<int32>((int32)look); 2186 2187 status_t status = B_ERROR; 2188 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2189 fLook = look; 2190 2191 // TODO: this could have changed the window size, and thus, we 2192 // need to get it from the server (and call _AdoptResize()). 2193 2194 return status; 2195 } 2196 2197 2198 window_look 2199 BWindow::Look() const 2200 { 2201 return fLook; 2202 } 2203 2204 2205 status_t 2206 BWindow::SetFeel(window_feel feel) 2207 { 2208 BAutolock locker(this); 2209 if (!locker.IsLocked()) 2210 return B_BAD_VALUE; 2211 2212 fLink->StartMessage(AS_SET_FEEL); 2213 fLink->Attach<int32>((int32)feel); 2214 2215 status_t status = B_ERROR; 2216 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2217 fFeel = feel; 2218 2219 return status; 2220 } 2221 2222 2223 window_feel 2224 BWindow::Feel() const 2225 { 2226 return fFeel; 2227 } 2228 2229 2230 status_t 2231 BWindow::SetFlags(uint32 flags) 2232 { 2233 BAutolock locker(this); 2234 if (!locker.IsLocked()) 2235 return B_BAD_VALUE; 2236 2237 fLink->StartMessage(AS_SET_FLAGS); 2238 fLink->Attach<uint32>(flags); 2239 2240 int32 status = B_ERROR; 2241 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2242 fFlags = flags; 2243 2244 return status; 2245 } 2246 2247 2248 uint32 2249 BWindow::Flags() const 2250 { 2251 return fFlags; 2252 } 2253 2254 2255 status_t 2256 BWindow::SetWindowAlignment(window_alignment mode, 2257 int32 h, int32 hOffset, int32 width, int32 widthOffset, 2258 int32 v, int32 vOffset, int32 height, int32 heightOffset) 2259 { 2260 if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0 2261 || (hOffset >= 0 && hOffset <= h) 2262 || (vOffset >= 0 && vOffset <= v) 2263 || (widthOffset >= 0 && widthOffset <= width) 2264 || (heightOffset >= 0 && heightOffset <= height)) 2265 return B_BAD_VALUE; 2266 2267 // TODO: test if hOffset = 0 and set it to 1 if true. 2268 2269 if (!Lock()) 2270 return B_ERROR; 2271 2272 fLink->StartMessage(AS_SET_ALIGNMENT); 2273 fLink->Attach<int32>((int32)mode); 2274 fLink->Attach<int32>(h); 2275 fLink->Attach<int32>(hOffset); 2276 fLink->Attach<int32>(width); 2277 fLink->Attach<int32>(widthOffset); 2278 fLink->Attach<int32>(v); 2279 fLink->Attach<int32>(vOffset); 2280 fLink->Attach<int32>(height); 2281 fLink->Attach<int32>(heightOffset); 2282 2283 status_t status = B_ERROR; 2284 fLink->FlushWithReply(status); 2285 2286 Unlock(); 2287 2288 return status; 2289 } 2290 2291 2292 status_t 2293 BWindow::GetWindowAlignment(window_alignment* mode, 2294 int32* h, int32* hOffset, int32* width, int32* widthOffset, 2295 int32* v, int32* vOffset, int32* height, int32* heightOffset) const 2296 { 2297 if (!const_cast<BWindow*>(this)->Lock()) 2298 return B_ERROR; 2299 2300 fLink->StartMessage(AS_GET_ALIGNMENT); 2301 2302 status_t status; 2303 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) { 2304 fLink->Read<int32>((int32*)mode); 2305 fLink->Read<int32>(h); 2306 fLink->Read<int32>(hOffset); 2307 fLink->Read<int32>(width); 2308 fLink->Read<int32>(widthOffset); 2309 fLink->Read<int32>(v); 2310 fLink->Read<int32>(hOffset); 2311 fLink->Read<int32>(height); 2312 fLink->Read<int32>(heightOffset); 2313 } 2314 2315 const_cast<BWindow*>(this)->Unlock(); 2316 return status; 2317 } 2318 2319 2320 uint32 2321 BWindow::Workspaces() const 2322 { 2323 if (!const_cast<BWindow*>(this)->Lock()) 2324 return 0; 2325 2326 uint32 workspaces = 0; 2327 2328 fLink->StartMessage(AS_GET_WORKSPACES); 2329 2330 status_t status; 2331 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2332 fLink->Read<uint32>(&workspaces); 2333 2334 const_cast<BWindow*>(this)->Unlock(); 2335 return workspaces; 2336 } 2337 2338 2339 void 2340 BWindow::SetWorkspaces(uint32 workspaces) 2341 { 2342 // TODO: don't forget about Tracker's background window. 2343 if (fFeel != B_NORMAL_WINDOW_FEEL) 2344 return; 2345 2346 if (Lock()) { 2347 fLink->StartMessage(AS_SET_WORKSPACES); 2348 fLink->Attach<uint32>(workspaces); 2349 fLink->Flush(); 2350 Unlock(); 2351 } 2352 } 2353 2354 2355 BView* 2356 BWindow::LastMouseMovedView() const 2357 { 2358 return fLastMouseMovedView; 2359 } 2360 2361 2362 void 2363 BWindow::MoveBy(float dx, float dy) 2364 { 2365 if ((dx != 0.0f || dy != 0.0f) && Lock()) { 2366 MoveTo(fFrame.left + dx, fFrame.top + dy); 2367 Unlock(); 2368 } 2369 } 2370 2371 2372 void 2373 BWindow::MoveTo(BPoint point) 2374 { 2375 MoveTo(point.x, point.y); 2376 } 2377 2378 2379 void 2380 BWindow::MoveTo(float x, float y) 2381 { 2382 if (!Lock()) 2383 return; 2384 2385 x = roundf(x); 2386 y = roundf(y); 2387 2388 if (fFrame.left != x || fFrame.top != y) { 2389 fLink->StartMessage(AS_WINDOW_MOVE); 2390 fLink->Attach<float>(x); 2391 fLink->Attach<float>(y); 2392 2393 status_t status; 2394 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2395 fFrame.OffsetTo(x, y); 2396 } 2397 2398 Unlock(); 2399 } 2400 2401 2402 void 2403 BWindow::ResizeBy(float dx, float dy) 2404 { 2405 if (Lock()) { 2406 ResizeTo(fFrame.Width() + dx, fFrame.Height() + dy); 2407 Unlock(); 2408 } 2409 } 2410 2411 2412 void 2413 BWindow::ResizeTo(float width, float height) 2414 { 2415 if (!Lock()) 2416 return; 2417 2418 width = roundf(width); 2419 height = roundf(height); 2420 2421 // stay in minimum & maximum frame limits 2422 if (width < fMinWidth) 2423 width = fMinWidth; 2424 else if (width > fMaxWidth) 2425 width = fMaxWidth; 2426 2427 if (height < fMinHeight) 2428 height = fMinHeight; 2429 else if (height > fMaxHeight) 2430 height = fMaxHeight; 2431 2432 if (width != fFrame.Width() || height != fFrame.Height()) { 2433 fLink->StartMessage(AS_WINDOW_RESIZE); 2434 fLink->Attach<float>(width); 2435 fLink->Attach<float>(height); 2436 2437 status_t status; 2438 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) { 2439 fFrame.right = fFrame.left + width; 2440 fFrame.bottom = fFrame.top + height; 2441 _AdoptResize(); 2442 } 2443 } 2444 2445 Unlock(); 2446 } 2447 2448 2449 void 2450 BWindow::ResizeToPreferred() 2451 { 2452 BAutolock locker(this); 2453 Layout(false); 2454 2455 float width = fTopView->PreferredSize().width; 2456 width = std::min(width, fTopView->MaxSize().width); 2457 width = std::max(width, fTopView->MinSize().width); 2458 2459 float height = fTopView->PreferredSize().height; 2460 height = std::min(width, fTopView->MaxSize().height); 2461 height = std::max(width, fTopView->MinSize().height); 2462 2463 if (GetLayout()->HasHeightForWidth()) 2464 GetLayout()->GetHeightForWidth(width, NULL, NULL, &height); 2465 2466 ResizeTo(width, height); 2467 } 2468 2469 2470 void 2471 BWindow::CenterIn(const BRect& rect) 2472 { 2473 BAutolock locker(this); 2474 2475 // Set size limits now if needed 2476 UpdateSizeLimits(); 2477 2478 MoveTo(BLayoutUtils::AlignInFrame(rect, Size(), 2479 BAlignment(B_ALIGN_HORIZONTAL_CENTER, 2480 B_ALIGN_VERTICAL_CENTER)).LeftTop()); 2481 MoveOnScreen(B_DO_NOT_RESIZE_TO_FIT | B_MOVE_IF_PARTIALLY_OFFSCREEN); 2482 } 2483 2484 2485 void 2486 BWindow::CenterOnScreen() 2487 { 2488 CenterIn(BScreen(this).Frame()); 2489 } 2490 2491 2492 // Centers the window on the screen with the passed in id. 2493 void 2494 BWindow::CenterOnScreen(screen_id id) 2495 { 2496 CenterIn(BScreen(id).Frame()); 2497 } 2498 2499 2500 void 2501 BWindow::MoveOnScreen(uint32 flags) 2502 { 2503 // Set size limits now if needed 2504 UpdateSizeLimits(); 2505 2506 BRect screenFrame = BScreen(this).Frame(); 2507 BRect frame = Frame(); 2508 2509 float borderWidth; 2510 float tabHeight; 2511 _GetDecoratorSize(&borderWidth, &tabHeight); 2512 2513 frame.InsetBy(-borderWidth, -borderWidth); 2514 frame.top -= tabHeight; 2515 2516 if ((flags & B_DO_NOT_RESIZE_TO_FIT) == 0) { 2517 // Make sure the window fits on the screen 2518 if (frame.Width() > screenFrame.Width()) 2519 frame.right -= frame.Width() - screenFrame.Width(); 2520 if (frame.Height() > screenFrame.Height()) 2521 frame.bottom -= frame.Height() - screenFrame.Height(); 2522 2523 BRect innerFrame = frame; 2524 innerFrame.top += tabHeight; 2525 innerFrame.InsetBy(borderWidth, borderWidth); 2526 ResizeTo(innerFrame.Width(), innerFrame.Height()); 2527 } 2528 2529 if (((flags & B_MOVE_IF_PARTIALLY_OFFSCREEN) == 0 2530 && !screenFrame.Contains(frame)) 2531 || !frame.Intersects(screenFrame)) { 2532 // Off and away 2533 CenterOnScreen(); 2534 return; 2535 } 2536 2537 // Move such that the upper left corner, and most of the window 2538 // will be visible. 2539 float left = frame.left; 2540 if (left < screenFrame.left) 2541 left = screenFrame.left; 2542 else if (frame.right > screenFrame.right) 2543 left = std::max(0.f, screenFrame.right - frame.Width()); 2544 2545 float top = frame.top; 2546 if (top < screenFrame.top) 2547 top = screenFrame.top; 2548 else if (frame.bottom > screenFrame.bottom) 2549 top = std::max(0.f, screenFrame.bottom - frame.Height()); 2550 2551 if (top != frame.top || left != frame.left) 2552 MoveTo(left + borderWidth, top + tabHeight + borderWidth); 2553 } 2554 2555 2556 void 2557 BWindow::Show() 2558 { 2559 bool runCalled = true; 2560 if (Lock()) { 2561 fShowLevel--; 2562 2563 _SendShowOrHideMessage(); 2564 2565 runCalled = fRunCalled; 2566 2567 Unlock(); 2568 } 2569 2570 if (!runCalled) { 2571 // This is the fist time Show() is called, which implicitly runs the 2572 // looper. NOTE: The window is still locked if it has not been 2573 // run yet, so accessing members is safe. 2574 if (fLink->SenderPort() < B_OK) { 2575 // We don't have valid app_server connection; there is no point 2576 // in starting our looper 2577 fThread = B_ERROR; 2578 return; 2579 } else 2580 Run(); 2581 } 2582 } 2583 2584 2585 void 2586 BWindow::Hide() 2587 { 2588 if (Lock()) { 2589 // If we are minimized and are about to be hidden, unminimize 2590 if (IsMinimized() && fShowLevel == 0) 2591 Minimize(false); 2592 2593 fShowLevel++; 2594 2595 _SendShowOrHideMessage(); 2596 2597 Unlock(); 2598 } 2599 } 2600 2601 2602 bool 2603 BWindow::IsHidden() const 2604 { 2605 return fShowLevel > 0; 2606 } 2607 2608 2609 bool 2610 BWindow::QuitRequested() 2611 { 2612 return BLooper::QuitRequested(); 2613 } 2614 2615 2616 thread_id 2617 BWindow::Run() 2618 { 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 // #pragma mark - C++ binary compatibility kludge 4089 4090 4091 extern "C" void 4092 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout) 4093 { 4094 // SetLayout() 4095 perform_data_set_layout data; 4096 data.layout = layout; 4097 window->Perform(PERFORM_CODE_SET_LAYOUT, &data); 4098 } 4099 4100 4101 void BWindow::_ReservedWindow2() {} 4102 void BWindow::_ReservedWindow3() {} 4103 void BWindow::_ReservedWindow4() {} 4104 void BWindow::_ReservedWindow5() {} 4105 void BWindow::_ReservedWindow6() {} 4106 void BWindow::_ReservedWindow7() {} 4107 void BWindow::_ReservedWindow8() {} 4108 4109