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