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