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