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