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