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 <Beep.h> 15 #include <Bitmap.h> 16 #include <Dragger.h> 17 #include <MenuItem.h> 18 #include <Message.h> 19 #include <PopUpMenu.h> 20 #include <Shelf.h> 21 #include <Window.h> 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 26 27 bool BDragger::sVisible; 28 bool BDragger::sInited; 29 BLocker BDragger::sLock("BDragger_sLock"); 30 BList BDragger::sList; 31 32 const static rgb_color kZombieColor = {220, 220, 220, 255}; 33 34 const unsigned char 35 kHandBitmap[] = { 36 255, 255, 0, 0, 0, 255, 255, 255, 37 255, 255, 0, 131, 131, 0, 255, 255, 38 0, 0, 0, 0, 131, 131, 0, 0, 39 0, 131, 0, 0, 131, 131, 0, 0, 40 0, 131, 131, 131, 131, 131, 0, 0, 41 255, 0, 131, 131, 131, 131, 0, 0, 42 255, 255, 0, 0, 0, 0, 0, 0, 43 255, 255, 255, 255, 255, 255, 0, 0 44 }; 45 46 47 BDragger::BDragger(BRect bounds, BView *target, uint32 rmask, uint32 flags) 48 : BView(bounds, "_dragger_", rmask, flags), 49 fTarget(target), 50 fRelation(TARGET_UNKNOWN), 51 fShelf(NULL), 52 fTransition(false), 53 fIsZombie(false), 54 fErrCount(0), 55 fPopUp(NULL) 56 { 57 fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false); 58 fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8); 59 } 60 61 62 BDragger::BDragger(BMessage *data) 63 : BView(data), 64 fTarget(NULL), 65 fRelation(TARGET_UNKNOWN), 66 fShelf(NULL), 67 fTransition(false), 68 fIsZombie(false), 69 fErrCount(0), 70 fPopUp(NULL) 71 { 72 data->FindInt32("_rel", (int32 *)&fRelation); 73 74 fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false); 75 fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8); 76 77 BMessage popupMsg; 78 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 else 102 return NULL; 103 } 104 105 106 status_t 107 BDragger::Archive(BMessage *data, bool deep) const 108 { 109 BMessage popupMsg; 110 status_t ret = B_OK; 111 112 if (fPopUp) { 113 ret = fPopUp->Archive(&popupMsg); 114 if (ret == B_OK) 115 ret = data->AddMessage("_popup", &popupMsg); 116 } 117 118 if (ret == B_OK) 119 ret = data->AddInt32("_rel", fRelation); 120 if (ret != B_OK) 121 return ret; 122 return BView::Archive(data, deep); 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 determine_relationship(); 138 ListManage(true); 139 } 140 141 142 void 143 BDragger::DetachedFromWindow() 144 { 145 ListManage(false); 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 192 } else { 193 bigtime_t time = system_time(); 194 bigtime_t clickSpeed = 0; 195 196 get_click_speed(&clickSpeed); 197 198 bool drag = false; 199 200 while (true) { 201 BPoint mousePoint; 202 GetMouse(&mousePoint, &buttons); 203 204 if (!buttons || system_time() > time + clickSpeed) 205 break; 206 207 if (mousePoint != where) { 208 drag = true; 209 break; 210 } 211 212 snooze(40000); 213 } 214 215 if (drag) { 216 BMessage archive(B_ARCHIVED_OBJECT); 217 218 if (fRelation == TARGET_IS_PARENT) 219 fTarget->Archive(&archive); 220 else if (fRelation == TARGET_IS_CHILD) 221 Archive(&archive); 222 else { 223 if (fTarget->Archive(&archive)) { 224 BMessage widget(B_ARCHIVED_OBJECT); 225 226 if (Archive(&widget)) 227 archive.AddMessage("__widget", &widget); 228 } 229 } 230 231 archive.AddInt32("be:actions", B_TRASH_TARGET); 232 233 BPoint offset; 234 drawing_mode mode; 235 BBitmap *bitmap = DragBitmap(&offset, &mode); 236 if (bitmap) 237 DragMessage(&archive, bitmap, mode, offset, this); 238 else 239 DragMessage(&archive, 240 ConvertFromScreen(fTarget->ConvertToScreen(fTarget->Bounds())), 241 this); 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) 267 Window()->PostMessage(kDeleteDragger, 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 if (fRelation == TARGET_IS_CHILD) { 275 fTransition = true; 276 Invalidate(); 277 Flush(); 278 fTransition = false; 279 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 // TODO: Implement. Should ask the registrar or the app server 326 return true; 327 } 328 329 330 BHandler * 331 BDragger::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, 332 int32 form, const char *property) 333 { 334 return BView::ResolveSpecifier(msg, index, specifier, form, property); 335 } 336 337 338 status_t 339 BDragger::GetSupportedSuites(BMessage *data) 340 { 341 return BView::GetSupportedSuites(data); 342 } 343 344 345 status_t 346 BDragger::Perform(perform_code d, void *arg) 347 { 348 return BView::Perform(d, arg); 349 } 350 351 352 void 353 BDragger::ResizeToPreferred() 354 { 355 BView::ResizeToPreferred(); 356 } 357 358 359 void 360 BDragger::GetPreferredSize(float *width, float *height) 361 { 362 BView::GetPreferredSize(width, height); 363 } 364 365 366 void 367 BDragger::MakeFocus(bool state) 368 { 369 BView::MakeFocus(state); 370 } 371 372 373 void 374 BDragger::AllAttached() 375 { 376 BView::AllAttached(); 377 } 378 379 380 void 381 BDragger::AllDetached() 382 { 383 BView::AllDetached(); 384 } 385 386 387 status_t 388 BDragger::SetPopUp(BPopUpMenu *context_menu) 389 { 390 if (fPopUp && fPopUp != context_menu) 391 delete fPopUp; 392 393 fPopUp = context_menu; 394 return B_OK; 395 } 396 397 398 BPopUpMenu * 399 BDragger::PopUp() const 400 { 401 if (!fPopUp && fTarget) 402 const_cast<BDragger *>(this)->BuildDefaultPopUp(); 403 404 return fPopUp; 405 } 406 407 408 bool 409 BDragger::InShelf() const 410 { 411 return fShelf != NULL; 412 } 413 414 415 BView * 416 BDragger::Target() const 417 { 418 return fTarget; 419 } 420 421 422 BBitmap * 423 BDragger::DragBitmap(BPoint *offset, drawing_mode *mode) 424 { 425 return NULL; 426 } 427 428 429 bool 430 BDragger::IsVisibilityChanging() const 431 { 432 return fTransition; 433 } 434 435 436 void BDragger::_ReservedDragger2() {} 437 void BDragger::_ReservedDragger3() {} 438 void BDragger::_ReservedDragger4() {} 439 440 441 BDragger & 442 BDragger::operator=(const BDragger &) 443 { 444 return *this; 445 } 446 447 448 void 449 BDragger::ListManage(bool add) 450 { 451 if (sLock.Lock()) { 452 bool drawn = AreDraggersDrawn(); 453 454 if (add) { 455 bool dragging = true; 456 457 sList.AddItem(this); 458 459 if (fShelf) 460 dragging = fShelf->AllowsDragging(); 461 462 if (!drawn && !dragging) { 463 if (fRelation != TARGET_IS_CHILD) 464 Hide(); 465 } 466 } else 467 sList.RemoveItem(this); 468 469 sLock.Unlock(); 470 } 471 } 472 473 474 status_t 475 BDragger::determine_relationship() 476 { 477 status_t err = B_OK; 478 479 if (fTarget) { 480 if (fTarget == Parent()) 481 fRelation = TARGET_IS_PARENT; 482 else if (fTarget == ChildAt(0)) 483 fRelation = TARGET_IS_CHILD; 484 else 485 fRelation = TARGET_IS_SIBLING; 486 } else { 487 if (fRelation == TARGET_IS_PARENT) 488 fTarget = Parent(); 489 else if (fRelation == TARGET_IS_CHILD) 490 fTarget = ChildAt(0); 491 else 492 err = B_ERROR; 493 } 494 495 return err; 496 } 497 498 499 status_t 500 BDragger::SetViewToDrag(BView *target) 501 { 502 if (target->Window() != Window()) 503 return B_ERROR; 504 505 fTarget = target; 506 507 if (Window()) 508 determine_relationship(); 509 510 return B_OK; 511 } 512 513 514 void 515 BDragger::SetShelf(BShelf *shelf) 516 { 517 fShelf = shelf; 518 } 519 520 521 void 522 BDragger::SetZombied(bool state) 523 { 524 fIsZombie = state; 525 526 if (state) { 527 SetLowColor(kZombieColor); 528 SetViewColor(kZombieColor); 529 } 530 } 531 532 533 void 534 BDragger::BuildDefaultPopUp() 535 { 536 fPopUp = new BPopUpMenu("Shelf", false, false, B_ITEMS_IN_COLUMN); 537 538 // About 539 BMessage *msg = new BMessage(B_ABOUT_REQUESTED); 540 541 const char *name = fTarget->Name(); 542 543 if (name) 544 msg->AddString("target", name); 545 546 char about[B_OS_NAME_LENGTH]; 547 snprintf(about, B_OS_NAME_LENGTH, "About %s", name); 548 549 fPopUp->AddItem(new BMenuItem(about, msg)); 550 551 // Separator 552 fPopUp->AddItem(new BSeparatorItem()); 553 554 // Delete 555 fPopUp->AddItem(new BMenuItem("Delete", new BMessage(kDeleteDragger))); 556 } 557 558 559 void 560 BDragger::ShowPopUp(BView *target, BPoint where) 561 { 562 BPoint point = ConvertToScreen(where); 563 564 if (!fPopUp && fTarget) 565 BuildDefaultPopUp(); 566 567 fPopUp->SetTargetForItems(fTarget); 568 569 float menuWidth, menuHeight; 570 fPopUp->GetPreferredSize(&menuWidth, &menuHeight); 571 BRect rect(0, 0, menuWidth, menuHeight); 572 rect.InsetBy(-0.5, -0.5); 573 rect.OffsetTo(point); 574 575 fPopUp->Go(point, true, false, rect, true); 576 } 577