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