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