1 /* 2 * Copyright 2001-2012, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Rene Gollent (rene@gollent.com) 8 * Alexandre Deckner (alex@zappotek.com) 9 */ 10 11 12 //! BDragger represents a replicant "handle". 13 14 15 #include <pthread.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 19 #include <Alert.h> 20 #include <Beep.h> 21 #include <Bitmap.h> 22 #include <Dragger.h> 23 #include <MenuItem.h> 24 #include <Message.h> 25 #include <PopUpMenu.h> 26 #include <Shelf.h> 27 #include <SystemCatalog.h> 28 #include <Window.h> 29 30 #include <AutoLocker.h> 31 32 #include <AppServerLink.h> 33 #include <DragTrackingFilter.h> 34 #include <binary_compatibility/Interface.h> 35 #include <ServerProtocol.h> 36 #include <ViewPrivate.h> 37 38 #include "ZombieReplicantView.h" 39 40 using BPrivate::gSystemCatalog; 41 42 #undef B_TRANSLATION_CONTEXT 43 #define B_TRANSLATION_CONTEXT "Dragger" 44 45 #undef B_TRANSLATE 46 #define B_TRANSLATE(str) \ 47 gSystemCatalog.GetString(B_TRANSLATE_MARK(str), "Dragger") 48 49 50 static const uint32 kMsgDragStarted = 'Drgs'; 51 52 static const unsigned char kHandBitmap[] = { 53 255, 255, 0, 0, 0, 255, 255, 255, 54 255, 255, 0, 131, 131, 0, 255, 255, 55 0, 0, 0, 0, 131, 131, 0, 0, 56 0, 131, 0, 0, 131, 131, 0, 0, 57 0, 131, 131, 131, 131, 131, 0, 0, 58 255, 0, 131, 131, 131, 131, 0, 0, 59 255, 255, 0, 0, 0, 0, 0, 0, 60 255, 255, 255, 255, 255, 255, 0, 0 61 }; 62 63 64 namespace { 65 66 struct DraggerManager { 67 bool visible; 68 bool visibleInitialized; 69 BList list; 70 71 DraggerManager() 72 : 73 visible(false), 74 visibleInitialized(false), 75 fLock("BDragger static") 76 { 77 } 78 79 bool Lock() 80 { 81 return fLock.Lock(); 82 } 83 84 void Unlock() 85 { 86 fLock.Unlock(); 87 } 88 89 static DraggerManager* Default() 90 { 91 if (sDefaultInstance == NULL) 92 pthread_once(&sDefaultInitOnce, &_InitSingleton); 93 94 return sDefaultInstance; 95 } 96 97 private: 98 static void _InitSingleton() 99 { 100 sDefaultInstance = new DraggerManager; 101 } 102 103 private: 104 BLocker fLock; 105 106 static pthread_once_t sDefaultInitOnce; 107 static DraggerManager* sDefaultInstance; 108 }; 109 110 pthread_once_t DraggerManager::sDefaultInitOnce = PTHREAD_ONCE_INIT; 111 DraggerManager* DraggerManager::sDefaultInstance = NULL; 112 113 } // unnamed namespace 114 115 116 BDragger::BDragger(BRect frame, BView* target, uint32 resizingMode, 117 uint32 flags) 118 : 119 BView(frame, "_dragger_", resizingMode, flags), 120 fTarget(target), 121 fRelation(TARGET_UNKNOWN), 122 fShelf(NULL), 123 fTransition(false), 124 fIsZombie(false), 125 fErrCount(0), 126 fPopUpIsCustom(false), 127 fPopUp(NULL) 128 { 129 _InitData(); 130 } 131 132 133 BDragger::BDragger(BView* target, uint32 flags) 134 : 135 BView("_dragger_", flags), 136 fTarget(target), 137 fRelation(TARGET_UNKNOWN), 138 fShelf(NULL), 139 fTransition(false), 140 fIsZombie(false), 141 fErrCount(0), 142 fPopUpIsCustom(false), 143 fPopUp(NULL) 144 { 145 _InitData(); 146 } 147 148 149 BDragger::BDragger(BMessage* data) 150 : 151 BView(data), 152 fTarget(NULL), 153 fRelation(TARGET_UNKNOWN), 154 fShelf(NULL), 155 fTransition(false), 156 fIsZombie(false), 157 fErrCount(0), 158 fPopUpIsCustom(false), 159 fPopUp(NULL) 160 { 161 data->FindInt32("_rel", (int32*)&fRelation); 162 163 _InitData(); 164 165 BMessage popupMsg; 166 if (data->FindMessage("_popup", &popupMsg) == B_OK) { 167 BArchivable* archivable = instantiate_object(&popupMsg); 168 169 if (archivable) { 170 fPopUp = dynamic_cast<BPopUpMenu*>(archivable); 171 fPopUpIsCustom = true; 172 } 173 } 174 } 175 176 177 BDragger::~BDragger() 178 { 179 delete fPopUp; 180 delete fBitmap; 181 } 182 183 184 BArchivable * 185 BDragger::Instantiate(BMessage* data) 186 { 187 if (validate_instantiation(data, "BDragger")) 188 return new BDragger(data); 189 return NULL; 190 } 191 192 193 status_t 194 BDragger::Archive(BMessage* data, bool deep) const 195 { 196 status_t ret = BView::Archive(data, deep); 197 if (ret != B_OK) 198 return ret; 199 200 BMessage popupMsg; 201 202 if (fPopUp != NULL && fPopUpIsCustom) { 203 bool windowLocked = fPopUp->Window()->Lock(); 204 205 ret = fPopUp->Archive(&popupMsg, deep); 206 207 if (windowLocked) { 208 fPopUp->Window()->Unlock(); 209 // TODO: Investigate, in some (rare) occasions the menu window 210 // has already been unlocked 211 } 212 213 if (ret == B_OK) 214 ret = data->AddMessage("_popup", &popupMsg); 215 } 216 217 if (ret == B_OK) 218 ret = data->AddInt32("_rel", fRelation); 219 return ret; 220 } 221 222 223 void 224 BDragger::AttachedToWindow() 225 { 226 if (fIsZombie) { 227 SetLowColor(kZombieColor); 228 SetViewColor(kZombieColor); 229 } else { 230 SetLowColor(B_TRANSPARENT_COLOR); 231 SetViewColor(B_TRANSPARENT_COLOR); 232 } 233 234 _DetermineRelationship(); 235 _AddToList(); 236 237 AddFilter(new DragTrackingFilter(this, kMsgDragStarted)); 238 } 239 240 241 void 242 BDragger::DetachedFromWindow() 243 { 244 _RemoveFromList(); 245 } 246 247 248 void 249 BDragger::Draw(BRect update) 250 { 251 BRect bounds(Bounds()); 252 253 if (AreDraggersDrawn() && (fShelf == NULL || fShelf->AllowsDragging())) { 254 if (Parent() != NULL && (Parent()->Flags() & B_DRAW_ON_CHILDREN) == 0) { 255 uint32 flags = Parent()->Flags(); 256 Parent()->SetFlags(flags | B_DRAW_ON_CHILDREN); 257 SetHighColor(Parent()->ViewColor()); 258 FillRect(Bounds()); 259 Parent()->Draw(Frame() & ConvertToParent(update)); 260 Parent()->Flush(); 261 Parent()->SetFlags(flags); 262 } 263 264 BPoint where = bounds.RightBottom() - BPoint(fBitmap->Bounds().Width(), 265 fBitmap->Bounds().Height()); 266 SetDrawingMode(B_OP_OVER); 267 DrawBitmap(fBitmap, where); 268 SetDrawingMode(B_OP_COPY); 269 270 if (fIsZombie) { 271 // TODO: should draw it differently ? 272 } 273 } else if (IsVisibilityChanging()) { 274 if (Parent() != NULL) { 275 if ((Parent()->Flags() & B_DRAW_ON_CHILDREN) == 0) { 276 uint32 flags = Parent()->Flags(); 277 Parent()->SetFlags(flags | B_DRAW_ON_CHILDREN); 278 Parent()->Invalidate(Frame() & ConvertToParent(update)); 279 Parent()->SetFlags(flags); 280 } 281 } else { 282 SetHighColor(255, 255, 255); 283 FillRect(bounds); 284 } 285 } 286 } 287 288 289 void 290 BDragger::MouseDown(BPoint where) 291 { 292 if (fTarget == NULL || !AreDraggersDrawn()) 293 return; 294 295 uint32 buttons; 296 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 297 298 if (fShelf != NULL && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0) 299 _ShowPopUp(fTarget, where); 300 } 301 302 303 void 304 BDragger::MouseUp(BPoint point) 305 { 306 BView::MouseUp(point); 307 } 308 309 310 void 311 BDragger::MouseMoved(BPoint point, uint32 code, const BMessage* msg) 312 { 313 BView::MouseMoved(point, code, msg); 314 } 315 316 317 void 318 BDragger::MessageReceived(BMessage* msg) 319 { 320 switch (msg->what) { 321 case B_TRASH_TARGET: 322 if (fShelf != NULL) 323 Window()->PostMessage(kDeleteReplicant, fTarget, NULL); 324 else { 325 BAlert* alert = new BAlert(B_TRANSLATE("Warning"), 326 B_TRANSLATE("Can't delete this replicant from its original " 327 "application. Life goes on."), 328 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_WIDEST, 329 B_WARNING_ALERT); 330 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 331 alert->Go(NULL); 332 } 333 break; 334 335 case _SHOW_DRAG_HANDLES_: 336 // This code is used whenever the "are draggers drawn" option is 337 // changed. 338 if (fRelation == TARGET_IS_CHILD) { 339 fTransition = true; 340 Draw(Bounds()); 341 Flush(); 342 fTransition = false; 343 } else { 344 if ((fShelf != NULL && fShelf->AllowsDragging() 345 && AreDraggersDrawn()) 346 || AreDraggersDrawn()) { 347 Show(); 348 } else 349 Hide(); 350 } 351 break; 352 353 case kMsgDragStarted: 354 if (fTarget != NULL) { 355 BMessage archive(B_ARCHIVED_OBJECT); 356 357 if (fRelation == TARGET_IS_PARENT) 358 fTarget->Archive(&archive); 359 else if (fRelation == TARGET_IS_CHILD) 360 Archive(&archive); 361 else if (fTarget->Archive(&archive)) { 362 BMessage archivedSelf(B_ARCHIVED_OBJECT); 363 364 if (Archive(&archivedSelf)) 365 archive.AddMessage("__widget", &archivedSelf); 366 } 367 368 archive.AddInt32("be:actions", B_TRASH_TARGET); 369 BPoint offset; 370 drawing_mode mode; 371 BBitmap* bitmap = DragBitmap(&offset, &mode); 372 if (bitmap != NULL) 373 DragMessage(&archive, bitmap, mode, offset, this); 374 else { 375 DragMessage(&archive, ConvertFromScreen( 376 fTarget->ConvertToScreen(fTarget->Bounds())), this); 377 } 378 } 379 break; 380 381 default: 382 BView::MessageReceived(msg); 383 break; 384 } 385 } 386 387 388 void 389 BDragger::FrameMoved(BPoint newPosition) 390 { 391 BView::FrameMoved(newPosition); 392 } 393 394 395 void 396 BDragger::FrameResized(float newWidth, float newHeight) 397 { 398 BView::FrameResized(newWidth, newHeight); 399 } 400 401 402 status_t 403 BDragger::ShowAllDraggers() 404 { 405 BPrivate::AppServerLink link; 406 link.StartMessage(AS_SET_SHOW_ALL_DRAGGERS); 407 link.Attach<bool>(true); 408 409 status_t status = link.Flush(); 410 if (status == B_OK) { 411 DraggerManager* manager = DraggerManager::Default(); 412 AutoLocker<DraggerManager> locker(manager); 413 manager->visible = true; 414 manager->visibleInitialized = true; 415 } 416 417 return status; 418 } 419 420 421 status_t 422 BDragger::HideAllDraggers() 423 { 424 BPrivate::AppServerLink link; 425 link.StartMessage(AS_SET_SHOW_ALL_DRAGGERS); 426 link.Attach<bool>(false); 427 428 status_t status = link.Flush(); 429 if (status == B_OK) { 430 DraggerManager* manager = DraggerManager::Default(); 431 AutoLocker<DraggerManager> locker(manager); 432 manager->visible = false; 433 manager->visibleInitialized = true; 434 } 435 436 return status; 437 } 438 439 440 bool 441 BDragger::AreDraggersDrawn() 442 { 443 DraggerManager* manager = DraggerManager::Default(); 444 AutoLocker<DraggerManager> locker(manager); 445 446 if (!manager->visibleInitialized) { 447 BPrivate::AppServerLink link; 448 link.StartMessage(AS_GET_SHOW_ALL_DRAGGERS); 449 450 status_t status; 451 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 452 link.Read<bool>(&manager->visible); 453 manager->visibleInitialized = true; 454 } else 455 return false; 456 } 457 458 return manager->visible; 459 } 460 461 462 BHandler* 463 BDragger::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 464 int32 form, const char* property) 465 { 466 return BView::ResolveSpecifier(message, index, specifier, form, property); 467 } 468 469 470 status_t 471 BDragger::GetSupportedSuites(BMessage* data) 472 { 473 return BView::GetSupportedSuites(data); 474 } 475 476 477 status_t 478 BDragger::Perform(perform_code code, void* _data) 479 { 480 switch (code) { 481 case PERFORM_CODE_MIN_SIZE: 482 ((perform_data_min_size*)_data)->return_value 483 = BDragger::MinSize(); 484 return B_OK; 485 case PERFORM_CODE_MAX_SIZE: 486 ((perform_data_max_size*)_data)->return_value 487 = BDragger::MaxSize(); 488 return B_OK; 489 case PERFORM_CODE_PREFERRED_SIZE: 490 ((perform_data_preferred_size*)_data)->return_value 491 = BDragger::PreferredSize(); 492 return B_OK; 493 case PERFORM_CODE_LAYOUT_ALIGNMENT: 494 ((perform_data_layout_alignment*)_data)->return_value 495 = BDragger::LayoutAlignment(); 496 return B_OK; 497 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 498 ((perform_data_has_height_for_width*)_data)->return_value 499 = BDragger::HasHeightForWidth(); 500 return B_OK; 501 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 502 { 503 perform_data_get_height_for_width* data 504 = (perform_data_get_height_for_width*)_data; 505 BDragger::GetHeightForWidth(data->width, &data->min, &data->max, 506 &data->preferred); 507 return B_OK; 508 } 509 case PERFORM_CODE_SET_LAYOUT: 510 { 511 perform_data_set_layout* data = (perform_data_set_layout*)_data; 512 BDragger::SetLayout(data->layout); 513 return B_OK; 514 } 515 case PERFORM_CODE_LAYOUT_INVALIDATED: 516 { 517 perform_data_layout_invalidated* data 518 = (perform_data_layout_invalidated*)_data; 519 BDragger::LayoutInvalidated(data->descendants); 520 return B_OK; 521 } 522 case PERFORM_CODE_DO_LAYOUT: 523 { 524 BDragger::DoLayout(); 525 return B_OK; 526 } 527 } 528 529 return BView::Perform(code, _data); 530 } 531 532 533 void 534 BDragger::ResizeToPreferred() 535 { 536 BView::ResizeToPreferred(); 537 } 538 539 540 void 541 BDragger::GetPreferredSize(float* _width, float* _height) 542 { 543 BView::GetPreferredSize(_width, _height); 544 } 545 546 547 void 548 BDragger::MakeFocus(bool state) 549 { 550 BView::MakeFocus(state); 551 } 552 553 554 void 555 BDragger::AllAttached() 556 { 557 BView::AllAttached(); 558 } 559 560 561 void 562 BDragger::AllDetached() 563 { 564 BView::AllDetached(); 565 } 566 567 568 status_t 569 BDragger::SetPopUp(BPopUpMenu* menu) 570 { 571 if (menu != NULL && menu != fPopUp) { 572 delete fPopUp; 573 fPopUp = menu; 574 fPopUpIsCustom = true; 575 return B_OK; 576 } 577 return B_ERROR; 578 } 579 580 581 BPopUpMenu* 582 BDragger::PopUp() const 583 { 584 if (fPopUp == NULL && fTarget) 585 const_cast<BDragger*>(this)->_BuildDefaultPopUp(); 586 587 return fPopUp; 588 } 589 590 591 bool 592 BDragger::InShelf() const 593 { 594 return fShelf != NULL; 595 } 596 597 598 BView* 599 BDragger::Target() const 600 { 601 return fTarget; 602 } 603 604 605 BBitmap* 606 BDragger::DragBitmap(BPoint* offset, drawing_mode* mode) 607 { 608 return NULL; 609 } 610 611 612 bool 613 BDragger::IsVisibilityChanging() const 614 { 615 return fTransition; 616 } 617 618 619 void BDragger::_ReservedDragger2() {} 620 void BDragger::_ReservedDragger3() {} 621 void BDragger::_ReservedDragger4() {} 622 623 624 BDragger& 625 BDragger::operator=(const BDragger&) 626 { 627 return *this; 628 } 629 630 631 /*static*/ void 632 BDragger::_UpdateShowAllDraggers(bool visible) 633 { 634 DraggerManager* manager = DraggerManager::Default(); 635 AutoLocker<DraggerManager> locker(manager); 636 637 manager->visibleInitialized = true; 638 manager->visible = visible; 639 640 for (int32 i = manager->list.CountItems(); i-- > 0;) { 641 BDragger* dragger = (BDragger*)manager->list.ItemAt(i); 642 BMessenger target(dragger); 643 target.SendMessage(_SHOW_DRAG_HANDLES_); 644 } 645 } 646 647 648 void 649 BDragger::_InitData() 650 { 651 fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false); 652 fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8); 653 } 654 655 656 void 657 BDragger::_AddToList() 658 { 659 DraggerManager* manager = DraggerManager::Default(); 660 AutoLocker<DraggerManager> locker(manager); 661 manager->list.AddItem(this); 662 663 bool allowsDragging = true; 664 if (fShelf) 665 allowsDragging = fShelf->AllowsDragging(); 666 667 if (!AreDraggersDrawn() || !allowsDragging) { 668 // The dragger is not shown - but we can't hide us in case we're the 669 // parent of the actual target view (because then you couldn't see 670 // it anymore). 671 if (fRelation != TARGET_IS_CHILD && !IsHidden()) 672 Hide(); 673 } 674 } 675 676 677 void 678 BDragger::_RemoveFromList() 679 { 680 DraggerManager* manager = DraggerManager::Default(); 681 AutoLocker<DraggerManager> locker(manager); 682 manager->list.RemoveItem(this); 683 } 684 685 686 status_t 687 BDragger::_DetermineRelationship() 688 { 689 if (fTarget != NULL) { 690 if (fTarget == Parent()) 691 fRelation = TARGET_IS_PARENT; 692 else if (fTarget == ChildAt(0)) 693 fRelation = TARGET_IS_CHILD; 694 else 695 fRelation = TARGET_IS_SIBLING; 696 } else { 697 if (fRelation == TARGET_IS_PARENT) 698 fTarget = Parent(); 699 else if (fRelation == TARGET_IS_CHILD) 700 fTarget = ChildAt(0); 701 else 702 return B_ERROR; 703 } 704 705 if (fRelation == TARGET_IS_PARENT) { 706 BRect bounds(Frame()); 707 BRect parentBounds(Parent()->Bounds()); 708 if (!parentBounds.Contains(bounds)) { 709 MoveTo(parentBounds.right - bounds.Width(), 710 parentBounds.bottom - bounds.Height()); 711 } 712 } 713 714 return B_OK; 715 } 716 717 718 status_t 719 BDragger::_SetViewToDrag(BView* target) 720 { 721 if (target->Window() != Window()) 722 return B_ERROR; 723 724 fTarget = target; 725 726 if (Window() != NULL) 727 _DetermineRelationship(); 728 729 return B_OK; 730 } 731 732 733 void 734 BDragger::_SetShelf(BShelf* shelf) 735 { 736 fShelf = shelf; 737 } 738 739 740 void 741 BDragger::_SetZombied(bool state) 742 { 743 fIsZombie = state; 744 745 if (state) { 746 SetLowColor(kZombieColor); 747 SetViewColor(kZombieColor); 748 } 749 } 750 751 752 void 753 BDragger::_BuildDefaultPopUp() 754 { 755 fPopUp = new BPopUpMenu("Shelf", false, false, B_ITEMS_IN_COLUMN); 756 757 // About 758 BMessage* msg = new BMessage(B_ABOUT_REQUESTED); 759 760 const char* name = fTarget->Name(); 761 if (name != NULL) 762 msg->AddString("target", name); 763 764 BString about(B_TRANSLATE("About %app" B_UTF8_ELLIPSIS)); 765 about.ReplaceFirst("%app", name); 766 767 fPopUp->AddItem(new BMenuItem(about.String(), msg)); 768 fPopUp->AddSeparatorItem(); 769 fPopUp->AddItem(new BMenuItem(B_TRANSLATE("Remove replicant"), 770 new BMessage(kDeleteReplicant))); 771 } 772 773 774 void 775 BDragger::_ShowPopUp(BView* target, BPoint where) 776 { 777 BPoint point = ConvertToScreen(where); 778 779 if (fPopUp == NULL && fTarget != NULL) 780 _BuildDefaultPopUp(); 781 782 fPopUp->SetTargetForItems(fTarget); 783 784 float menuWidth, menuHeight; 785 fPopUp->GetPreferredSize(&menuWidth, &menuHeight); 786 BRect rect(0, 0, menuWidth, menuHeight); 787 rect.InsetBy(-0.5, -0.5); 788 rect.OffsetTo(point); 789 790 fPopUp->Go(point, true, false, rect, true); 791 } 792 793 794 #if __GNUC__ < 3 795 796 extern "C" BBitmap* 797 _ReservedDragger1__8BDragger(BDragger* dragger, BPoint* offset, 798 drawing_mode* mode) 799 { 800 return dragger->BDragger::DragBitmap(offset, mode); 801 } 802 803 #endif 804