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