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