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 void 1863 BWindow::RemoveShortcut(uint32 key, uint32 modifiers) 1864 { 1865 Shortcut* shortcut = _FindShortcut(key, modifiers); 1866 if (shortcut != NULL) { 1867 fShortcuts.RemoveItem(shortcut); 1868 delete shortcut; 1869 } else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) { 1870 // the quit shortcut is a fake shortcut 1871 fNoQuitShortcut = true; 1872 } 1873 } 1874 1875 1876 BButton* 1877 BWindow::DefaultButton() const 1878 { 1879 // TODO: What about locking?!? 1880 return fDefaultButton; 1881 } 1882 1883 1884 void 1885 BWindow::SetDefaultButton(BButton* button) 1886 { 1887 // TODO: What about locking?!? 1888 if (fDefaultButton == button) 1889 return; 1890 1891 if (fDefaultButton != NULL) { 1892 // tell old button it's no longer the default one 1893 BButton* oldDefault = fDefaultButton; 1894 oldDefault->MakeDefault(false); 1895 oldDefault->Invalidate(); 1896 } 1897 1898 fDefaultButton = button; 1899 1900 if (button != NULL) { 1901 // notify new default button 1902 fDefaultButton->MakeDefault(true); 1903 fDefaultButton->Invalidate(); 1904 } 1905 } 1906 1907 1908 bool 1909 BWindow::NeedsUpdate() const 1910 { 1911 if (!const_cast<BWindow*>(this)->Lock()) 1912 return false; 1913 1914 fLink->StartMessage(AS_NEEDS_UPDATE); 1915 1916 int32 code = B_ERROR; 1917 fLink->FlushWithReply(code); 1918 1919 const_cast<BWindow*>(this)->Unlock(); 1920 1921 return code == B_OK; 1922 } 1923 1924 1925 void 1926 BWindow::UpdateIfNeeded() 1927 { 1928 // works only from the window thread 1929 if (find_thread(NULL) != Thread()) 1930 return; 1931 1932 // if the queue is already locked we are called recursivly 1933 // from our own dispatched update message 1934 if (((const BMessageQueue*)MessageQueue())->IsLocked()) 1935 return; 1936 1937 if (!Lock()) 1938 return; 1939 1940 // make sure all requests that would cause an update have 1941 // arrived at the server 1942 Sync(); 1943 1944 // Since we're blocking the event loop, we need to retrieve 1945 // all messages that are pending on the port. 1946 _DequeueAll(); 1947 1948 BMessageQueue* queue = MessageQueue(); 1949 1950 // First process and remove any _UPDATE_ message in the queue 1951 // With the current design, there can only be one at a time 1952 1953 while (true) { 1954 queue->Lock(); 1955 1956 BMessage* message = queue->FindMessage(_UPDATE_, 0); 1957 queue->RemoveMessage(message); 1958 1959 queue->Unlock(); 1960 1961 if (message == NULL) 1962 break; 1963 1964 BWindow::DispatchMessage(message, this); 1965 delete message; 1966 } 1967 1968 Unlock(); 1969 } 1970 1971 1972 BView* 1973 BWindow::FindView(const char* viewName) const 1974 { 1975 BAutolock locker(const_cast<BWindow*>(this)); 1976 if (!locker.IsLocked()) 1977 return NULL; 1978 1979 return fTopView->FindView(viewName); 1980 } 1981 1982 1983 BView* 1984 BWindow::FindView(BPoint point) const 1985 { 1986 BAutolock locker(const_cast<BWindow*>(this)); 1987 if (!locker.IsLocked()) 1988 return NULL; 1989 1990 // point is assumed to be in window coordinates, 1991 // fTopView has same bounds as window 1992 return _FindView(fTopView, point); 1993 } 1994 1995 1996 BView* 1997 BWindow::CurrentFocus() const 1998 { 1999 return fFocus; 2000 } 2001 2002 2003 void 2004 BWindow::Activate(bool active) 2005 { 2006 if (!Lock()) 2007 return; 2008 2009 if (!IsHidden()) { 2010 fMinimized = false; 2011 // activating a window will also unminimize it 2012 2013 fLink->StartMessage(AS_ACTIVATE_WINDOW); 2014 fLink->Attach<bool>(active); 2015 fLink->Flush(); 2016 } 2017 2018 Unlock(); 2019 } 2020 2021 2022 void 2023 BWindow::WindowActivated(bool state) 2024 { 2025 // hook function 2026 // does nothing 2027 } 2028 2029 2030 void 2031 BWindow::ConvertToScreen(BPoint* point) const 2032 { 2033 point->x += fFrame.left; 2034 point->y += fFrame.top; 2035 } 2036 2037 2038 BPoint 2039 BWindow::ConvertToScreen(BPoint point) const 2040 { 2041 return point + fFrame.LeftTop(); 2042 } 2043 2044 2045 void 2046 BWindow::ConvertFromScreen(BPoint* point) const 2047 { 2048 point->x -= fFrame.left; 2049 point->y -= fFrame.top; 2050 } 2051 2052 2053 BPoint 2054 BWindow::ConvertFromScreen(BPoint point) const 2055 { 2056 return point - fFrame.LeftTop(); 2057 } 2058 2059 2060 void 2061 BWindow::ConvertToScreen(BRect* rect) const 2062 { 2063 rect->OffsetBy(fFrame.LeftTop()); 2064 } 2065 2066 2067 BRect 2068 BWindow::ConvertToScreen(BRect rect) const 2069 { 2070 return rect.OffsetByCopy(fFrame.LeftTop()); 2071 } 2072 2073 2074 void 2075 BWindow::ConvertFromScreen(BRect* rect) const 2076 { 2077 rect->OffsetBy(-fFrame.left, -fFrame.top); 2078 } 2079 2080 2081 BRect 2082 BWindow::ConvertFromScreen(BRect rect) const 2083 { 2084 return rect.OffsetByCopy(-fFrame.left, -fFrame.top); 2085 } 2086 2087 2088 bool 2089 BWindow::IsMinimized() const 2090 { 2091 BAutolock locker(const_cast<BWindow*>(this)); 2092 if (!locker.IsLocked()) 2093 return false; 2094 2095 return fMinimized; 2096 } 2097 2098 2099 BRect 2100 BWindow::Bounds() const 2101 { 2102 return BRect(0, 0, fFrame.Width(), fFrame.Height()); 2103 } 2104 2105 2106 BRect 2107 BWindow::Frame() const 2108 { 2109 return fFrame; 2110 } 2111 2112 2113 BRect 2114 BWindow::DecoratorFrame() const 2115 { 2116 BRect decoratorFrame(Frame()); 2117 BRect tabRect(0, 0, 0, 0); 2118 2119 float borderWidth = 5.0; 2120 2121 BMessage settings; 2122 if (GetDecoratorSettings(&settings) == B_OK) { 2123 settings.FindRect("tab frame", &tabRect); 2124 settings.FindFloat("border width", &borderWidth); 2125 } else { 2126 // probably no-border window look 2127 if (fLook == B_NO_BORDER_WINDOW_LOOK) 2128 borderWidth = 0.f; 2129 else if (fLook == B_BORDERED_WINDOW_LOOK) 2130 borderWidth = 1.f; 2131 // else use fall-back values from above 2132 } 2133 2134 if (fLook & kLeftTitledWindowLook) { 2135 decoratorFrame.top -= borderWidth; 2136 decoratorFrame.left -= tabRect.Width(); 2137 decoratorFrame.right += borderWidth; 2138 decoratorFrame.bottom += borderWidth; 2139 } else { 2140 decoratorFrame.top -= tabRect.Height(); 2141 decoratorFrame.left -= borderWidth; 2142 decoratorFrame.right += borderWidth; 2143 decoratorFrame.bottom += borderWidth; 2144 } 2145 2146 return decoratorFrame; 2147 } 2148 2149 2150 BSize 2151 BWindow::Size() const 2152 { 2153 return BSize(fFrame.Width(), fFrame.Height()); 2154 } 2155 2156 2157 const char* 2158 BWindow::Title() const 2159 { 2160 return fTitle; 2161 } 2162 2163 2164 void 2165 BWindow::SetTitle(const char* title) 2166 { 2167 if (title == NULL) 2168 title = ""; 2169 2170 free(fTitle); 2171 fTitle = strdup(title); 2172 2173 _SetName(title); 2174 2175 // we notify the app_server so we can actually see the change 2176 if (Lock()) { 2177 fLink->StartMessage(AS_SET_WINDOW_TITLE); 2178 fLink->AttachString(fTitle); 2179 fLink->Flush(); 2180 Unlock(); 2181 } 2182 } 2183 2184 2185 bool 2186 BWindow::IsActive() const 2187 { 2188 return fActive; 2189 } 2190 2191 2192 void 2193 BWindow::SetKeyMenuBar(BMenuBar* bar) 2194 { 2195 fKeyMenuBar = bar; 2196 } 2197 2198 2199 BMenuBar* 2200 BWindow::KeyMenuBar() const 2201 { 2202 return fKeyMenuBar; 2203 } 2204 2205 2206 bool 2207 BWindow::IsModal() const 2208 { 2209 return fFeel == B_MODAL_SUBSET_WINDOW_FEEL 2210 || fFeel == B_MODAL_APP_WINDOW_FEEL 2211 || fFeel == B_MODAL_ALL_WINDOW_FEEL 2212 || fFeel == kMenuWindowFeel; 2213 } 2214 2215 2216 bool 2217 BWindow::IsFloating() const 2218 { 2219 return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL 2220 || fFeel == B_FLOATING_APP_WINDOW_FEEL 2221 || fFeel == B_FLOATING_ALL_WINDOW_FEEL; 2222 } 2223 2224 2225 status_t 2226 BWindow::AddToSubset(BWindow* window) 2227 { 2228 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL 2229 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL 2230 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL)) 2231 return B_BAD_VALUE; 2232 2233 if (!Lock()) 2234 return B_ERROR; 2235 2236 status_t status = B_ERROR; 2237 fLink->StartMessage(AS_ADD_TO_SUBSET); 2238 fLink->Attach<int32>(_get_object_token_(window)); 2239 fLink->FlushWithReply(status); 2240 2241 Unlock(); 2242 2243 return status; 2244 } 2245 2246 2247 status_t 2248 BWindow::RemoveFromSubset(BWindow* window) 2249 { 2250 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL 2251 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL 2252 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL)) 2253 return B_BAD_VALUE; 2254 2255 if (!Lock()) 2256 return B_ERROR; 2257 2258 status_t status = B_ERROR; 2259 fLink->StartMessage(AS_REMOVE_FROM_SUBSET); 2260 fLink->Attach<int32>(_get_object_token_(window)); 2261 fLink->FlushWithReply(status); 2262 2263 Unlock(); 2264 2265 return status; 2266 } 2267 2268 2269 status_t 2270 BWindow::Perform(perform_code code, void* _data) 2271 { 2272 switch (code) { 2273 case PERFORM_CODE_SET_LAYOUT: 2274 { 2275 perform_data_set_layout* data = (perform_data_set_layout*)_data; 2276 BWindow::SetLayout(data->layout); 2277 return B_OK; 2278 } 2279 } 2280 2281 return BLooper::Perform(code, _data); 2282 } 2283 2284 2285 status_t 2286 BWindow::SetType(window_type type) 2287 { 2288 window_look look; 2289 window_feel feel; 2290 _DecomposeType(type, &look, &feel); 2291 2292 status_t status = SetLook(look); 2293 if (status == B_OK) 2294 status = SetFeel(feel); 2295 2296 return status; 2297 } 2298 2299 2300 window_type 2301 BWindow::Type() const 2302 { 2303 return _ComposeType(fLook, fFeel); 2304 } 2305 2306 2307 status_t 2308 BWindow::SetLook(window_look look) 2309 { 2310 BAutolock locker(this); 2311 if (!locker.IsLocked()) 2312 return B_BAD_VALUE; 2313 2314 fLink->StartMessage(AS_SET_LOOK); 2315 fLink->Attach<int32>((int32)look); 2316 2317 status_t status = B_ERROR; 2318 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2319 fLook = look; 2320 2321 // TODO: this could have changed the window size, and thus, we 2322 // need to get it from the server (and call _AdoptResize()). 2323 2324 return status; 2325 } 2326 2327 2328 window_look 2329 BWindow::Look() const 2330 { 2331 return fLook; 2332 } 2333 2334 2335 status_t 2336 BWindow::SetFeel(window_feel feel) 2337 { 2338 BAutolock locker(this); 2339 if (!locker.IsLocked()) 2340 return B_BAD_VALUE; 2341 2342 fLink->StartMessage(AS_SET_FEEL); 2343 fLink->Attach<int32>((int32)feel); 2344 2345 status_t status = B_ERROR; 2346 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2347 fFeel = feel; 2348 2349 return status; 2350 } 2351 2352 2353 window_feel 2354 BWindow::Feel() const 2355 { 2356 return fFeel; 2357 } 2358 2359 2360 status_t 2361 BWindow::SetFlags(uint32 flags) 2362 { 2363 BAutolock locker(this); 2364 if (!locker.IsLocked()) 2365 return B_BAD_VALUE; 2366 2367 fLink->StartMessage(AS_SET_FLAGS); 2368 fLink->Attach<uint32>(flags); 2369 2370 int32 status = B_ERROR; 2371 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2372 fFlags = flags; 2373 2374 return status; 2375 } 2376 2377 2378 uint32 2379 BWindow::Flags() const 2380 { 2381 return fFlags; 2382 } 2383 2384 2385 status_t 2386 BWindow::SetWindowAlignment(window_alignment mode, 2387 int32 h, int32 hOffset, int32 width, int32 widthOffset, 2388 int32 v, int32 vOffset, int32 height, int32 heightOffset) 2389 { 2390 if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0 2391 || (hOffset >= 0 && hOffset <= h) 2392 || (vOffset >= 0 && vOffset <= v) 2393 || (widthOffset >= 0 && widthOffset <= width) 2394 || (heightOffset >= 0 && heightOffset <= height)) 2395 return B_BAD_VALUE; 2396 2397 // TODO: test if hOffset = 0 and set it to 1 if true. 2398 2399 if (!Lock()) 2400 return B_ERROR; 2401 2402 fLink->StartMessage(AS_SET_ALIGNMENT); 2403 fLink->Attach<int32>((int32)mode); 2404 fLink->Attach<int32>(h); 2405 fLink->Attach<int32>(hOffset); 2406 fLink->Attach<int32>(width); 2407 fLink->Attach<int32>(widthOffset); 2408 fLink->Attach<int32>(v); 2409 fLink->Attach<int32>(vOffset); 2410 fLink->Attach<int32>(height); 2411 fLink->Attach<int32>(heightOffset); 2412 2413 status_t status = B_ERROR; 2414 fLink->FlushWithReply(status); 2415 2416 Unlock(); 2417 2418 return status; 2419 } 2420 2421 2422 status_t 2423 BWindow::GetWindowAlignment(window_alignment* mode, 2424 int32* h, int32* hOffset, int32* width, int32* widthOffset, 2425 int32* v, int32* vOffset, int32* height, int32* heightOffset) const 2426 { 2427 if (!const_cast<BWindow*>(this)->Lock()) 2428 return B_ERROR; 2429 2430 fLink->StartMessage(AS_GET_ALIGNMENT); 2431 2432 status_t status; 2433 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) { 2434 fLink->Read<int32>((int32*)mode); 2435 fLink->Read<int32>(h); 2436 fLink->Read<int32>(hOffset); 2437 fLink->Read<int32>(width); 2438 fLink->Read<int32>(widthOffset); 2439 fLink->Read<int32>(v); 2440 fLink->Read<int32>(hOffset); 2441 fLink->Read<int32>(height); 2442 fLink->Read<int32>(heightOffset); 2443 } 2444 2445 const_cast<BWindow*>(this)->Unlock(); 2446 return status; 2447 } 2448 2449 2450 uint32 2451 BWindow::Workspaces() const 2452 { 2453 if (!const_cast<BWindow*>(this)->Lock()) 2454 return 0; 2455 2456 uint32 workspaces = 0; 2457 2458 fLink->StartMessage(AS_GET_WORKSPACES); 2459 2460 status_t status; 2461 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2462 fLink->Read<uint32>(&workspaces); 2463 2464 const_cast<BWindow*>(this)->Unlock(); 2465 return workspaces; 2466 } 2467 2468 2469 void 2470 BWindow::SetWorkspaces(uint32 workspaces) 2471 { 2472 // TODO: don't forget about Tracker's background window. 2473 if (fFeel != B_NORMAL_WINDOW_FEEL) 2474 return; 2475 2476 if (Lock()) { 2477 fLink->StartMessage(AS_SET_WORKSPACES); 2478 fLink->Attach<uint32>(workspaces); 2479 fLink->Flush(); 2480 Unlock(); 2481 } 2482 } 2483 2484 2485 BView* 2486 BWindow::LastMouseMovedView() const 2487 { 2488 return fLastMouseMovedView; 2489 } 2490 2491 2492 void 2493 BWindow::MoveBy(float dx, float dy) 2494 { 2495 if ((dx != 0.0f || dy != 0.0f) && Lock()) { 2496 MoveTo(fFrame.left + dx, fFrame.top + dy); 2497 Unlock(); 2498 } 2499 } 2500 2501 2502 void 2503 BWindow::MoveTo(BPoint point) 2504 { 2505 MoveTo(point.x, point.y); 2506 } 2507 2508 2509 void 2510 BWindow::MoveTo(float x, float y) 2511 { 2512 if (!Lock()) 2513 return; 2514 2515 x = roundf(x); 2516 y = roundf(y); 2517 2518 if (fFrame.left != x || fFrame.top != y) { 2519 fLink->StartMessage(AS_WINDOW_MOVE); 2520 fLink->Attach<float>(x); 2521 fLink->Attach<float>(y); 2522 2523 status_t status; 2524 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2525 fFrame.OffsetTo(x, y); 2526 } 2527 2528 Unlock(); 2529 } 2530 2531 2532 void 2533 BWindow::ResizeBy(float dx, float dy) 2534 { 2535 if (Lock()) { 2536 ResizeTo(fFrame.Width() + dx, fFrame.Height() + dy); 2537 Unlock(); 2538 } 2539 } 2540 2541 2542 void 2543 BWindow::ResizeTo(float width, float height) 2544 { 2545 if (!Lock()) 2546 return; 2547 2548 width = roundf(width); 2549 height = roundf(height); 2550 2551 // stay in minimum & maximum frame limits 2552 if (width < fMinWidth) 2553 width = fMinWidth; 2554 else if (width > fMaxWidth) 2555 width = fMaxWidth; 2556 2557 if (height < fMinHeight) 2558 height = fMinHeight; 2559 else if (height > fMaxHeight) 2560 height = fMaxHeight; 2561 2562 if (width != fFrame.Width() || height != fFrame.Height()) { 2563 fLink->StartMessage(AS_WINDOW_RESIZE); 2564 fLink->Attach<float>(width); 2565 fLink->Attach<float>(height); 2566 2567 status_t status; 2568 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) { 2569 fFrame.right = fFrame.left + width; 2570 fFrame.bottom = fFrame.top + height; 2571 _AdoptResize(); 2572 } 2573 } 2574 2575 Unlock(); 2576 } 2577 2578 2579 void 2580 BWindow::CenterIn(const BRect& rect) 2581 { 2582 // Set size limits now if needed 2583 UpdateSizeLimits(); 2584 2585 MoveTo(BLayoutUtils::AlignInFrame(rect, Size(), 2586 BAlignment(B_ALIGN_HORIZONTAL_CENTER, 2587 B_ALIGN_VERTICAL_CENTER)).LeftTop()); 2588 } 2589 2590 2591 void 2592 BWindow::CenterOnScreen() 2593 { 2594 BScreen screen(this); 2595 CenterIn(screen.Frame()); 2596 } 2597 2598 2599 void 2600 BWindow::Show() 2601 { 2602 bool runCalled = true; 2603 if (Lock()) { 2604 fShowLevel--; 2605 2606 _SendShowOrHideMessage(); 2607 2608 runCalled = fRunCalled; 2609 2610 Unlock(); 2611 } 2612 2613 if (!runCalled) { 2614 // This is the fist time Show() is called, which implicitly runs the 2615 // looper. NOTE: The window is still locked if it has not been 2616 // run yet, so accessing members is safe. 2617 if (fLink->SenderPort() < B_OK) { 2618 // We don't have valid app_server connection; there is no point 2619 // in starting our looper 2620 fThread = B_ERROR; 2621 return; 2622 } else 2623 Run(); 2624 } 2625 } 2626 2627 2628 void 2629 BWindow::Hide() 2630 { 2631 if (Lock()) { 2632 // If we are minimized and are about to be hidden, unminimize 2633 if (IsMinimized() && fShowLevel == 0) 2634 Minimize(false); 2635 2636 fShowLevel++; 2637 2638 _SendShowOrHideMessage(); 2639 2640 Unlock(); 2641 } 2642 } 2643 2644 2645 bool 2646 BWindow::IsHidden() const 2647 { 2648 return fShowLevel > 0; 2649 } 2650 2651 2652 bool 2653 BWindow::QuitRequested() 2654 { 2655 return BLooper::QuitRequested(); 2656 } 2657 2658 2659 thread_id 2660 BWindow::Run() 2661 { 2662 return BLooper::Run(); 2663 } 2664 2665 2666 void 2667 BWindow::SetLayout(BLayout* layout) 2668 { 2669 fTopView->SetLayout(layout); 2670 } 2671 2672 2673 BLayout* 2674 BWindow::GetLayout() const 2675 { 2676 return fTopView->GetLayout(); 2677 } 2678 2679 2680 void 2681 BWindow::InvalidateLayout(bool descendants) 2682 { 2683 fTopView->InvalidateLayout(descendants); 2684 } 2685 2686 2687 void 2688 BWindow::Layout(bool force) 2689 { 2690 UpdateSizeLimits(); 2691 2692 // Do the actual layout 2693 fTopView->Layout(force); 2694 } 2695 2696 2697 status_t 2698 BWindow::GetSupportedSuites(BMessage* data) 2699 { 2700 if (data == NULL) 2701 return B_BAD_VALUE; 2702 2703 status_t status = data->AddString("suites", "suite/vnd.Be-window"); 2704 if (status == B_OK) { 2705 BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo); 2706 2707 status = data->AddFlat("messages", &propertyInfo); 2708 if (status == B_OK) 2709 status = BLooper::GetSupportedSuites(data); 2710 } 2711 2712 return status; 2713 } 2714 2715 2716 BHandler* 2717 BWindow::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier, 2718 int32 what, const char* property) 2719 { 2720 if (msg->what == B_WINDOW_MOVE_BY 2721 || msg->what == B_WINDOW_MOVE_TO) 2722 return this; 2723 2724 BPropertyInfo propertyInfo(sWindowPropInfo); 2725 if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) { 2726 if (!strcmp(property, "View")) { 2727 // we will NOT pop the current specifier 2728 return fTopView; 2729 } else if (!strcmp(property, "MenuBar")) { 2730 if (fKeyMenuBar) { 2731 msg->PopSpecifier(); 2732 return fKeyMenuBar; 2733 } else { 2734 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 2735 replyMsg.AddInt32("error", B_NAME_NOT_FOUND); 2736 replyMsg.AddString("message", 2737 "This window doesn't have a main MenuBar"); 2738 msg->SendReply(&replyMsg); 2739 return NULL; 2740 } 2741 } else 2742 return this; 2743 } 2744 2745 return BLooper::ResolveSpecifier(msg, index, specifier, what, property); 2746 } 2747 2748 2749 // #pragma mark - Private Methods 2750 2751 2752 void 2753 BWindow::_InitData(BRect frame, const char* title, window_look look, 2754 window_feel feel, uint32 flags, uint32 workspace, int32 bitmapToken) 2755 { 2756 STRACE(("BWindow::InitData()\n")); 2757 2758 if (be_app == NULL) { 2759 debugger("You need a valid BApplication object before interacting with " 2760 "the app_server"); 2761 return; 2762 } 2763 2764 frame.left = roundf(frame.left); 2765 frame.top = roundf(frame.top); 2766 frame.right = roundf(frame.right); 2767 frame.bottom = roundf(frame.bottom); 2768 2769 fFrame = frame; 2770 2771 if (title == NULL) 2772 title = ""; 2773 2774 fTitle = strdup(title); 2775 2776 _SetName(title); 2777 2778 fFeel = feel; 2779 fLook = look; 2780 fFlags = flags | B_ASYNCHRONOUS_CONTROLS; 2781 2782 fInTransaction = bitmapToken >= 0; 2783 fUpdateRequested = false; 2784 fActive = false; 2785 fShowLevel = 1; 2786 2787 fTopView = NULL; 2788 fFocus = NULL; 2789 fLastMouseMovedView = NULL; 2790 fIdleMouseRunner = NULL; 2791 fKeyMenuBar = NULL; 2792 fDefaultButton = NULL; 2793 2794 // Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message 2795 // get sent to the application, and not one of our handlers. 2796 // It is only installed for non-modal windows, though. 2797 fNoQuitShortcut = IsModal(); 2798 2799 if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) { 2800 // Modal windows default to non-closable, but you can add the shortcut manually, 2801 // if a different behaviour is wanted 2802 AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED)); 2803 } 2804 2805 AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL); 2806 AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL); 2807 AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL); 2808 AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL); 2809 2810 // Window modifier keys 2811 AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY, 2812 new BMessage(_MINIMIZE_), NULL); 2813 AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY, 2814 new BMessage(_ZOOM_), NULL); 2815 AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY, 2816 new BMessage(B_HIDE_APPLICATION), NULL); 2817 AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY, 2818 new BMessage(_SEND_TO_FRONT_), NULL); 2819 AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY, 2820 new BMessage(_SEND_BEHIND_), NULL); 2821 2822 // Workspace modifier keys 2823 BMessage* message; 2824 message = new BMessage(_SWITCH_WORKSPACE_); 2825 message->AddInt32("delta_x", -1); 2826 AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL); 2827 2828 message = new BMessage(_SWITCH_WORKSPACE_); 2829 message->AddInt32("delta_x", 1); 2830 AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL); 2831 2832 message = new BMessage(_SWITCH_WORKSPACE_); 2833 message->AddInt32("delta_y", -1); 2834 AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL); 2835 2836 message = new BMessage(_SWITCH_WORKSPACE_); 2837 message->AddInt32("delta_y", 1); 2838 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_CONTROL_KEY, message, NULL); 2839 2840 message = new BMessage(_SWITCH_WORKSPACE_); 2841 message->AddBool("take_me_there", true); 2842 message->AddInt32("delta_x", -1); 2843 AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL); 2844 2845 message = new BMessage(_SWITCH_WORKSPACE_); 2846 message->AddBool("take_me_there", true); 2847 message->AddInt32("delta_x", 1); 2848 AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL); 2849 2850 message = new BMessage(_SWITCH_WORKSPACE_); 2851 message->AddBool("take_me_there", true); 2852 message->AddInt32("delta_y", -1); 2853 AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL); 2854 2855 message = new BMessage(_SWITCH_WORKSPACE_); 2856 message->AddBool("take_me_there", true); 2857 message->AddInt32("delta_y", 1); 2858 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_CONTROL_KEY | B_SHIFT_KEY, message, NULL); 2859 2860 // We set the default pulse rate, but we don't start the pulse 2861 fPulseRate = 500000; 2862 fPulseRunner = NULL; 2863 2864 fIsFilePanel = false; 2865 2866 fMenuSem = -1; 2867 2868 fMinimized = false; 2869 2870 fMaxZoomHeight = 32768.0; 2871 fMaxZoomWidth = 32768.0; 2872 fMinHeight = 0.0; 2873 fMinWidth = 0.0; 2874 fMaxHeight = 32768.0; 2875 fMaxWidth = 32768.0; 2876 2877 fLastViewToken = B_NULL_TOKEN; 2878 2879 // TODO: other initializations! 2880 fOffscreen = false; 2881 2882 // Create the server-side window 2883 2884 port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, "w<app_server"); 2885 if (receivePort < B_OK) { 2886 // TODO: huh? 2887 debugger("Could not create BWindow's receive port, used for interacting with the app_server!"); 2888 delete this; 2889 return; 2890 } 2891 2892 STRACE(("BWindow::InitData(): contacting app_server...\n")); 2893 2894 // let app_server know that a window has been created. 2895 fLink = new(std::nothrow) BPrivate::PortLink( 2896 BApplication::Private::ServerLink()->SenderPort(), receivePort); 2897 if (fLink == NULL) { 2898 // Zombie! 2899 return; 2900 } 2901 2902 { 2903 BPrivate::AppServerLink lockLink; 2904 // we're talking to the server application using our own 2905 // communication channel (fLink) - we better make sure no one 2906 // interferes by locking that channel (which AppServerLink does 2907 // implicetly) 2908 2909 if (bitmapToken < 0) { 2910 fLink->StartMessage(AS_CREATE_WINDOW); 2911 } else { 2912 fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW); 2913 fLink->Attach<int32>(bitmapToken); 2914 fOffscreen = true; 2915 } 2916 2917 fLink->Attach<BRect>(fFrame); 2918 fLink->Attach<uint32>((uint32)fLook); 2919 fLink->Attach<uint32>((uint32)fFeel); 2920 fLink->Attach<uint32>(fFlags); 2921 fLink->Attach<uint32>(workspace); 2922 fLink->Attach<int32>(_get_object_token_(this)); 2923 fLink->Attach<port_id>(receivePort); 2924 fLink->Attach<port_id>(fMsgPort); 2925 fLink->AttachString(title); 2926 2927 port_id sendPort; 2928 int32 code; 2929 if (fLink->FlushWithReply(code) == B_OK 2930 && code == B_OK 2931 && fLink->Read<port_id>(&sendPort) == B_OK) { 2932 // read the frame size and its limits that were really 2933 // enforced on the server side 2934 2935 fLink->Read<BRect>(&fFrame); 2936 fLink->Read<float>(&fMinWidth); 2937 fLink->Read<float>(&fMaxWidth); 2938 fLink->Read<float>(&fMinHeight); 2939 fLink->Read<float>(&fMaxHeight); 2940 2941 fMaxZoomWidth = fMaxWidth; 2942 fMaxZoomHeight = fMaxHeight; 2943 } else 2944 sendPort = -1; 2945 2946 // Redirect our link to the new window connection 2947 fLink->SetSenderPort(sendPort); 2948 } 2949 2950 STRACE(("Server says that our send port is %ld\n", sendPort)); 2951 STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False")); 2952 2953 _CreateTopView(); 2954 } 2955 2956 2957 //! Rename the handler and its thread 2958 void 2959 BWindow::_SetName(const char* title) 2960 { 2961 if (title == NULL) 2962 title = ""; 2963 2964 // we will change BWindow's thread name to "w>window title" 2965 2966 char threadName[B_OS_NAME_LENGTH]; 2967 strcpy(threadName, "w>"); 2968 #ifdef __HAIKU__ 2969 strlcat(threadName, title, B_OS_NAME_LENGTH); 2970 #else 2971 int32 length = strlen(title); 2972 length = min_c(length, B_OS_NAME_LENGTH - 3); 2973 memcpy(threadName + 2, title, length); 2974 threadName[length + 2] = '\0'; 2975 #endif 2976 2977 // change the handler's name 2978 SetName(threadName); 2979 2980 // if the message loop has been started... 2981 if (Thread() >= B_OK) 2982 rename_thread(Thread(), threadName); 2983 } 2984 2985 2986 //! Reads all pending messages from the window port and put them into the queue. 2987 void 2988 BWindow::_DequeueAll() 2989 { 2990 // Get message count from port 2991 int32 count = port_count(fMsgPort); 2992 2993 for (int32 i = 0; i < count; i++) { 2994 BMessage* message = MessageFromPort(0); 2995 if (message != NULL) 2996 fDirectTarget->Queue()->AddMessage(message); 2997 } 2998 } 2999 3000 3001 /*! This here is an almost complete code duplication to BLooper::task_looper() 3002 but with some important differences: 3003 a) it uses the _DetermineTarget() method to tell what the later target of 3004 a message will be, if no explicit target is supplied. 3005 b) it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message 3006 to all of its intended targets, and to add all fields the target would 3007 expect in such a message. 3008 3009 This is important because the app_server sends all input events to the 3010 preferred handler, and expects them to be correctly distributed to their 3011 intended targets. 3012 */ 3013 void 3014 BWindow::task_looper() 3015 { 3016 STRACE(("info: BWindow::task_looper() started.\n")); 3017 3018 // Check that looper is locked (should be) 3019 AssertLocked(); 3020 Unlock(); 3021 3022 if (IsLocked()) 3023 debugger("window must not be locked!"); 3024 3025 while (!fTerminating) { 3026 // Did we get a message? 3027 BMessage* msg = MessageFromPort(); 3028 if (msg) 3029 _AddMessagePriv(msg); 3030 3031 // Get message count from port 3032 int32 msgCount = port_count(fMsgPort); 3033 for (int32 i = 0; i < msgCount; ++i) { 3034 // Read 'count' messages from port (so we will not block) 3035 // We use zero as our timeout since we know there is stuff there 3036 msg = MessageFromPort(0); 3037 // Add messages to queue 3038 if (msg) 3039 _AddMessagePriv(msg); 3040 } 3041 3042 bool dispatchNextMessage = true; 3043 while (!fTerminating && dispatchNextMessage) { 3044 // Get next message from queue (assign to fLastMessage) 3045 fLastMessage = fDirectTarget->Queue()->NextMessage(); 3046 3047 // Lock the looper 3048 if (!Lock()) 3049 break; 3050 3051 if (!fLastMessage) { 3052 // No more messages: Unlock the looper and terminate the 3053 // dispatch loop. 3054 dispatchNextMessage = false; 3055 } else { 3056 // Get the target handler 3057 BMessage::Private messagePrivate(fLastMessage); 3058 bool usePreferred = messagePrivate.UsePreferredTarget(); 3059 BHandler* handler = NULL; 3060 bool dropMessage = false; 3061 3062 if (usePreferred) { 3063 handler = PreferredHandler(); 3064 if (handler == NULL) 3065 handler = this; 3066 } else { 3067 gDefaultTokens.GetToken(messagePrivate.GetTarget(), 3068 B_HANDLER_TOKEN, (void**)&handler); 3069 3070 // if this handler doesn't belong to us, we drop the message 3071 if (handler != NULL && handler->Looper() != this) { 3072 dropMessage = true; 3073 handler = NULL; 3074 } 3075 } 3076 3077 if ((handler == NULL && !dropMessage) || usePreferred) 3078 handler = _DetermineTarget(fLastMessage, handler); 3079 3080 unpack_cookie cookie; 3081 while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) { 3082 // if there is no target handler, the message is dropped 3083 if (handler != NULL) { 3084 _SanitizeMessage(fLastMessage, handler, usePreferred); 3085 3086 // Is this a scripting message? 3087 if (fLastMessage->HasSpecifiers()) { 3088 int32 index = 0; 3089 // Make sure the current specifier is kosher 3090 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 3091 handler = resolve_specifier(handler, fLastMessage); 3092 } 3093 3094 if (handler != NULL) 3095 handler = _TopLevelFilter(fLastMessage, handler); 3096 3097 if (handler != NULL) 3098 DispatchMessage(fLastMessage, handler); 3099 } 3100 3101 // Delete the current message 3102 delete fLastMessage; 3103 fLastMessage = NULL; 3104 } 3105 } 3106 3107 if (fTerminating) { 3108 // we leave the looper locked when we quit 3109 return; 3110 } 3111 3112 Unlock(); 3113 3114 // Are any messages on the port? 3115 if (port_count(fMsgPort) > 0) { 3116 // Do outer loop 3117 dispatchNextMessage = false; 3118 } 3119 } 3120 } 3121 } 3122 3123 3124 window_type 3125 BWindow::_ComposeType(window_look look, window_feel feel) const 3126 { 3127 switch (feel) { 3128 case B_NORMAL_WINDOW_FEEL: 3129 switch (look) { 3130 case B_TITLED_WINDOW_LOOK: 3131 return B_TITLED_WINDOW; 3132 3133 case B_DOCUMENT_WINDOW_LOOK: 3134 return B_DOCUMENT_WINDOW; 3135 3136 case B_BORDERED_WINDOW_LOOK: 3137 return B_BORDERED_WINDOW; 3138 3139 default: 3140 return B_UNTYPED_WINDOW; 3141 } 3142 break; 3143 3144 case B_MODAL_APP_WINDOW_FEEL: 3145 if (look == B_MODAL_WINDOW_LOOK) 3146 return B_MODAL_WINDOW; 3147 break; 3148 3149 case B_FLOATING_APP_WINDOW_FEEL: 3150 if (look == B_FLOATING_WINDOW_LOOK) 3151 return B_FLOATING_WINDOW; 3152 break; 3153 3154 default: 3155 return B_UNTYPED_WINDOW; 3156 } 3157 3158 return B_UNTYPED_WINDOW; 3159 } 3160 3161 3162 void 3163 BWindow::_DecomposeType(window_type type, window_look* _look, 3164 window_feel* _feel) const 3165 { 3166 switch (type) { 3167 case B_DOCUMENT_WINDOW: 3168 *_look = B_DOCUMENT_WINDOW_LOOK; 3169 *_feel = B_NORMAL_WINDOW_FEEL; 3170 break; 3171 3172 case B_MODAL_WINDOW: 3173 *_look = B_MODAL_WINDOW_LOOK; 3174 *_feel = B_MODAL_APP_WINDOW_FEEL; 3175 break; 3176 3177 case B_FLOATING_WINDOW: 3178 *_look = B_FLOATING_WINDOW_LOOK; 3179 *_feel = B_FLOATING_APP_WINDOW_FEEL; 3180 break; 3181 3182 case B_BORDERED_WINDOW: 3183 *_look = B_BORDERED_WINDOW_LOOK; 3184 *_feel = B_NORMAL_WINDOW_FEEL; 3185 break; 3186 3187 case B_TITLED_WINDOW: 3188 case B_UNTYPED_WINDOW: 3189 default: 3190 *_look = B_TITLED_WINDOW_LOOK; 3191 *_feel = B_NORMAL_WINDOW_FEEL; 3192 break; 3193 } 3194 } 3195 3196 3197 void 3198 BWindow::_CreateTopView() 3199 { 3200 STRACE(("_CreateTopView(): enter\n")); 3201 3202 BRect frame = fFrame.OffsetToCopy(B_ORIGIN); 3203 // TODO: what to do here about std::nothrow? 3204 fTopView = new BView(frame, "fTopView", 3205 B_FOLLOW_ALL, B_WILL_DRAW); 3206 fTopView->fTopLevelView = true; 3207 3208 //inhibit check_lock() 3209 fLastViewToken = _get_object_token_(fTopView); 3210 3211 // set fTopView's owner, add it to window's eligible handler list 3212 // and also set its next handler to be this window. 3213 3214 STRACE(("Calling setowner fTopView = %p this = %p.\n", 3215 fTopView, this)); 3216 3217 fTopView->_SetOwner(this); 3218 3219 // we can't use AddChild() because this is the top view 3220 fTopView->_CreateSelf(); 3221 3222 STRACE(("BuildTopView ended\n")); 3223 } 3224 3225 3226 /*! 3227 Resizes the top view to match the window size. This will also 3228 adapt the size of all its child views as needed. 3229 This method has to be called whenever the frame of the window 3230 changes. 3231 */ 3232 void 3233 BWindow::_AdoptResize() 3234 { 3235 // Resize views according to their resize modes - this 3236 // saves us some server communication, as the server 3237 // does the same with our views on its side. 3238 3239 int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width()); 3240 int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height()); 3241 if (deltaWidth == 0 && deltaHeight == 0) 3242 return; 3243 3244 fTopView->_ResizeBy(deltaWidth, deltaHeight); 3245 } 3246 3247 3248 void 3249 BWindow::_SetFocus(BView* focusView, bool notifyInputServer) 3250 { 3251 if (fFocus == focusView) 3252 return; 3253 3254 // we notify the input server if we are passing focus 3255 // from a view which has the B_INPUT_METHOD_AWARE to a one 3256 // which does not, or vice-versa 3257 if (notifyInputServer && fActive) { 3258 bool inputMethodAware = false; 3259 if (focusView) 3260 inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE; 3261 BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW); 3262 BMessenger messenger(focusView); 3263 BMessage reply; 3264 if (focusView) 3265 msg.AddMessenger("view", messenger); 3266 _control_input_server_(&msg, &reply); 3267 } 3268 3269 fFocus = focusView; 3270 SetPreferredHandler(focusView); 3271 } 3272 3273 3274 /*! 3275 \brief Determines the target of a message received for the 3276 focus view. 3277 */ 3278 BHandler* 3279 BWindow::_DetermineTarget(BMessage* message, BHandler* target) 3280 { 3281 if (target == NULL) 3282 target = this; 3283 3284 switch (message->what) { 3285 case B_KEY_DOWN: 3286 case B_KEY_UP: 3287 { 3288 // if we have a default button, it might want to hear 3289 // about pressing the <enter> key 3290 int32 rawChar; 3291 if (DefaultButton() != NULL 3292 && message->FindInt32("raw_char", &rawChar) == B_OK 3293 && rawChar == B_ENTER) 3294 return DefaultButton(); 3295 3296 // supposed to fall through 3297 } 3298 case B_UNMAPPED_KEY_DOWN: 3299 case B_UNMAPPED_KEY_UP: 3300 case B_MODIFIERS_CHANGED: 3301 // these messages should be dispatched by the focus view 3302 if (CurrentFocus() != NULL) 3303 return CurrentFocus(); 3304 break; 3305 3306 case B_MOUSE_DOWN: 3307 case B_MOUSE_UP: 3308 case B_MOUSE_MOVED: 3309 case B_MOUSE_WHEEL_CHANGED: 3310 case B_MOUSE_IDLE: 3311 // is there a token of the view that is currently under the mouse? 3312 int32 token; 3313 if (message->FindInt32("_view_token", &token) == B_OK) { 3314 BView* view = _FindView(token); 3315 if (view != NULL) 3316 return view; 3317 } 3318 3319 // if there is no valid token in the message, we try our 3320 // luck with the last target, if available 3321 if (fLastMouseMovedView != NULL) 3322 return fLastMouseMovedView; 3323 break; 3324 3325 case B_PULSE: 3326 case B_QUIT_REQUESTED: 3327 // TODO: test whether R5 will let BView dispatch these messages 3328 return this; 3329 3330 case _MESSAGE_DROPPED_: 3331 if (fLastMouseMovedView != NULL) 3332 return fLastMouseMovedView; 3333 break; 3334 3335 default: 3336 break; 3337 } 3338 3339 return target; 3340 } 3341 3342 3343 /*! \brief Determines whether or not this message has targeted the focus view. 3344 3345 This will return \c false only if the message did not go to the preferred 3346 handler, or if the packed message does not contain address the focus view 3347 at all. 3348 */ 3349 bool 3350 BWindow::_IsFocusMessage(BMessage* message) 3351 { 3352 BMessage::Private messagePrivate(message); 3353 if (!messagePrivate.UsePreferredTarget()) 3354 return false; 3355 3356 bool feedFocus; 3357 if (message->HasInt32("_token") 3358 && (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)) 3359 return false; 3360 3361 return true; 3362 } 3363 3364 3365 /*! \brief Distributes the message to its intended targets. This is done for 3366 all messages that should go to the preferred handler. 3367 3368 Returns \c true in case the message should still be dispatched 3369 */ 3370 bool 3371 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message, 3372 BHandler** _target, bool* _usePreferred) 3373 { 3374 if (cookie.message == NULL) 3375 return false; 3376 3377 if (cookie.index == 0 && !cookie.tokens_scanned) { 3378 // We were called the first time for this message 3379 3380 if (!*_usePreferred) { 3381 // only consider messages targeted at the preferred handler 3382 cookie.message = NULL; 3383 return true; 3384 } 3385 3386 // initialize our cookie 3387 cookie.message = *_message; 3388 cookie.focus = *_target; 3389 3390 if (cookie.focus != NULL) 3391 cookie.focus_token = _get_object_token_(*_target); 3392 3393 if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED) 3394 cookie.last_view_token = _get_object_token_(fLastMouseMovedView); 3395 3396 *_usePreferred = false; 3397 } 3398 3399 _DequeueAll(); 3400 3401 // distribute the message to all targets specified in the 3402 // message directly (but not to the focus view) 3403 3404 for (int32 token; !cookie.tokens_scanned 3405 && cookie.message->FindInt32("_token", cookie.index, &token) 3406 == B_OK; 3407 cookie.index++) { 3408 // focus view is preferred and should get its message directly 3409 if (token == cookie.focus_token) { 3410 cookie.found_focus = true; 3411 continue; 3412 } 3413 if (token == cookie.last_view_token) 3414 continue; 3415 3416 BView* target = _FindView(token); 3417 if (target == NULL) 3418 continue; 3419 3420 *_message = new BMessage(*cookie.message); 3421 *_target = target; 3422 cookie.index++; 3423 return true; 3424 } 3425 3426 cookie.tokens_scanned = true; 3427 3428 // if there is a last mouse moved view, and the new focus is 3429 // different, the previous view wants to get its B_EXITED_VIEW 3430 // message 3431 if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL 3432 && fLastMouseMovedView != cookie.focus) { 3433 *_message = new BMessage(*cookie.message); 3434 *_target = fLastMouseMovedView; 3435 cookie.last_view_token = B_NULL_TOKEN; 3436 return true; 3437 } 3438 3439 bool dispatchToFocus = true; 3440 3441 // check if the focus token is still valid (could have been removed in the mean time) 3442 BHandler* handler; 3443 if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK 3444 || handler->Looper() != this) 3445 dispatchToFocus = false; 3446 3447 if (dispatchToFocus && cookie.index > 0) { 3448 // should this message still be dispatched by the focus view? 3449 bool feedFocus; 3450 if (!cookie.found_focus 3451 && (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK 3452 || feedFocus == false)) 3453 dispatchToFocus = false; 3454 } 3455 3456 if (!dispatchToFocus) { 3457 delete cookie.message; 3458 cookie.message = NULL; 3459 return false; 3460 } 3461 3462 *_message = cookie.message; 3463 *_target = cookie.focus; 3464 *_usePreferred = true; 3465 cookie.message = NULL; 3466 return true; 3467 } 3468 3469 3470 /*! Some messages don't get to the window in a shape an application should see. 3471 This method is supposed to give a message the last grinding before 3472 it's acceptable for the receiving application. 3473 */ 3474 void 3475 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred) 3476 { 3477 if (target == NULL) 3478 return; 3479 3480 switch (message->what) { 3481 case B_MOUSE_MOVED: 3482 case B_MOUSE_UP: 3483 case B_MOUSE_DOWN: 3484 { 3485 BPoint where; 3486 if (message->FindPoint("screen_where", &where) != B_OK) 3487 break; 3488 3489 BView* view = dynamic_cast<BView*>(target); 3490 3491 if (!view || message->what == B_MOUSE_MOVED) { 3492 // add local window coordinates, only 3493 // for regular mouse moved messages 3494 message->AddPoint("where", ConvertFromScreen(where)); 3495 } 3496 3497 if (view != NULL) { 3498 // add local view coordinates 3499 BPoint viewWhere = view->ConvertFromScreen(where); 3500 if (message->what != B_MOUSE_MOVED) { 3501 // Yep, the meaning of "where" is different 3502 // for regular mouse moved messages versus 3503 // mouse up/down! 3504 message->AddPoint("where", viewWhere); 3505 } 3506 message->AddPoint("be:view_where", viewWhere); 3507 3508 if (message->what == B_MOUSE_MOVED) { 3509 // is there a token of the view that is currently under 3510 // the mouse? 3511 BView* viewUnderMouse = NULL; 3512 int32 token; 3513 if (message->FindInt32("_view_token", &token) == B_OK) 3514 viewUnderMouse = _FindView(token); 3515 3516 // add transit information 3517 uint32 transit 3518 = _TransitForMouseMoved(view, viewUnderMouse); 3519 message->AddInt32("be:transit", transit); 3520 3521 if (usePreferred) 3522 fLastMouseMovedView = viewUnderMouse; 3523 } 3524 } 3525 break; 3526 } 3527 3528 case _MESSAGE_DROPPED_: 3529 { 3530 uint32 originalWhat; 3531 if (message->FindInt32("_original_what", (int32*)&originalWhat) == B_OK) { 3532 message->what = originalWhat; 3533 message->RemoveName("_original_what"); 3534 } 3535 break; 3536 } 3537 } 3538 } 3539 3540 3541 /*! 3542 This is called by BView::GetMouse() when a B_MOUSE_MOVED message 3543 is removed from the queue. 3544 It allows the window to update the last mouse moved view, and 3545 let it decide if this message should be kept. It will also remove 3546 the message from the queue. 3547 You need to hold the message queue lock when calling this method! 3548 3549 \return true if this message can be used to get the mouse data from, 3550 \return false if this is not meant for the public. 3551 */ 3552 bool 3553 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage) 3554 { 3555 BMessage::Private messagePrivate(message); 3556 if (!messagePrivate.UsePreferredTarget()) { 3557 // this message is targeted at a specific handler, so we should 3558 // not steal it 3559 return false; 3560 } 3561 3562 int32 token; 3563 if (message->FindInt32("_token", 0, &token) == B_OK) { 3564 // This message has other targets, so we can't remove it; 3565 // just prevent it from being sent to the preferred handler 3566 // again (if it should have gotten it at all). 3567 bool feedFocus; 3568 if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus) 3569 return false; 3570 3571 message->RemoveName("_feed_focus"); 3572 deleteMessage = false; 3573 } else { 3574 deleteMessage = true; 3575 3576 if (message->what == B_MOUSE_MOVED) { 3577 // We need to update the last mouse moved view, as this message 3578 // won't make it to _SanitizeMessage() anymore. 3579 BView* viewUnderMouse = NULL; 3580 int32 token; 3581 if (message->FindInt32("_view_token", &token) == B_OK) 3582 viewUnderMouse = _FindView(token); 3583 3584 // Don't remove important transit messages! 3585 uint32 transit = _TransitForMouseMoved(fLastMouseMovedView, 3586 viewUnderMouse); 3587 if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW) 3588 deleteMessage = false; 3589 } 3590 3591 if (deleteMessage) { 3592 // The message is only thought for the preferred handler, so we 3593 // can just remove it. 3594 MessageQueue()->RemoveMessage(message); 3595 } 3596 } 3597 3598 return true; 3599 } 3600 3601 3602 uint32 3603 BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const 3604 { 3605 uint32 transit; 3606 if (viewUnderMouse == view) { 3607 // the mouse is over the target view 3608 if (fLastMouseMovedView != view) 3609 transit = B_ENTERED_VIEW; 3610 else 3611 transit = B_INSIDE_VIEW; 3612 } else { 3613 // the mouse is not over the target view 3614 if (view == fLastMouseMovedView) 3615 transit = B_EXITED_VIEW; 3616 else 3617 transit = B_OUTSIDE_VIEW; 3618 } 3619 return transit; 3620 } 3621 3622 3623 /*! Forwards the key to the switcher 3624 */ 3625 void 3626 BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat) 3627 { 3628 // only send the first key press, no repeats 3629 if (repeat) 3630 return; 3631 3632 BMessenger deskbar(kDeskbarSignature); 3633 if (!deskbar.IsValid()) { 3634 // TODO: have some kind of fallback-handling in case the Deskbar is 3635 // not available? 3636 return; 3637 } 3638 3639 BMessage message('TASK'); 3640 message.AddInt32("key", rawKey); 3641 message.AddInt32("modifiers", modifiers); 3642 message.AddInt64("when", system_time()); 3643 message.AddInt32("team", Team()); 3644 deskbar.SendMessage(&message); 3645 } 3646 3647 3648 /*! Handles keyboard input before it gets forwarded to the target handler. 3649 This includes shortcut evaluation, keyboard navigation, etc. 3650 3651 \return handled if true, the event was already handled, and will not 3652 be forwarded to the target handler. 3653 3654 TODO: must also convert the incoming key to the font encoding of the target 3655 */ 3656 bool 3657 BWindow::_HandleKeyDown(BMessage* event) 3658 { 3659 // Only handle special functions when the event targeted the active focus 3660 // view 3661 if (!_IsFocusMessage(event)) 3662 return false; 3663 3664 const char* string = NULL; 3665 if (event->FindString("bytes", &string) != B_OK) 3666 return false; 3667 3668 char key = string[0]; 3669 3670 uint32 modifiers; 3671 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK) 3672 modifiers = 0; 3673 3674 // handle BMenuBar key 3675 if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0 && fKeyMenuBar) { 3676 fKeyMenuBar->StartMenuBar(0, true, false, NULL); 3677 return true; 3678 } 3679 3680 // Keyboard navigation through views 3681 // (B_OPTION_KEY makes BTextViews and friends navigable, even in editing 3682 // mode) 3683 if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) { 3684 _KeyboardNavigation(); 3685 return true; 3686 } 3687 3688 int32 rawKey; 3689 event->FindInt32("key", &rawKey); 3690 3691 // Deskbar's Switcher 3692 if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) { 3693 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat")); 3694 return true; 3695 } 3696 3697 // Optionally close window when the escape key is pressed 3698 if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) { 3699 BMessage message(B_QUIT_REQUESTED); 3700 message.AddBool("shortcut", true); 3701 3702 PostMessage(&message); 3703 return true; 3704 } 3705 3706 // PrtScr key takes a screenshot 3707 if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) { 3708 // With no modifier keys the best way to get a screenshot is by 3709 // calling the screenshot CLI 3710 if (modifiers == 0) { 3711 be_roster->Launch("application/x-vnd.haiku-screenshot-cli"); 3712 return true; 3713 } 3714 3715 // Prepare a message based on the modifier keys pressed and launch the 3716 // screenshot GUI 3717 BMessage message(B_ARGV_RECEIVED); 3718 int32 argc = 1; 3719 message.AddString("argv", "Screenshot"); 3720 if ((modifiers & B_CONTROL_KEY) != 0) { 3721 argc++; 3722 message.AddString("argv", "--clipboard"); 3723 } 3724 if ((modifiers & B_SHIFT_KEY) != 0) { 3725 argc++; 3726 message.AddString("argv", "--silent"); 3727 } 3728 message.AddInt32("argc", argc); 3729 be_roster->Launch("application/x-vnd.haiku-screenshot", &message); 3730 return true; 3731 } 3732 3733 // Handle shortcuts 3734 if ((modifiers & B_COMMAND_KEY) != 0) { 3735 // Command+q has been pressed, so, we will quit 3736 // the shortcut mechanism doesn't allow handlers outside the window 3737 if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) { 3738 BMessage message(B_QUIT_REQUESTED); 3739 message.AddBool("shortcut", true); 3740 3741 be_app->PostMessage(&message); 3742 // eat the event 3743 return true; 3744 } 3745 3746 // Pretend that the user opened a menu, to give the subclass a 3747 // chance to update it's menus. This may install new shortcuts, 3748 // which is why we have to call it here, before trying to find 3749 // a shortcut for the given key. 3750 MenusBeginning(); 3751 3752 Shortcut* shortcut = _FindShortcut(key, modifiers); 3753 if (shortcut != NULL) { 3754 // TODO: would be nice to move this functionality to 3755 // a Shortcut::Invoke() method - but since BMenu::InvokeItem() 3756 // (and BMenuItem::Invoke()) are private, I didn't want 3757 // to mess with them (BMenuItem::Invoke() is public in 3758 // Dano/Zeta, though, maybe we should just follow their 3759 // example) 3760 if (shortcut->MenuItem() != NULL) { 3761 BMenu* menu = shortcut->MenuItem()->Menu(); 3762 if (menu != NULL) 3763 MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true); 3764 } else { 3765 BHandler* target = shortcut->Target(); 3766 if (target == NULL) 3767 target = CurrentFocus(); 3768 3769 if (shortcut->Message() != NULL) { 3770 BMessage message(*shortcut->Message()); 3771 3772 if (message.ReplaceInt64("when", system_time()) != B_OK) 3773 message.AddInt64("when", system_time()); 3774 if (message.ReplaceBool("shortcut", true) != B_OK) 3775 message.AddBool("shortcut", true); 3776 3777 PostMessage(&message, target); 3778 } 3779 } 3780 } 3781 3782 MenusEnded(); 3783 3784 // we always eat the event if the command key was pressed 3785 return true; 3786 } 3787 3788 // TODO: convert keys to the encoding of the target view 3789 3790 return false; 3791 } 3792 3793 3794 bool 3795 BWindow::_HandleUnmappedKeyDown(BMessage* event) 3796 { 3797 // Only handle special functions when the event targeted the active focus 3798 // view 3799 if (!_IsFocusMessage(event)) 3800 return false; 3801 3802 uint32 modifiers; 3803 int32 rawKey; 3804 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK 3805 || event->FindInt32("key", &rawKey)) 3806 return false; 3807 3808 // Deskbar's Switcher 3809 if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) { 3810 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat")); 3811 return true; 3812 } 3813 3814 return false; 3815 } 3816 3817 3818 void 3819 BWindow::_KeyboardNavigation() 3820 { 3821 BMessage* message = CurrentMessage(); 3822 if (message == NULL) 3823 return; 3824 3825 const char* bytes; 3826 uint32 modifiers; 3827 if (message->FindString("bytes", &bytes) != B_OK 3828 || bytes[0] != B_TAB) 3829 return; 3830 3831 message->FindInt32("modifiers", (int32*)&modifiers); 3832 3833 BView* nextFocus; 3834 int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0 3835 ? B_NAVIGABLE_JUMP : B_NAVIGABLE; 3836 if (modifiers & B_SHIFT_KEY) 3837 nextFocus = _FindPreviousNavigable(fFocus, jumpGroups); 3838 else 3839 nextFocus = _FindNextNavigable(fFocus, jumpGroups); 3840 3841 if (nextFocus && nextFocus != fFocus) { 3842 nextFocus->MakeFocus(true); 3843 } 3844 } 3845 3846 3847 BMessage* 3848 BWindow::ConvertToMessage(void* raw, int32 code) 3849 { 3850 return BLooper::ConvertToMessage(raw, code); 3851 } 3852 3853 3854 BWindow::Shortcut* 3855 BWindow::_FindShortcut(uint32 key, uint32 modifiers) 3856 { 3857 int32 count = fShortcuts.CountItems(); 3858 3859 key = Shortcut::PrepareKey(key); 3860 modifiers = Shortcut::PrepareModifiers(modifiers); 3861 3862 for (int32 index = 0; index < count; index++) { 3863 Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index); 3864 3865 if (shortcut->Matches(key, modifiers)) 3866 return shortcut; 3867 } 3868 3869 return NULL; 3870 } 3871 3872 3873 BView* 3874 BWindow::_FindView(int32 token) 3875 { 3876 BHandler* handler; 3877 if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN, 3878 (void**)&handler) != B_OK) { 3879 return NULL; 3880 } 3881 3882 // the view must belong to us in order to be found by this method 3883 BView* view = dynamic_cast<BView*>(handler); 3884 if (view != NULL && view->Window() == this) 3885 return view; 3886 3887 return NULL; 3888 } 3889 3890 3891 BView* 3892 BWindow::_FindView(BView* view, BPoint point) const 3893 { 3894 // point is assumed to be already in view's coordinates 3895 if (!view->IsHidden() && view->Bounds().Contains(point)) { 3896 if (!view->fFirstChild) 3897 return view; 3898 else { 3899 BView* child = view->fFirstChild; 3900 while (child != NULL) { 3901 BPoint childPoint = point - child->Frame().LeftTop(); 3902 BView* subView = _FindView(child, childPoint); 3903 if (subView != NULL) 3904 return subView; 3905 3906 child = child->fNextSibling; 3907 } 3908 } 3909 return view; 3910 } 3911 return NULL; 3912 } 3913 3914 3915 BView* 3916 BWindow::_FindNextNavigable(BView* focus, uint32 flags) 3917 { 3918 if (focus == NULL) 3919 focus = fTopView; 3920 3921 BView* nextFocus = focus; 3922 3923 // Search the tree for views that accept focus (depth search) 3924 while (true) { 3925 if (nextFocus->fFirstChild) 3926 nextFocus = nextFocus->fFirstChild; 3927 else if (nextFocus->fNextSibling) 3928 nextFocus = nextFocus->fNextSibling; 3929 else { 3930 // go to the nearest parent with a next sibling 3931 while (!nextFocus->fNextSibling && nextFocus->fParent) { 3932 nextFocus = nextFocus->fParent; 3933 } 3934 3935 if (nextFocus == fTopView) { 3936 // if we started with the top view, we traversed the whole tree already 3937 if (nextFocus == focus) 3938 return NULL; 3939 3940 nextFocus = nextFocus->fFirstChild; 3941 } else 3942 nextFocus = nextFocus->fNextSibling; 3943 } 3944 3945 if (nextFocus == focus || nextFocus == NULL) { 3946 // When we get here it means that the hole tree has been 3947 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set! 3948 return NULL; 3949 } 3950 3951 if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0) 3952 return nextFocus; 3953 } 3954 } 3955 3956 3957 BView* 3958 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags) 3959 { 3960 if (focus == NULL) 3961 focus = fTopView; 3962 3963 BView* previousFocus = focus; 3964 3965 // Search the tree for the previous view that accept focus 3966 while (true) { 3967 if (previousFocus->fPreviousSibling) { 3968 // find the last child in the previous sibling 3969 previousFocus = _LastViewChild(previousFocus->fPreviousSibling); 3970 } else { 3971 previousFocus = previousFocus->fParent; 3972 if (previousFocus == fTopView) 3973 previousFocus = _LastViewChild(fTopView); 3974 } 3975 3976 if (previousFocus == focus || previousFocus == NULL) { 3977 // When we get here it means that the hole tree has been 3978 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set! 3979 return NULL; 3980 } 3981 3982 if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0) 3983 return previousFocus; 3984 } 3985 } 3986 3987 3988 /*! 3989 Returns the last child in a view hierarchy. 3990 Needed only by _FindPreviousNavigable(). 3991 */ 3992 BView* 3993 BWindow::_LastViewChild(BView* parent) 3994 { 3995 while (true) { 3996 BView* last = parent->fFirstChild; 3997 if (last == NULL) 3998 return parent; 3999 4000 while (last->fNextSibling) { 4001 last = last->fNextSibling; 4002 } 4003 4004 parent = last; 4005 } 4006 } 4007 4008 4009 void 4010 BWindow::SetIsFilePanel(bool isFilePanel) 4011 { 4012 fIsFilePanel = isFilePanel; 4013 } 4014 4015 4016 bool 4017 BWindow::IsFilePanel() const 4018 { 4019 return fIsFilePanel; 4020 } 4021 4022 4023 void 4024 BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const 4025 { 4026 // fallback in case retrieving the decorator settings fails 4027 // (highly unlikely) 4028 float borderWidth = 5.0; 4029 float tabHeight = 21.0; 4030 4031 BMessage settings; 4032 if (GetDecoratorSettings(&settings) == B_OK) { 4033 BRect tabRect; 4034 if (settings.FindRect("tab frame", &tabRect) == B_OK) 4035 tabHeight = tabRect.Height(); 4036 settings.FindFloat("border width", &borderWidth); 4037 } else { 4038 // probably no-border window look 4039 if (fLook == B_NO_BORDER_WINDOW_LOOK) { 4040 borderWidth = 0.0; 4041 tabHeight = 0.0; 4042 } 4043 // else use fall-back values from above 4044 } 4045 4046 if (_borderWidth != NULL) 4047 *_borderWidth = borderWidth; 4048 if (_tabHeight != NULL) 4049 *_tabHeight = tabHeight; 4050 } 4051 4052 4053 void 4054 BWindow::_SendShowOrHideMessage() 4055 { 4056 fLink->StartMessage(AS_SHOW_OR_HIDE_WINDOW); 4057 fLink->Attach<int32>(fShowLevel); 4058 fLink->Flush(); 4059 } 4060 4061 4062 // #pragma mark - C++ binary compatibility kludge 4063 4064 4065 extern "C" void 4066 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout) 4067 { 4068 // SetLayout() 4069 perform_data_set_layout data; 4070 data.layout = layout; 4071 window->Perform(PERFORM_CODE_SET_LAYOUT, &data); 4072 } 4073 4074 4075 void BWindow::_ReservedWindow2() {} 4076 void BWindow::_ReservedWindow3() {} 4077 void BWindow::_ReservedWindow4() {} 4078 void BWindow::_ReservedWindow5() {} 4079 void BWindow::_ReservedWindow6() {} 4080 void BWindow::_ReservedWindow7() {} 4081 void BWindow::_ReservedWindow8() {} 4082 4083