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