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