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 if (origin != fFrame.LeftTop()) { 1191 // TODO: remove code duplicatation with 1192 // B_WINDOW_MOVED case... 1193 //printf("window position was not up to date\n"); 1194 fFrame.OffsetTo(origin); 1195 FrameMoved(origin); 1196 } 1197 if (width != fFrame.Width() || height != fFrame.Height()) { 1198 // TODO: remove code duplicatation with 1199 // B_WINDOW_RESIZED case... 1200 //printf("window size was not up to date\n"); 1201 fFrame.right = fFrame.left + width; 1202 fFrame.bottom = fFrame.top + height; 1203 1204 _AdoptResize(); 1205 FrameResized(width, height); 1206 } 1207 1208 // read tokens for views that need to be drawn 1209 // NOTE: we need to read the tokens completely 1210 // first, we cannot draw views in between reading 1211 // the tokens, since other communication would likely 1212 // mess up the data in the link. 1213 struct ViewUpdateInfo { 1214 int32 token; 1215 BRect updateRect; 1216 }; 1217 BList infos(20); 1218 while (true) { 1219 // read next token and create/add ViewUpdateInfo 1220 int32 token; 1221 status_t error = fLink->Read<int32>(&token); 1222 if (error < B_OK || token == B_NULL_TOKEN) 1223 break; 1224 ViewUpdateInfo* info = new(std::nothrow) ViewUpdateInfo; 1225 if (info == NULL || !infos.AddItem(info)) { 1226 delete info; 1227 break; 1228 } 1229 info->token = token; 1230 // read culmulated update rect (is in screen coords) 1231 error = fLink->Read<BRect>(&(info->updateRect)); 1232 if (error < B_OK) 1233 break; 1234 } 1235 // draw 1236 int32 count = infos.CountItems(); 1237 for (int32 i = 0; i < count; i++) { 1238 //bigtime_t drawStart = system_time(); 1239 ViewUpdateInfo* info 1240 = (ViewUpdateInfo*)infos.ItemAtFast(i); 1241 if (BView* view = _FindView(info->token)) 1242 view->_Draw(info->updateRect); 1243 else { 1244 printf("_UPDATE_ - didn't find view by token: %" 1245 B_PRId32 "\n", info->token); 1246 } 1247 //drawTime += system_time() - drawStart; 1248 } 1249 // NOTE: The tokens are actually hirachically sorted, 1250 // so traversing the list in revers and calling 1251 // child->_DrawAfterChildren() actually works like intended. 1252 for (int32 i = count - 1; i >= 0; i--) { 1253 ViewUpdateInfo* info 1254 = (ViewUpdateInfo*)infos.ItemAtFast(i); 1255 if (BView* view = _FindView(info->token)) 1256 view->_DrawAfterChildren(info->updateRect); 1257 delete info; 1258 } 1259 1260 //printf(" %ld views drawn, total Draw() time: %lld\n", count, drawTime); 1261 } 1262 1263 fLink->StartMessage(AS_END_UPDATE); 1264 fLink->Flush(); 1265 fInTransaction = false; 1266 fUpdateRequested = false; 1267 1268 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now); 1269 break; 1270 } 1271 1272 case _MENUS_DONE_: 1273 MenusEnded(); 1274 break; 1275 1276 // These two are obviously some kind of old scripting messages 1277 // this is NOT an app_server message and we have to be cautious 1278 case B_WINDOW_MOVE_BY: 1279 { 1280 BPoint offset; 1281 if (message->FindPoint("data", &offset) == B_OK) 1282 MoveBy(offset.x, offset.y); 1283 else 1284 message->SendReply(B_MESSAGE_NOT_UNDERSTOOD); 1285 break; 1286 } 1287 1288 // this is NOT an app_server message and we have to be cautious 1289 case B_WINDOW_MOVE_TO: 1290 { 1291 BPoint origin; 1292 if (message->FindPoint("data", &origin) == B_OK) 1293 MoveTo(origin); 1294 else 1295 message->SendReply(B_MESSAGE_NOT_UNDERSTOOD); 1296 break; 1297 } 1298 1299 case B_LAYOUT_WINDOW: 1300 { 1301 Layout(false); 1302 break; 1303 } 1304 1305 case B_COLORS_UPDATED: 1306 { 1307 fTopView->_ColorsUpdated(message); 1308 target->MessageReceived(message); 1309 break; 1310 } 1311 1312 case B_FONTS_UPDATED: 1313 { 1314 fTopView->_FontsUpdated(message); 1315 target->MessageReceived(message); 1316 break; 1317 } 1318 1319 default: 1320 BLooper::DispatchMessage(message, target); 1321 break; 1322 } 1323 } 1324 1325 1326 void 1327 BWindow::FrameMoved(BPoint newPosition) 1328 { 1329 // does nothing 1330 // Hook function 1331 } 1332 1333 1334 void 1335 BWindow::FrameResized(float newWidth, float newHeight) 1336 { 1337 // does nothing 1338 // Hook function 1339 } 1340 1341 1342 void 1343 BWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 1344 { 1345 // does nothing 1346 // Hook function 1347 } 1348 1349 1350 void 1351 BWindow::WorkspaceActivated(int32 workspace, bool state) 1352 { 1353 // does nothing 1354 // Hook function 1355 } 1356 1357 1358 void 1359 BWindow::MenusBeginning() 1360 { 1361 // does nothing 1362 // Hook function 1363 } 1364 1365 1366 void 1367 BWindow::MenusEnded() 1368 { 1369 // does nothing 1370 // Hook function 1371 } 1372 1373 1374 void 1375 BWindow::SetSizeLimits(float minWidth, float maxWidth, 1376 float minHeight, float maxHeight) 1377 { 1378 if (minWidth > maxWidth || minHeight > maxHeight) 1379 return; 1380 1381 if (!Lock()) 1382 return; 1383 1384 fLink->StartMessage(AS_SET_SIZE_LIMITS); 1385 fLink->Attach<float>(minWidth); 1386 fLink->Attach<float>(maxWidth); 1387 fLink->Attach<float>(minHeight); 1388 fLink->Attach<float>(maxHeight); 1389 1390 int32 code; 1391 if (fLink->FlushWithReply(code) == B_OK 1392 && code == B_OK) { 1393 // read the values that were really enforced on 1394 // the server side (the window frame could have 1395 // been changed, too) 1396 fLink->Read<BRect>(&fFrame); 1397 fLink->Read<float>(&fMinWidth); 1398 fLink->Read<float>(&fMaxWidth); 1399 fLink->Read<float>(&fMinHeight); 1400 fLink->Read<float>(&fMaxHeight); 1401 1402 _AdoptResize(); 1403 // TODO: the same has to be done for SetLook() (that can alter 1404 // the size limits, and hence, the size of the window 1405 } 1406 Unlock(); 1407 } 1408 1409 1410 void 1411 BWindow::GetSizeLimits(float* _minWidth, float* _maxWidth, float* _minHeight, 1412 float* _maxHeight) 1413 { 1414 // TODO: What about locking?!? 1415 if (_minHeight != NULL) 1416 *_minHeight = fMinHeight; 1417 if (_minWidth != NULL) 1418 *_minWidth = fMinWidth; 1419 if (_maxHeight != NULL) 1420 *_maxHeight = fMaxHeight; 1421 if (_maxWidth != NULL) 1422 *_maxWidth = fMaxWidth; 1423 } 1424 1425 1426 void 1427 BWindow::UpdateSizeLimits() 1428 { 1429 BAutolock locker(this); 1430 1431 if ((fFlags & B_AUTO_UPDATE_SIZE_LIMITS) != 0) { 1432 // Get min/max constraints of the top view and enforce window 1433 // size limits respectively. 1434 BSize minSize = fTopView->MinSize(); 1435 BSize maxSize = fTopView->MaxSize(); 1436 SetSizeLimits(minSize.width, maxSize.width, 1437 minSize.height, maxSize.height); 1438 } 1439 } 1440 1441 1442 status_t 1443 BWindow::SetDecoratorSettings(const BMessage& settings) 1444 { 1445 // flatten the given settings into a buffer and send 1446 // it to the app_server to apply the settings to the 1447 // decorator 1448 1449 int32 size = settings.FlattenedSize(); 1450 char buffer[size]; 1451 status_t status = settings.Flatten(buffer, size); 1452 if (status != B_OK) 1453 return status; 1454 1455 if (!Lock()) 1456 return B_ERROR; 1457 1458 status = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS); 1459 1460 if (status == B_OK) 1461 status = fLink->Attach<int32>(size); 1462 1463 if (status == B_OK) 1464 status = fLink->Attach(buffer, size); 1465 1466 if (status == B_OK) 1467 status = fLink->Flush(); 1468 1469 Unlock(); 1470 1471 return status; 1472 } 1473 1474 1475 status_t 1476 BWindow::GetDecoratorSettings(BMessage* settings) const 1477 { 1478 // read a flattened settings message from the app_server 1479 // and put it into settings 1480 1481 if (!const_cast<BWindow*>(this)->Lock()) 1482 return B_ERROR; 1483 1484 status_t status = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS); 1485 1486 if (status == B_OK) { 1487 int32 code; 1488 status = fLink->FlushWithReply(code); 1489 if (status == B_OK && code != B_OK) 1490 status = code; 1491 } 1492 1493 if (status == B_OK) { 1494 int32 size; 1495 status = fLink->Read<int32>(&size); 1496 if (status == B_OK) { 1497 char buffer[size]; 1498 status = fLink->Read(buffer, size); 1499 if (status == B_OK) { 1500 status = settings->Unflatten(buffer); 1501 } 1502 } 1503 } 1504 1505 const_cast<BWindow*>(this)->Unlock(); 1506 1507 return status; 1508 } 1509 1510 1511 void 1512 BWindow::SetZoomLimits(float maxWidth, float maxHeight) 1513 { 1514 // TODO: What about locking?!? 1515 if (maxWidth > fMaxWidth) 1516 maxWidth = fMaxWidth; 1517 else 1518 fMaxZoomWidth = maxWidth; 1519 1520 if (maxHeight > fMaxHeight) 1521 maxHeight = fMaxHeight; 1522 else 1523 fMaxZoomHeight = maxHeight; 1524 } 1525 1526 1527 void 1528 BWindow::Zoom(BPoint origin, float width, float height) 1529 { 1530 // the default implementation of this hook function 1531 // just does the obvious: 1532 MoveTo(origin); 1533 ResizeTo(width, height); 1534 } 1535 1536 1537 void 1538 BWindow::Zoom() 1539 { 1540 // TODO: What about locking?!? 1541 1542 // From BeBook: 1543 // The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced 1544 // from the smallest of three rectangles: 1545 1546 // 1) the rectangle defined by SetZoomLimits() and, 1547 // 2) the rectangle defined by SetSizeLimits() 1548 float maxZoomWidth = std::min(fMaxZoomWidth, fMaxWidth); 1549 float maxZoomHeight = std::min(fMaxZoomHeight, fMaxHeight); 1550 1551 // 3) the screen rectangle 1552 BRect screenFrame = (BScreen(this)).Frame(); 1553 maxZoomWidth = std::min(maxZoomWidth, screenFrame.Width()); 1554 maxZoomHeight = std::min(maxZoomHeight, screenFrame.Height()); 1555 1556 BRect zoomArea = screenFrame; // starts at screen size 1557 1558 BDeskbar deskbar; 1559 BRect deskbarFrame = deskbar.Frame(); 1560 bool isShiftDown = (modifiers() & B_SHIFT_KEY) != 0; 1561 if (!isShiftDown && !deskbar.IsAutoHide()) { 1562 // remove area taken up by Deskbar unless hidden or shift is held down 1563 switch (deskbar.Location()) { 1564 case B_DESKBAR_TOP: 1565 zoomArea.top = deskbarFrame.bottom + 2; 1566 break; 1567 1568 case B_DESKBAR_BOTTOM: 1569 zoomArea.bottom = deskbarFrame.top - 2; 1570 break; 1571 1572 // in vertical mode, only if not always on top and not auto-raise 1573 case B_DESKBAR_LEFT_TOP: 1574 case B_DESKBAR_LEFT_BOTTOM: 1575 if (!deskbar.IsAlwaysOnTop() && !deskbar.IsAutoRaise()) 1576 zoomArea.left = deskbarFrame.right + 2; 1577 break; 1578 1579 default: 1580 case B_DESKBAR_RIGHT_TOP: 1581 case B_DESKBAR_RIGHT_BOTTOM: 1582 if (!deskbar.IsAlwaysOnTop() && !deskbar.IsAutoRaise()) 1583 zoomArea.right = deskbarFrame.left - 2; 1584 break; 1585 } 1586 } 1587 1588 // TODO: Broken for tab on left side windows... 1589 float borderWidth; 1590 float tabHeight; 1591 _GetDecoratorSize(&borderWidth, &tabHeight); 1592 1593 // remove the area taken up by the tab and border 1594 zoomArea.left += borderWidth; 1595 zoomArea.top += borderWidth + tabHeight; 1596 zoomArea.right -= borderWidth; 1597 zoomArea.bottom -= borderWidth; 1598 1599 // inset towards center vertically first to see if there will be room 1600 // above or below Deskbar 1601 if (zoomArea.Height() > maxZoomHeight) 1602 zoomArea.InsetBy(0, roundf((zoomArea.Height() - maxZoomHeight) / 2)); 1603 1604 if (zoomArea.top > deskbarFrame.bottom 1605 || zoomArea.bottom < deskbarFrame.top) { 1606 // there is room above or below Deskbar, start from screen width 1607 // minus borders instead of desktop width minus borders 1608 zoomArea.left = screenFrame.left + borderWidth; 1609 zoomArea.right = screenFrame.right - borderWidth; 1610 } 1611 1612 // inset towards center 1613 if (zoomArea.Width() > maxZoomWidth) 1614 zoomArea.InsetBy(roundf((zoomArea.Width() - maxZoomWidth) / 2), 0); 1615 1616 // Un-Zoom 1617 1618 if (fPreviousFrame.IsValid() 1619 // NOTE: don't check for fFrame.LeftTop() == zoomArea.LeftTop() 1620 // -> makes it easier on the user to get a window back into place 1621 && fFrame.Width() == zoomArea.Width() 1622 && fFrame.Height() == zoomArea.Height()) { 1623 // already zoomed! 1624 Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(), 1625 fPreviousFrame.Height()); 1626 return; 1627 } 1628 1629 // Zoom 1630 1631 // remember fFrame for later "unzooming" 1632 fPreviousFrame = fFrame; 1633 1634 Zoom(zoomArea.LeftTop(), zoomArea.Width(), zoomArea.Height()); 1635 } 1636 1637 1638 void 1639 BWindow::ScreenChanged(BRect screenSize, color_space depth) 1640 { 1641 // Hook function 1642 } 1643 1644 1645 void 1646 BWindow::SetPulseRate(bigtime_t rate) 1647 { 1648 // TODO: What about locking?!? 1649 if (rate < 0 1650 || (rate == fPulseRate && !((rate == 0) ^ (fPulseRunner == NULL)))) 1651 return; 1652 1653 fPulseRate = rate; 1654 1655 if (rate > 0) { 1656 if (fPulseRunner == NULL) { 1657 BMessage message(B_PULSE); 1658 fPulseRunner = new(std::nothrow) BMessageRunner(BMessenger(this), 1659 &message, rate); 1660 } else { 1661 fPulseRunner->SetInterval(rate); 1662 } 1663 } else { 1664 // rate == 0 1665 delete fPulseRunner; 1666 fPulseRunner = NULL; 1667 } 1668 } 1669 1670 1671 bigtime_t 1672 BWindow::PulseRate() const 1673 { 1674 return fPulseRate; 1675 } 1676 1677 1678 void 1679 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem* item) 1680 { 1681 Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, item); 1682 if (shortcut == NULL) 1683 return; 1684 1685 // removes the shortcut if it already exists! 1686 RemoveShortcut(key, modifiers); 1687 1688 fShortcuts.AddItem(shortcut); 1689 } 1690 1691 1692 void 1693 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message) 1694 { 1695 AddShortcut(key, modifiers, message, this); 1696 } 1697 1698 1699 void 1700 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message, 1701 BHandler* target) 1702 { 1703 if (message == NULL) 1704 return; 1705 1706 Shortcut* shortcut = new(std::nothrow) Shortcut(key, modifiers, message, 1707 target); 1708 if (shortcut == NULL) 1709 return; 1710 1711 // removes the shortcut if it already exists! 1712 RemoveShortcut(key, modifiers); 1713 1714 fShortcuts.AddItem(shortcut); 1715 } 1716 1717 1718 bool 1719 BWindow::HasShortcut(uint32 key, uint32 modifiers) 1720 { 1721 return _FindShortcut(key, modifiers) != NULL; 1722 } 1723 1724 1725 void 1726 BWindow::RemoveShortcut(uint32 key, uint32 modifiers) 1727 { 1728 Shortcut* shortcut = _FindShortcut(key, modifiers); 1729 if (shortcut != NULL) { 1730 fShortcuts.RemoveItem(shortcut); 1731 delete shortcut; 1732 } else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) { 1733 // the quit shortcut is a fake shortcut 1734 fNoQuitShortcut = true; 1735 } 1736 } 1737 1738 1739 BButton* 1740 BWindow::DefaultButton() const 1741 { 1742 // TODO: What about locking?!? 1743 return fDefaultButton; 1744 } 1745 1746 1747 void 1748 BWindow::SetDefaultButton(BButton* button) 1749 { 1750 // TODO: What about locking?!? 1751 if (fDefaultButton == button) 1752 return; 1753 1754 if (fDefaultButton != NULL) { 1755 // tell old button it's no longer the default one 1756 BButton* oldDefault = fDefaultButton; 1757 oldDefault->MakeDefault(false); 1758 oldDefault->Invalidate(); 1759 } 1760 1761 fDefaultButton = button; 1762 1763 if (button != NULL) { 1764 // notify new default button 1765 fDefaultButton->MakeDefault(true); 1766 fDefaultButton->Invalidate(); 1767 } 1768 } 1769 1770 1771 bool 1772 BWindow::NeedsUpdate() const 1773 { 1774 if (!const_cast<BWindow*>(this)->Lock()) 1775 return false; 1776 1777 fLink->StartMessage(AS_NEEDS_UPDATE); 1778 1779 int32 code = B_ERROR; 1780 fLink->FlushWithReply(code); 1781 1782 const_cast<BWindow*>(this)->Unlock(); 1783 1784 return code == B_OK; 1785 } 1786 1787 1788 void 1789 BWindow::UpdateIfNeeded() 1790 { 1791 // works only from the window thread 1792 if (find_thread(NULL) != Thread()) 1793 return; 1794 1795 // if the queue is already locked we are called recursivly 1796 // from our own dispatched update message 1797 if (((const BMessageQueue*)MessageQueue())->IsLocked()) 1798 return; 1799 1800 if (!Lock()) 1801 return; 1802 1803 // make sure all requests that would cause an update have 1804 // arrived at the server 1805 Sync(); 1806 1807 // Since we're blocking the event loop, we need to retrieve 1808 // all messages that are pending on the port. 1809 _DequeueAll(); 1810 1811 BMessageQueue* queue = MessageQueue(); 1812 1813 // First process and remove any _UPDATE_ message in the queue 1814 // With the current design, there can only be one at a time 1815 1816 while (true) { 1817 queue->Lock(); 1818 1819 BMessage* message = queue->FindMessage(_UPDATE_, 0); 1820 queue->RemoveMessage(message); 1821 1822 queue->Unlock(); 1823 1824 if (message == NULL) 1825 break; 1826 1827 BWindow::DispatchMessage(message, this); 1828 delete message; 1829 } 1830 1831 Unlock(); 1832 } 1833 1834 1835 BView* 1836 BWindow::FindView(const char* viewName) const 1837 { 1838 BAutolock locker(const_cast<BWindow*>(this)); 1839 if (!locker.IsLocked()) 1840 return NULL; 1841 1842 return fTopView->FindView(viewName); 1843 } 1844 1845 1846 BView* 1847 BWindow::FindView(BPoint point) const 1848 { 1849 BAutolock locker(const_cast<BWindow*>(this)); 1850 if (!locker.IsLocked()) 1851 return NULL; 1852 1853 // point is assumed to be in window coordinates, 1854 // fTopView has same bounds as window 1855 return _FindView(fTopView, point); 1856 } 1857 1858 1859 BView* 1860 BWindow::CurrentFocus() const 1861 { 1862 return fFocus; 1863 } 1864 1865 1866 void 1867 BWindow::Activate(bool active) 1868 { 1869 if (!Lock()) 1870 return; 1871 1872 if (!IsHidden()) { 1873 fMinimized = false; 1874 // activating a window will also unminimize it 1875 1876 fLink->StartMessage(AS_ACTIVATE_WINDOW); 1877 fLink->Attach<bool>(active); 1878 fLink->Flush(); 1879 } 1880 1881 Unlock(); 1882 } 1883 1884 1885 void 1886 BWindow::WindowActivated(bool focus) 1887 { 1888 // hook function 1889 // does nothing 1890 } 1891 1892 1893 void 1894 BWindow::ConvertToScreen(BPoint* point) const 1895 { 1896 point->x += fFrame.left; 1897 point->y += fFrame.top; 1898 } 1899 1900 1901 BPoint 1902 BWindow::ConvertToScreen(BPoint point) const 1903 { 1904 return point + fFrame.LeftTop(); 1905 } 1906 1907 1908 void 1909 BWindow::ConvertFromScreen(BPoint* point) const 1910 { 1911 point->x -= fFrame.left; 1912 point->y -= fFrame.top; 1913 } 1914 1915 1916 BPoint 1917 BWindow::ConvertFromScreen(BPoint point) const 1918 { 1919 return point - fFrame.LeftTop(); 1920 } 1921 1922 1923 void 1924 BWindow::ConvertToScreen(BRect* rect) const 1925 { 1926 rect->OffsetBy(fFrame.LeftTop()); 1927 } 1928 1929 1930 BRect 1931 BWindow::ConvertToScreen(BRect rect) const 1932 { 1933 return rect.OffsetByCopy(fFrame.LeftTop()); 1934 } 1935 1936 1937 void 1938 BWindow::ConvertFromScreen(BRect* rect) const 1939 { 1940 rect->OffsetBy(-fFrame.left, -fFrame.top); 1941 } 1942 1943 1944 BRect 1945 BWindow::ConvertFromScreen(BRect rect) const 1946 { 1947 return rect.OffsetByCopy(-fFrame.left, -fFrame.top); 1948 } 1949 1950 1951 bool 1952 BWindow::IsMinimized() const 1953 { 1954 BAutolock locker(const_cast<BWindow*>(this)); 1955 if (!locker.IsLocked()) 1956 return false; 1957 1958 return fMinimized; 1959 } 1960 1961 1962 BRect 1963 BWindow::Bounds() const 1964 { 1965 return BRect(0, 0, fFrame.Width(), fFrame.Height()); 1966 } 1967 1968 1969 BRect 1970 BWindow::Frame() const 1971 { 1972 return fFrame; 1973 } 1974 1975 1976 BRect 1977 BWindow::DecoratorFrame() const 1978 { 1979 BRect decoratorFrame(Frame()); 1980 BRect tabRect(0, 0, 0, 0); 1981 1982 float borderWidth = 5.0; 1983 1984 BMessage settings; 1985 if (GetDecoratorSettings(&settings) == B_OK) { 1986 settings.FindRect("tab frame", &tabRect); 1987 settings.FindFloat("border width", &borderWidth); 1988 } else { 1989 // probably no-border window look 1990 if (fLook == B_NO_BORDER_WINDOW_LOOK) 1991 borderWidth = 0.f; 1992 else if (fLook == B_BORDERED_WINDOW_LOOK) 1993 borderWidth = 1.f; 1994 // else use fall-back values from above 1995 } 1996 1997 if (fLook == kLeftTitledWindowLook) { 1998 decoratorFrame.top -= borderWidth; 1999 decoratorFrame.left -= borderWidth + tabRect.Width(); 2000 decoratorFrame.right += borderWidth; 2001 decoratorFrame.bottom += borderWidth; 2002 } else { 2003 decoratorFrame.top -= borderWidth + tabRect.Height(); 2004 decoratorFrame.left -= borderWidth; 2005 decoratorFrame.right += borderWidth; 2006 decoratorFrame.bottom += borderWidth; 2007 } 2008 2009 return decoratorFrame; 2010 } 2011 2012 2013 BSize 2014 BWindow::Size() const 2015 { 2016 return BSize(fFrame.Width(), fFrame.Height()); 2017 } 2018 2019 2020 const char* 2021 BWindow::Title() const 2022 { 2023 return fTitle; 2024 } 2025 2026 2027 void 2028 BWindow::SetTitle(const char* title) 2029 { 2030 if (title == NULL) 2031 title = ""; 2032 2033 free(fTitle); 2034 fTitle = strdup(title); 2035 2036 _SetName(title); 2037 2038 // we notify the app_server so we can actually see the change 2039 if (Lock()) { 2040 fLink->StartMessage(AS_SET_WINDOW_TITLE); 2041 fLink->AttachString(fTitle); 2042 fLink->Flush(); 2043 Unlock(); 2044 } 2045 } 2046 2047 2048 bool 2049 BWindow::IsActive() const 2050 { 2051 return fActive; 2052 } 2053 2054 2055 void 2056 BWindow::SetKeyMenuBar(BMenuBar* bar) 2057 { 2058 fKeyMenuBar = bar; 2059 } 2060 2061 2062 BMenuBar* 2063 BWindow::KeyMenuBar() const 2064 { 2065 return fKeyMenuBar; 2066 } 2067 2068 2069 bool 2070 BWindow::IsModal() const 2071 { 2072 return fFeel == B_MODAL_SUBSET_WINDOW_FEEL 2073 || fFeel == B_MODAL_APP_WINDOW_FEEL 2074 || fFeel == B_MODAL_ALL_WINDOW_FEEL 2075 || fFeel == kMenuWindowFeel; 2076 } 2077 2078 2079 bool 2080 BWindow::IsFloating() const 2081 { 2082 return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL 2083 || fFeel == B_FLOATING_APP_WINDOW_FEEL 2084 || fFeel == B_FLOATING_ALL_WINDOW_FEEL; 2085 } 2086 2087 2088 status_t 2089 BWindow::AddToSubset(BWindow* window) 2090 { 2091 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL 2092 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL 2093 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL)) 2094 return B_BAD_VALUE; 2095 2096 if (!Lock()) 2097 return B_ERROR; 2098 2099 status_t status = B_ERROR; 2100 fLink->StartMessage(AS_ADD_TO_SUBSET); 2101 fLink->Attach<int32>(_get_object_token_(window)); 2102 fLink->FlushWithReply(status); 2103 2104 Unlock(); 2105 2106 return status; 2107 } 2108 2109 2110 status_t 2111 BWindow::RemoveFromSubset(BWindow* window) 2112 { 2113 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL 2114 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL 2115 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL)) 2116 return B_BAD_VALUE; 2117 2118 if (!Lock()) 2119 return B_ERROR; 2120 2121 status_t status = B_ERROR; 2122 fLink->StartMessage(AS_REMOVE_FROM_SUBSET); 2123 fLink->Attach<int32>(_get_object_token_(window)); 2124 fLink->FlushWithReply(status); 2125 2126 Unlock(); 2127 2128 return status; 2129 } 2130 2131 2132 status_t 2133 BWindow::Perform(perform_code code, void* _data) 2134 { 2135 switch (code) { 2136 case PERFORM_CODE_SET_LAYOUT: 2137 { 2138 perform_data_set_layout* data = (perform_data_set_layout*)_data; 2139 BWindow::SetLayout(data->layout); 2140 return B_OK; 2141 } 2142 } 2143 2144 return BLooper::Perform(code, _data); 2145 } 2146 2147 2148 status_t 2149 BWindow::SetType(window_type type) 2150 { 2151 window_look look; 2152 window_feel feel; 2153 _DecomposeType(type, &look, &feel); 2154 2155 status_t status = SetLook(look); 2156 if (status == B_OK) 2157 status = SetFeel(feel); 2158 2159 return status; 2160 } 2161 2162 2163 window_type 2164 BWindow::Type() const 2165 { 2166 return _ComposeType(fLook, fFeel); 2167 } 2168 2169 2170 status_t 2171 BWindow::SetLook(window_look look) 2172 { 2173 BAutolock locker(this); 2174 if (!locker.IsLocked()) 2175 return B_BAD_VALUE; 2176 2177 fLink->StartMessage(AS_SET_LOOK); 2178 fLink->Attach<int32>((int32)look); 2179 2180 status_t status = B_ERROR; 2181 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2182 fLook = look; 2183 2184 // TODO: this could have changed the window size, and thus, we 2185 // need to get it from the server (and call _AdoptResize()). 2186 2187 return status; 2188 } 2189 2190 2191 window_look 2192 BWindow::Look() const 2193 { 2194 return fLook; 2195 } 2196 2197 2198 status_t 2199 BWindow::SetFeel(window_feel feel) 2200 { 2201 BAutolock locker(this); 2202 if (!locker.IsLocked()) 2203 return B_BAD_VALUE; 2204 2205 fLink->StartMessage(AS_SET_FEEL); 2206 fLink->Attach<int32>((int32)feel); 2207 2208 status_t status = B_ERROR; 2209 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2210 fFeel = feel; 2211 2212 return status; 2213 } 2214 2215 2216 window_feel 2217 BWindow::Feel() const 2218 { 2219 return fFeel; 2220 } 2221 2222 2223 status_t 2224 BWindow::SetFlags(uint32 flags) 2225 { 2226 BAutolock locker(this); 2227 if (!locker.IsLocked()) 2228 return B_BAD_VALUE; 2229 2230 fLink->StartMessage(AS_SET_FLAGS); 2231 fLink->Attach<uint32>(flags); 2232 2233 int32 status = B_ERROR; 2234 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2235 fFlags = flags; 2236 2237 return status; 2238 } 2239 2240 2241 uint32 2242 BWindow::Flags() const 2243 { 2244 return fFlags; 2245 } 2246 2247 2248 status_t 2249 BWindow::SetWindowAlignment(window_alignment mode, 2250 int32 h, int32 hOffset, int32 width, int32 widthOffset, 2251 int32 v, int32 vOffset, int32 height, int32 heightOffset) 2252 { 2253 if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0 2254 || (hOffset >= 0 && hOffset <= h) 2255 || (vOffset >= 0 && vOffset <= v) 2256 || (widthOffset >= 0 && widthOffset <= width) 2257 || (heightOffset >= 0 && heightOffset <= height)) 2258 return B_BAD_VALUE; 2259 2260 // TODO: test if hOffset = 0 and set it to 1 if true. 2261 2262 if (!Lock()) 2263 return B_ERROR; 2264 2265 fLink->StartMessage(AS_SET_ALIGNMENT); 2266 fLink->Attach<int32>((int32)mode); 2267 fLink->Attach<int32>(h); 2268 fLink->Attach<int32>(hOffset); 2269 fLink->Attach<int32>(width); 2270 fLink->Attach<int32>(widthOffset); 2271 fLink->Attach<int32>(v); 2272 fLink->Attach<int32>(vOffset); 2273 fLink->Attach<int32>(height); 2274 fLink->Attach<int32>(heightOffset); 2275 2276 status_t status = B_ERROR; 2277 fLink->FlushWithReply(status); 2278 2279 Unlock(); 2280 2281 return status; 2282 } 2283 2284 2285 status_t 2286 BWindow::GetWindowAlignment(window_alignment* mode, 2287 int32* h, int32* hOffset, int32* width, int32* widthOffset, 2288 int32* v, int32* vOffset, int32* height, int32* heightOffset) const 2289 { 2290 if (!const_cast<BWindow*>(this)->Lock()) 2291 return B_ERROR; 2292 2293 fLink->StartMessage(AS_GET_ALIGNMENT); 2294 2295 status_t status; 2296 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) { 2297 fLink->Read<int32>((int32*)mode); 2298 fLink->Read<int32>(h); 2299 fLink->Read<int32>(hOffset); 2300 fLink->Read<int32>(width); 2301 fLink->Read<int32>(widthOffset); 2302 fLink->Read<int32>(v); 2303 fLink->Read<int32>(hOffset); 2304 fLink->Read<int32>(height); 2305 fLink->Read<int32>(heightOffset); 2306 } 2307 2308 const_cast<BWindow*>(this)->Unlock(); 2309 return status; 2310 } 2311 2312 2313 uint32 2314 BWindow::Workspaces() const 2315 { 2316 if (!const_cast<BWindow*>(this)->Lock()) 2317 return 0; 2318 2319 uint32 workspaces = 0; 2320 2321 fLink->StartMessage(AS_GET_WORKSPACES); 2322 2323 status_t status; 2324 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2325 fLink->Read<uint32>(&workspaces); 2326 2327 const_cast<BWindow*>(this)->Unlock(); 2328 return workspaces; 2329 } 2330 2331 2332 void 2333 BWindow::SetWorkspaces(uint32 workspaces) 2334 { 2335 // TODO: don't forget about Tracker's background window. 2336 if (fFeel != B_NORMAL_WINDOW_FEEL) 2337 return; 2338 2339 if (Lock()) { 2340 fLink->StartMessage(AS_SET_WORKSPACES); 2341 fLink->Attach<uint32>(workspaces); 2342 fLink->Flush(); 2343 Unlock(); 2344 } 2345 } 2346 2347 2348 BView* 2349 BWindow::LastMouseMovedView() const 2350 { 2351 return fLastMouseMovedView; 2352 } 2353 2354 2355 void 2356 BWindow::MoveBy(float dx, float dy) 2357 { 2358 if ((dx != 0.0f || dy != 0.0f) && Lock()) { 2359 MoveTo(fFrame.left + dx, fFrame.top + dy); 2360 Unlock(); 2361 } 2362 } 2363 2364 2365 void 2366 BWindow::MoveTo(BPoint point) 2367 { 2368 MoveTo(point.x, point.y); 2369 } 2370 2371 2372 void 2373 BWindow::MoveTo(float x, float y) 2374 { 2375 if (!Lock()) 2376 return; 2377 2378 x = roundf(x); 2379 y = roundf(y); 2380 2381 if (fFrame.left != x || fFrame.top != y) { 2382 fLink->StartMessage(AS_WINDOW_MOVE); 2383 fLink->Attach<float>(x); 2384 fLink->Attach<float>(y); 2385 2386 status_t status; 2387 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2388 fFrame.OffsetTo(x, y); 2389 } 2390 2391 Unlock(); 2392 } 2393 2394 2395 void 2396 BWindow::ResizeBy(float dx, float dy) 2397 { 2398 if (Lock()) { 2399 ResizeTo(fFrame.Width() + dx, fFrame.Height() + dy); 2400 Unlock(); 2401 } 2402 } 2403 2404 2405 void 2406 BWindow::ResizeTo(float width, float height) 2407 { 2408 if (!Lock()) 2409 return; 2410 2411 width = roundf(width); 2412 height = roundf(height); 2413 2414 // stay in minimum & maximum frame limits 2415 if (width < fMinWidth) 2416 width = fMinWidth; 2417 else if (width > fMaxWidth) 2418 width = fMaxWidth; 2419 2420 if (height < fMinHeight) 2421 height = fMinHeight; 2422 else if (height > fMaxHeight) 2423 height = fMaxHeight; 2424 2425 if (width != fFrame.Width() || height != fFrame.Height()) { 2426 fLink->StartMessage(AS_WINDOW_RESIZE); 2427 fLink->Attach<float>(width); 2428 fLink->Attach<float>(height); 2429 2430 status_t status; 2431 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) { 2432 fFrame.right = fFrame.left + width; 2433 fFrame.bottom = fFrame.top + height; 2434 _AdoptResize(); 2435 } 2436 } 2437 2438 Unlock(); 2439 } 2440 2441 2442 void 2443 BWindow::ResizeToPreferred() 2444 { 2445 BAutolock locker(this); 2446 Layout(false); 2447 2448 float width = fTopView->PreferredSize().width; 2449 width = std::min(width, fTopView->MaxSize().width); 2450 width = std::max(width, fTopView->MinSize().width); 2451 2452 float height = fTopView->PreferredSize().height; 2453 height = std::min(width, fTopView->MaxSize().height); 2454 height = std::max(width, fTopView->MinSize().height); 2455 2456 if (GetLayout()->HasHeightForWidth()) 2457 GetLayout()->GetHeightForWidth(width, NULL, NULL, &height); 2458 2459 ResizeTo(width, height); 2460 } 2461 2462 2463 void 2464 BWindow::CenterIn(const BRect& rect) 2465 { 2466 BAutolock locker(this); 2467 2468 // Set size limits now if needed 2469 UpdateSizeLimits(); 2470 2471 MoveTo(BLayoutUtils::AlignInFrame(rect, Size(), 2472 BAlignment(B_ALIGN_HORIZONTAL_CENTER, 2473 B_ALIGN_VERTICAL_CENTER)).LeftTop()); 2474 MoveOnScreen(B_DO_NOT_RESIZE_TO_FIT | B_MOVE_IF_PARTIALLY_OFFSCREEN); 2475 } 2476 2477 2478 void 2479 BWindow::CenterOnScreen() 2480 { 2481 CenterIn(BScreen(this).Frame()); 2482 } 2483 2484 2485 // Centers the window on the screen with the passed in id. 2486 void 2487 BWindow::CenterOnScreen(screen_id id) 2488 { 2489 CenterIn(BScreen(id).Frame()); 2490 } 2491 2492 2493 void 2494 BWindow::MoveOnScreen(uint32 flags) 2495 { 2496 // Set size limits now if needed 2497 UpdateSizeLimits(); 2498 2499 BRect screenFrame = BScreen(this).Frame(); 2500 BRect frame = Frame(); 2501 2502 float borderWidth; 2503 float tabHeight; 2504 _GetDecoratorSize(&borderWidth, &tabHeight); 2505 2506 frame.InsetBy(-borderWidth, -borderWidth); 2507 frame.top -= tabHeight; 2508 2509 if ((flags & B_DO_NOT_RESIZE_TO_FIT) == 0) { 2510 // Make sure the window fits on the screen 2511 if (frame.Width() > screenFrame.Width()) 2512 frame.right -= frame.Width() - screenFrame.Width(); 2513 if (frame.Height() > screenFrame.Height()) 2514 frame.bottom -= frame.Height() - screenFrame.Height(); 2515 2516 BRect innerFrame = frame; 2517 innerFrame.top += tabHeight; 2518 innerFrame.InsetBy(borderWidth, borderWidth); 2519 ResizeTo(innerFrame.Width(), innerFrame.Height()); 2520 } 2521 2522 if (((flags & B_MOVE_IF_PARTIALLY_OFFSCREEN) == 0 2523 && !screenFrame.Contains(frame)) 2524 || !frame.Intersects(screenFrame)) { 2525 // Off and away 2526 CenterOnScreen(); 2527 return; 2528 } 2529 2530 // Move such that the upper left corner, and most of the window 2531 // will be visible. 2532 float left = frame.left; 2533 if (left < screenFrame.left) 2534 left = screenFrame.left; 2535 else if (frame.right > screenFrame.right) 2536 left = std::max(0.f, screenFrame.right - frame.Width()); 2537 2538 float top = frame.top; 2539 if (top < screenFrame.top) 2540 top = screenFrame.top; 2541 else if (frame.bottom > screenFrame.bottom) 2542 top = std::max(0.f, screenFrame.bottom - frame.Height()); 2543 2544 if (top != frame.top || left != frame.left) 2545 MoveTo(left + borderWidth, top + tabHeight + borderWidth); 2546 } 2547 2548 2549 void 2550 BWindow::Show() 2551 { 2552 bool runCalled = true; 2553 if (Lock()) { 2554 fShowLevel--; 2555 2556 _SendShowOrHideMessage(); 2557 2558 runCalled = fRunCalled; 2559 2560 Unlock(); 2561 } 2562 2563 if (!runCalled) { 2564 // This is the fist time Show() is called, which implicitly runs the 2565 // looper. NOTE: The window is still locked if it has not been 2566 // run yet, so accessing members is safe. 2567 if (fLink->SenderPort() < B_OK) { 2568 // We don't have valid app_server connection; there is no point 2569 // in starting our looper 2570 fThread = B_ERROR; 2571 return; 2572 } else 2573 Run(); 2574 } 2575 } 2576 2577 2578 void 2579 BWindow::Hide() 2580 { 2581 if (Lock()) { 2582 // If we are minimized and are about to be hidden, unminimize 2583 if (IsMinimized() && fShowLevel == 0) 2584 Minimize(false); 2585 2586 fShowLevel++; 2587 2588 _SendShowOrHideMessage(); 2589 2590 Unlock(); 2591 } 2592 } 2593 2594 2595 bool 2596 BWindow::IsHidden() const 2597 { 2598 return fShowLevel > 0; 2599 } 2600 2601 2602 bool 2603 BWindow::QuitRequested() 2604 { 2605 return BLooper::QuitRequested(); 2606 } 2607 2608 2609 thread_id 2610 BWindow::Run() 2611 { 2612 return BLooper::Run(); 2613 } 2614 2615 2616 void 2617 BWindow::SetLayout(BLayout* layout) 2618 { 2619 // Adopt layout's colors for fTopView 2620 if (layout != NULL) 2621 fTopView->AdoptViewColors(layout->View()); 2622 2623 fTopView->SetLayout(layout); 2624 } 2625 2626 2627 BLayout* 2628 BWindow::GetLayout() const 2629 { 2630 return fTopView->GetLayout(); 2631 } 2632 2633 2634 void 2635 BWindow::InvalidateLayout(bool descendants) 2636 { 2637 fTopView->InvalidateLayout(descendants); 2638 } 2639 2640 2641 void 2642 BWindow::Layout(bool force) 2643 { 2644 UpdateSizeLimits(); 2645 2646 // Do the actual layout 2647 fTopView->Layout(force); 2648 } 2649 2650 2651 bool 2652 BWindow::IsOffscreenWindow() const 2653 { 2654 return fOffscreen; 2655 } 2656 2657 2658 status_t 2659 BWindow::GetSupportedSuites(BMessage* data) 2660 { 2661 if (data == NULL) 2662 return B_BAD_VALUE; 2663 2664 status_t status = data->AddString("suites", "suite/vnd.Be-window"); 2665 if (status == B_OK) { 2666 BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo); 2667 2668 status = data->AddFlat("messages", &propertyInfo); 2669 if (status == B_OK) 2670 status = BLooper::GetSupportedSuites(data); 2671 } 2672 2673 return status; 2674 } 2675 2676 2677 BHandler* 2678 BWindow::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 2679 int32 what, const char* property) 2680 { 2681 if (message->what == B_WINDOW_MOVE_BY 2682 || message->what == B_WINDOW_MOVE_TO) 2683 return this; 2684 2685 BPropertyInfo propertyInfo(sWindowPropInfo); 2686 if (propertyInfo.FindMatch(message, index, specifier, what, property) >= 0) { 2687 if (strcmp(property, "View") == 0) { 2688 // we will NOT pop the current specifier 2689 return fTopView; 2690 } else if (strcmp(property, "MenuBar") == 0) { 2691 if (fKeyMenuBar) { 2692 message->PopSpecifier(); 2693 return fKeyMenuBar; 2694 } else { 2695 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 2696 replyMsg.AddInt32("error", B_NAME_NOT_FOUND); 2697 replyMsg.AddString("message", 2698 "This window doesn't have a main MenuBar"); 2699 message->SendReply(&replyMsg); 2700 return NULL; 2701 } 2702 } else 2703 return this; 2704 } 2705 2706 return BLooper::ResolveSpecifier(message, index, specifier, what, property); 2707 } 2708 2709 2710 // #pragma mark - Private Methods 2711 2712 2713 void 2714 BWindow::_InitData(BRect frame, const char* title, window_look look, 2715 window_feel feel, uint32 flags, uint32 workspace, int32 bitmapToken) 2716 { 2717 STRACE(("BWindow::InitData()\n")); 2718 2719 if (be_app == NULL) { 2720 debugger("You need a valid BApplication object before interacting with " 2721 "the app_server"); 2722 return; 2723 } 2724 2725 frame.left = roundf(frame.left); 2726 frame.top = roundf(frame.top); 2727 frame.right = roundf(frame.right); 2728 frame.bottom = roundf(frame.bottom); 2729 2730 fFrame = frame; 2731 2732 if (title == NULL) 2733 title = ""; 2734 2735 fTitle = strdup(title); 2736 2737 _SetName(title); 2738 2739 fFeel = feel; 2740 fLook = look; 2741 fFlags = flags | B_ASYNCHRONOUS_CONTROLS; 2742 2743 fInTransaction = bitmapToken >= 0; 2744 fUpdateRequested = false; 2745 fActive = false; 2746 fShowLevel = 1; 2747 2748 fTopView = NULL; 2749 fFocus = NULL; 2750 fLastMouseMovedView = NULL; 2751 fKeyMenuBar = NULL; 2752 fDefaultButton = NULL; 2753 2754 // Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message 2755 // get sent to the application, and not one of our handlers. 2756 // It is only installed for non-modal windows, though. 2757 fNoQuitShortcut = IsModal(); 2758 2759 if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) { 2760 // Modal windows default to non-closable, but you can add the 2761 // shortcut manually, if a different behaviour is wanted 2762 AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED)); 2763 } 2764 2765 // Edit modifier keys 2766 2767 AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL); 2768 AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL); 2769 AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL); 2770 AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL); 2771 2772 // Window modifier keys 2773 2774 AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY, 2775 new BMessage(_MINIMIZE_), NULL); 2776 AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY, 2777 new BMessage(_ZOOM_), NULL); 2778 AddShortcut('Z', B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY, 2779 new BMessage(_ZOOM_), NULL); 2780 AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY, 2781 new BMessage(B_HIDE_APPLICATION), NULL); 2782 AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY, 2783 new BMessage(_SEND_TO_FRONT_), NULL); 2784 AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY, 2785 new BMessage(_SEND_BEHIND_), NULL); 2786 2787 // We set the default pulse rate, but we don't start the pulse 2788 fPulseRate = 500000; 2789 fPulseRunner = NULL; 2790 2791 fIsFilePanel = false; 2792 2793 fMenuSem = -1; 2794 2795 fMinimized = false; 2796 2797 fMaxZoomHeight = 32768.0; 2798 fMaxZoomWidth = 32768.0; 2799 fMinHeight = 0.0; 2800 fMinWidth = 0.0; 2801 fMaxHeight = 32768.0; 2802 fMaxWidth = 32768.0; 2803 2804 fLastViewToken = B_NULL_TOKEN; 2805 2806 // TODO: other initializations! 2807 fOffscreen = false; 2808 2809 // Create the server-side window 2810 2811 port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, 2812 "w<app_server"); 2813 if (receivePort < B_OK) { 2814 // TODO: huh? 2815 debugger("Could not create BWindow's receive port, used for " 2816 "interacting with the app_server!"); 2817 delete this; 2818 return; 2819 } 2820 2821 STRACE(("BWindow::InitData(): contacting app_server...\n")); 2822 2823 // let app_server know that a window has been created. 2824 fLink = new(std::nothrow) BPrivate::PortLink( 2825 BApplication::Private::ServerLink()->SenderPort(), receivePort); 2826 if (fLink == NULL) { 2827 // Zombie! 2828 return; 2829 } 2830 2831 { 2832 BPrivate::AppServerLink lockLink; 2833 // we're talking to the server application using our own 2834 // communication channel (fLink) - we better make sure no one 2835 // interferes by locking that channel (which AppServerLink does 2836 // implicetly) 2837 2838 if (bitmapToken < 0) { 2839 fLink->StartMessage(AS_CREATE_WINDOW); 2840 } else { 2841 fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW); 2842 fLink->Attach<int32>(bitmapToken); 2843 fOffscreen = true; 2844 } 2845 2846 fLink->Attach<BRect>(fFrame); 2847 fLink->Attach<uint32>((uint32)fLook); 2848 fLink->Attach<uint32>((uint32)fFeel); 2849 fLink->Attach<uint32>(fFlags); 2850 fLink->Attach<uint32>(workspace); 2851 fLink->Attach<int32>(_get_object_token_(this)); 2852 fLink->Attach<port_id>(receivePort); 2853 fLink->Attach<port_id>(fMsgPort); 2854 fLink->AttachString(title); 2855 2856 port_id sendPort; 2857 int32 code; 2858 if (fLink->FlushWithReply(code) == B_OK 2859 && code == B_OK 2860 && fLink->Read<port_id>(&sendPort) == B_OK) { 2861 // read the frame size and its limits that were really 2862 // enforced on the server side 2863 2864 fLink->Read<BRect>(&fFrame); 2865 fLink->Read<float>(&fMinWidth); 2866 fLink->Read<float>(&fMaxWidth); 2867 fLink->Read<float>(&fMinHeight); 2868 fLink->Read<float>(&fMaxHeight); 2869 2870 fMaxZoomWidth = fMaxWidth; 2871 fMaxZoomHeight = fMaxHeight; 2872 } else 2873 sendPort = -1; 2874 2875 // Redirect our link to the new window connection 2876 fLink->SetSenderPort(sendPort); 2877 STRACE(("Server says that our send port is %ld\n", sendPort)); 2878 } 2879 2880 STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False")); 2881 2882 _CreateTopView(); 2883 } 2884 2885 2886 //! Rename the handler and its thread 2887 void 2888 BWindow::_SetName(const char* title) 2889 { 2890 if (title == NULL) 2891 title = ""; 2892 2893 // we will change BWindow's thread name to "w>window title" 2894 2895 char threadName[B_OS_NAME_LENGTH]; 2896 strcpy(threadName, "w>"); 2897 #ifdef __HAIKU__ 2898 strlcat(threadName, title, B_OS_NAME_LENGTH); 2899 #else 2900 int32 length = strlen(title); 2901 length = min_c(length, B_OS_NAME_LENGTH - 3); 2902 memcpy(threadName + 2, title, length); 2903 threadName[length + 2] = '\0'; 2904 #endif 2905 2906 // change the handler's name 2907 SetName(threadName); 2908 2909 // if the message loop has been started... 2910 if (Thread() >= B_OK) 2911 rename_thread(Thread(), threadName); 2912 } 2913 2914 2915 //! Reads all pending messages from the window port and put them into the queue. 2916 void 2917 BWindow::_DequeueAll() 2918 { 2919 // Get message count from port 2920 int32 count = port_count(fMsgPort); 2921 2922 for (int32 i = 0; i < count; i++) { 2923 BMessage* message = MessageFromPort(0); 2924 if (message != NULL) 2925 fDirectTarget->Queue()->AddMessage(message); 2926 } 2927 } 2928 2929 2930 /*! This here is an almost complete code duplication to BLooper::task_looper() 2931 but with some important differences: 2932 a) it uses the _DetermineTarget() method to tell what the later target of 2933 a message will be, if no explicit target is supplied. 2934 b) it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message 2935 to all of its intended targets, and to add all fields the target would 2936 expect in such a message. 2937 2938 This is important because the app_server sends all input events to the 2939 preferred handler, and expects them to be correctly distributed to their 2940 intended targets. 2941 */ 2942 void 2943 BWindow::task_looper() 2944 { 2945 STRACE(("info: BWindow::task_looper() started.\n")); 2946 2947 // Check that looper is locked (should be) 2948 AssertLocked(); 2949 Unlock(); 2950 2951 if (IsLocked()) 2952 debugger("window must not be locked!"); 2953 2954 while (!fTerminating) { 2955 // Did we get a message? 2956 BMessage* msg = MessageFromPort(); 2957 if (msg) 2958 _AddMessagePriv(msg); 2959 2960 // Get message count from port 2961 int32 msgCount = port_count(fMsgPort); 2962 for (int32 i = 0; i < msgCount; ++i) { 2963 // Read 'count' messages from port (so we will not block) 2964 // We use zero as our timeout since we know there is stuff there 2965 msg = MessageFromPort(0); 2966 // Add messages to queue 2967 if (msg) 2968 _AddMessagePriv(msg); 2969 } 2970 2971 bool dispatchNextMessage = true; 2972 while (!fTerminating && dispatchNextMessage) { 2973 // Get next message from queue (assign to fLastMessage after 2974 // locking) 2975 BMessage* message = fDirectTarget->Queue()->NextMessage(); 2976 2977 // Lock the looper 2978 if (!Lock()) { 2979 delete message; 2980 break; 2981 } 2982 2983 fLastMessage = message; 2984 2985 if (fLastMessage == NULL) { 2986 // No more messages: Unlock the looper and terminate the 2987 // dispatch loop. 2988 dispatchNextMessage = false; 2989 } else { 2990 // Get the target handler 2991 BMessage::Private messagePrivate(fLastMessage); 2992 bool usePreferred = messagePrivate.UsePreferredTarget(); 2993 BHandler* handler = NULL; 2994 bool dropMessage = false; 2995 2996 if (usePreferred) { 2997 handler = PreferredHandler(); 2998 if (handler == NULL) 2999 handler = this; 3000 } else { 3001 gDefaultTokens.GetToken(messagePrivate.GetTarget(), 3002 B_HANDLER_TOKEN, (void**)&handler); 3003 3004 // if this handler doesn't belong to us, we drop the message 3005 if (handler != NULL && handler->Looper() != this) { 3006 dropMessage = true; 3007 handler = NULL; 3008 } 3009 } 3010 3011 if ((handler == NULL && !dropMessage) || usePreferred) 3012 handler = _DetermineTarget(fLastMessage, handler); 3013 3014 unpack_cookie cookie; 3015 while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) { 3016 // if there is no target handler, the message is dropped 3017 if (handler != NULL) { 3018 _SanitizeMessage(fLastMessage, handler, usePreferred); 3019 3020 // Is this a scripting message? 3021 if (fLastMessage->HasSpecifiers()) { 3022 int32 index = 0; 3023 // Make sure the current specifier is kosher 3024 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 3025 handler = resolve_specifier(handler, fLastMessage); 3026 } 3027 3028 if (handler != NULL) 3029 handler = _TopLevelFilter(fLastMessage, handler); 3030 3031 if (handler != NULL) 3032 DispatchMessage(fLastMessage, handler); 3033 } 3034 3035 // Delete the current message 3036 delete fLastMessage; 3037 fLastMessage = NULL; 3038 } 3039 } 3040 3041 if (fTerminating) { 3042 // we leave the looper locked when we quit 3043 return; 3044 } 3045 3046 Unlock(); 3047 3048 // Are any messages on the port? 3049 if (port_count(fMsgPort) > 0) { 3050 // Do outer loop 3051 dispatchNextMessage = false; 3052 } 3053 } 3054 } 3055 } 3056 3057 3058 window_type 3059 BWindow::_ComposeType(window_look look, window_feel feel) const 3060 { 3061 switch (feel) { 3062 case B_NORMAL_WINDOW_FEEL: 3063 switch (look) { 3064 case B_TITLED_WINDOW_LOOK: 3065 return B_TITLED_WINDOW; 3066 3067 case B_DOCUMENT_WINDOW_LOOK: 3068 return B_DOCUMENT_WINDOW; 3069 3070 case B_BORDERED_WINDOW_LOOK: 3071 return B_BORDERED_WINDOW; 3072 3073 default: 3074 return B_UNTYPED_WINDOW; 3075 } 3076 break; 3077 3078 case B_MODAL_APP_WINDOW_FEEL: 3079 if (look == B_MODAL_WINDOW_LOOK) 3080 return B_MODAL_WINDOW; 3081 break; 3082 3083 case B_FLOATING_APP_WINDOW_FEEL: 3084 if (look == B_FLOATING_WINDOW_LOOK) 3085 return B_FLOATING_WINDOW; 3086 break; 3087 3088 default: 3089 return B_UNTYPED_WINDOW; 3090 } 3091 3092 return B_UNTYPED_WINDOW; 3093 } 3094 3095 3096 void 3097 BWindow::_DecomposeType(window_type type, window_look* _look, 3098 window_feel* _feel) const 3099 { 3100 switch (type) { 3101 case B_DOCUMENT_WINDOW: 3102 *_look = B_DOCUMENT_WINDOW_LOOK; 3103 *_feel = B_NORMAL_WINDOW_FEEL; 3104 break; 3105 3106 case B_MODAL_WINDOW: 3107 *_look = B_MODAL_WINDOW_LOOK; 3108 *_feel = B_MODAL_APP_WINDOW_FEEL; 3109 break; 3110 3111 case B_FLOATING_WINDOW: 3112 *_look = B_FLOATING_WINDOW_LOOK; 3113 *_feel = B_FLOATING_APP_WINDOW_FEEL; 3114 break; 3115 3116 case B_BORDERED_WINDOW: 3117 *_look = B_BORDERED_WINDOW_LOOK; 3118 *_feel = B_NORMAL_WINDOW_FEEL; 3119 break; 3120 3121 case B_TITLED_WINDOW: 3122 case B_UNTYPED_WINDOW: 3123 default: 3124 *_look = B_TITLED_WINDOW_LOOK; 3125 *_feel = B_NORMAL_WINDOW_FEEL; 3126 break; 3127 } 3128 } 3129 3130 3131 void 3132 BWindow::_CreateTopView() 3133 { 3134 STRACE(("_CreateTopView(): enter\n")); 3135 3136 BRect frame = fFrame.OffsetToCopy(B_ORIGIN); 3137 // TODO: what to do here about std::nothrow? 3138 fTopView = new BView(frame, "fTopView", B_FOLLOW_ALL, B_WILL_DRAW); 3139 fTopView->fTopLevelView = true; 3140 3141 //inhibit check_lock() 3142 fLastViewToken = _get_object_token_(fTopView); 3143 3144 // set fTopView's owner, add it to window's eligible handler list 3145 // and also set its next handler to be this window. 3146 3147 STRACE(("Calling setowner fTopView = %p this = %p.\n", 3148 fTopView, this)); 3149 3150 fTopView->_SetOwner(this); 3151 3152 // we can't use AddChild() because this is the top view 3153 fTopView->_CreateSelf(); 3154 STRACE(("BuildTopView ended\n")); 3155 } 3156 3157 3158 /*! 3159 Resizes the top view to match the window size. This will also 3160 adapt the size of all its child views as needed. 3161 This method has to be called whenever the frame of the window 3162 changes. 3163 */ 3164 void 3165 BWindow::_AdoptResize() 3166 { 3167 // Resize views according to their resize modes - this 3168 // saves us some server communication, as the server 3169 // does the same with our views on its side. 3170 3171 int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width()); 3172 int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height()); 3173 if (deltaWidth == 0 && deltaHeight == 0) 3174 return; 3175 3176 fTopView->_ResizeBy(deltaWidth, deltaHeight); 3177 } 3178 3179 3180 void 3181 BWindow::_SetFocus(BView* focusView, bool notifyInputServer) 3182 { 3183 if (fFocus == focusView) 3184 return; 3185 3186 // we notify the input server if we are passing focus 3187 // from a view which has the B_INPUT_METHOD_AWARE to a one 3188 // which does not, or vice-versa 3189 if (notifyInputServer && fActive) { 3190 bool inputMethodAware = false; 3191 if (focusView) 3192 inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE; 3193 BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW); 3194 BMessenger messenger(focusView); 3195 BMessage reply; 3196 if (focusView) 3197 msg.AddMessenger("view", messenger); 3198 _control_input_server_(&msg, &reply); 3199 } 3200 3201 fFocus = focusView; 3202 SetPreferredHandler(focusView); 3203 } 3204 3205 3206 /*! 3207 \brief Determines the target of a message received for the 3208 focus view. 3209 */ 3210 BHandler* 3211 BWindow::_DetermineTarget(BMessage* message, BHandler* target) 3212 { 3213 if (target == NULL) 3214 target = this; 3215 3216 switch (message->what) { 3217 case B_KEY_DOWN: 3218 case B_KEY_UP: 3219 { 3220 // if we have a default button, it might want to hear 3221 // about pressing the <enter> key 3222 const int32 kNonLockModifierKeys = B_SHIFT_KEY | B_COMMAND_KEY 3223 | B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY; 3224 int32 rawChar; 3225 if (DefaultButton() != NULL 3226 && message->FindInt32("raw_char", &rawChar) == B_OK 3227 && rawChar == B_ENTER 3228 && (modifiers() & kNonLockModifierKeys) == 0) 3229 return DefaultButton(); 3230 3231 // supposed to fall through 3232 } 3233 case B_UNMAPPED_KEY_DOWN: 3234 case B_UNMAPPED_KEY_UP: 3235 case B_MODIFIERS_CHANGED: 3236 // these messages should be dispatched by the focus view 3237 if (CurrentFocus() != NULL) 3238 return CurrentFocus(); 3239 break; 3240 3241 case B_MOUSE_DOWN: 3242 case B_MOUSE_UP: 3243 case B_MOUSE_MOVED: 3244 case B_MOUSE_WHEEL_CHANGED: 3245 case B_MOUSE_IDLE: 3246 // is there a token of the view that is currently under the mouse? 3247 int32 token; 3248 if (message->FindInt32("_view_token", &token) == B_OK) { 3249 BView* view = _FindView(token); 3250 if (view != NULL) 3251 return view; 3252 } 3253 3254 // if there is no valid token in the message, we try our 3255 // luck with the last target, if available 3256 if (fLastMouseMovedView != NULL) 3257 return fLastMouseMovedView; 3258 break; 3259 3260 case B_PULSE: 3261 case B_QUIT_REQUESTED: 3262 // TODO: test whether R5 will let BView dispatch these messages 3263 return this; 3264 3265 case _MESSAGE_DROPPED_: 3266 if (fLastMouseMovedView != NULL) 3267 return fLastMouseMovedView; 3268 break; 3269 3270 default: 3271 break; 3272 } 3273 3274 return target; 3275 } 3276 3277 3278 /*! \brief Determines whether or not this message has targeted the focus view. 3279 3280 This will return \c false only if the message did not go to the preferred 3281 handler, or if the packed message does not contain address the focus view 3282 at all. 3283 */ 3284 bool 3285 BWindow::_IsFocusMessage(BMessage* message) 3286 { 3287 BMessage::Private messagePrivate(message); 3288 if (!messagePrivate.UsePreferredTarget()) 3289 return false; 3290 3291 bool feedFocus; 3292 if (message->HasInt32("_token") 3293 && (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)) 3294 return false; 3295 3296 return true; 3297 } 3298 3299 3300 /*! \brief Distributes the message to its intended targets. This is done for 3301 all messages that should go to the preferred handler. 3302 3303 Returns \c true in case the message should still be dispatched 3304 */ 3305 bool 3306 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message, 3307 BHandler** _target, bool* _usePreferred) 3308 { 3309 if (cookie.message == NULL) 3310 return false; 3311 3312 if (cookie.index == 0 && !cookie.tokens_scanned) { 3313 // We were called the first time for this message 3314 3315 if (!*_usePreferred) { 3316 // only consider messages targeted at the preferred handler 3317 cookie.message = NULL; 3318 return true; 3319 } 3320 3321 // initialize our cookie 3322 cookie.message = *_message; 3323 cookie.focus = *_target; 3324 3325 if (cookie.focus != NULL) 3326 cookie.focus_token = _get_object_token_(*_target); 3327 3328 if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED) 3329 cookie.last_view_token = _get_object_token_(fLastMouseMovedView); 3330 3331 *_usePreferred = false; 3332 } 3333 3334 _DequeueAll(); 3335 3336 // distribute the message to all targets specified in the 3337 // message directly (but not to the focus view) 3338 3339 for (int32 token; !cookie.tokens_scanned 3340 && cookie.message->FindInt32("_token", cookie.index, &token) 3341 == B_OK; 3342 cookie.index++) { 3343 // focus view is preferred and should get its message directly 3344 if (token == cookie.focus_token) { 3345 cookie.found_focus = true; 3346 continue; 3347 } 3348 if (token == cookie.last_view_token) 3349 continue; 3350 3351 BView* target = _FindView(token); 3352 if (target == NULL) 3353 continue; 3354 3355 *_message = new BMessage(*cookie.message); 3356 // the secondary copies of the message should not be treated as focus 3357 // messages, otherwise there will be unintended side effects, i.e. 3358 // keyboard shortcuts getting processed multiple times. 3359 (*_message)->RemoveName("_feed_focus"); 3360 *_target = target; 3361 cookie.index++; 3362 return true; 3363 } 3364 3365 cookie.tokens_scanned = true; 3366 3367 // if there is a last mouse moved view, and the new focus is 3368 // different, the previous view wants to get its B_EXITED_VIEW 3369 // message 3370 if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL 3371 && fLastMouseMovedView != cookie.focus) { 3372 *_message = new BMessage(*cookie.message); 3373 *_target = fLastMouseMovedView; 3374 cookie.last_view_token = B_NULL_TOKEN; 3375 return true; 3376 } 3377 3378 bool dispatchToFocus = true; 3379 3380 // check if the focus token is still valid (could have been removed in the mean time) 3381 BHandler* handler; 3382 if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK 3383 || handler->Looper() != this) 3384 dispatchToFocus = false; 3385 3386 if (dispatchToFocus && cookie.index > 0) { 3387 // should this message still be dispatched by the focus view? 3388 bool feedFocus; 3389 if (!cookie.found_focus 3390 && (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK 3391 || feedFocus == false)) 3392 dispatchToFocus = false; 3393 } 3394 3395 if (!dispatchToFocus) { 3396 delete cookie.message; 3397 cookie.message = NULL; 3398 return false; 3399 } 3400 3401 *_message = cookie.message; 3402 *_target = cookie.focus; 3403 *_usePreferred = true; 3404 cookie.message = NULL; 3405 return true; 3406 } 3407 3408 3409 /*! Some messages don't get to the window in a shape an application should see. 3410 This method is supposed to give a message the last grinding before 3411 it's acceptable for the receiving application. 3412 */ 3413 void 3414 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred) 3415 { 3416 if (target == NULL) 3417 return; 3418 3419 switch (message->what) { 3420 case B_MOUSE_MOVED: 3421 case B_MOUSE_UP: 3422 case B_MOUSE_DOWN: 3423 { 3424 BPoint where; 3425 if (message->FindPoint("screen_where", &where) != B_OK) 3426 break; 3427 3428 BView* view = dynamic_cast<BView*>(target); 3429 3430 if (view == NULL || message->what == B_MOUSE_MOVED) { 3431 // add local window coordinates, only 3432 // for regular mouse moved messages 3433 message->AddPoint("where", ConvertFromScreen(where)); 3434 } 3435 3436 if (view != NULL) { 3437 // add local view coordinates 3438 BPoint viewWhere = view->ConvertFromScreen(where); 3439 if (message->what != B_MOUSE_MOVED) { 3440 // Yep, the meaning of "where" is different 3441 // for regular mouse moved messages versus 3442 // mouse up/down! 3443 message->AddPoint("where", viewWhere); 3444 } 3445 message->AddPoint("be:view_where", viewWhere); 3446 3447 if (message->what == B_MOUSE_MOVED) { 3448 // is there a token of the view that is currently under 3449 // the mouse? 3450 BView* viewUnderMouse = NULL; 3451 int32 token; 3452 if (message->FindInt32("_view_token", &token) == B_OK) 3453 viewUnderMouse = _FindView(token); 3454 3455 // add transit information 3456 uint32 transit 3457 = _TransitForMouseMoved(view, viewUnderMouse); 3458 message->AddInt32("be:transit", transit); 3459 3460 if (usePreferred) 3461 fLastMouseMovedView = viewUnderMouse; 3462 } 3463 } 3464 break; 3465 } 3466 3467 case B_MOUSE_IDLE: 3468 { 3469 // App Server sends screen coordinates, convert the point to 3470 // local view coordinates, then add the point in be:view_where 3471 BPoint where; 3472 if (message->FindPoint("screen_where", &where) != B_OK) 3473 break; 3474 3475 BView* view = dynamic_cast<BView*>(target); 3476 if (view != NULL) { 3477 // add local view coordinates 3478 message->AddPoint("be:view_where", 3479 view->ConvertFromScreen(where)); 3480 } 3481 break; 3482 } 3483 3484 case _MESSAGE_DROPPED_: 3485 { 3486 uint32 originalWhat; 3487 if (message->FindInt32("_original_what", 3488 (int32*)&originalWhat) == B_OK) { 3489 message->what = originalWhat; 3490 message->RemoveName("_original_what"); 3491 } 3492 break; 3493 } 3494 } 3495 } 3496 3497 3498 /*! 3499 This is called by BView::GetMouse() when a B_MOUSE_MOVED message 3500 is removed from the queue. 3501 It allows the window to update the last mouse moved view, and 3502 let it decide if this message should be kept. It will also remove 3503 the message from the queue. 3504 You need to hold the message queue lock when calling this method! 3505 3506 \return true if this message can be used to get the mouse data from, 3507 \return false if this is not meant for the public. 3508 */ 3509 bool 3510 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage) 3511 { 3512 BMessage::Private messagePrivate(message); 3513 if (!messagePrivate.UsePreferredTarget()) { 3514 // this message is targeted at a specific handler, so we should 3515 // not steal it 3516 return false; 3517 } 3518 3519 int32 token; 3520 if (message->FindInt32("_token", 0, &token) == B_OK) { 3521 // This message has other targets, so we can't remove it; 3522 // just prevent it from being sent to the preferred handler 3523 // again (if it should have gotten it at all). 3524 bool feedFocus; 3525 if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus) 3526 return false; 3527 3528 message->RemoveName("_feed_focus"); 3529 deleteMessage = false; 3530 } else { 3531 deleteMessage = true; 3532 3533 if (message->what == B_MOUSE_MOVED) { 3534 // We need to update the last mouse moved view, as this message 3535 // won't make it to _SanitizeMessage() anymore. 3536 BView* viewUnderMouse = NULL; 3537 int32 token; 3538 if (message->FindInt32("_view_token", &token) == B_OK) 3539 viewUnderMouse = _FindView(token); 3540 3541 // Don't remove important transit messages! 3542 uint32 transit = _TransitForMouseMoved(fLastMouseMovedView, 3543 viewUnderMouse); 3544 if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW) 3545 deleteMessage = false; 3546 } 3547 3548 if (deleteMessage) { 3549 // The message is only thought for the preferred handler, so we 3550 // can just remove it. 3551 MessageQueue()->RemoveMessage(message); 3552 } 3553 } 3554 3555 return true; 3556 } 3557 3558 3559 uint32 3560 BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const 3561 { 3562 uint32 transit; 3563 if (viewUnderMouse == view) { 3564 // the mouse is over the target view 3565 if (fLastMouseMovedView != view) 3566 transit = B_ENTERED_VIEW; 3567 else 3568 transit = B_INSIDE_VIEW; 3569 } else { 3570 // the mouse is not over the target view 3571 if (view == fLastMouseMovedView) 3572 transit = B_EXITED_VIEW; 3573 else 3574 transit = B_OUTSIDE_VIEW; 3575 } 3576 return transit; 3577 } 3578 3579 3580 /*! Forwards the key to the switcher 3581 */ 3582 void 3583 BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat) 3584 { 3585 // only send the first key press, no repeats 3586 if (repeat) 3587 return; 3588 3589 BMessenger deskbar(kDeskbarSignature); 3590 if (!deskbar.IsValid()) { 3591 // TODO: have some kind of fallback-handling in case the Deskbar is 3592 // not available? 3593 return; 3594 } 3595 3596 BMessage message('TASK'); 3597 message.AddInt32("key", rawKey); 3598 message.AddInt32("modifiers", modifiers); 3599 message.AddInt64("when", system_time()); 3600 message.AddInt32("team", Team()); 3601 deskbar.SendMessage(&message); 3602 } 3603 3604 3605 /*! Handles keyboard input before it gets forwarded to the target handler. 3606 This includes shortcut evaluation, keyboard navigation, etc. 3607 3608 \return handled if true, the event was already handled, and will not 3609 be forwarded to the target handler. 3610 3611 TODO: must also convert the incoming key to the font encoding of the target 3612 */ 3613 bool 3614 BWindow::_HandleKeyDown(BMessage* event) 3615 { 3616 // Only handle special functions when the event targeted the active focus 3617 // view 3618 if (!_IsFocusMessage(event)) 3619 return false; 3620 3621 const char* bytes = NULL; 3622 if (event->FindString("bytes", &bytes) != B_OK) 3623 return false; 3624 3625 char key = bytes[0]; 3626 3627 uint32 modifiers; 3628 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK) 3629 modifiers = 0; 3630 3631 // handle BMenuBar key 3632 if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0 3633 && fKeyMenuBar != NULL) { 3634 fKeyMenuBar->StartMenuBar(0, true, false, NULL); 3635 return true; 3636 } 3637 3638 // Keyboard navigation through views 3639 // (B_OPTION_KEY makes BTextViews and friends navigable, even in editing 3640 // mode) 3641 if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) { 3642 _KeyboardNavigation(); 3643 return true; 3644 } 3645 3646 int32 rawKey; 3647 event->FindInt32("key", &rawKey); 3648 3649 // Deskbar's Switcher 3650 if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) { 3651 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat")); 3652 return true; 3653 } 3654 3655 // Optionally close window when the escape key is pressed 3656 if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) { 3657 BMessage message(B_QUIT_REQUESTED); 3658 message.AddBool("shortcut", true); 3659 3660 PostMessage(&message); 3661 return true; 3662 } 3663 3664 // PrtScr key takes a screenshot 3665 if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) { 3666 // With no modifier keys the best way to get a screenshot is by 3667 // calling the screenshot CLI 3668 if (modifiers == 0) { 3669 be_roster->Launch("application/x-vnd.haiku-screenshot-cli"); 3670 return true; 3671 } 3672 3673 // Prepare a message based on the modifier keys pressed and launch the 3674 // screenshot GUI 3675 BMessage message(B_ARGV_RECEIVED); 3676 int32 argc = 1; 3677 message.AddString("argv", "Screenshot"); 3678 if ((modifiers & B_CONTROL_KEY) != 0) { 3679 argc++; 3680 message.AddString("argv", "--clipboard"); 3681 } 3682 if ((modifiers & B_SHIFT_KEY) != 0) { 3683 argc++; 3684 message.AddString("argv", "--silent"); 3685 } 3686 message.AddInt32("argc", argc); 3687 be_roster->Launch("application/x-vnd.haiku-screenshot", &message); 3688 return true; 3689 } 3690 3691 // Handle shortcuts 3692 if ((modifiers & B_COMMAND_KEY) != 0) { 3693 // Command+q has been pressed, so, we will quit 3694 // the shortcut mechanism doesn't allow handlers outside the window 3695 if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) { 3696 BMessage message(B_QUIT_REQUESTED); 3697 message.AddBool("shortcut", true); 3698 3699 be_app->PostMessage(&message); 3700 // eat the event 3701 return true; 3702 } 3703 3704 // Send Command+Left and Command+Right to textview if it has focus 3705 if (key == B_LEFT_ARROW || key == B_RIGHT_ARROW) { 3706 // check key before doing expensive dynamic_cast 3707 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 3708 if (textView != NULL) { 3709 textView->KeyDown(bytes, modifiers); 3710 // eat the event 3711 return true; 3712 } 3713 } 3714 3715 // Pretend that the user opened a menu, to give the subclass a 3716 // chance to update it's menus. This may install new shortcuts, 3717 // which is why we have to call it here, before trying to find 3718 // a shortcut for the given key. 3719 MenusBeginning(); 3720 3721 Shortcut* shortcut = _FindShortcut(key, modifiers); 3722 if (shortcut != NULL) { 3723 // TODO: would be nice to move this functionality to 3724 // a Shortcut::Invoke() method - but since BMenu::InvokeItem() 3725 // (and BMenuItem::Invoke()) are private, I didn't want 3726 // to mess with them (BMenuItem::Invoke() is public in 3727 // Dano/Zeta, though, maybe we should just follow their 3728 // example) 3729 if (shortcut->MenuItem() != NULL) { 3730 BMenu* menu = shortcut->MenuItem()->Menu(); 3731 if (menu != NULL) 3732 MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true); 3733 } else { 3734 BHandler* target = shortcut->Target(); 3735 if (target == NULL) 3736 target = CurrentFocus(); 3737 3738 if (shortcut->Message() != NULL) { 3739 BMessage message(*shortcut->Message()); 3740 3741 if (message.ReplaceInt64("when", system_time()) != B_OK) 3742 message.AddInt64("when", system_time()); 3743 if (message.ReplaceBool("shortcut", true) != B_OK) 3744 message.AddBool("shortcut", true); 3745 3746 PostMessage(&message, target); 3747 } 3748 } 3749 } 3750 3751 MenusEnded(); 3752 3753 // we always eat the event if the command key was pressed 3754 return true; 3755 } 3756 3757 // TODO: convert keys to the encoding of the target view 3758 3759 return false; 3760 } 3761 3762 3763 bool 3764 BWindow::_HandleUnmappedKeyDown(BMessage* event) 3765 { 3766 // Only handle special functions when the event targeted the active focus 3767 // view 3768 if (!_IsFocusMessage(event)) 3769 return false; 3770 3771 uint32 modifiers; 3772 int32 rawKey; 3773 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK 3774 || event->FindInt32("key", &rawKey)) 3775 return false; 3776 3777 // Deskbar's Switcher 3778 if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) { 3779 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat")); 3780 return true; 3781 } 3782 3783 return false; 3784 } 3785 3786 3787 void 3788 BWindow::_KeyboardNavigation() 3789 { 3790 BMessage* message = CurrentMessage(); 3791 if (message == NULL) 3792 return; 3793 3794 const char* bytes; 3795 uint32 modifiers; 3796 if (message->FindString("bytes", &bytes) != B_OK || bytes[0] != B_TAB) 3797 return; 3798 3799 message->FindInt32("modifiers", (int32*)&modifiers); 3800 3801 BView* nextFocus; 3802 int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0 3803 ? B_NAVIGABLE_JUMP : B_NAVIGABLE; 3804 if (modifiers & B_SHIFT_KEY) 3805 nextFocus = _FindPreviousNavigable(fFocus, jumpGroups); 3806 else 3807 nextFocus = _FindNextNavigable(fFocus, jumpGroups); 3808 3809 if (nextFocus != NULL && nextFocus != fFocus) 3810 nextFocus->MakeFocus(true); 3811 } 3812 3813 3814 /*! 3815 \brief Return the position of the window centered horizontally to the passed 3816 in \a frame and vertically 3/4 from the top of \a frame. 3817 3818 If the window is on the borders 3819 3820 \param width The width of the window. 3821 \param height The height of the window. 3822 \param frame The \a frame to center the window in. 3823 3824 \return The new window position. 3825 */ 3826 BPoint 3827 BWindow::AlertPosition(const BRect& frame) 3828 { 3829 float width = Bounds().Width(); 3830 float height = Bounds().Height(); 3831 3832 BPoint point(frame.left + (frame.Width() / 2.0f) - (width / 2.0f), 3833 frame.top + (frame.Height() / 4.0f) - ceil(height / 3.0f)); 3834 3835 BRect screenFrame = BScreen(this).Frame(); 3836 if (frame == screenFrame) { 3837 // reference frame is screen frame, skip the below adjustments 3838 return point; 3839 } 3840 3841 float borderWidth; 3842 float tabHeight; 3843 _GetDecoratorSize(&borderWidth, &tabHeight); 3844 3845 // clip the x position within the horizontal edges of the screen 3846 if (point.x < screenFrame.left + borderWidth) 3847 point.x = screenFrame.left + borderWidth; 3848 else if (point.x + width > screenFrame.right - borderWidth) 3849 point.x = screenFrame.right - borderWidth - width; 3850 3851 // lower the window down if it is covering the window tab 3852 float tabPosition = frame.LeftTop().y + tabHeight + borderWidth; 3853 if (point.y < tabPosition) 3854 point.y = tabPosition; 3855 3856 // clip the y position within the vertical edges of the screen 3857 if (point.y < screenFrame.top + borderWidth) 3858 point.y = screenFrame.top + borderWidth; 3859 else if (point.y + height > screenFrame.bottom - borderWidth) 3860 point.y = screenFrame.bottom - borderWidth - height; 3861 3862 return point; 3863 } 3864 3865 3866 BMessage* 3867 BWindow::ConvertToMessage(void* raw, int32 code) 3868 { 3869 return BLooper::ConvertToMessage(raw, code); 3870 } 3871 3872 3873 BWindow::Shortcut* 3874 BWindow::_FindShortcut(uint32 key, uint32 modifiers) 3875 { 3876 int32 count = fShortcuts.CountItems(); 3877 3878 key = Shortcut::PrepareKey(key); 3879 modifiers = Shortcut::PrepareModifiers(modifiers); 3880 3881 for (int32 index = 0; index < count; index++) { 3882 Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index); 3883 3884 if (shortcut->Matches(key, modifiers)) 3885 return shortcut; 3886 } 3887 3888 return NULL; 3889 } 3890 3891 3892 BView* 3893 BWindow::_FindView(int32 token) 3894 { 3895 BHandler* handler; 3896 if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN, 3897 (void**)&handler) != B_OK) { 3898 return NULL; 3899 } 3900 3901 // the view must belong to us in order to be found by this method 3902 BView* view = dynamic_cast<BView*>(handler); 3903 if (view != NULL && view->Window() == this) 3904 return view; 3905 3906 return NULL; 3907 } 3908 3909 3910 BView* 3911 BWindow::_FindView(BView* view, BPoint point) const 3912 { 3913 // point is assumed to be already in view's coordinates 3914 if (!view->IsHidden(view) && view->Bounds().Contains(point)) { 3915 if (view->fFirstChild == NULL) 3916 return view; 3917 else { 3918 BView* child = view->fFirstChild; 3919 while (child != NULL) { 3920 BPoint childPoint = point - child->Frame().LeftTop(); 3921 BView* subView = _FindView(child, childPoint); 3922 if (subView != NULL) 3923 return subView; 3924 3925 child = child->fNextSibling; 3926 } 3927 } 3928 return view; 3929 } 3930 return NULL; 3931 } 3932 3933 3934 BView* 3935 BWindow::_FindNextNavigable(BView* focus, uint32 flags) 3936 { 3937 if (focus == NULL) 3938 focus = fTopView; 3939 3940 BView* nextFocus = focus; 3941 3942 // Search the tree for views that accept focus (depth search) 3943 while (true) { 3944 if (nextFocus->fFirstChild) 3945 nextFocus = nextFocus->fFirstChild; 3946 else if (nextFocus->fNextSibling) 3947 nextFocus = nextFocus->fNextSibling; 3948 else { 3949 // go to the nearest parent with a next sibling 3950 while (!nextFocus->fNextSibling && nextFocus->fParent) { 3951 nextFocus = nextFocus->fParent; 3952 } 3953 3954 if (nextFocus == fTopView) { 3955 // if we started with the top view, we traversed the whole tree already 3956 if (nextFocus == focus) 3957 return NULL; 3958 3959 nextFocus = nextFocus->fFirstChild; 3960 } else 3961 nextFocus = nextFocus->fNextSibling; 3962 } 3963 3964 if (nextFocus == focus || nextFocus == NULL) { 3965 // When we get here it means that the hole tree has been 3966 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set! 3967 return NULL; 3968 } 3969 3970 if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0) 3971 return nextFocus; 3972 } 3973 } 3974 3975 3976 BView* 3977 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags) 3978 { 3979 if (focus == NULL) 3980 focus = fTopView; 3981 3982 BView* previousFocus = focus; 3983 3984 // Search the tree for the previous view that accept focus 3985 while (true) { 3986 if (previousFocus->fPreviousSibling) { 3987 // find the last child in the previous sibling 3988 previousFocus = _LastViewChild(previousFocus->fPreviousSibling); 3989 } else { 3990 previousFocus = previousFocus->fParent; 3991 if (previousFocus == fTopView) 3992 previousFocus = _LastViewChild(fTopView); 3993 } 3994 3995 if (previousFocus == focus || previousFocus == NULL) { 3996 // When we get here it means that the hole tree has been 3997 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set! 3998 return NULL; 3999 } 4000 4001 if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0) 4002 return previousFocus; 4003 } 4004 } 4005 4006 4007 /*! 4008 Returns the last child in a view hierarchy. 4009 Needed only by _FindPreviousNavigable(). 4010 */ 4011 BView* 4012 BWindow::_LastViewChild(BView* parent) 4013 { 4014 while (true) { 4015 BView* last = parent->fFirstChild; 4016 if (last == NULL) 4017 return parent; 4018 4019 while (last->fNextSibling) { 4020 last = last->fNextSibling; 4021 } 4022 4023 parent = last; 4024 } 4025 } 4026 4027 4028 void 4029 BWindow::SetIsFilePanel(bool isFilePanel) 4030 { 4031 fIsFilePanel = isFilePanel; 4032 } 4033 4034 4035 bool 4036 BWindow::IsFilePanel() const 4037 { 4038 return fIsFilePanel; 4039 } 4040 4041 4042 void 4043 BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const 4044 { 4045 // fallback in case retrieving the decorator settings fails 4046 // (highly unlikely) 4047 float borderWidth = 5.0; 4048 float tabHeight = 21.0; 4049 4050 BMessage settings; 4051 if (GetDecoratorSettings(&settings) == B_OK) { 4052 BRect tabRect; 4053 if (settings.FindRect("tab frame", &tabRect) == B_OK) 4054 tabHeight = tabRect.Height(); 4055 settings.FindFloat("border width", &borderWidth); 4056 } else { 4057 // probably no-border window look 4058 if (fLook == B_NO_BORDER_WINDOW_LOOK) { 4059 borderWidth = 0.0; 4060 tabHeight = 0.0; 4061 } 4062 // else use fall-back values from above 4063 } 4064 4065 if (_borderWidth != NULL) 4066 *_borderWidth = borderWidth; 4067 if (_tabHeight != NULL) 4068 *_tabHeight = tabHeight; 4069 } 4070 4071 4072 void 4073 BWindow::_SendShowOrHideMessage() 4074 { 4075 fLink->StartMessage(AS_SHOW_OR_HIDE_WINDOW); 4076 fLink->Attach<int32>(fShowLevel); 4077 fLink->Flush(); 4078 } 4079 4080 4081 // #pragma mark - C++ binary compatibility kludge 4082 4083 4084 extern "C" void 4085 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout) 4086 { 4087 // SetLayout() 4088 perform_data_set_layout data; 4089 data.layout = layout; 4090 window->Perform(PERFORM_CODE_SET_LAYOUT, &data); 4091 } 4092 4093 4094 void BWindow::_ReservedWindow2() {} 4095 void BWindow::_ReservedWindow3() {} 4096 void BWindow::_ReservedWindow4() {} 4097 void BWindow::_ReservedWindow5() {} 4098 void BWindow::_ReservedWindow6() {} 4099 void BWindow::_ReservedWindow7() {} 4100 void BWindow::_ReservedWindow8() {} 4101 4102