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