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