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