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