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