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