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