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