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