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 //bigtime_t drawTime = 0; 1087 STRACE(("info:BWindow handling _UPDATE_.\n")); 1088 BRect updateRect; 1089 1090 fLink->StartMessage(AS_BEGIN_UPDATE); 1091 fInTransaction = true; 1092 1093 int32 code; 1094 if (fLink->FlushWithReply(code) == B_OK 1095 && code == B_OK) { 1096 // read current window position and size first, 1097 // the update rect is in screen coordinates... 1098 // so we need to be up to date 1099 BPoint origin; 1100 fLink->Read<BPoint>(&origin); 1101 float width; 1102 float height; 1103 fLink->Read<float>(&width); 1104 fLink->Read<float>(&height); 1105 if (origin != fFrame.LeftTop()) { 1106 // TODO: remove code duplicatation with 1107 // B_WINDOW_MOVED case... 1108 //printf("window position was not up to date\n"); 1109 fFrame.OffsetTo(origin); 1110 FrameMoved(origin); 1111 } 1112 if (width != fFrame.Width() || height != fFrame.Height()) { 1113 // TODO: remove code duplicatation with 1114 // B_WINDOW_RESIZED case... 1115 //printf("window size was not up to date\n"); 1116 fFrame.right = fFrame.left + width; 1117 fFrame.bottom = fFrame.top + height; 1118 1119 _AdoptResize(); 1120 FrameResized(width, height); 1121 } 1122 1123 // read culmulated update rect (is in screen coords) 1124 fLink->Read<BRect>(&updateRect); 1125 1126 // read tokens for views that need to be drawn 1127 // NOTE: we need to read the tokens completely 1128 // first, or other calls would likely mess up the 1129 // data in the link. 1130 BList tokens(20); 1131 int32 token; 1132 status_t error = fLink->Read<int32>(&token); 1133 while (error >= B_OK && token != B_NULL_TOKEN) { 1134 tokens.AddItem((void*)token); 1135 error = fLink->Read<int32>(&token); 1136 } 1137 // draw 1138 int32 count = tokens.CountItems(); 1139 for (int32 i = 0; i < count; i++) { 1140 //bigtime_t drawStart = system_time(); 1141 if (BView* view = _FindView((int32)tokens.ItemAtFast(i))) 1142 view->_Draw(updateRect); 1143 else 1144 printf("_UPDATE_ - didn't find view by token: %ld\n", (int32)tokens.ItemAtFast(i)); 1145 //drawTime += system_time() - drawStart; 1146 } 1147 // TODO: the tokens are actually hirachically sorted, 1148 // so traversing the list in revers and calling 1149 // child->DrawAfterChildren would actually work correctly, 1150 // only that drawing outside a view is not yet supported 1151 // in the app_server. 1152 //printf(" %ld views drawn, total Draw() time: %lld\n", count, drawTime); 1153 } 1154 1155 fLink->StartMessage(AS_END_UPDATE); 1156 fLink->Flush(); 1157 fInTransaction = false; 1158 1159 //printf("BWindow(%s) - UPDATE took %lld usecs\n", Title(), system_time() - now); 1160 break; 1161 } 1162 1163 case _MENUS_DONE_: 1164 MenusEnded(); 1165 break; 1166 1167 // These two are obviously some kind of old scripting messages 1168 // this is NOT an app_server message and we have to be cautious 1169 case B_WINDOW_MOVE_BY: 1170 { 1171 BPoint offset; 1172 if (msg->FindPoint("data", &offset) == B_OK) 1173 MoveBy(offset.x, offset.y); 1174 else 1175 msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD); 1176 break; 1177 } 1178 1179 // this is NOT an app_server message and we have to be cautious 1180 case B_WINDOW_MOVE_TO: 1181 { 1182 BPoint origin; 1183 if (msg->FindPoint("data", &origin) == B_OK) 1184 MoveTo(origin); 1185 else 1186 msg->SendReply(B_MESSAGE_NOT_UNDERSTOOD); 1187 break; 1188 } 1189 1190 case B_LAYOUT_WINDOW: 1191 { 1192 if (fFlags & B_AUTO_UPDATE_SIZE_LIMITS) { 1193 // Get min/max constraints of the top view and enforce window 1194 // size limits respectively. 1195 BSize minSize = fTopView->MinSize(); 1196 BSize maxSize = fTopView->MaxSize(); 1197 SetSizeLimits(minSize.width, maxSize.width, 1198 minSize.height, maxSize.height); 1199 } 1200 1201 // do the actual layout 1202 fTopView->Layout(false); 1203 1204 break; 1205 } 1206 1207 default: 1208 BLooper::DispatchMessage(msg, target); 1209 break; 1210 } 1211 } 1212 1213 1214 void 1215 BWindow::FrameMoved(BPoint new_position) 1216 { 1217 // does nothing 1218 // Hook function 1219 } 1220 1221 1222 void 1223 BWindow::FrameResized(float new_width, float new_height) 1224 { 1225 // does nothing 1226 // Hook function 1227 } 1228 1229 1230 void 1231 BWindow::WorkspacesChanged(uint32 old_ws, uint32 new_ws) 1232 { 1233 // does nothing 1234 // Hook function 1235 } 1236 1237 1238 void 1239 BWindow::WorkspaceActivated(int32 ws, bool state) 1240 { 1241 // does nothing 1242 // Hook function 1243 } 1244 1245 1246 void 1247 BWindow::MenusBeginning() 1248 { 1249 // does nothing 1250 // Hook function 1251 } 1252 1253 1254 void 1255 BWindow::MenusEnded() 1256 { 1257 // does nothing 1258 // Hook function 1259 } 1260 1261 1262 void 1263 BWindow::SetSizeLimits(float minWidth, float maxWidth, 1264 float minHeight, float maxHeight) 1265 { 1266 if (minWidth > maxWidth || minHeight > maxHeight) 1267 return; 1268 1269 if (!Lock()) 1270 return; 1271 1272 fLink->StartMessage(AS_SET_SIZE_LIMITS); 1273 fLink->Attach<float>(minWidth); 1274 fLink->Attach<float>(maxWidth); 1275 fLink->Attach<float>(minHeight); 1276 fLink->Attach<float>(maxHeight); 1277 1278 int32 code; 1279 if (fLink->FlushWithReply(code) == B_OK 1280 && code == B_OK) { 1281 // read the values that were really enforced on 1282 // the server side (the window frame could have 1283 // been changed, too) 1284 fLink->Read<BRect>(&fFrame); 1285 fLink->Read<float>(&fMinWidth); 1286 fLink->Read<float>(&fMaxWidth); 1287 fLink->Read<float>(&fMinHeight); 1288 fLink->Read<float>(&fMaxHeight); 1289 1290 _AdoptResize(); 1291 // TODO: the same has to be done for SetLook() (that can alter 1292 // the size limits, and hence, the size of the window 1293 } 1294 Unlock(); 1295 } 1296 1297 1298 void 1299 BWindow::GetSizeLimits(float *minWidth, float *maxWidth, 1300 float *minHeight, float *maxHeight) 1301 { 1302 // TODO: What about locking?!? 1303 *minHeight = fMinHeight; 1304 *minWidth = fMinWidth; 1305 *maxHeight = fMaxHeight; 1306 *maxWidth = fMaxWidth; 1307 } 1308 1309 1310 status_t 1311 BWindow::SetDecoratorSettings(const BMessage& settings) 1312 { 1313 // flatten the given settings into a buffer and send 1314 // it to the app_server to apply the settings to the 1315 // decorator 1316 1317 int32 size = settings.FlattenedSize(); 1318 char buffer[size]; 1319 status_t ret = settings.Flatten(buffer, size); 1320 1321 if (ret < B_OK) 1322 return ret; 1323 1324 if (!Lock()) 1325 return B_ERROR; 1326 1327 ret = fLink->StartMessage(AS_SET_DECORATOR_SETTINGS); 1328 1329 if (ret == B_OK) 1330 ret = fLink->Attach<int32>(size); 1331 1332 if (ret == B_OK) 1333 ret = fLink->Attach(buffer, size); 1334 1335 if (ret == B_OK) 1336 ret = fLink->Flush(); 1337 1338 Unlock(); 1339 1340 return ret; 1341 } 1342 1343 1344 status_t 1345 BWindow::GetDecoratorSettings(BMessage* settings) const 1346 { 1347 // read a flattened settings message from the app_server 1348 // and put it into settings 1349 1350 if (!const_cast<BWindow*>(this)->Lock()) 1351 return B_ERROR; 1352 1353 status_t ret = fLink->StartMessage(AS_GET_DECORATOR_SETTINGS); 1354 1355 if (ret == B_OK) { 1356 int32 code; 1357 ret = fLink->FlushWithReply(code); 1358 if (ret == B_OK && code != B_OK) 1359 ret = code; 1360 } 1361 1362 if (ret == B_OK) { 1363 int32 size; 1364 ret = fLink->Read<int32>(&size); 1365 if (ret == B_OK) { 1366 char buffer[size]; 1367 ret = fLink->Read(buffer, size); 1368 if (ret == B_OK) { 1369 ret = settings->Unflatten(buffer); 1370 } 1371 } 1372 } 1373 1374 const_cast<BWindow*>(this)->Unlock(); 1375 1376 return ret; 1377 } 1378 1379 1380 void 1381 BWindow::SetZoomLimits(float maxWidth, float maxHeight) 1382 { 1383 // TODO: What about locking?!? 1384 if (maxWidth > fMaxWidth) 1385 maxWidth = fMaxWidth; 1386 else 1387 fMaxZoomWidth = maxWidth; 1388 1389 if (maxHeight > fMaxHeight) 1390 maxHeight = fMaxHeight; 1391 else 1392 fMaxZoomHeight = maxHeight; 1393 } 1394 1395 1396 void 1397 BWindow::Zoom(BPoint leftTop, float width, float height) 1398 { 1399 // the default implementation of this hook function 1400 // just does the obvious: 1401 MoveTo(leftTop); 1402 ResizeTo(width, height); 1403 } 1404 1405 1406 void 1407 BWindow::Zoom() 1408 { 1409 // TODO: What about locking?!? 1410 /* 1411 from BeBook: 1412 However, if the window's rectangle already matches these "zoom" dimensions 1413 (give or take a few pixels), Zoom() passes the window's previous 1414 ("non-zoomed") size and location. (??????) 1415 */ 1416 1417 /* From BeBook: 1418 The dimensions that non-virtual Zoom() passes to hook Zoom() are deduced from 1419 the smallest of three rectangles: 1420 */ 1421 1422 // TODO: make more elaborate (figure out this window's 1423 // tab height and border width... maybe ask app_server) 1424 float borderWidth = 5.0; 1425 float tabHeight = 26.0; 1426 1427 // 1) the rectangle defined by SetZoomLimits(), 1428 float zoomedWidth = fMaxZoomWidth; 1429 float zoomedHeight = fMaxZoomHeight; 1430 1431 // 2) the rectangle defined by SetSizeLimits() 1432 if (fMaxWidth < zoomedWidth) 1433 zoomedWidth = fMaxWidth; 1434 if (fMaxHeight < zoomedHeight) 1435 zoomedHeight = fMaxHeight; 1436 1437 // 3) the screen rectangle 1438 BScreen screen(this); 1439 float screenWidth = screen.Frame().Width() - 2 * borderWidth; 1440 float screenHeight = screen.Frame().Height() - (borderWidth + tabHeight); 1441 if (screenWidth < zoomedWidth) 1442 zoomedWidth = screenWidth; 1443 if (screenHeight < zoomedHeight) 1444 zoomedHeight = screenHeight; 1445 1446 BPoint zoomedLeftTop = screen.Frame().LeftTop() + BPoint(borderWidth, tabHeight); 1447 1448 // UN-ZOOM: 1449 if (fPreviousFrame.IsValid() 1450 // NOTE: don't check for fFrame.LeftTop() == zoomedLeftTop 1451 // -> makes it easier on the user to get a window back into place 1452 && fFrame.Width() == zoomedWidth 1453 && fFrame.Height() == zoomedHeight) { 1454 // already zoomed! 1455 Zoom(fPreviousFrame.LeftTop(), fPreviousFrame.Width(), fPreviousFrame.Height()); 1456 return; 1457 } 1458 1459 // ZOOM: 1460 1461 // remember fFrame for later "unzooming" 1462 fPreviousFrame = fFrame; 1463 1464 Zoom(zoomedLeftTop, zoomedWidth, zoomedHeight); 1465 } 1466 1467 1468 void 1469 BWindow::ScreenChanged(BRect screen_size, color_space depth) 1470 { 1471 // Hook function 1472 } 1473 1474 1475 void 1476 BWindow::SetPulseRate(bigtime_t rate) 1477 { 1478 // TODO: What about locking?!? 1479 if (rate < 0 || (rate == fPulseRate && !(rate == 0 ^ fPulseRunner == NULL))) 1480 return; 1481 1482 fPulseRate = rate; 1483 1484 if (rate > 0) { 1485 if (fPulseRunner == NULL) { 1486 BMessage message(B_PULSE); 1487 fPulseRunner = new BMessageRunner(BMessenger(this), 1488 &message, rate); 1489 } else { 1490 fPulseRunner->SetInterval(rate); 1491 } 1492 } else { 1493 // rate == 0 1494 delete fPulseRunner; 1495 fPulseRunner = NULL; 1496 } 1497 } 1498 1499 1500 bigtime_t 1501 BWindow::PulseRate() const 1502 { 1503 return fPulseRate; 1504 } 1505 1506 1507 void 1508 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMenuItem *item) 1509 { 1510 Shortcut* shortcut = new Shortcut(key, modifiers, item); 1511 1512 // removes the shortcut if it already exists! 1513 RemoveShortcut(key, modifiers); 1514 1515 fShortcuts.AddItem(shortcut); 1516 } 1517 1518 1519 void 1520 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage *message) 1521 { 1522 AddShortcut(key, modifiers, message, this); 1523 } 1524 1525 1526 void 1527 BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage *message, BHandler *target) 1528 { 1529 if (message == NULL) 1530 return; 1531 1532 Shortcut* shortcut = new Shortcut(key, modifiers, message, target); 1533 1534 // removes the shortcut if it already exists! 1535 RemoveShortcut(key, modifiers); 1536 1537 fShortcuts.AddItem(shortcut); 1538 } 1539 1540 1541 void 1542 BWindow::RemoveShortcut(uint32 key, uint32 modifiers) 1543 { 1544 Shortcut* shortcut = _FindShortcut(key, modifiers); 1545 if (shortcut != NULL) { 1546 fShortcuts.RemoveItem(shortcut); 1547 delete shortcut; 1548 } else if ((key == 'q' || key == 'Q') && modifiers == B_COMMAND_KEY) { 1549 // the quit shortcut is a fake shortcut 1550 fNoQuitShortcut = true; 1551 } 1552 } 1553 1554 1555 BButton * 1556 BWindow::DefaultButton() const 1557 { 1558 // TODO: What about locking?!? 1559 return fDefaultButton; 1560 } 1561 1562 1563 void 1564 BWindow::SetDefaultButton(BButton *button) 1565 { 1566 // TODO: What about locking?!? 1567 if (fDefaultButton == button) 1568 return; 1569 1570 if (fDefaultButton != NULL) { 1571 // tell old button it's no longer the default one 1572 BButton *oldDefault = fDefaultButton; 1573 oldDefault->MakeDefault(false); 1574 oldDefault->Invalidate(); 1575 } 1576 1577 fDefaultButton = button; 1578 1579 if (button != NULL) { 1580 // notify new default button 1581 fDefaultButton->MakeDefault(true); 1582 fDefaultButton->Invalidate(); 1583 } 1584 } 1585 1586 1587 bool 1588 BWindow::NeedsUpdate() const 1589 { 1590 if (!const_cast<BWindow *>(this)->Lock()) 1591 return false; 1592 1593 fLink->StartMessage(AS_NEEDS_UPDATE); 1594 1595 int32 code = B_ERROR; 1596 fLink->FlushWithReply(code); 1597 1598 const_cast<BWindow *>(this)->Unlock(); 1599 1600 return code == B_OK; 1601 } 1602 1603 1604 void 1605 BWindow::UpdateIfNeeded() 1606 { 1607 // works only from the window thread 1608 if (find_thread(NULL) != Thread()) 1609 return; 1610 1611 // if the queue is already locked we are called recursivly 1612 // from our own dispatched update message 1613 if (((const BMessageQueue *)MessageQueue())->IsLocked()) 1614 return; 1615 1616 if (!Lock()) 1617 return; 1618 1619 // make sure all requests that would cause an update have 1620 // arrived at the server 1621 Sync(); 1622 1623 // Since we're blocking the event loop, we need to retrieve 1624 // all messages that are pending on the port. 1625 _DequeueAll(); 1626 1627 BMessageQueue *queue = MessageQueue(); 1628 queue->Lock(); 1629 1630 // First process and remove any _UPDATE_ message in the queue 1631 // With the current design, there can only be one at a time 1632 1633 BMessage *msg; 1634 for (int32 i = 0; (msg = queue->FindMessage(i)) != NULL; i++) { 1635 if (msg->what == _UPDATE_) { 1636 BWindow::DispatchMessage(msg, this); 1637 // we need to make sure that no overridden method is called 1638 // here; for BWindow::DispatchMessage() we now exactly what 1639 // will happen 1640 queue->RemoveMessage(msg); 1641 delete msg; 1642 break; 1643 // NOTE: "i" would have to be decreased if there were 1644 // multiple _UPDATE_ messages and we would not break! 1645 } 1646 } 1647 1648 queue->Unlock(); 1649 Unlock(); 1650 } 1651 1652 1653 BView * 1654 BWindow::FindView(const char *viewName) const 1655 { 1656 BAutolock _(const_cast<BWindow*>(this)); 1657 return fTopView->FindView(viewName); 1658 } 1659 1660 1661 BView * 1662 BWindow::FindView(BPoint point) const 1663 { 1664 BAutolock _(const_cast<BWindow*>(this)); 1665 // point is assumed to be in window coordinates, 1666 // fTopView has same bounds as window 1667 return _FindView(fTopView, point); 1668 } 1669 1670 1671 BView * 1672 BWindow::CurrentFocus() const 1673 { 1674 return fFocus; 1675 } 1676 1677 1678 void 1679 BWindow::Activate(bool active) 1680 { 1681 if (!Lock()) 1682 return; 1683 1684 if (!IsHidden()) { 1685 fLink->StartMessage(AS_ACTIVATE_WINDOW); 1686 fLink->Attach<bool>(active); 1687 fLink->Flush(); 1688 } 1689 1690 Unlock(); 1691 } 1692 1693 1694 void 1695 BWindow::WindowActivated(bool state) 1696 { 1697 // hook function 1698 // does nothing 1699 } 1700 1701 1702 void 1703 BWindow::ConvertToScreen(BPoint *point) const 1704 { 1705 point->x += fFrame.left; 1706 point->y += fFrame.top; 1707 } 1708 1709 1710 BPoint 1711 BWindow::ConvertToScreen(BPoint point) const 1712 { 1713 return point + fFrame.LeftTop(); 1714 } 1715 1716 1717 void 1718 BWindow::ConvertFromScreen(BPoint *point) const 1719 { 1720 point->x -= fFrame.left; 1721 point->y -= fFrame.top; 1722 } 1723 1724 1725 BPoint 1726 BWindow::ConvertFromScreen(BPoint point) const 1727 { 1728 return point - fFrame.LeftTop(); 1729 } 1730 1731 1732 void 1733 BWindow::ConvertToScreen(BRect *rect) const 1734 { 1735 rect->OffsetBy(fFrame.LeftTop()); 1736 } 1737 1738 1739 BRect 1740 BWindow::ConvertToScreen(BRect rect) const 1741 { 1742 return rect.OffsetByCopy(fFrame.LeftTop()); 1743 } 1744 1745 1746 void 1747 BWindow::ConvertFromScreen(BRect* rect) const 1748 { 1749 rect->OffsetBy(-fFrame.left, -fFrame.top); 1750 } 1751 1752 1753 BRect 1754 BWindow::ConvertFromScreen(BRect rect) const 1755 { 1756 return rect.OffsetByCopy(-fFrame.left, -fFrame.top); 1757 } 1758 1759 1760 bool 1761 BWindow::IsMinimized() const 1762 { 1763 // Hiding takes precendence over minimization!!! 1764 if (IsHidden()) 1765 return false; 1766 1767 return fMinimized; 1768 } 1769 1770 1771 BRect 1772 BWindow::Bounds() const 1773 { 1774 return BRect(0, 0, fFrame.Width(), fFrame.Height()); 1775 } 1776 1777 1778 BRect 1779 BWindow::Frame() const 1780 { 1781 return fFrame; 1782 } 1783 1784 1785 const char * 1786 BWindow::Title() const 1787 { 1788 return fTitle; 1789 } 1790 1791 1792 void 1793 BWindow::SetTitle(const char *title) 1794 { 1795 if (title == NULL) 1796 title = ""; 1797 1798 free(fTitle); 1799 fTitle = strdup(title); 1800 1801 // we will change BWindow's thread name to "w>window title" 1802 1803 char threadName[B_OS_NAME_LENGTH]; 1804 strcpy(threadName, "w>"); 1805 #ifdef __HAIKU__ 1806 strlcat(threadName, title, B_OS_NAME_LENGTH); 1807 #else 1808 int32 length = strlen(title); 1809 length = min_c(length, B_OS_NAME_LENGTH - 3); 1810 memcpy(threadName + 2, title, length); 1811 threadName[length + 2] = '\0'; 1812 #endif 1813 1814 // change the handler's name 1815 SetName(threadName); 1816 1817 // if the message loop has been started... 1818 if (Thread() >= B_OK) 1819 rename_thread(Thread(), threadName); 1820 1821 // we notify the app_server so we can actually see the change 1822 if (Lock()) { 1823 fLink->StartMessage(AS_SET_WINDOW_TITLE); 1824 fLink->AttachString(fTitle); 1825 fLink->Flush(); 1826 Unlock(); 1827 } 1828 } 1829 1830 1831 bool 1832 BWindow::IsActive() const 1833 { 1834 return fActive; 1835 } 1836 1837 1838 void 1839 BWindow::SetKeyMenuBar(BMenuBar *bar) 1840 { 1841 fKeyMenuBar = bar; 1842 } 1843 1844 1845 BMenuBar * 1846 BWindow::KeyMenuBar() const 1847 { 1848 return fKeyMenuBar; 1849 } 1850 1851 1852 bool 1853 BWindow::IsModal() const 1854 { 1855 return fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1856 || fFeel == B_MODAL_APP_WINDOW_FEEL 1857 || fFeel == B_MODAL_ALL_WINDOW_FEEL; 1858 } 1859 1860 1861 bool 1862 BWindow::IsFloating() const 1863 { 1864 return fFeel == B_FLOATING_SUBSET_WINDOW_FEEL 1865 || fFeel == B_FLOATING_APP_WINDOW_FEEL 1866 || fFeel == B_FLOATING_ALL_WINDOW_FEEL; 1867 } 1868 1869 1870 status_t 1871 BWindow::AddToSubset(BWindow *window) 1872 { 1873 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL 1874 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL 1875 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL)) 1876 return B_BAD_VALUE; 1877 1878 if (!Lock()) 1879 return B_ERROR; 1880 1881 status_t status = B_ERROR; 1882 fLink->StartMessage(AS_ADD_TO_SUBSET); 1883 fLink->Attach<int32>(_get_object_token_(window)); 1884 fLink->FlushWithReply(status); 1885 1886 Unlock(); 1887 1888 return status; 1889 } 1890 1891 1892 status_t 1893 BWindow::RemoveFromSubset(BWindow *window) 1894 { 1895 if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL 1896 || (fFeel != B_MODAL_SUBSET_WINDOW_FEEL 1897 && fFeel != B_FLOATING_SUBSET_WINDOW_FEEL)) 1898 return B_BAD_VALUE; 1899 1900 if (!Lock()) 1901 return B_ERROR; 1902 1903 status_t status = B_ERROR; 1904 fLink->StartMessage(AS_REMOVE_FROM_SUBSET); 1905 fLink->Attach<int32>(_get_object_token_(window)); 1906 fLink->FlushWithReply(status); 1907 1908 Unlock(); 1909 1910 return status; 1911 } 1912 1913 1914 status_t 1915 BWindow::Perform(perform_code d, void *arg) 1916 { 1917 return BLooper::Perform(d, arg); 1918 } 1919 1920 1921 status_t 1922 BWindow::SetType(window_type type) 1923 { 1924 window_look look; 1925 window_feel feel; 1926 _DecomposeType(type, &look, &feel); 1927 1928 status_t status = SetLook(look); 1929 if (status == B_OK) 1930 status = SetFeel(feel); 1931 1932 return status; 1933 } 1934 1935 1936 window_type 1937 BWindow::Type() const 1938 { 1939 return _ComposeType(fLook, fFeel); 1940 } 1941 1942 1943 status_t 1944 BWindow::SetLook(window_look look) 1945 { 1946 BAutolock locker(this); 1947 1948 fLink->StartMessage(AS_SET_LOOK); 1949 fLink->Attach<int32>((int32)look); 1950 1951 status_t status = B_ERROR; 1952 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 1953 fLook = look; 1954 1955 // TODO: this could have changed the window size, and thus, we 1956 // need to get it from the server (and call _AdoptResize()). 1957 1958 return status; 1959 } 1960 1961 1962 window_look 1963 BWindow::Look() const 1964 { 1965 return fLook; 1966 } 1967 1968 1969 status_t 1970 BWindow::SetFeel(window_feel feel) 1971 { 1972 BAutolock locker(this); 1973 1974 fLink->StartMessage(AS_SET_FEEL); 1975 fLink->Attach<int32>((int32)feel); 1976 1977 status_t status = B_ERROR; 1978 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 1979 fFeel = feel; 1980 1981 return status; 1982 } 1983 1984 1985 window_feel 1986 BWindow::Feel() const 1987 { 1988 return fFeel; 1989 } 1990 1991 1992 status_t 1993 BWindow::SetFlags(uint32 flags) 1994 { 1995 BAutolock locker(this); 1996 1997 fLink->StartMessage(AS_SET_FLAGS); 1998 fLink->Attach<uint32>(flags); 1999 2000 int32 status = B_ERROR; 2001 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2002 fFlags = flags; 2003 2004 return status; 2005 } 2006 2007 2008 uint32 2009 BWindow::Flags() const 2010 { 2011 return fFlags; 2012 } 2013 2014 2015 status_t 2016 BWindow::SetWindowAlignment(window_alignment mode, 2017 int32 h, int32 hOffset, int32 width, int32 widthOffset, 2018 int32 v, int32 vOffset, int32 height, int32 heightOffset) 2019 { 2020 if ((mode & (B_BYTE_ALIGNMENT | B_PIXEL_ALIGNMENT)) == 0 2021 || (hOffset >= 0 && hOffset <= h) 2022 || (vOffset >= 0 && vOffset <= v) 2023 || (widthOffset >= 0 && widthOffset <= width) 2024 || (heightOffset >= 0 && heightOffset <= height)) 2025 return B_BAD_VALUE; 2026 2027 // TODO: test if hOffset = 0 and set it to 1 if true. 2028 2029 if (!Lock()) 2030 return B_ERROR; 2031 2032 fLink->StartMessage(AS_SET_ALIGNMENT); 2033 fLink->Attach<int32>((int32)mode); 2034 fLink->Attach<int32>(h); 2035 fLink->Attach<int32>(hOffset); 2036 fLink->Attach<int32>(width); 2037 fLink->Attach<int32>(widthOffset); 2038 fLink->Attach<int32>(v); 2039 fLink->Attach<int32>(vOffset); 2040 fLink->Attach<int32>(height); 2041 fLink->Attach<int32>(heightOffset); 2042 2043 status_t status = B_ERROR; 2044 fLink->FlushWithReply(status); 2045 2046 Unlock(); 2047 2048 return status; 2049 } 2050 2051 2052 status_t 2053 BWindow::GetWindowAlignment(window_alignment *mode, 2054 int32 *h, int32 *hOffset, int32 *width, int32 *widthOffset, 2055 int32 *v, int32 *vOffset, int32 *height, int32 *heightOffset) const 2056 { 2057 if (!const_cast<BWindow *>(this)->Lock()) 2058 return B_ERROR; 2059 2060 fLink->StartMessage(AS_GET_ALIGNMENT); 2061 2062 status_t status; 2063 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) { 2064 fLink->Read<int32>((int32 *)mode); 2065 fLink->Read<int32>(h); 2066 fLink->Read<int32>(hOffset); 2067 fLink->Read<int32>(width); 2068 fLink->Read<int32>(widthOffset); 2069 fLink->Read<int32>(v); 2070 fLink->Read<int32>(hOffset); 2071 fLink->Read<int32>(height); 2072 fLink->Read<int32>(heightOffset); 2073 } 2074 2075 const_cast<BWindow *>(this)->Unlock(); 2076 return status; 2077 } 2078 2079 2080 uint32 2081 BWindow::Workspaces() const 2082 { 2083 if (!const_cast<BWindow *>(this)->Lock()) 2084 return 0; 2085 2086 uint32 workspaces = 0; 2087 2088 fLink->StartMessage(AS_GET_WORKSPACES); 2089 2090 status_t status; 2091 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2092 fLink->Read<uint32>(&workspaces); 2093 2094 const_cast<BWindow *>(this)->Unlock(); 2095 return workspaces; 2096 } 2097 2098 2099 void 2100 BWindow::SetWorkspaces(uint32 workspaces) 2101 { 2102 // TODO: don't forget about Tracker's background window. 2103 if (fFeel != B_NORMAL_WINDOW_FEEL) 2104 return; 2105 2106 if (Lock()) { 2107 fLink->StartMessage(AS_SET_WORKSPACES); 2108 fLink->Attach<uint32>(workspaces); 2109 fLink->Flush(); 2110 Unlock(); 2111 } 2112 } 2113 2114 2115 BView * 2116 BWindow::LastMouseMovedView() const 2117 { 2118 return fLastMouseMovedView; 2119 } 2120 2121 2122 void 2123 BWindow::MoveBy(float dx, float dy) 2124 { 2125 if ((dx == 0.0 && dy == 0.0) || !Lock()) 2126 return; 2127 2128 fLink->StartMessage(AS_WINDOW_MOVE); 2129 fLink->Attach<float>(dx); 2130 fLink->Attach<float>(dy); 2131 2132 status_t status; 2133 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 2134 fFrame.OffsetBy(dx, dy); 2135 2136 Unlock(); 2137 } 2138 2139 2140 void 2141 BWindow::MoveTo(BPoint point) 2142 { 2143 if (!Lock()) 2144 return; 2145 2146 point.x = roundf(point.x); 2147 point.y = roundf(point.y); 2148 2149 if (fFrame.left != point.x || fFrame.top != point.y) { 2150 float xOffset = point.x - fFrame.left; 2151 float yOffset = point.y - fFrame.top; 2152 2153 MoveBy(xOffset, yOffset); 2154 } 2155 2156 Unlock(); 2157 } 2158 2159 2160 void 2161 BWindow::MoveTo(float x, float y) 2162 { 2163 MoveTo(BPoint(x, y)); 2164 } 2165 2166 2167 void 2168 BWindow::ResizeBy(float dx, float dy) 2169 { 2170 if (!Lock()) 2171 return; 2172 2173 dx = roundf(dx); 2174 dy = roundf(dy); 2175 2176 // stay in minimum & maximum frame limits 2177 if (fFrame.Width() + dx < fMinWidth) 2178 dx = fMinWidth - fFrame.Width(); 2179 if (fFrame.Width() + dx > fMaxWidth) 2180 dx = fMaxWidth - fFrame.Width(); 2181 if (fFrame.Height() + dy < fMinHeight) 2182 dy = fMinHeight - fFrame.Height(); 2183 if (fFrame.Height() + dy > fMaxHeight) 2184 dy = fMaxHeight - fFrame.Height(); 2185 2186 if (dx != 0.0 || dy != 0.0) { 2187 fLink->StartMessage(AS_WINDOW_RESIZE); 2188 fLink->Attach<float>(dx); 2189 fLink->Attach<float>(dy); 2190 2191 status_t status; 2192 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) { 2193 fFrame.SetRightBottom(fFrame.RightBottom() + BPoint(dx, dy)); 2194 _AdoptResize(); 2195 } 2196 } 2197 Unlock(); 2198 } 2199 2200 2201 void 2202 BWindow::ResizeTo(float width, float height) 2203 { 2204 if (Lock()) { 2205 ResizeBy(width - fFrame.Width(), height - fFrame.Height()); 2206 Unlock(); 2207 } 2208 } 2209 2210 2211 void 2212 BWindow::Show() 2213 { 2214 if (!fRunCalled) { 2215 // this is the fist time Show() is called, which implicetly runs the looper 2216 if (fLink->SenderPort() < B_OK) { 2217 // We don't have valid app_server connection; there is no point 2218 // in starting our looper 2219 fThread = B_ERROR; 2220 return; 2221 } else 2222 Run(); 2223 } 2224 2225 if (Lock()) { 2226 fShowLevel++; 2227 2228 if (fShowLevel == 1) { 2229 STRACE(("BWindow(%s): sending AS_SHOW_WINDOW message...\n", Name())); 2230 fLink->StartMessage(AS_SHOW_WINDOW); 2231 fLink->Flush(); 2232 } 2233 2234 Unlock(); 2235 } 2236 } 2237 2238 2239 void 2240 BWindow::Hide() 2241 { 2242 if (!Lock()) 2243 return; 2244 2245 if (--fShowLevel == 0) { 2246 fLink->StartMessage(AS_HIDE_WINDOW); 2247 fLink->Flush(); 2248 } 2249 2250 Unlock(); 2251 } 2252 2253 2254 bool 2255 BWindow::IsHidden() const 2256 { 2257 return fShowLevel <= 0; 2258 } 2259 2260 2261 bool 2262 BWindow::QuitRequested() 2263 { 2264 return BLooper::QuitRequested(); 2265 } 2266 2267 2268 thread_id 2269 BWindow::Run() 2270 { 2271 return BLooper::Run(); 2272 } 2273 2274 2275 void 2276 BWindow::SetLayout(BLayout* layout) 2277 { 2278 fTopView->SetLayout(layout); 2279 } 2280 2281 2282 BLayout* 2283 BWindow::GetLayout() const 2284 { 2285 return fTopView->GetLayout(); 2286 } 2287 2288 2289 void 2290 BWindow::InvalidateLayout(bool descendants) 2291 { 2292 fTopView->InvalidateLayout(descendants); 2293 } 2294 2295 2296 status_t 2297 BWindow::GetSupportedSuites(BMessage *data) 2298 { 2299 if (data == NULL) 2300 return B_BAD_VALUE; 2301 2302 status_t status = data->AddString("suites", "suite/vnd.Be-window"); 2303 if (status == B_OK) { 2304 BPropertyInfo propertyInfo(sWindowPropInfo, sWindowValueInfo); 2305 2306 status = data->AddFlat("messages", &propertyInfo); 2307 if (status == B_OK) 2308 status = BLooper::GetSupportedSuites(data); 2309 } 2310 2311 return status; 2312 } 2313 2314 2315 BHandler * 2316 BWindow::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, 2317 int32 what, const char *property) 2318 { 2319 if (msg->what == B_WINDOW_MOVE_BY 2320 || msg->what == B_WINDOW_MOVE_TO) 2321 return this; 2322 2323 BPropertyInfo propertyInfo(sWindowPropInfo); 2324 if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) { 2325 if (!strcmp(property, "View")) { 2326 // we will NOT pop the current specifier 2327 return fTopView; 2328 } else if (!strcmp(property, "MenuBar")) { 2329 if (fKeyMenuBar) { 2330 msg->PopSpecifier(); 2331 return fKeyMenuBar; 2332 } else { 2333 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 2334 replyMsg.AddInt32("error", B_NAME_NOT_FOUND); 2335 replyMsg.AddString("message", "This window doesn't have a main MenuBar"); 2336 msg->SendReply(&replyMsg); 2337 return NULL; 2338 } 2339 } else 2340 return this; 2341 } 2342 2343 return BLooper::ResolveSpecifier(msg, index, specifier, what, property); 2344 } 2345 2346 2347 // #pragma mark - Private Methods 2348 2349 2350 void 2351 BWindow::_InitData(BRect frame, const char* title, window_look look, 2352 window_feel feel, uint32 flags, uint32 workspace, int32 bitmapToken) 2353 { 2354 STRACE(("BWindow::InitData()\n")); 2355 2356 if (be_app == NULL) { 2357 debugger("You need a valid BApplication object before interacting with the app_server"); 2358 return; 2359 } 2360 2361 frame.left = roundf(frame.left); 2362 frame.top = roundf(frame.top); 2363 frame.right = roundf(frame.right); 2364 frame.bottom = roundf(frame.bottom); 2365 2366 fFrame = frame; 2367 2368 if (title == NULL) 2369 title = ""; 2370 fTitle = strdup(title); 2371 SetName(title); 2372 2373 fFeel = feel; 2374 fLook = look; 2375 fFlags = flags; 2376 2377 fInTransaction = false; 2378 fActive = false; 2379 fShowLevel = 0; 2380 2381 fTopView = NULL; 2382 fFocus = NULL; 2383 fLastMouseMovedView = NULL; 2384 fKeyMenuBar = NULL; 2385 fDefaultButton = NULL; 2386 2387 // Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message 2388 // get sent to the application, and not one of our handlers. 2389 // It is only installed for non-modal windows, though. 2390 fNoQuitShortcut = IsModal(); 2391 2392 if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) { 2393 // Modal windows default to non-closable, but you can add the shortcut manually, 2394 // if a different behaviour is wanted 2395 AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED)); 2396 } 2397 2398 AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL); 2399 AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL); 2400 AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL); 2401 AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL); 2402 2403 // We set the default pulse rate, but we don't start the pulse 2404 fPulseRate = 500000; 2405 fPulseRunner = NULL; 2406 2407 // TODO: see if you can use 'fViewsNeedPulse' 2408 2409 fIsFilePanel = false; 2410 2411 // TODO: see WHEN is this used! 2412 fMaskActivated = false; 2413 2414 // TODO: see WHEN is this used! 2415 fWaitingForMenu = false; 2416 fMenuSem = -1; 2417 2418 fMinimized = false; 2419 2420 fMaxZoomHeight = 32768.0; 2421 fMaxZoomWidth = 32768.0; 2422 fMinHeight = 0.0; 2423 fMinWidth = 0.0; 2424 fMaxHeight = 32768.0; 2425 fMaxWidth = 32768.0; 2426 2427 fLastViewToken = B_NULL_TOKEN; 2428 2429 // TODO: other initializations! 2430 2431 // Create the server-side window 2432 2433 port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, "w<app_server"); 2434 if (receivePort < B_OK) { 2435 // TODO: huh? 2436 debugger("Could not create BWindow's receive port, used for interacting with the app_server!"); 2437 delete this; 2438 return; 2439 } 2440 2441 STRACE(("BWindow::InitData(): contacting app_server...\n")); 2442 2443 // HERE we are in BApplication's thread, so for locking we use be_app variable 2444 // we'll lock the be_app to be sure we're the only one writing at BApplication's server port 2445 bool locked = false; 2446 if (!be_app->IsLocked()) { 2447 be_app->Lock(); 2448 locked = true; 2449 } 2450 2451 // let app_server know that a window has been created. 2452 fLink = new BPrivate::PortLink( 2453 BApplication::Private::ServerLink()->SenderPort(), receivePort); 2454 2455 if (bitmapToken < 0) { 2456 fLink->StartMessage(AS_CREATE_WINDOW); 2457 } else { 2458 fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW); 2459 fLink->Attach<int32>(bitmapToken); 2460 } 2461 2462 fLink->Attach<BRect>(fFrame); 2463 fLink->Attach<uint32>((uint32)fLook); 2464 fLink->Attach<uint32>((uint32)fFeel); 2465 fLink->Attach<uint32>(fFlags); 2466 fLink->Attach<uint32>(workspace); 2467 fLink->Attach<int32>(_get_object_token_(this)); 2468 fLink->Attach<port_id>(receivePort); 2469 fLink->Attach<port_id>(fMsgPort); 2470 fLink->AttachString(title); 2471 2472 port_id sendPort; 2473 int32 code; 2474 if (fLink->FlushWithReply(code) == B_OK 2475 && code == B_OK 2476 && fLink->Read<port_id>(&sendPort) == B_OK) { 2477 // read the frame size and its limits that were really 2478 // enforced on the server side 2479 2480 fLink->Read<BRect>(&fFrame); 2481 fLink->Read<float>(&fMinWidth); 2482 fLink->Read<float>(&fMaxWidth); 2483 fLink->Read<float>(&fMinHeight); 2484 fLink->Read<float>(&fMaxHeight); 2485 2486 fMaxZoomWidth = fMaxWidth; 2487 fMaxZoomHeight = fMaxHeight; 2488 } else 2489 sendPort = -1; 2490 2491 // Redirect our link to the new window connection 2492 fLink->SetSenderPort(sendPort); 2493 2494 if (locked) 2495 be_app->Unlock(); 2496 2497 STRACE(("Server says that our send port is %ld\n", sendPort)); 2498 STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False")); 2499 2500 _CreateTopView(); 2501 } 2502 2503 2504 //! Reads all pending messages from the window port and put them into the queue. 2505 void 2506 BWindow::_DequeueAll() 2507 { 2508 // Get message count from port 2509 int32 count = port_count(fMsgPort); 2510 2511 for (int32 i = 0; i < count; i++) { 2512 BMessage *message = MessageFromPort(0); 2513 if (message != NULL) 2514 fDirectTarget->Queue()->AddMessage(message); 2515 } 2516 } 2517 2518 2519 /*! This here is an almost complete code duplication to BLooper::task_looper() 2520 but with some important differences: 2521 a) it uses the _DetermineTarget() method to tell what the later target of 2522 a message will be, if no explicit target is supplied. 2523 b) it calls _UnpackMessage() and _SanitizeMessage() to duplicate the message 2524 to all of its intended targets, and to add all fields the target would 2525 expect in such a message. 2526 2527 This is important because the app_server sends all input events to the 2528 preferred handler, and expects them to be correctly distributed to their 2529 intended targets. 2530 */ 2531 void 2532 BWindow::task_looper() 2533 { 2534 STRACE(("info: BWindow::task_looper() started.\n")); 2535 2536 // Check that looper is locked (should be) 2537 AssertLocked(); 2538 Unlock(); 2539 2540 if (IsLocked()) 2541 debugger("window must not be locked!"); 2542 2543 while (!fTerminating) { 2544 // Did we get a message? 2545 BMessage* msg = MessageFromPort(); 2546 if (msg) 2547 _AddMessagePriv(msg); 2548 2549 // Get message count from port 2550 int32 msgCount = port_count(fMsgPort); 2551 for (int32 i = 0; i < msgCount; ++i) { 2552 // Read 'count' messages from port (so we will not block) 2553 // We use zero as our timeout since we know there is stuff there 2554 msg = MessageFromPort(0); 2555 // Add messages to queue 2556 if (msg) 2557 _AddMessagePriv(msg); 2558 } 2559 2560 bool dispatchNextMessage = true; 2561 while (!fTerminating && dispatchNextMessage) { 2562 // Get next message from queue (assign to fLastMessage) 2563 fLastMessage = fDirectTarget->Queue()->NextMessage(); 2564 2565 // Lock the looper 2566 if (!Lock()) 2567 break; 2568 2569 if (!fLastMessage) { 2570 // No more messages: Unlock the looper and terminate the 2571 // dispatch loop. 2572 dispatchNextMessage = false; 2573 } else { 2574 // Get the target handler 2575 BMessage::Private messagePrivate(fLastMessage); 2576 bool usePreferred = messagePrivate.UsePreferredTarget(); 2577 BHandler *handler = NULL; 2578 bool dropMessage = false; 2579 2580 if (usePreferred) { 2581 handler = PreferredHandler(); 2582 if (handler == NULL) 2583 handler = this; 2584 } else { 2585 gDefaultTokens.GetToken(messagePrivate.GetTarget(), 2586 B_HANDLER_TOKEN, (void **)&handler); 2587 2588 // if this handler doesn't belong to us, we drop the message 2589 if (handler != NULL && handler->Looper() != this) { 2590 dropMessage = true; 2591 handler = NULL; 2592 } 2593 } 2594 2595 if ((handler == NULL && !dropMessage) || usePreferred) 2596 handler = _DetermineTarget(fLastMessage, handler); 2597 2598 unpack_cookie cookie; 2599 while (_UnpackMessage(cookie, &fLastMessage, &handler, &usePreferred)) { 2600 // if there is no target handler, the message is dropped 2601 if (handler != NULL) { 2602 _SanitizeMessage(fLastMessage, handler, usePreferred); 2603 2604 // Is this a scripting message? 2605 if (fLastMessage->HasSpecifiers()) { 2606 int32 index = 0; 2607 // Make sure the current specifier is kosher 2608 if (fLastMessage->GetCurrentSpecifier(&index) == B_OK) 2609 handler = resolve_specifier(handler, fLastMessage); 2610 } 2611 2612 if (handler != NULL) 2613 handler = _TopLevelFilter(fLastMessage, handler); 2614 2615 if (handler != NULL) 2616 DispatchMessage(fLastMessage, handler); 2617 } 2618 2619 // Delete the current message 2620 delete fLastMessage; 2621 fLastMessage = NULL; 2622 } 2623 } 2624 2625 if (fTerminating) { 2626 // we leave the looper locked when we quit 2627 return; 2628 } 2629 2630 Unlock(); 2631 2632 // Are any messages on the port? 2633 if (port_count(fMsgPort) > 0) { 2634 // Do outer loop 2635 dispatchNextMessage = false; 2636 } 2637 } 2638 } 2639 } 2640 2641 2642 window_type 2643 BWindow::_ComposeType(window_look look, window_feel feel) const 2644 { 2645 switch (feel) { 2646 case B_NORMAL_WINDOW_FEEL: 2647 switch (look) { 2648 case B_TITLED_WINDOW_LOOK: 2649 return B_TITLED_WINDOW; 2650 2651 case B_DOCUMENT_WINDOW_LOOK: 2652 return B_DOCUMENT_WINDOW; 2653 2654 case B_BORDERED_WINDOW_LOOK: 2655 return B_BORDERED_WINDOW; 2656 2657 default: 2658 return B_UNTYPED_WINDOW; 2659 } 2660 break; 2661 2662 case B_MODAL_APP_WINDOW_FEEL: 2663 if (look == B_MODAL_WINDOW_LOOK) 2664 return B_MODAL_WINDOW; 2665 break; 2666 2667 case B_FLOATING_APP_WINDOW_FEEL: 2668 if (look == B_FLOATING_WINDOW_LOOK) 2669 return B_FLOATING_WINDOW; 2670 break; 2671 2672 default: 2673 return B_UNTYPED_WINDOW; 2674 } 2675 2676 return B_UNTYPED_WINDOW; 2677 } 2678 2679 2680 void 2681 BWindow::_DecomposeType(window_type type, window_look *_look, 2682 window_feel *_feel) const 2683 { 2684 switch (type) { 2685 case B_DOCUMENT_WINDOW: 2686 *_look = B_DOCUMENT_WINDOW_LOOK; 2687 *_feel = B_NORMAL_WINDOW_FEEL; 2688 break; 2689 2690 case B_MODAL_WINDOW: 2691 *_look = B_MODAL_WINDOW_LOOK; 2692 *_feel = B_MODAL_APP_WINDOW_FEEL; 2693 break; 2694 2695 case B_FLOATING_WINDOW: 2696 *_look = B_FLOATING_WINDOW_LOOK; 2697 *_feel = B_FLOATING_APP_WINDOW_FEEL; 2698 break; 2699 2700 case B_BORDERED_WINDOW: 2701 *_look = B_BORDERED_WINDOW_LOOK; 2702 *_feel = B_NORMAL_WINDOW_FEEL; 2703 break; 2704 2705 case B_TITLED_WINDOW: 2706 case B_UNTYPED_WINDOW: 2707 default: 2708 *_look = B_TITLED_WINDOW_LOOK; 2709 *_feel = B_NORMAL_WINDOW_FEEL; 2710 break; 2711 } 2712 } 2713 2714 2715 void 2716 BWindow::_CreateTopView() 2717 { 2718 STRACE(("_CreateTopView(): enter\n")); 2719 2720 BRect frame = fFrame.OffsetToCopy(B_ORIGIN); 2721 fTopView = new BView(frame, "fTopView", 2722 B_FOLLOW_ALL, B_WILL_DRAW); 2723 fTopView->fTopLevelView = true; 2724 2725 //inhibit check_lock() 2726 fLastViewToken = _get_object_token_(fTopView); 2727 2728 // set fTopView's owner, add it to window's eligible handler list 2729 // and also set its next handler to be this window. 2730 2731 STRACE(("Calling setowner fTopView = %p this = %p.\n", 2732 fTopView, this)); 2733 2734 fTopView->_SetOwner(this); 2735 2736 // we can't use AddChild() because this is the top view 2737 fTopView->_CreateSelf(); 2738 2739 STRACE(("BuildTopView ended\n")); 2740 } 2741 2742 2743 /*! 2744 Resizes the top view to match the window size. This will also 2745 adapt the size of all its child views as needed. 2746 This method has to be called whenever the frame of the window 2747 changes. 2748 */ 2749 void 2750 BWindow::_AdoptResize() 2751 { 2752 // Resize views according to their resize modes - this 2753 // saves us some server communication, as the server 2754 // does the same with our views on its side. 2755 2756 int32 deltaWidth = (int32)(fFrame.Width() - fTopView->Bounds().Width()); 2757 int32 deltaHeight = (int32)(fFrame.Height() - fTopView->Bounds().Height()); 2758 if (deltaWidth == 0 && deltaHeight == 0) 2759 return; 2760 2761 fTopView->_ResizeBy(deltaWidth, deltaHeight); 2762 } 2763 2764 2765 void 2766 BWindow::_SetFocus(BView *focusView, bool notifyInputServer) 2767 { 2768 if (fFocus == focusView) 2769 return; 2770 2771 // we notify the input server if we are passing focus 2772 // from a view which has the B_INPUT_METHOD_AWARE to a one 2773 // which does not, or vice-versa 2774 if (notifyInputServer) { 2775 bool inputMethodAware = false; 2776 if (focusView) 2777 inputMethodAware = focusView->Flags() & B_INPUT_METHOD_AWARE; 2778 BMessage msg(inputMethodAware ? IS_FOCUS_IM_AWARE_VIEW : IS_UNFOCUS_IM_AWARE_VIEW); 2779 BMessenger messenger(focusView); 2780 BMessage reply; 2781 if (focusView) 2782 msg.AddMessenger("view", messenger); 2783 _control_input_server_(&msg, &reply); 2784 } 2785 2786 fFocus = focusView; 2787 SetPreferredHandler(focusView); 2788 } 2789 2790 2791 /*! 2792 \brief Determines the target of a message received for the 2793 focus view. 2794 */ 2795 BHandler * 2796 BWindow::_DetermineTarget(BMessage *message, BHandler *target) 2797 { 2798 if (target == NULL) 2799 target = this; 2800 2801 switch (message->what) { 2802 case B_KEY_DOWN: 2803 case B_KEY_UP: 2804 { 2805 // if we have a default button, it might want to hear 2806 // about pressing the <enter> key 2807 int32 rawChar; 2808 if (DefaultButton() != NULL 2809 && message->FindInt32("raw_char", &rawChar) == B_OK 2810 && rawChar == B_ENTER) 2811 return DefaultButton(); 2812 2813 // supposed to fall through 2814 } 2815 case B_UNMAPPED_KEY_DOWN: 2816 case B_UNMAPPED_KEY_UP: 2817 case B_MODIFIERS_CHANGED: 2818 // these messages should be dispatched by the focus view 2819 if (CurrentFocus() != NULL) 2820 return CurrentFocus(); 2821 break; 2822 2823 case B_MOUSE_DOWN: 2824 case B_MOUSE_UP: 2825 case B_MOUSE_MOVED: 2826 case B_MOUSE_WHEEL_CHANGED: 2827 // is there a token of the view that is currently under the mouse? 2828 int32 token; 2829 if (message->FindInt32("_view_token", &token) == B_OK) { 2830 BView* view = _FindView(token); 2831 if (view != NULL) 2832 return view; 2833 } 2834 2835 // if there is no valid token in the message, we try our 2836 // luck with the last target, if available 2837 if (fLastMouseMovedView != NULL) 2838 return fLastMouseMovedView; 2839 break; 2840 2841 case B_PULSE: 2842 case B_QUIT_REQUESTED: 2843 // TODO: test wether R5 will let BView dispatch these messages 2844 return this; 2845 2846 case _MESSAGE_DROPPED_: 2847 if (fLastMouseMovedView != NULL) 2848 return fLastMouseMovedView; 2849 break; 2850 2851 default: 2852 break; 2853 } 2854 2855 return target; 2856 } 2857 2858 2859 /*! 2860 \brief Distributes the message to its intended targets. This is done for 2861 all messages that should go to the preferred handler. 2862 2863 Returns \c true in case the message should still be dispatched 2864 */ 2865 bool 2866 BWindow::_UnpackMessage(unpack_cookie& cookie, BMessage** _message, BHandler** _target, 2867 bool* _usePreferred) 2868 { 2869 if (cookie.message == NULL) 2870 return false; 2871 2872 if (cookie.index == 0 && !cookie.tokens_scanned) { 2873 // We were called the first time for this message 2874 2875 if (!*_usePreferred) { 2876 // only consider messages targeted at the preferred handler 2877 cookie.message = NULL; 2878 return true; 2879 } 2880 2881 // initialize our cookie 2882 cookie.message = *_message; 2883 cookie.focus = *_target; 2884 2885 if (cookie.focus != NULL) 2886 cookie.focus_token = _get_object_token_(*_target); 2887 2888 if (fLastMouseMovedView != NULL && cookie.message->what == B_MOUSE_MOVED) 2889 cookie.last_view_token = _get_object_token_(fLastMouseMovedView); 2890 2891 *_usePreferred = false; 2892 } 2893 2894 _DequeueAll(); 2895 2896 // distribute the message to all targets specified in the 2897 // message directly (but not to the focus view) 2898 2899 for (int32 token; !cookie.tokens_scanned 2900 && cookie.message->FindInt32("_token", cookie.index, &token) == B_OK; 2901 cookie.index++) { 2902 // focus view is preferred and should get its message directly 2903 if (token == cookie.focus_token) { 2904 cookie.found_focus = true; 2905 continue; 2906 } 2907 if (token == cookie.last_view_token) 2908 continue; 2909 2910 BView* target = _FindView(token); 2911 if (target == NULL) 2912 continue; 2913 2914 *_message = new BMessage(*cookie.message); 2915 *_target = target; 2916 cookie.index++; 2917 return true; 2918 } 2919 2920 cookie.tokens_scanned = true; 2921 2922 // if there is a last mouse moved view, and the new focus is 2923 // different, the previous view wants to get its B_EXITED_VIEW 2924 // message 2925 if (cookie.last_view_token != B_NULL_TOKEN && fLastMouseMovedView != NULL 2926 && fLastMouseMovedView != cookie.focus) { 2927 *_message = new BMessage(*cookie.message); 2928 *_target = fLastMouseMovedView; 2929 cookie.last_view_token = B_NULL_TOKEN; 2930 return true; 2931 } 2932 2933 bool dispatchToFocus = true; 2934 2935 // check if the focus token is still valid (could have been removed in the mean time) 2936 BHandler* handler; 2937 if (gDefaultTokens.GetToken(cookie.focus_token, B_HANDLER_TOKEN, (void**)&handler) != B_OK 2938 || handler->Looper() != this) 2939 dispatchToFocus = false; 2940 2941 if (dispatchToFocus && cookie.index > 0) { 2942 // should this message still be dispatched by the focus view? 2943 bool feedFocus; 2944 if (!cookie.found_focus 2945 && (cookie.message->FindBool("_feed_focus", &feedFocus) != B_OK 2946 || feedFocus == false)) 2947 dispatchToFocus = false; 2948 } 2949 2950 if (!dispatchToFocus) { 2951 delete cookie.message; 2952 cookie.message = NULL; 2953 return false; 2954 } 2955 2956 *_message = cookie.message; 2957 *_target = cookie.focus; 2958 *_usePreferred = true; 2959 cookie.message = NULL; 2960 return true; 2961 } 2962 2963 2964 /*! 2965 Some messages don't get to the window in a shape an application should see. 2966 This method is supposed to give a message the last grinding before 2967 it's acceptable for the receiving application. 2968 */ 2969 void 2970 BWindow::_SanitizeMessage(BMessage* message, BHandler* target, bool usePreferred) 2971 { 2972 if (target == NULL) 2973 return; 2974 2975 switch (message->what) { 2976 case B_MOUSE_MOVED: 2977 case B_MOUSE_UP: 2978 case B_MOUSE_DOWN: 2979 { 2980 BPoint where; 2981 if (message->FindPoint("screen_where", &where) != B_OK) 2982 break; 2983 2984 // add local window coordinates 2985 message->AddPoint("where", ConvertFromScreen(where)); 2986 2987 BView* view = dynamic_cast<BView*>(target); 2988 if (view != NULL) { 2989 // add local view coordinates 2990 message->AddPoint("be:view_where", view->ConvertFromScreen(where)); 2991 2992 if (message->what == B_MOUSE_MOVED) { 2993 // is there a token of the view that is currently under the mouse? 2994 BView* viewUnderMouse = NULL; 2995 int32 token; 2996 if (message->FindInt32("_view_token", &token) == B_OK) 2997 viewUnderMouse = _FindView(token); 2998 2999 // add transit information 3000 int32 transit; 3001 if (viewUnderMouse == view) { 3002 // the mouse is over the target view 3003 if (fLastMouseMovedView != view) 3004 transit = B_ENTERED_VIEW; 3005 else 3006 transit = B_INSIDE_VIEW; 3007 } else { 3008 // the mouse is not over the target view 3009 if (view == fLastMouseMovedView) 3010 transit = B_EXITED_VIEW; 3011 else 3012 transit = B_OUTSIDE_VIEW; 3013 } 3014 3015 message->AddInt32("be:transit", transit); 3016 3017 if (usePreferred || viewUnderMouse == NULL) 3018 fLastMouseMovedView = viewUnderMouse; 3019 } 3020 } 3021 break; 3022 } 3023 3024 case _MESSAGE_DROPPED_: 3025 { 3026 uint32 originalWhat; 3027 if (message->FindInt32("_original_what", (int32*)&originalWhat) == B_OK) { 3028 message->what = originalWhat; 3029 message->RemoveName("_original_what"); 3030 } 3031 break; 3032 } 3033 } 3034 } 3035 3036 3037 /*! 3038 This is called by BView::GetMouse() when a B_MOUSE_MOVED message 3039 is removed from the queue. 3040 It allows the window to update the last mouse moved view, and 3041 let it decide if this message should be kept. It will also remove 3042 the message from the queue. 3043 You need to hold the message queue lock when calling this method! 3044 3045 \return true if this message can be used to get the mouse data from, 3046 \return false if this is not meant for the public. 3047 */ 3048 bool 3049 BWindow::_StealMouseMessage(BMessage* message, bool& deleteMessage) 3050 { 3051 BMessage::Private messagePrivate(message); 3052 if (!messagePrivate.UsePreferredTarget()) { 3053 // this message is targeted at a specific handler, so we should 3054 // not steal it 3055 return false; 3056 } 3057 3058 int32 token; 3059 if (message->FindInt32("_token", 0, &token) == B_OK) { 3060 // This message has other targets, so we can't remove it; 3061 // just prevent it from being sent to the preferred handler 3062 // again (if it should have gotten it at all). 3063 bool feedFocus; 3064 if (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus) 3065 return false; 3066 3067 message->RemoveName("_feed_focus"); 3068 deleteMessage = false; 3069 } else { 3070 // The message is only thought for the preferred handler, so we 3071 // can just remove it. 3072 MessageQueue()->RemoveMessage(message); 3073 deleteMessage = true; 3074 3075 if (message->what == B_MOUSE_MOVED) { 3076 // We need to update the last mouse moved view, as this message 3077 // won't make it to _SanitizeMessage() anymore 3078 BView* viewUnderMouse = NULL; 3079 int32 token; 3080 if (message->FindInt32("_view_token", &token) == B_OK) 3081 viewUnderMouse = _FindView(token); 3082 3083 fLastMouseMovedView = viewUnderMouse; 3084 } 3085 } 3086 3087 return true; 3088 } 3089 3090 3091 /*! 3092 Handles keyboard input before it gets forwarded to the target handler. 3093 This includes shortcut evaluation, keyboard navigation, etc. 3094 3095 \return handled if true, the event was already handled, and will not 3096 be forwarded to the target handler. 3097 3098 TODO: must also convert the incoming key to the font encoding of the target 3099 */ 3100 bool 3101 BWindow::_HandleKeyDown(BMessage* event) 3102 { 3103 const char *string = NULL; 3104 if (event->FindString("bytes", &string) != B_OK) 3105 return false; 3106 3107 char key = string[0]; 3108 3109 uint32 modifiers; 3110 if (event->FindInt32("modifiers", (int32*)&modifiers) != B_OK) 3111 modifiers = 0; 3112 3113 // handle BMenuBar key 3114 if (key == B_ESCAPE && (modifiers & B_COMMAND_KEY) != 0 3115 && fKeyMenuBar) { 3116 // TODO: ask Marc about 'fWaitingForMenu' member! 3117 3118 // fWaitingForMenu = true; 3119 fKeyMenuBar->StartMenuBar(0, true, false, NULL); 3120 return true; 3121 } 3122 3123 // Keyboard navigation through views 3124 // (B_OPTION_KEY makes BTextViews and friends navigable, even in editing mode) 3125 if (key == B_TAB && (modifiers & (B_COMMAND_KEY | B_OPTION_KEY)) != 0) { 3126 _KeyboardNavigation(); 3127 return true; 3128 } 3129 3130 // Deskbar's Switcher 3131 if (key == B_TAB && (modifiers & B_CONTROL_KEY) != 0) { 3132 BMessenger deskbar(kDeskbarSignature); 3133 int32 rawKey; 3134 if (event->FindInt32("key", &rawKey) == B_OK 3135 && !event->HasInt32("be:key_repeat") 3136 && deskbar.IsValid()) { 3137 // only send the first key press, no repeats 3138 BMessage message('TASK'); 3139 message.AddInt32("key", rawKey); 3140 message.AddInt32("modifiers", modifiers); 3141 message.AddInt64("when", system_time()); 3142 message.AddInt32("team", Team()); 3143 deskbar.SendMessage(&message); 3144 } 3145 return true; 3146 } 3147 3148 // Optionally close window when the escape key is pressed 3149 if (key == B_ESCAPE && (Flags() & B_CLOSE_ON_ESCAPE) != 0) { 3150 BMessage message(B_QUIT_REQUESTED); 3151 message.AddBool("shortcut", true); 3152 3153 PostMessage(&message); 3154 return true; 3155 } 3156 3157 // Handle shortcuts 3158 if ((modifiers & B_COMMAND_KEY) != 0) { 3159 // Command+q has been pressed, so, we will quit 3160 // the shortcut mechanism doesn't allow handlers outside the window 3161 if (!fNoQuitShortcut && (key == 'Q' || key == 'q')) { 3162 BMessage message(B_QUIT_REQUESTED); 3163 message.AddBool("shortcut", true); 3164 3165 be_app->PostMessage(&message); 3166 return true; 3167 } 3168 3169 MenusBeginning(); 3170 3171 Shortcut* shortcut = _FindShortcut(key, modifiers); 3172 if (shortcut != NULL) { 3173 // TODO: would be nice to move this functionality to 3174 // a Shortcut::Invoke() method - but since BMenu::InvokeItem() 3175 // (and BMenuItem::Invoke()) are private, I didn't want 3176 // to mess with them (BMenuItem::Invoke() is public in 3177 // Dano/Zeta, though, maybe we should just follow their 3178 // example) 3179 if (shortcut->MenuItem() != NULL) { 3180 BMenu* menu = shortcut->MenuItem()->Menu(); 3181 if (menu != NULL) 3182 menu->InvokeItem(shortcut->MenuItem(), true); 3183 } else { 3184 BHandler* target = shortcut->Target(); 3185 if (target == NULL) 3186 target = CurrentFocus(); 3187 3188 if (shortcut->Message() != NULL) { 3189 BMessage message(*shortcut->Message()); 3190 3191 if (message.ReplaceInt64("when", system_time()) != B_OK) 3192 message.AddInt64("when", system_time()); 3193 if (message.ReplaceBool("shortcut", true) != B_OK) 3194 message.AddBool("shortcut", true); 3195 3196 PostMessage(&message, target); 3197 } 3198 } 3199 } 3200 3201 MenusEnded(); 3202 3203 // we always eat the event if the command key was pressed 3204 return true; 3205 } 3206 3207 // TODO: convert keys to the encoding of the target view 3208 3209 return false; 3210 } 3211 3212 3213 void 3214 BWindow::_KeyboardNavigation() 3215 { 3216 BMessage *message = CurrentMessage(); 3217 if (message == NULL) 3218 return; 3219 3220 const char *bytes; 3221 uint32 modifiers; 3222 if (message->FindString("bytes", &bytes) != B_OK 3223 || bytes[0] != B_TAB) 3224 return; 3225 3226 message->FindInt32("modifiers", (int32*)&modifiers); 3227 3228 BView *nextFocus; 3229 int32 jumpGroups = modifiers & B_CONTROL_KEY ? B_NAVIGABLE_JUMP : B_NAVIGABLE; 3230 if (modifiers & B_SHIFT_KEY) 3231 nextFocus = _FindPreviousNavigable(fFocus, jumpGroups); 3232 else 3233 nextFocus = _FindNextNavigable(fFocus, jumpGroups); 3234 3235 if (nextFocus && nextFocus != fFocus) { 3236 nextFocus->MakeFocus(true); 3237 } 3238 } 3239 3240 3241 BMessage * 3242 BWindow::ConvertToMessage(void *raw, int32 code) 3243 { 3244 return BLooper::ConvertToMessage(raw, code); 3245 } 3246 3247 3248 BWindow::Shortcut * 3249 BWindow::_FindShortcut(uint32 key, uint32 modifiers) 3250 { 3251 int32 count = fShortcuts.CountItems(); 3252 3253 key = Shortcut::PrepareKey(key); 3254 modifiers = Shortcut::PrepareModifiers(modifiers); 3255 3256 for (int32 index = 0; index < count; index++) { 3257 Shortcut *shortcut = (Shortcut *)fShortcuts.ItemAt(index); 3258 3259 if (shortcut->Matches(key, modifiers)) 3260 return shortcut; 3261 } 3262 3263 return NULL; 3264 } 3265 3266 3267 BView * 3268 BWindow::_FindView(int32 token) 3269 { 3270 BHandler* handler; 3271 if (gDefaultTokens.GetToken(token, B_HANDLER_TOKEN, (void**)&handler) != B_OK) 3272 return NULL; 3273 3274 // the view must belong to us in order to be found by this method 3275 BView* view = dynamic_cast<BView*>(handler); 3276 if (view != NULL && view->Window() == this) 3277 return view; 3278 3279 return NULL; 3280 } 3281 3282 3283 BView* 3284 BWindow::_FindView(BView* view, BPoint point) const 3285 { 3286 // point is assumed to be already in view's coordinates 3287 // TODO: since BView::Bounds() potentially queries the app_server anyway, 3288 // we could just let the app_server answer this query directly. 3289 if (view->Bounds().Contains(point) && !view->fFirstChild) 3290 return view; 3291 3292 BView* child = view->fFirstChild; 3293 3294 while (child != NULL) { 3295 BPoint childPoint = point - child->LeftTop(); 3296 if ((view = _FindView(child, childPoint)) != NULL) 3297 return view; 3298 3299 child = child->fNextSibling; 3300 } 3301 3302 return NULL; 3303 } 3304 3305 3306 BView* 3307 BWindow::_FindNextNavigable(BView* focus, uint32 flags) 3308 { 3309 if (focus == NULL) 3310 focus = fTopView; 3311 3312 BView* nextFocus = focus; 3313 3314 // Search the tree for views that accept focus (depth search) 3315 while (true) { 3316 if (nextFocus->fFirstChild) 3317 nextFocus = nextFocus->fFirstChild; 3318 else if (nextFocus->fNextSibling) 3319 nextFocus = nextFocus->fNextSibling; 3320 else { 3321 // go to the nearest parent with a next sibling 3322 while (!nextFocus->fNextSibling && nextFocus->fParent) { 3323 nextFocus = nextFocus->fParent; 3324 } 3325 3326 if (nextFocus == fTopView) { 3327 // if we started with the top view, we traversed the whole tree already 3328 if (nextFocus == focus) 3329 return NULL; 3330 3331 nextFocus = nextFocus->fFirstChild; 3332 } else 3333 nextFocus = nextFocus->fNextSibling; 3334 } 3335 3336 if (nextFocus == focus || nextFocus == NULL) { 3337 // When we get here it means that the hole tree has been 3338 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set! 3339 return NULL; 3340 } 3341 3342 if (!nextFocus->IsHidden() && (nextFocus->Flags() & flags) != 0) 3343 return nextFocus; 3344 } 3345 } 3346 3347 3348 BView * 3349 BWindow::_FindPreviousNavigable(BView* focus, uint32 flags) 3350 { 3351 if (focus == NULL) 3352 focus = fTopView; 3353 3354 BView* previousFocus = focus; 3355 3356 // Search the tree for the previous view that accept focus 3357 while (true) { 3358 if (previousFocus->fPreviousSibling) { 3359 // find the last child in the previous sibling 3360 previousFocus = _LastViewChild(previousFocus->fPreviousSibling); 3361 } else { 3362 previousFocus = previousFocus->fParent; 3363 if (previousFocus == fTopView) 3364 previousFocus = _LastViewChild(fTopView); 3365 } 3366 3367 if (previousFocus == focus || previousFocus == NULL) { 3368 // When we get here it means that the hole tree has been 3369 // searched and there is no view with B_NAVIGABLE(_JUMP) flag set! 3370 return NULL; 3371 } 3372 3373 if (!previousFocus->IsHidden() && (previousFocus->Flags() & flags) != 0) 3374 return previousFocus; 3375 } 3376 } 3377 3378 3379 /*! 3380 Returns the last child in a view hierarchy. 3381 Needed only by _FindPreviousNavigable(). 3382 */ 3383 BView * 3384 BWindow::_LastViewChild(BView *parent) 3385 { 3386 while (true) { 3387 BView *last = parent->fFirstChild; 3388 if (last == NULL) 3389 return parent; 3390 3391 while (last->fNextSibling) { 3392 last = last->fNextSibling; 3393 } 3394 3395 parent = last; 3396 } 3397 } 3398 3399 3400 void 3401 BWindow::SetIsFilePanel(bool isFilePanel) 3402 { 3403 fIsFilePanel = isFilePanel; 3404 } 3405 3406 3407 bool 3408 BWindow::IsFilePanel() const 3409 { 3410 return fIsFilePanel; 3411 } 3412 3413 3414 //------------------------------------------------------------------------------ 3415 // Virtual reserved Functions 3416 3417 void BWindow::_ReservedWindow1() {} 3418 void BWindow::_ReservedWindow2() {} 3419 void BWindow::_ReservedWindow3() {} 3420 void BWindow::_ReservedWindow4() {} 3421 void BWindow::_ReservedWindow5() {} 3422 void BWindow::_ReservedWindow6() {} 3423 void BWindow::_ReservedWindow7() {} 3424 void BWindow::_ReservedWindow8() {} 3425 3426 void 3427 BWindow::PrintToStream() const 3428 { 3429 printf("BWindow '%s' data:\ 3430 Title = %s\ 3431 Token = %ld\ 3432 InTransaction = %s\ 3433 Active = %s\ 3434 fShowLevel = %d\ 3435 Flags = %lx\ 3436 send_port = %ld\ 3437 receive_port = %ld\ 3438 fTopView name = %s\ 3439 focus view name = %s\ 3440 lastMouseMoved = %s\ 3441 fLink = %p\ 3442 KeyMenuBar name = %s\ 3443 DefaultButton = %s\ 3444 # of shortcuts = %ld", 3445 Name(), fTitle, 3446 _get_object_token_(this), 3447 fInTransaction == true ? "yes" : "no", 3448 fActive == true ? "yes" : "no", 3449 fShowLevel, 3450 fFlags, 3451 fLink->SenderPort(), 3452 fLink->ReceiverPort(), 3453 fTopView != NULL ? fTopView->Name() : "NULL", 3454 fFocus != NULL ? fFocus->Name() : "NULL", 3455 fLastMouseMovedView != NULL ? fLastMouseMovedView->Name() : "NULL", 3456 fLink, 3457 fKeyMenuBar != NULL ? fKeyMenuBar->Name() : "NULL", 3458 fDefaultButton != NULL ? fDefaultButton->Name() : "NULL", 3459 fShortcuts.CountItems()); 3460 /* 3461 for( int32 i=0; i<accelList.CountItems(); i++){ 3462 _BCmdKey *key = (_BCmdKey*)accelList.ItemAt(i); 3463 printf("\tShortCut %ld: char %s\n\t\t message: \n", i, (key->key > 127)?"ASCII":"UNICODE"); 3464 key->message->PrintToStream(); 3465 } 3466 */ 3467 printf("\ 3468 topViewToken = %ld\ 3469 isFilePanel = %s\ 3470 MaskActivated = %s\ 3471 pulseRate = %lld\ 3472 waitingForMenu = %s\ 3473 minimized = %s\ 3474 Menu semaphore = %ld\ 3475 maxZoomHeight = %f\ 3476 maxZoomWidth = %f\ 3477 minWindHeight = %f\ 3478 minWindWidth = %f\ 3479 maxWindHeight = %f\ 3480 maxWindWidth = %f\ 3481 frame = ( %f, %f, %f, %f )\ 3482 look = %d\ 3483 feel = %d\ 3484 lastViewToken = %ld\ 3485 pulseRunner = %s\n", 3486 fTopViewToken, 3487 fIsFilePanel==true?"Yes":"No", 3488 fMaskActivated==true?"Yes":"No", 3489 fPulseRate, 3490 fWaitingForMenu==true?"Yes":"No", 3491 fMinimized==true?"Yes":"No", 3492 fMenuSem, 3493 fMaxZoomHeight, 3494 fMaxZoomWidth, 3495 fMinHeight, 3496 fMinWidth, 3497 fMaxHeight, 3498 fMaxWidth, 3499 fFrame.left, fFrame.top, fFrame.right, fFrame.bottom, 3500 (int16)fLook, 3501 (int16)fFeel, 3502 fLastViewToken, 3503 fPulseRunner != NULL ? "In place" : "NULL"); 3504 } 3505 3506 /* 3507 TODO list: 3508 *) test arguments for SetWindowAligment 3509 *) call hook functions: MenusBeginning, MenusEnded. Add menu activation code. 3510 */ 3511 3512