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