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