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