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