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