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