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