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