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