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