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