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