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