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