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