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