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 {} 187 }; 188 189 static value_info sWindowValueInfo[] = { 190 { 191 "MoveTo", 'WDMT', B_COMMAND_KIND, 192 "Moves to the position in the BPoint data" 193 }, 194 195 { 196 "MoveBy", 'WDMB', B_COMMAND_KIND, 197 "Moves by the offsets in the BPoint data" 198 }, 199 200 { 201 "ResizeTo", 'WDRT', B_COMMAND_KIND, 202 "Resize to the size in the BPoint data" 203 }, 204 205 { 206 "ResizeBy", 'WDRB', B_COMMAND_KIND, 207 "Resize by the offsets in the BPoint data" 208 }, 209 210 {} 211 }; 212 213 214 void 215 _set_menu_sem_(BWindow* window, sem_id sem) 216 { 217 if (window != NULL) 218 window->fMenuSem = sem; 219 } 220 221 222 // #pragma mark - 223 224 225 BWindow::unpack_cookie::unpack_cookie() 226 : 227 message((BMessage*)~0UL), 228 // message == NULL is our exit condition 229 index(0), 230 focus_token(B_NULL_TOKEN), 231 last_view_token(B_NULL_TOKEN), 232 found_focus(false), 233 tokens_scanned(false) 234 { 235 } 236 237 238 // #pragma mark - BWindow::Shortcut 239 240 241 BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMenuItem* item) 242 : 243 fKey(PrepareKey(key)), 244 fModifiers(PrepareModifiers(modifiers)), 245 fMenuItem(item), 246 fMessage(NULL), 247 fTarget(NULL) 248 { 249 } 250 251 252 BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMessage* message, 253 BHandler* target) 254 : 255 fKey(PrepareKey(key)), 256 fModifiers(PrepareModifiers(modifiers)), 257 fMenuItem(NULL), 258 fMessage(message), 259 fTarget(target) 260 { 261 } 262 263 264 BWindow::Shortcut::~Shortcut() 265 { 266 // we own the message, if any 267 delete fMessage; 268 } 269 270 271 bool 272 BWindow::Shortcut::Matches(uint32 key, uint32 modifiers) const 273 { 274 return fKey == key && fModifiers == modifiers; 275 } 276 277 278 /*static*/ 279 uint32 280 BWindow::Shortcut::AllowedModifiers() 281 { 282 return B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY | B_CONTROL_KEY 283 | B_MENU_KEY; 284 } 285 286 287 /*static*/ 288 uint32 289 BWindow::Shortcut::PrepareModifiers(uint32 modifiers) 290 { 291 return (modifiers & AllowedModifiers()) | B_COMMAND_KEY; 292 } 293 294 295 /*static*/ 296 uint32 297 BWindow::Shortcut::PrepareKey(uint32 key) 298 { 299 return BUnicodeChar::ToLower(key); 300 } 301 302 303 // #pragma mark - BWindow 304 305 306 BWindow::BWindow(BRect frame, const char* title, window_type type, 307 uint32 flags, uint32 workspace) 308 : 309 BLooper(title, B_DISPLAY_PRIORITY) 310 { 311 window_look look; 312 window_feel feel; 313 _DecomposeType(type, &look, &feel); 314 315 _InitData(frame, title, look, feel, flags, workspace); 316 } 317 318 319 BWindow::BWindow(BRect frame, const char* title, window_look look, 320 window_feel feel, uint32 flags, uint32 workspace) 321 : 322 BLooper(title, B_DISPLAY_PRIORITY) 323 { 324 _InitData(frame, title, look, feel, flags, workspace); 325 } 326 327 328 BWindow::BWindow(BMessage* data) 329 : 330 BLooper(data) 331 { 332 data->FindRect("_frame", &fFrame); 333 334 const char* title; 335 data->FindString("_title", &title); 336 337 window_look look; 338 data->FindInt32("_wlook", (int32*)&look); 339 340 window_feel feel; 341 data->FindInt32("_wfeel", (int32*)&feel); 342 343 if (data->FindInt32("_flags", (int32*)&fFlags) != B_OK) 344 fFlags = 0; 345 346 uint32 workspaces; 347 data->FindInt32("_wspace", (int32*)&workspaces); 348 349 uint32 type; 350 if (data->FindInt32("_type", (int32*)&type) == B_OK) 351 _DecomposeType((window_type)type, &fLook, &fFeel); 352 353 // connect to app_server and initialize data 354 _InitData(fFrame, title, look, feel, fFlags, workspaces); 355 356 if (data->FindFloat("_zoom", 0, &fMaxZoomWidth) == B_OK 357 && data->FindFloat("_zoom", 1, &fMaxZoomHeight) == B_OK) 358 SetZoomLimits(fMaxZoomWidth, fMaxZoomHeight); 359 360 if (data->FindFloat("_sizel", 0, &fMinWidth) == B_OK 361 && data->FindFloat("_sizel", 1, &fMinHeight) == B_OK 362 && data->FindFloat("_sizel", 2, &fMaxWidth) == B_OK 363 && data->FindFloat("_sizel", 3, &fMaxHeight) == B_OK) 364 SetSizeLimits(fMinWidth, fMaxWidth, 365 fMinHeight, fMaxHeight); 366 367 if (data->FindInt64("_pulse", &fPulseRate) == B_OK) 368 SetPulseRate(fPulseRate); 369 370 BMessage msg; 371 int32 i = 0; 372 while (data->FindMessage("_views", i++, &msg) == B_OK) { 373 BArchivable* obj = instantiate_object(&msg); 374 if (BView* child = dynamic_cast<BView*>(obj)) 375 AddChild(child); 376 } 377 } 378 379 380 BWindow::BWindow(BRect frame, int32 bitmapToken) 381 : 382 BLooper("offscreen bitmap") 383 { 384 _DecomposeType(B_UNTYPED_WINDOW, &fLook, &fFeel); 385 _InitData(frame, "offscreen", fLook, fFeel, 0, 0, bitmapToken); 386 } 387 388 389 BWindow::~BWindow() 390 { 391 if (BMenu* menu = dynamic_cast<BMenu*>(fFocus)) { 392 MenuPrivate(menu).QuitTracking(); 393 } 394 395 // The BWindow is locked when the destructor is called, 396 // we need to unlock because the menubar thread tries 397 // to post a message, which will deadlock otherwise. 398 // TODO: I replaced Unlock() with UnlockFully() because the window 399 // was kept locked after that in case it was closed using ALT-W. 400 // There might be an extra Lock() somewhere in the quitting path... 401 UnlockFully(); 402 403 // Wait if a menu is still tracking 404 if (fMenuSem > 0) { 405 while (acquire_sem(fMenuSem) == B_INTERRUPTED) 406 ; 407 } 408 409 Lock(); 410 411 fTopView->RemoveSelf(); 412 delete fTopView; 413 414 // remove all remaining shortcuts 415 int32 shortCutCount = fShortcuts.CountItems(); 416 for (int32 i = 0; i < shortCutCount; i++) { 417 delete (Shortcut*)fShortcuts.ItemAtFast(i); 418 } 419 420 // TODO: release other dynamically-allocated objects 421 free(fTitle); 422 423 // disable pulsing 424 SetPulseRate(0); 425 426 // tell app_server about our demise 427 fLink->StartMessage(AS_DELETE_WINDOW); 428 // sync with the server so that for example 429 // a BBitmap can be sure that there are no 430 // more pending messages that are executed 431 // after the bitmap is deleted (which uses 432 // a different link and server side thread) 433 int32 code; 434 fLink->FlushWithReply(code); 435 436 // the sender port belongs to the app_server 437 delete_port(fLink->ReceiverPort()); 438 delete fLink; 439 } 440 441 442 BArchivable* 443 BWindow::Instantiate(BMessage* data) 444 { 445 if (!validate_instantiation(data, "BWindow")) 446 return NULL; 447 448 return new(std::nothrow) BWindow(data); 449 } 450 451 452 status_t 453 BWindow::Archive(BMessage* data, bool deep) const 454 { 455 status_t ret = BLooper::Archive(data, deep); 456 457 if (ret == B_OK) 458 ret = data->AddRect("_frame", fFrame); 459 if (ret == B_OK) 460 ret = data->AddString("_title", fTitle); 461 if (ret == B_OK) 462 ret = data->AddInt32("_wlook", fLook); 463 if (ret == B_OK) 464 ret = data->AddInt32("_wfeel", fFeel); 465 if (ret == B_OK && fFlags != 0) 466 ret = data->AddInt32("_flags", fFlags); 467 if (ret == B_OK) 468 ret = data->AddInt32("_wspace", (uint32)Workspaces()); 469 470 if (ret == B_OK && !_ComposeType(fLook, fFeel)) 471 ret = data->AddInt32("_type", (uint32)Type()); 472 473 if (fMaxZoomWidth != 32768.0 || fMaxZoomHeight != 32768.0) { 474 if (ret == B_OK) 475 ret = data->AddFloat("_zoom", fMaxZoomWidth); 476 if (ret == B_OK) 477 ret = data->AddFloat("_zoom", fMaxZoomHeight); 478 } 479 480 if (fMinWidth != 0.0 || fMinHeight != 0.0 481 || fMaxWidth != 32768.0 || fMaxHeight != 32768.0) { 482 if (ret == B_OK) 483 ret = data->AddFloat("_sizel", fMinWidth); 484 if (ret == B_OK) 485 ret = data->AddFloat("_sizel", fMinHeight); 486 if (ret == B_OK) 487 ret = data->AddFloat("_sizel", fMaxWidth); 488 if (ret == B_OK) 489 ret = data->AddFloat("_sizel", fMaxHeight); 490 } 491 492 if (ret == B_OK && fPulseRate != 500000) 493 data->AddInt64("_pulse", fPulseRate); 494 495 if (ret == B_OK && deep) { 496 int32 noOfViews = CountChildren(); 497 for (int32 i = 0; i < noOfViews; i++){ 498 BMessage childArchive; 499 ret = ChildAt(i)->Archive(&childArchive, true); 500 if (ret == B_OK) 501 ret = data->AddMessage("_views", &childArchive); 502 if (ret != B_OK) 503 break; 504 } 505 } 506 507 return ret; 508 } 509 510 511 void 512 BWindow::Quit() 513 { 514 if (!IsLocked()) { 515 const char* name = Name(); 516 if (name == NULL) 517 name = "no-name"; 518 519 printf("ERROR - you must Lock a looper before calling Quit(), " 520 "team=%" B_PRId32 ", looper=%s\n", Team(), name); 521 } 522 523 // Try to lock 524 if (!Lock()){ 525 // We're toast already 526 return; 527 } 528 529 while (!IsHidden()) { 530 Hide(); 531 } 532 533 if (fFlags & B_QUIT_ON_WINDOW_CLOSE) 534 be_app->PostMessage(B_QUIT_REQUESTED); 535 536 BLooper::Quit(); 537 } 538 539 540 void 541 BWindow::AddChild(BView* child, BView* before) 542 { 543 BAutolock locker(this); 544 if (locker.IsLocked()) 545 fTopView->AddChild(child, before); 546 } 547 548 549 void 550 BWindow::AddChild(BLayoutItem* child) 551 { 552 BAutolock locker(this); 553 if (locker.IsLocked()) 554 fTopView->AddChild(child); 555 } 556 557 558 bool 559 BWindow::RemoveChild(BView* child) 560 { 561 BAutolock locker(this); 562 if (!locker.IsLocked()) 563 return false; 564 565 return fTopView->RemoveChild(child); 566 } 567 568 569 int32 570 BWindow::CountChildren() const 571 { 572 BAutolock locker(const_cast<BWindow*>(this)); 573 if (!locker.IsLocked()) 574 return 0; 575 576 return fTopView->CountChildren(); 577 } 578 579 580 BView* 581 BWindow::ChildAt(int32 index) const 582 { 583 BAutolock locker(const_cast<BWindow*>(this)); 584 if (!locker.IsLocked()) 585 return NULL; 586 587 return fTopView->ChildAt(index); 588 } 589 590 591 void 592 BWindow::Minimize(bool minimize) 593 { 594 if (IsModal() || IsFloating() || IsHidden() || fMinimized == minimize 595 || !Lock()) 596 return; 597 598 fMinimized = minimize; 599 600 fLink->StartMessage(AS_MINIMIZE_WINDOW); 601 fLink->Attach<bool>(minimize); 602 fLink->Flush(); 603 604 Unlock(); 605 } 606 607 608 status_t 609 BWindow::SendBehind(const BWindow* window) 610 { 611 if (!Lock()) 612 return B_ERROR; 613 614 fLink->StartMessage(AS_SEND_BEHIND); 615 fLink->Attach<int32>(window != NULL ? _get_object_token_(window) : -1); 616 fLink->Attach<team_id>(Team()); 617 618 status_t status = B_ERROR; 619 fLink->FlushWithReply(status); 620 621 Unlock(); 622 623 return status; 624 } 625 626 627 void 628 BWindow::Flush() const 629 { 630 if (const_cast<BWindow*>(this)->Lock()) { 631 fLink->Flush(); 632 const_cast<BWindow*>(this)->Unlock(); 633 } 634 } 635 636 637 void 638 BWindow::Sync() const 639 { 640 if (!const_cast<BWindow*>(this)->Lock()) 641 return; 642 643 fLink->StartMessage(AS_SYNC); 644 645 // waiting for the reply is the actual syncing 646 int32 code; 647 fLink->FlushWithReply(code); 648 649 const_cast<BWindow*>(this)->Unlock(); 650 } 651 652 653 void 654 BWindow::DisableUpdates() 655 { 656 if (Lock()) { 657 fLink->StartMessage(AS_DISABLE_UPDATES); 658 fLink->Flush(); 659 Unlock(); 660 } 661 } 662 663 664 void 665 BWindow::EnableUpdates() 666 { 667 if (Lock()) { 668 fLink->StartMessage(AS_ENABLE_UPDATES); 669 fLink->Flush(); 670 Unlock(); 671 } 672 } 673 674 675 void 676 BWindow::BeginViewTransaction() 677 { 678 if (Lock()) { 679 fInTransaction = true; 680 Unlock(); 681 } 682 } 683 684 685 void 686 BWindow::EndViewTransaction() 687 { 688 if (Lock()) { 689 if (fInTransaction) 690 fLink->Flush(); 691 fInTransaction = false; 692 Unlock(); 693 } 694 } 695 696 697 bool 698 BWindow::InViewTransaction() const 699 { 700 BAutolock locker(const_cast<BWindow*>(this)); 701 return fInTransaction; 702 } 703 704 705 bool 706 BWindow::IsFront() const 707 { 708 BAutolock locker(const_cast<BWindow*>(this)); 709 if (!locker.IsLocked()) 710 return false; 711 712 fLink->StartMessage(AS_IS_FRONT_WINDOW); 713 714 status_t status; 715 if (fLink->FlushWithReply(status) == B_OK) 716 return status >= B_OK; 717 718 return false; 719 } 720 721 722 void 723 BWindow::MessageReceived(BMessage* message) 724 { 725 if (!message->HasSpecifiers()) { 726 if (message->what == B_KEY_DOWN) 727 _KeyboardNavigation(); 728 729 if (message->what == (int32)kMsgAppServerRestarted) { 730 fLink->SetSenderPort( 731 BApplication::Private::ServerLink()->SenderPort()); 732 733 BPrivate::AppServerLink lockLink; 734 // we're talking to the server application using our own 735 // communication channel (fLink) - we better make sure no one 736 // interferes by locking that channel (which AppServerLink does 737 // implicitly) 738 739 fLink->StartMessage(AS_CREATE_WINDOW); 740 741 fLink->Attach<BRect>(fFrame); 742 fLink->Attach<uint32>((uint32)fLook); 743 fLink->Attach<uint32>((uint32)fFeel); 744 fLink->Attach<uint32>(fFlags); 745 fLink->Attach<uint32>(0); 746 fLink->Attach<int32>(_get_object_token_(this)); 747 fLink->Attach<port_id>(fLink->ReceiverPort()); 748 fLink->Attach<port_id>(fMsgPort); 749 fLink->AttachString(fTitle); 750 751 port_id sendPort; 752 int32 code; 753 if (fLink->FlushWithReply(code) == B_OK 754 && code == B_OK 755 && fLink->Read<port_id>(&sendPort) == B_OK) { 756 // read the frame size and its limits that were really 757 // enforced on the server side 758 759 fLink->Read<BRect>(&fFrame); 760 fLink->Read<float>(&fMinWidth); 761 fLink->Read<float>(&fMaxWidth); 762 fLink->Read<float>(&fMinHeight); 763 fLink->Read<float>(&fMaxHeight); 764 765 fMaxZoomWidth = fMaxWidth; 766 fMaxZoomHeight = fMaxHeight; 767 } else 768 sendPort = -1; 769 770 // Redirect our link to the new window connection 771 fLink->SetSenderPort(sendPort); 772 773 // connect all views to the server again 774 fTopView->_CreateSelf(); 775 776 _SendShowOrHideMessage(); 777 } 778 779 return BLooper::MessageReceived(message); 780 } 781 782 BMessage replyMsg(B_REPLY); 783 bool handled = false; 784 785 BMessage specifier; 786 int32 what; 787 const char* prop; 788 int32 index; 789 790 if (message->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) 791 return BLooper::MessageReceived(message); 792 793 BPropertyInfo propertyInfo(sWindowPropInfo); 794 switch (propertyInfo.FindMatch(message, index, &specifier, what, prop)) { 795 case 0: 796 if (message->what == B_GET_PROPERTY) { 797 replyMsg.AddBool("result", IsActive()); 798 handled = true; 799 } else if (message->what == B_SET_PROPERTY) { 800 bool newActive; 801 if (message->FindBool("data", &newActive) == B_OK) { 802 Activate(newActive); 803 handled = true; 804 } 805 } 806 break; 807 case 1: 808 if (message->what == B_GET_PROPERTY) { 809 replyMsg.AddInt32("result", (uint32)Feel()); 810 handled = true; 811 } else { 812 uint32 newFeel; 813 if (message->FindInt32("data", (int32*)&newFeel) == B_OK) { 814 SetFeel((window_feel)newFeel); 815 handled = true; 816 } 817 } 818 break; 819 case 2: 820 if (message->what == B_GET_PROPERTY) { 821 replyMsg.AddInt32("result", Flags()); 822 handled = true; 823 } else { 824 uint32 newFlags; 825 if (message->FindInt32("data", (int32*)&newFlags) == B_OK) { 826 SetFlags(newFlags); 827 handled = true; 828 } 829 } 830 break; 831 case 3: 832 if (message->what == B_GET_PROPERTY) { 833 replyMsg.AddRect("result", Frame()); 834 handled = true; 835 } else { 836 BRect newFrame; 837 if (message->FindRect("data", &newFrame) == B_OK) { 838 MoveTo(newFrame.LeftTop()); 839 ResizeTo(newFrame.Width(), newFrame.Height()); 840 handled = true; 841 } 842 } 843 break; 844 case 4: 845 if (message->what == B_GET_PROPERTY) { 846 replyMsg.AddBool("result", IsHidden()); 847 handled = true; 848 } else { 849 bool hide; 850 if (message->FindBool("data", &hide) == B_OK) { 851 if (hide) { 852 if (!IsHidden()) 853 Hide(); 854 } else if (IsHidden()) 855 Show(); 856 handled = true; 857 } 858 } 859 break; 860 case 5: 861 if (message->what == B_GET_PROPERTY) { 862 replyMsg.AddInt32("result", (uint32)Look()); 863 handled = true; 864 } else { 865 uint32 newLook; 866 if (message->FindInt32("data", (int32*)&newLook) == B_OK) { 867 SetLook((window_look)newLook); 868 handled = true; 869 } 870 } 871 break; 872 case 6: 873 if (message->what == B_GET_PROPERTY) { 874 replyMsg.AddString("result", Title()); 875 handled = true; 876 } else { 877 const char* newTitle = NULL; 878 if (message->FindString("data", &newTitle) == B_OK) { 879 SetTitle(newTitle); 880 handled = true; 881 } 882 } 883 break; 884 case 7: 885 if (message->what == B_GET_PROPERTY) { 886 replyMsg.AddInt32( "result", Workspaces()); 887 handled = true; 888 } else { 889 uint32 newWorkspaces; 890 if (message->FindInt32("data", (int32*)&newWorkspaces) == B_OK) { 891 SetWorkspaces(newWorkspaces); 892 handled = true; 893 } 894 } 895 break; 896 case 11: 897 if (message->what == B_GET_PROPERTY) { 898 replyMsg.AddBool("result", IsMinimized()); 899 handled = true; 900 } else { 901 bool minimize; 902 if (message->FindBool("data", &minimize) == B_OK) { 903 Minimize(minimize); 904 handled = true; 905 } 906 } 907 break; 908 case 12: 909 if (message->what == B_GET_PROPERTY) { 910 BMessage settings; 911 if (GetDecoratorSettings(&settings) == B_OK) { 912 BRect frame; 913 if (settings.FindRect("tab frame", &frame) == B_OK) { 914 replyMsg.AddRect("result", frame); 915 handled = true; 916 } 917 } 918 } 919 break; 920 default: 921 return BLooper::MessageReceived(message); 922 } 923 924 if (handled) { 925 if (message->what == B_SET_PROPERTY) 926 replyMsg.AddInt32("error", B_OK); 927 } else { 928 replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD; 929 replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX); 930 replyMsg.AddString("message", "Didn't understand the specifier(s)"); 931 } 932 message->SendReply(&replyMsg); 933 } 934 935 936 void 937 BWindow::DispatchMessage(BMessage* message, BHandler* target) 938 { 939 if (message == NULL) 940 return; 941 942 switch (message->what) { 943 case B_ZOOM: 944 Zoom(); 945 break; 946 947 case _MINIMIZE_: 948 // Used by the minimize shortcut 949 if ((Flags() & B_NOT_MINIMIZABLE) == 0) 950 Minimize(true); 951 break; 952 953 case _ZOOM_: 954 // Used by the zoom shortcut 955 if ((Flags() & B_NOT_ZOOMABLE) == 0) 956 Zoom(); 957 break; 958 959 case _SEND_BEHIND_: 960 SendBehind(NULL); 961 break; 962 963 case _SEND_TO_FRONT_: 964 Activate(); 965 break; 966 967 case B_MINIMIZE: 968 { 969 bool minimize; 970 if (message->FindBool("minimize", &minimize) == B_OK) 971 Minimize(minimize); 972 break; 973 } 974 975 case B_HIDE_APPLICATION: 976 { 977 // Hide all applications with the same signature 978 // (ie. those that are part of the same group to be consistent 979 // to what the Deskbar shows you). 980 app_info info; 981 be_app->GetAppInfo(&info); 982 983 BList list; 984 be_roster->GetAppList(info.signature, &list); 985 986 for (int32 i = 0; i < list.CountItems(); i++) { 987 do_minimize_team(BRect(), (team_id)(addr_t)list.ItemAt(i), 988 false); 989 } 990 break; 991 } 992 993 case B_WINDOW_RESIZED: 994 { 995 int32 width, height; 996 if (message->FindInt32("width", &width) == B_OK 997 && message->FindInt32("height", &height) == B_OK) { 998 // combine with pending resize notifications 999 BMessage* pendingMessage; 1000 while ((pendingMessage 1001 = MessageQueue()->FindMessage(B_WINDOW_RESIZED, 0))) { 1002 int32 nextWidth; 1003 if (pendingMessage->FindInt32("width", &nextWidth) == B_OK) 1004 width = nextWidth; 1005 1006 int32 nextHeight; 1007 if (pendingMessage->FindInt32("height", &nextHeight) 1008 == B_OK) { 1009 height = nextHeight; 1010 } 1011 1012 MessageQueue()->RemoveMessage(pendingMessage); 1013 delete pendingMessage; 1014 // this deletes the first *additional* message 1015 // fCurrentMessage is safe 1016 } 1017 if (width != fFrame.Width() || height != fFrame.Height()) { 1018 // NOTE: we might have already handled the resize 1019 // in an _UPDATE_ message 1020 fFrame.right = fFrame.left + width; 1021 fFrame.bottom = fFrame.top + height; 1022 1023 _AdoptResize(); 1024 // FrameResized(width, height); 1025 } 1026 // call hook function anyways 1027 // TODO: When a window is resized programmatically, 1028 // it receives this message, and maybe it is wise to 1029 // keep the asynchronous nature of this process to 1030 // not risk breaking any apps. 1031 FrameResized(width, height); 1032 } 1033 break; 1034 } 1035 1036 case B_WINDOW_MOVED: 1037 { 1038 BPoint origin; 1039 if (message->FindPoint("where", &origin) == B_OK) { 1040 if (fFrame.LeftTop() != origin) { 1041 // NOTE: we might have already handled the move 1042 // in an _UPDATE_ message 1043 fFrame.OffsetTo(origin); 1044 1045 // FrameMoved(origin); 1046 } 1047 // call hook function anyways 1048 // TODO: When a window is moved programmatically, 1049 // it receives this message, and maybe it is wise to 1050 // keep the asynchronous nature of this process to 1051 // not risk breaking any apps. 1052 FrameMoved(origin); 1053 } 1054 break; 1055 } 1056 1057 case B_WINDOW_ACTIVATED: 1058 if (target != this) { 1059 target->MessageReceived(message); 1060 break; 1061 } 1062 1063 bool active; 1064 if (message->FindBool("active", &active) != B_OK) 1065 break; 1066 1067 // find latest activation message 1068 1069 while (true) { 1070 BMessage* pendingMessage = MessageQueue()->FindMessage( 1071 B_WINDOW_ACTIVATED, 0); 1072 if (pendingMessage == NULL) 1073 break; 1074 1075 bool nextActive; 1076 if (pendingMessage->FindBool("active", &nextActive) == B_OK) 1077 active = nextActive; 1078 1079 MessageQueue()->RemoveMessage(pendingMessage); 1080 delete pendingMessage; 1081 } 1082 1083 if (active != fActive) { 1084 fActive = active; 1085 1086 WindowActivated(active); 1087 1088 // call hook function 'WindowActivated(bool)' for all 1089 // views attached to this window. 1090 fTopView->_Activate(active); 1091 1092 // we notify the input server if we are gaining or losing focus 1093 // from a view which has the B_INPUT_METHOD_AWARE on a window 1094 // activation 1095 if (!active) 1096 break; 1097 bool inputMethodAware = false; 1098 if (fFocus) 1099 inputMethodAware = fFocus->Flags() & B_INPUT_METHOD_AWARE; 1100 BMessage message(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW); 1101 BMessenger messenger(fFocus); 1102 BMessage reply; 1103 if (fFocus) 1104 message.AddMessenger("view", messenger); 1105 _control_input_server_(&message, &reply); 1106 } 1107 break; 1108 1109 case B_SCREEN_CHANGED: 1110 if (target == this) { 1111 BRect frame; 1112 uint32 mode; 1113 if (message->FindRect("frame", &frame) == B_OK 1114 && message->FindInt32("mode", (int32*)&mode) == B_OK) { 1115 // 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 int32 rawChar; 3328 if (DefaultButton() != NULL 3329 && message->FindInt32("raw_char", &rawChar) == B_OK 3330 && rawChar == B_ENTER) 3331 return DefaultButton(); 3332 3333 // supposed to fall through 3334 } 3335 case B_UNMAPPED_KEY_DOWN: 3336 case B_UNMAPPED_KEY_UP: 3337 case B_MODIFIERS_CHANGED: 3338 // these messages should be dispatched by the focus view 3339 if (CurrentFocus() != NULL) 3340 return CurrentFocus(); 3341 break; 3342 3343 case B_MOUSE_DOWN: 3344 case B_MOUSE_UP: 3345 case B_MOUSE_MOVED: 3346 case B_MOUSE_WHEEL_CHANGED: 3347 case B_MOUSE_IDLE: 3348 // is there a token of the view that is currently under the mouse? 3349 int32 token; 3350 if (message->FindInt32("_view_token", &token) == B_OK) { 3351 BView* view = _FindView(token); 3352 if (view != NULL) 3353 return view; 3354 } 3355 3356 // if there is no valid token in the message, we try our 3357 // luck with the last target, if available 3358 if (fLastMouseMovedView != NULL) 3359 return fLastMouseMovedView; 3360 break; 3361 3362 case B_PULSE: 3363 case B_QUIT_REQUESTED: 3364 // TODO: test whether R5 will let BView dispatch these messages 3365 return this; 3366 3367 case _MESSAGE_DROPPED_: 3368 if (fLastMouseMovedView != NULL) 3369 return fLastMouseMovedView; 3370 break; 3371 3372 default: 3373 break; 3374 } 3375 3376 return target; 3377 } 3378 3379 3380 /*! \brief Determines whether or not this message has targeted the focus view. 3381 3382 This will return \c false only if the message did not go to the preferred 3383 handler, or if the packed message does not contain address the focus view 3384 at all. 3385 */ 3386 bool 3387 BWindow::_IsFocusMessage(BMessage* message) 3388 { 3389 BMessage::Private messagePrivate(message); 3390 if (!messagePrivate.UsePreferredTarget()) 3391 return false; 3392 3393 bool feedFocus; 3394 if (message->HasInt32("_token") 3395 && (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus)) 3396 return false; 3397 3398 return true; 3399 } 3400 3401 3402 /*! \brief Distributes the message to its intended targets. This is done for 3403 all messages that should go to the preferred handler. 3404 3405 Returns \c true in case the message should still be dispatched 3406 */ 3407 bool 3408 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message, 3409 BHandler** _target, bool* _usePreferred) 3410 { 3411 if (cookie.message == NULL) 3412 return false; 3413 3414 if (cookie.index == 0 && !cookie.tokens_scanned) { 3415 // We were called the first time for this message 3416 3417 if (!*_usePreferred) { 3418 // only consider messages targeted at the preferred handler 3419 cookie.message = NULL; 3420 return true; 3421 } 3422 3423 // initialize our cookie 3424 cookie.message = *_message; 3425 cookie.focus = *_target; 3426 3427 if (cookie.focus != NULL) 3428 cookie.focus_token = _get_object_token_(*_target); 3429 3430 if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED) 3431 cookie.last_view_token = _get_object_token_(fLastMouseMovedView); 3432 3433 *_usePreferred = false; 3434 } 3435 3436 _DequeueAll(); 3437 3438 // distribute the message to all targets specified in the 3439 // message directly (but not to the focus view) 3440 3441 for (int32 token; !cookie.tokens_scanned 3442 && cookie.message->FindInt32("_token", cookie.index, &token) 3443 == B_OK; 3444 cookie.index++) { 3445 // focus view is preferred and should get its message directly 3446 if (token == cookie.focus_token) { 3447 cookie.found_focus = true; 3448 continue; 3449 } 3450 if (token == cookie.last_view_token) 3451 continue; 3452 3453 BView* target = _FindView(token); 3454 if (target == NULL) 3455 continue; 3456 3457 *_message = new BMessage(*cookie.message); 3458 // the secondary copies of the message should not be treated as focus 3459 // messages, otherwise there will be unintended side effects, i.e. 3460 // keyboard shortcuts getting processed multiple times. 3461 (*_message)->RemoveName("_feed_focus"); 3462 *_target = target; 3463 cookie.index++; 3464 return true; 3465 } 3466 3467 cookie.tokens_scanned = true; 3468 3469 // if there is a last mouse moved view, and the new focus is 3470 // different, the previous view wants to get its B_EXITED_VIEW 3471 // message 3472 if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL 3473 && fLastMouseMovedView != cookie.focus) { 3474 *_message = new BMessage(*cookie.message); 3475 *_target = fLastMouseMovedView; 3476 cookie.last_view_token = B_NULL_TOKEN; 3477 return true; 3478 } 3479 3480 bool dispatchToFocus = true; 3481 3482 // check if the focus token is still valid (could have been removed in the mean time) 3483 BHandler* handler; 3484 if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK 3485 || handler->Looper() != this) 3486 dispatchToFocus = false; 3487 3488 if (dispatchToFocus && cookie.index > 0) { 3489 // should this message still be dispatched by the focus view? 3490 bool feedFocus; 3491 if (!cookie.found_focus 3492 && (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK 3493 || feedFocus == false)) 3494 dispatchToFocus = false; 3495 } 3496 3497 if (!dispatchToFocus) { 3498 delete cookie.message; 3499 cookie.message = NULL; 3500 return false; 3501 } 3502 3503 *_message = cookie.message; 3504 *_target = cookie.focus; 3505 *_usePreferred = true; 3506 cookie.message = NULL; 3507 return true; 3508 } 3509 3510 3511 /*! Some messages don't get to the window in a shape an application should see. 3512 This method is supposed to give a message the last grinding before 3513 it's acceptable for the receiving application. 3514 */ 3515 void 3516 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred) 3517 { 3518 if (target == NULL) 3519 return; 3520 3521 switch (message->what) { 3522 case B_MOUSE_MOVED: 3523 case B_MOUSE_UP: 3524 case B_MOUSE_DOWN: 3525 { 3526 BPoint where; 3527 if (message->FindPoint("screen_where", &where) != B_OK) 3528 break; 3529 3530 BView* view = dynamic_cast<BView*>(target); 3531 3532 if (view == NULL || message->what == B_MOUSE_MOVED) { 3533 // add local window coordinates, only 3534 // for regular mouse moved messages 3535 message->AddPoint("where", ConvertFromScreen(where)); 3536 } 3537 3538 if (view != NULL) { 3539 // add local view coordinates 3540 BPoint viewWhere = view->ConvertFromScreen(where); 3541 if (message->what != B_MOUSE_MOVED) { 3542 // Yep, the meaning of "where" is different 3543 // for regular mouse moved messages versus 3544 // mouse up/down! 3545 message->AddPoint("where", viewWhere); 3546 } 3547 message->AddPoint("be:view_where", viewWhere); 3548 3549 if (message->what == B_MOUSE_MOVED) { 3550 // is there a token of the view that is currently under 3551 // the mouse? 3552 BView* viewUnderMouse = NULL; 3553 int32 token; 3554 if (message->FindInt32("_view_token", &token) == B_OK) 3555 viewUnderMouse = _FindView(token); 3556 3557 // add transit information 3558 uint32 transit 3559 = _TransitForMouseMoved(view, viewUnderMouse); 3560 message->AddInt32("be:transit", transit); 3561 3562 if (usePreferred) 3563 fLastMouseMovedView = viewUnderMouse; 3564 } 3565 } 3566 break; 3567 } 3568 3569 case B_MOUSE_IDLE: 3570 { 3571 // App Server sends screen coordinates, convert the point to 3572 // local view coordinates, then add the point in be:view_where 3573 BPoint where; 3574 if (message->FindPoint("screen_where", &where) != B_OK) 3575 break; 3576 3577 BView* view = dynamic_cast<BView*>(target); 3578 if (view != NULL) { 3579 // add local view coordinates 3580 message->AddPoint("be:view_where", 3581 view->ConvertFromScreen(where)); 3582 } 3583 break; 3584 } 3585 3586 case _MESSAGE_DROPPED_: 3587 { 3588 uint32 originalWhat; 3589 if (message->FindInt32("_original_what", 3590 (int32*)&originalWhat) == B_OK) { 3591 message->what = originalWhat; 3592 message->RemoveName("_original_what"); 3593 } 3594 break; 3595 } 3596 } 3597 } 3598 3599 3600 /*! 3601 This is called by BView::GetMouse() when a B_MOUSE_MOVED message 3602 is removed from the queue. 3603 It allows the window to update the last mouse moved view, and 3604 let it decide if this message should be kept. It will also remove 3605 the message from the queue. 3606 You need to hold the message queue lock when calling this method! 3607 3608 \return true if this message can be used to get the mouse data from, 3609 \return false if this is not meant for the public. 3610 */ 3611 bool 3612 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage) 3613 { 3614 BMessage::Private messagePrivate(message); 3615 if (!messagePrivate.UsePreferredTarget()) { 3616 // this message is targeted at a specific handler, so we should 3617 // not steal it 3618 return false; 3619 } 3620 3621 int32 token; 3622 if (message->FindInt32("_token", 0, &token) == B_OK) { 3623 // This message has other targets, so we can't remove it; 3624 // just prevent it from being sent to the preferred handler 3625 // again (if it should have gotten it at all). 3626 bool feedFocus; 3627 if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus) 3628 return false; 3629 3630 message->RemoveName("_feed_focus"); 3631 deleteMessage = false; 3632 } else { 3633 deleteMessage = true; 3634 3635 if (message->what == B_MOUSE_MOVED) { 3636 // We need to update the last mouse moved view, as this message 3637 // won't make it to _SanitizeMessage() anymore. 3638 BView* viewUnderMouse = NULL; 3639 int32 token; 3640 if (message->FindInt32("_view_token", &token) == B_OK) 3641 viewUnderMouse = _FindView(token); 3642 3643 // Don't remove important transit messages! 3644 uint32 transit = _TransitForMouseMoved(fLastMouseMovedView, 3645 viewUnderMouse); 3646 if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW) 3647 deleteMessage = false; 3648 } 3649 3650 if (deleteMessage) { 3651 // The message is only thought for the preferred handler, so we 3652 // can just remove it. 3653 MessageQueue()->RemoveMessage(message); 3654 } 3655 } 3656 3657 return true; 3658 } 3659 3660 3661 uint32 3662 BWindow::_TransitForMouseMoved(BView* view, BView* viewUnderMouse) const 3663 { 3664 uint32 transit; 3665 if (viewUnderMouse == view) { 3666 // the mouse is over the target view 3667 if (fLastMouseMovedView != view) 3668 transit = B_ENTERED_VIEW; 3669 else 3670 transit = B_INSIDE_VIEW; 3671 } else { 3672 // the mouse is not over the target view 3673 if (view == fLastMouseMovedView) 3674 transit = B_EXITED_VIEW; 3675 else 3676 transit = B_OUTSIDE_VIEW; 3677 } 3678 return transit; 3679 } 3680 3681 3682 /*! Forwards the key to the switcher 3683 */ 3684 void 3685 BWindow::_Switcher(int32 rawKey, uint32 modifiers, bool repeat) 3686 { 3687 // only send the first key press, no repeats 3688 if (repeat) 3689 return; 3690 3691 BMessenger deskbar(kDeskbarSignature); 3692 if (!deskbar.IsValid()) { 3693 // TODO: have some kind of fallback-handling in case the Deskbar is 3694 // not available? 3695 return; 3696 } 3697 3698 BMessage message('TASK'); 3699 message.AddInt32("key", rawKey); 3700 message.AddInt32("modifiers", modifiers); 3701 message.AddInt64("when", system_time()); 3702 message.AddInt32("team", Team()); 3703 deskbar.SendMessage(&message); 3704 } 3705 3706 3707 /*! Handles keyboard input before it gets forwarded to the target handler. 3708 This includes shortcut evaluation, keyboard navigation, etc. 3709 3710 \return handled if true, the event was already handled, and will not 3711 be forwarded to the target handler. 3712 3713 TODO: must also convert the incoming key to the font encoding of the target 3714 */ 3715 bool 3716 BWindow::_HandleKeyDown(BMessage* event) 3717 { 3718 // Only handle special functions when the event targeted the active focus 3719 // view 3720 if (!_IsFocusMessage(event)) 3721 return false; 3722 3723 const char* bytes = NULL; 3724 if (event->FindString("bytes", &bytes) != B_OK) 3725 return false; 3726 3727 char key = bytes[0]; 3728 3729 uint32 modifiers; 3730 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK) 3731 modifiers = 0; 3732 3733 // handle BMenuBar key 3734 if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0 3735 && fKeyMenuBar != NULL) { 3736 fKeyMenuBar->StartMenuBar(0, true, false, NULL); 3737 return true; 3738 } 3739 3740 // Keyboard navigation through views 3741 // (B_OPTION_KEY makes BTextViews and friends navigable, even in editing 3742 // mode) 3743 if (key == B_TAB && (modifiers & B_OPTION_KEY) != 0) { 3744 _KeyboardNavigation(); 3745 return true; 3746 } 3747 3748 int32 rawKey; 3749 event->FindInt32("key", &rawKey); 3750 3751 // Deskbar's Switcher 3752 if ((key == B_TAB || rawKey == 0x11) && (modifiers & B_CONTROL_KEY) != 0) { 3753 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat")); 3754 return true; 3755 } 3756 3757 // Optionally close window when the escape key is pressed 3758 if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) { 3759 BMessage message(B_QUIT_REQUESTED); 3760 message.AddBool("shortcut", true); 3761 3762 PostMessage(&message); 3763 return true; 3764 } 3765 3766 // PrtScr key takes a screenshot 3767 if (key == B_FUNCTION_KEY && rawKey == B_PRINT_KEY) { 3768 // With no modifier keys the best way to get a screenshot is by 3769 // calling the screenshot CLI 3770 if (modifiers == 0) { 3771 be_roster->Launch("application/x-vnd.haiku-screenshot-cli"); 3772 return true; 3773 } 3774 3775 // Prepare a message based on the modifier keys pressed and launch the 3776 // screenshot GUI 3777 BMessage message(B_ARGV_RECEIVED); 3778 int32 argc = 1; 3779 message.AddString("argv", "Screenshot"); 3780 if ((modifiers & B_CONTROL_KEY) != 0) { 3781 argc++; 3782 message.AddString("argv", "--clipboard"); 3783 } 3784 if ((modifiers & B_SHIFT_KEY) != 0) { 3785 argc++; 3786 message.AddString("argv", "--silent"); 3787 } 3788 message.AddInt32("argc", argc); 3789 be_roster->Launch("application/x-vnd.haiku-screenshot", &message); 3790 return true; 3791 } 3792 3793 // Handle shortcuts 3794 if ((modifiers & B_COMMAND_KEY) != 0) { 3795 // Command+q has been pressed, so, we will quit 3796 // the shortcut mechanism doesn't allow handlers outside the window 3797 if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) { 3798 BMessage message(B_QUIT_REQUESTED); 3799 message.AddBool("shortcut", true); 3800 3801 be_app->PostMessage(&message); 3802 // eat the event 3803 return true; 3804 } 3805 3806 // Send Command+Left and Command+Right to textview if it has focus 3807 if (key == B_LEFT_ARROW || key == B_RIGHT_ARROW) { 3808 // check key before doing expensive dynamic_cast 3809 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 3810 if (textView != NULL) { 3811 textView->KeyDown(bytes, modifiers); 3812 // eat the event 3813 return true; 3814 } 3815 } 3816 3817 // Pretend that the user opened a menu, to give the subclass a 3818 // chance to update it's menus. This may install new shortcuts, 3819 // which is why we have to call it here, before trying to find 3820 // a shortcut for the given key. 3821 MenusBeginning(); 3822 3823 Shortcut* shortcut = _FindShortcut(key, modifiers); 3824 if (shortcut != NULL) { 3825 // TODO: would be nice to move this functionality to 3826 // a Shortcut::Invoke() method - but since BMenu::InvokeItem() 3827 // (and BMenuItem::Invoke()) are private, I didn't want 3828 // to mess with them (BMenuItem::Invoke() is public in 3829 // Dano/Zeta, though, maybe we should just follow their 3830 // example) 3831 if (shortcut->MenuItem() != NULL) { 3832 BMenu* menu = shortcut->MenuItem()->Menu(); 3833 if (menu != NULL) 3834 MenuPrivate(menu).InvokeItem(shortcut->MenuItem(), true); 3835 } else { 3836 BHandler* target = shortcut->Target(); 3837 if (target == NULL) 3838 target = CurrentFocus(); 3839 3840 if (shortcut->Message() != NULL) { 3841 BMessage message(*shortcut->Message()); 3842 3843 if (message.ReplaceInt64("when", system_time()) != B_OK) 3844 message.AddInt64("when", system_time()); 3845 if (message.ReplaceBool("shortcut", true) != B_OK) 3846 message.AddBool("shortcut", true); 3847 3848 PostMessage(&message, target); 3849 } 3850 } 3851 } 3852 3853 MenusEnded(); 3854 3855 // we always eat the event if the command key was pressed 3856 return true; 3857 } 3858 3859 // TODO: convert keys to the encoding of the target view 3860 3861 return false; 3862 } 3863 3864 3865 bool 3866 BWindow::_HandleUnmappedKeyDown(BMessage* event) 3867 { 3868 // Only handle special functions when the event targeted the active focus 3869 // view 3870 if (!_IsFocusMessage(event)) 3871 return false; 3872 3873 uint32 modifiers; 3874 int32 rawKey; 3875 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK 3876 || event->FindInt32("key", &rawKey)) 3877 return false; 3878 3879 // Deskbar's Switcher 3880 if (rawKey == 0x11 && (modifiers & B_CONTROL_KEY) != 0) { 3881 _Switcher(rawKey, modifiers, event->HasInt32("be:key_repeat")); 3882 return true; 3883 } 3884 3885 return false; 3886 } 3887 3888 3889 void 3890 BWindow::_KeyboardNavigation() 3891 { 3892 BMessage* message = CurrentMessage(); 3893 if (message == NULL) 3894 return; 3895 3896 const char* bytes; 3897 uint32 modifiers; 3898 if (message->FindString("bytes", &bytes) != B_OK || bytes[0] != B_TAB) 3899 return; 3900 3901 message->FindInt32("modifiers", (int32*)&modifiers); 3902 3903 BView* nextFocus; 3904 int32 jumpGroups = (modifiers & B_OPTION_KEY) != 0 3905 ? B_NAVIGABLE_JUMP : B_NAVIGABLE; 3906 if (modifiers & B_SHIFT_KEY) 3907 nextFocus = _FindPreviousNavigable(fFocus, jumpGroups); 3908 else 3909 nextFocus = _FindNextNavigable(fFocus, jumpGroups); 3910 3911 if (nextFocus != NULL && nextFocus != fFocus) 3912 nextFocus->MakeFocus(true); 3913 } 3914 3915 3916 /*! 3917 \brief Return the position of the window centered horizontally to the passed 3918 in \a frame and vertically 3/4 from the top of \a frame. 3919 3920 If the window is on the borders 3921 3922 \param width The width of the window. 3923 \param height The height of the window. 3924 \param frame The \a frame to center the window in. 3925 3926 \return The new window position. 3927 */ 3928 BPoint 3929 BWindow::AlertPosition(const BRect& frame) 3930 { 3931 float width = Bounds().Width(); 3932 float height = Bounds().Height(); 3933 3934 BPoint point(frame.left + (frame.Width() / 2.0f) - (width / 2.0f), 3935 frame.top + (frame.Height() / 4.0f) - ceil(height / 3.0f)); 3936 3937 BRect screenFrame = BScreen(this).Frame(); 3938 if (frame == screenFrame) { 3939 // reference frame is screen frame, skip the below adjustments 3940 return point; 3941 } 3942 3943 float borderWidth; 3944 float tabHeight; 3945 _GetDecoratorSize(&borderWidth, &tabHeight); 3946 3947 // clip the x position within the horizontal edges of the screen 3948 if (point.x < screenFrame.left + borderWidth) 3949 point.x = screenFrame.left + borderWidth; 3950 else if (point.x + width > screenFrame.right - borderWidth) 3951 point.x = screenFrame.right - borderWidth - width; 3952 3953 // lower the window down if it is covering the window tab 3954 float tabPosition = frame.LeftTop().y + tabHeight + borderWidth; 3955 if (point.y < tabPosition) 3956 point.y = tabPosition; 3957 3958 // clip the y position within the vertical edges of the screen 3959 if (point.y < screenFrame.top + borderWidth) 3960 point.y = screenFrame.top + borderWidth; 3961 else if (point.y + height > screenFrame.bottom - borderWidth) 3962 point.y = screenFrame.bottom - borderWidth - height; 3963 3964 return point; 3965 } 3966 3967 3968 BMessage* 3969 BWindow::ConvertToMessage(void* raw, int32 code) 3970 { 3971 return BLooper::ConvertToMessage(raw, code); 3972 } 3973 3974 3975 BWindow::Shortcut* 3976 BWindow::_FindShortcut(uint32 key, uint32 modifiers) 3977 { 3978 int32 count = fShortcuts.CountItems(); 3979 3980 key = Shortcut::PrepareKey(key); 3981 modifiers = Shortcut::PrepareModifiers(modifiers); 3982 3983 for (int32 index = 0; index < count; index++) { 3984 Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index); 3985 3986 if (shortcut->Matches(key, modifiers)) 3987 return shortcut; 3988 } 3989 3990 return NULL; 3991 } 3992 3993 3994 BView* 3995 BWindow::_FindView(int32 token) 3996 { 3997 BHandler* handler; 3998 if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN, 3999 (void**)&handler) != B_OK) { 4000 return NULL; 4001 } 4002 4003 // the view must belong to us in order to be found by this method 4004 BView* view = dynamic_cast<BView*>(handler); 4005 if (view != NULL && view->Window() == this) 4006 return view; 4007 4008 return NULL; 4009 } 4010 4011 4012 BView* 4013 BWindow::_FindView(BView* view, BPoint point) const 4014 { 4015 // point is assumed to be already in view's coordinates 4016 if (!view->IsHidden() && view->Bounds().Contains(point)) { 4017 if (view->fFirstChild == NULL) 4018 return view; 4019 else { 4020 BView* child = view->fFirstChild; 4021 while (child != NULL) { 4022 BPoint childPoint = point - child->Frame().LeftTop(); 4023 BView* subView = _FindView(child, childPoint); 4024 if (subView != NULL) 4025 return subView; 4026 4027 child = child->fNextSibling; 4028 } 4029 } 4030 return view; 4031 } 4032 return NULL; 4033 } 4034 4035 4036 BView* 4037 BWindow::_FindNextNavigable(BView* focus, uint32 flags) 4038 { 4039 if (focus == NULL) 4040 focus = fTopView; 4041 4042 BView* nextFocus = focus; 4043 4044 // Search the tree for views that accept focus (depth search) 4045 while (true) { 4046 if (nextFocus->fFirstChild) 4047 nextFocus = nextFocus->fFirstChild; 4048 else if (nextFocus->fNextSibling) 4049 nextFocus = nextFocus->fNextSibling; 4050 else { 4051 // go to the nearest parent with a next sibling 4052 while (!nextFocus->fNextSibling && nextFocus->fParent) { 4053 nextFocus = nextFocus->fParent; 4054 } 4055 4056 if (nextFocus == fTopView) { 4057 // if we started with the top view, we traversed the whole tree already 4058 if (nextFocus == focus) 4059 return NULL; 4060 4061 nextFocus = nextFocus->fFirstChild; 4062 } else 4063 nextFocus = nextFocus->fNextSibling; 4064 } 4065 4066 if (nextFocus == focus || nextFocus == NULL) { 4067 // When we get here it means that the hole tree has been 4068 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set! 4069 return NULL; 4070 } 4071 4072 if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0) 4073 return nextFocus; 4074 } 4075 } 4076 4077 4078 BView* 4079 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags) 4080 { 4081 if (focus == NULL) 4082 focus = fTopView; 4083 4084 BView* previousFocus = focus; 4085 4086 // Search the tree for the previous view that accept focus 4087 while (true) { 4088 if (previousFocus->fPreviousSibling) { 4089 // find the last child in the previous sibling 4090 previousFocus = _LastViewChild(previousFocus->fPreviousSibling); 4091 } else { 4092 previousFocus = previousFocus->fParent; 4093 if (previousFocus == fTopView) 4094 previousFocus = _LastViewChild(fTopView); 4095 } 4096 4097 if (previousFocus == focus || previousFocus == NULL) { 4098 // When we get here it means that the hole tree has been 4099 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set! 4100 return NULL; 4101 } 4102 4103 if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0) 4104 return previousFocus; 4105 } 4106 } 4107 4108 4109 /*! 4110 Returns the last child in a view hierarchy. 4111 Needed only by _FindPreviousNavigable(). 4112 */ 4113 BView* 4114 BWindow::_LastViewChild(BView* parent) 4115 { 4116 while (true) { 4117 BView* last = parent->fFirstChild; 4118 if (last == NULL) 4119 return parent; 4120 4121 while (last->fNextSibling) { 4122 last = last->fNextSibling; 4123 } 4124 4125 parent = last; 4126 } 4127 } 4128 4129 4130 void 4131 BWindow::SetIsFilePanel(bool isFilePanel) 4132 { 4133 fIsFilePanel = isFilePanel; 4134 } 4135 4136 4137 bool 4138 BWindow::IsFilePanel() const 4139 { 4140 return fIsFilePanel; 4141 } 4142 4143 4144 void 4145 BWindow::_GetDecoratorSize(float* _borderWidth, float* _tabHeight) const 4146 { 4147 // fallback in case retrieving the decorator settings fails 4148 // (highly unlikely) 4149 float borderWidth = 5.0; 4150 float tabHeight = 21.0; 4151 4152 BMessage settings; 4153 if (GetDecoratorSettings(&settings) == B_OK) { 4154 BRect tabRect; 4155 if (settings.FindRect("tab frame", &tabRect) == B_OK) 4156 tabHeight = tabRect.Height(); 4157 settings.FindFloat("border width", &borderWidth); 4158 } else { 4159 // probably no-border window look 4160 if (fLook == B_NO_BORDER_WINDOW_LOOK) { 4161 borderWidth = 0.0; 4162 tabHeight = 0.0; 4163 } 4164 // else use fall-back values from above 4165 } 4166 4167 if (_borderWidth != NULL) 4168 *_borderWidth = borderWidth; 4169 if (_tabHeight != NULL) 4170 *_tabHeight = tabHeight; 4171 } 4172 4173 4174 void 4175 BWindow::_SendShowOrHideMessage() 4176 { 4177 fLink->StartMessage(AS_SHOW_OR_HIDE_WINDOW); 4178 fLink->Attach<int32>(fShowLevel); 4179 fLink->Flush(); 4180 } 4181 4182 4183 // #pragma mark - C++ binary compatibility kludge 4184 4185 4186 extern "C" void 4187 _ReservedWindow1__7BWindow(BWindow* window, BLayout* layout) 4188 { 4189 // SetLayout() 4190 perform_data_set_layout data; 4191 data.layout = layout; 4192 window->Perform(PERFORM_CODE_SET_LAYOUT, &data); 4193 } 4194 4195 4196 void BWindow::_ReservedWindow2() {} 4197 void BWindow::_ReservedWindow3() {} 4198 void BWindow::_ReservedWindow4() {} 4199 void BWindow::_ReservedWindow5() {} 4200 void BWindow::_ReservedWindow6() {} 4201 void BWindow::_ReservedWindow7() {} 4202 void BWindow::_ReservedWindow8() {} 4203 4204