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