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