1 // DiagramView.cpp 2 3 #include "DiagramView.h" 4 #include "DiagramDefs.h" 5 #include "DiagramBox.h" 6 #include "DiagramEndPoint.h" 7 #include "DiagramWire.h" 8 9 #include <Bitmap.h> 10 #include <Message.h> 11 #include <ScrollBar.h> 12 13 __USE_CORTEX_NAMESPACE 14 15 #include <Debug.h> 16 #define D_METHOD(x) //PRINT (x) 17 #define D_HOOK(x) //PRINT (x) 18 #define D_MESSAGE(x) //PRINT (x) 19 #define D_MOUSE(x) //PRINT (x) 20 #define D_DRAW(x) //PRINT (x) 21 22 // -------------------------------------------------------- // 23 // *** ctor/dtor 24 // -------------------------------------------------------- // 25 26 DiagramView::DiagramView( 27 BRect frame, 28 const char *name, 29 bool multiSelection, 30 uint32 resizingMode, 31 uint32 flags) 32 : BView(frame, name, resizingMode, B_WILL_DRAW | B_FRAME_EVENTS | flags), 33 DiagramItemGroup(DiagramItem::M_BOX | DiagramItem::M_WIRE), 34 m_lastButton(0), 35 m_clickCount(0), 36 m_lastClickPoint(-1.0, -1.0), 37 m_pressedButton(0), 38 m_draggedWire(0), 39 m_useBackgroundBitmap(false), 40 m_backgroundBitmap(0) 41 { 42 D_METHOD(("DiagramView::DiagramView()\n")); 43 SetViewColor(B_TRANSPARENT_COLOR); 44 m_dataRect = Bounds(); 45 } 46 47 DiagramView::~DiagramView() 48 { 49 D_METHOD(("DiagramView::~DiagramView()\n")); 50 } 51 52 // -------------------------------------------------------- // 53 // *** hook functions 54 // -------------------------------------------------------- // 55 56 void DiagramView::messageDragged( 57 BPoint point, 58 uint32 transit, 59 const BMessage *message) 60 { 61 D_METHOD(("DiagramView::messageDragged()\n")); 62 switch (message->what) 63 { 64 case M_WIRE_DRAGGED: 65 { 66 D_MESSAGE(("DiagramView::messageDragged(M_WIRE_DROPPED)\n")); 67 if (!m_draggedWire) 68 { 69 DiagramEndPoint *fromEndPoint; 70 if (message->FindPointer("from", reinterpret_cast<void **>(&fromEndPoint)) == B_OK) 71 { 72 _beginWireTracking(fromEndPoint); 73 } 74 } 75 trackWire(point); 76 break; 77 } 78 } 79 } 80 81 void DiagramView::messageDropped( 82 BPoint point, 83 BMessage *message) 84 { 85 D_METHOD(("DiagramView::messageDropped()\n")); 86 switch (message->what) 87 { 88 case M_WIRE_DRAGGED: 89 { 90 D_MESSAGE(("DiagramView::messageDropped(M_WIRE_DROPPED)\n")); 91 DiagramEndPoint *fromWhich = 0; 92 if (message->FindPointer("from", reinterpret_cast<void **>(&fromWhich)) == B_OK) 93 { 94 connectionAborted(fromWhich); 95 } 96 break; 97 } 98 } 99 } 100 101 // -------------------------------------------------------- // 102 // *** derived from BView 103 // -------------------------------------------------------- // 104 105 // initial scrollbar update [e.moon 16nov99] 106 void DiagramView::AttachedToWindow() 107 { 108 D_METHOD(("DiagramView::AttachedToWindow()\n")); 109 _updateScrollBars(); 110 } 111 112 void DiagramView::Draw( 113 BRect updateRect) 114 { 115 D_METHOD(("DiagramView::Draw()\n")); 116 drawBackground(updateRect); 117 drawItems(updateRect, DiagramItem::M_WIRE); 118 drawItems(updateRect, DiagramItem::M_BOX); 119 } 120 121 void DiagramView::FrameResized( 122 float width, 123 float height) 124 { 125 D_METHOD(("DiagramView::FrameResized()\n")); 126 _updateScrollBars(); 127 } 128 129 void DiagramView::GetPreferredSize( 130 float *width, 131 float *height) { 132 D_HOOK(("DiagramView::GetPreferredSize()\n")); 133 134 *width = m_dataRect.Width() + 10.0; 135 *height = m_dataRect.Height() + 10.0; 136 } 137 138 void DiagramView::MessageReceived( 139 BMessage *message) 140 { 141 D_METHOD(("DiagramView::MessageReceived()\n")); 142 switch (message->what) 143 { 144 case M_SELECTION_CHANGED: 145 { 146 D_MESSAGE(("DiagramView::MessageReceived(M_SELECTION_CHANGED)\n")); 147 DiagramItem *item; 148 if (message->FindPointer("item", reinterpret_cast<void **>(&item)) == B_OK) 149 { 150 bool deselectOthers = true; 151 message->FindBool("replace", &deselectOthers); 152 if (item->isSelected() && !deselectOthers && multipleSelection()) 153 { 154 if (deselectItem(item)) 155 { 156 selectionChanged(); 157 } 158 } 159 else if (selectItem(item, deselectOthers)) 160 { 161 sortItems(item->type(), &compareSelectionTime); 162 selectionChanged(); 163 } 164 } 165 break; 166 } 167 case M_WIRE_DROPPED: 168 { 169 D_MESSAGE(("DiagramView::MessageReceived(M_WIRE_DROPPED)\n")); 170 DiagramEndPoint *fromWhich = 0; 171 DiagramEndPoint *toWhich = 0; 172 bool success = false; 173 if (message->FindPointer("from", reinterpret_cast<void **>(&fromWhich)) == B_OK) 174 { 175 if ((message->FindPointer("to", reinterpret_cast<void **>(&toWhich)) == B_OK) 176 && (message->FindBool("success", &success) == B_OK)) 177 { 178 if (success && fromWhich && toWhich) 179 { 180 _endWireTracking(); 181 DiagramWire *wire = createWire(fromWhich, toWhich); 182 if (wire && addItem(wire)) 183 { 184 connectionEstablished(fromWhich, toWhich); 185 break; 186 } 187 } 188 } 189 } 190 connectionAborted(fromWhich); 191 break; 192 } 193 default: 194 { 195 if (message->WasDropped()) 196 { 197 BPoint point = ConvertFromScreen(message->DropPoint()); 198 DiagramItem *item = itemUnder(point); 199 if (item) 200 { 201 item->messageDropped(point, message); 202 return; 203 } 204 else 205 { 206 messageDropped(point, message); 207 } 208 } 209 else 210 { 211 BView::MessageReceived(message); 212 } 213 } 214 } 215 } 216 217 void DiagramView::KeyDown( 218 const char *bytes, 219 int32 numBytes) 220 { 221 D_METHOD(("DiagramView::KeyDown()\n")); 222 switch (bytes[0]) 223 { 224 case B_LEFT_ARROW: 225 { 226 float x; 227 getItemAlignment(&x, 0); 228 BRegion updateRegion; 229 dragSelectionBy(-x, 0.0, &updateRegion); 230 for (int32 i = 0; i < updateRegion.CountRects(); i++) 231 Invalidate(updateRegion.RectAt(i)); 232 updateDataRect(); 233 break; 234 } 235 case B_RIGHT_ARROW: 236 { 237 float x; 238 getItemAlignment(&x, 0); 239 BRegion updateRegion; 240 dragSelectionBy(x, 0.0, &updateRegion); 241 for (int32 i = 0; i < updateRegion.CountRects(); i++) 242 Invalidate(updateRegion.RectAt(i)); 243 updateDataRect(); 244 break; 245 } 246 case B_UP_ARROW: 247 { 248 float y; 249 getItemAlignment(0, &y); 250 BRegion updateRegion; 251 dragSelectionBy(0.0, -y, &updateRegion); 252 for (int32 i = 0; i < updateRegion.CountRects(); i++) 253 Invalidate(updateRegion.RectAt(i)); 254 updateDataRect(); 255 break; 256 } 257 case B_DOWN_ARROW: 258 { 259 float y; 260 getItemAlignment(0, &y); 261 BRegion updateRegion; 262 dragSelectionBy(0.0, y, &updateRegion); 263 for (int32 i = 0; i < updateRegion.CountRects(); i++) 264 Invalidate(updateRegion.RectAt(i)); 265 updateDataRect(); 266 break; 267 } 268 default: 269 { 270 BView::KeyDown(bytes, numBytes); 271 break; 272 } 273 } 274 } 275 276 void DiagramView::MouseDown( 277 BPoint point) 278 { 279 D_METHOD(("DiagramView::MouseDown()\n")); 280 281 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); 282 283 // update click count 284 BMessage* message = Window()->CurrentMessage(); 285 int32 clicks = message->FindInt32("clicks"); 286 int32 buttons = message->FindInt32("buttons"); 287 288 bool moved = (fabs(point.x - m_lastClickPoint.x) > 2.0 || fabs(point.y - m_lastClickPoint.y) > 2.0); 289 if (!moved && (buttons == m_lastButton) && (clicks > 1)) 290 { 291 m_clickCount++; 292 } 293 else 294 { 295 m_clickCount = 1; 296 } 297 m_lastButton = buttons; 298 m_lastClickPoint = point; 299 m_lastDragPoint = ConvertToScreen(point); 300 301 // [e.moon 16nov99] scroll on 3rd button 302 m_pressedButton = buttons; 303 if(m_pressedButton == B_TERTIARY_MOUSE_BUTTON) { 304 return; 305 } 306 307 // was an item clicked ? 308 DiagramItem *item = itemUnder(point); 309 if (item) 310 { 311 item->mouseDown(point, m_lastButton, m_clickCount); 312 } 313 else // no, the background was clicked 314 { 315 if (!(modifiers() & B_SHIFT_KEY) && deselectAll(DiagramItem::M_ANY)) 316 selectionChanged(); 317 if (multipleSelection() && (m_lastButton == B_PRIMARY_MOUSE_BUTTON) && !(modifiers() & B_CONTROL_KEY)) 318 _beginRectTracking(point); 319 mouseDown(point, m_lastButton, m_clickCount); 320 } 321 } 322 323 void DiagramView::MouseMoved( 324 BPoint point, 325 uint32 transit, 326 const BMessage *message) 327 { 328 D_METHOD(("DiagramView::MouseMoved()\n")); 329 330 331 // [e.moon 16nov99] 3rd-button scrolling 332 if(m_pressedButton == B_TERTIARY_MOUSE_BUTTON) { 333 334 // fetch/store screen point; calculate distance travelled 335 ConvertToScreen(&point); 336 float xDelta = m_lastDragPoint.x - point.x; 337 float yDelta = m_lastDragPoint.y - point.y; 338 m_lastDragPoint = point; 339 340 // constrain to scrollbar limits 341 BScrollBar* hScroll = ScrollBar(B_HORIZONTAL); 342 if(xDelta && hScroll) { 343 float val = hScroll->Value(); 344 float min, max; 345 hScroll->GetRange(&min, &max); 346 347 if(val + xDelta < min) 348 xDelta = 0; 349 350 if(val + xDelta > max) 351 xDelta = 0; 352 } 353 354 BScrollBar* vScroll = ScrollBar(B_VERTICAL); 355 if(yDelta && vScroll) { 356 float val = vScroll->Value(); 357 float min, max; 358 vScroll->GetRange(&min, &max); 359 360 if(val + yDelta < min) 361 yDelta = 0; 362 363 if(val + yDelta > max) 364 yDelta = 0; 365 } 366 367 // scroll 368 if(xDelta == 0.0 && yDelta == 0.0) 369 return; 370 371 ScrollBy(xDelta, yDelta); 372 return; 373 } 374 375 if (message) 376 { 377 switch (message->what) 378 { 379 case M_RECT_TRACKING: 380 { 381 BPoint origin; 382 if (message->FindPoint("origin", &origin) == B_OK) 383 { 384 _trackRect(origin, point); 385 } 386 break; 387 } 388 case M_BOX_DRAGGED: 389 { 390 DiagramBox *box; 391 BPoint offset; 392 if ((message->FindPointer("item", reinterpret_cast<void **>(&box)) == B_OK) 393 && (message->FindPoint("offset", &offset) == B_OK)) 394 { 395 if (box) 396 { 397 BRegion updateRegion; 398 dragSelectionBy(point.x - box->frame().left - offset.x, 399 point.y - box->frame().top - offset.y, 400 &updateRegion); 401 updateDataRect(); 402 for (int32 i = 0; i < updateRegion.CountRects(); i++) 403 { 404 Invalidate(updateRegion.RectAt(i)); 405 } 406 } 407 } 408 break; 409 } 410 default: // unkwown message -> redirect to messageDragged() 411 { 412 DiagramItem *last = lastItemUnder(); 413 if (transit == B_EXITED_VIEW) 414 { 415 if (last) 416 { 417 last->messageDragged(point, B_EXITED_VIEW, message); 418 messageDragged(point, B_EXITED_VIEW, message); 419 } 420 } 421 else 422 { 423 DiagramItem *item = itemUnder(point); 424 if (item) 425 { 426 if (item != last) 427 { 428 if (last) 429 last->messageDragged(point, B_EXITED_VIEW, message); 430 item->messageDragged(point, B_ENTERED_VIEW, message); 431 } 432 else 433 { 434 item->messageDragged(point, B_INSIDE_VIEW, message); 435 } 436 } 437 else if (last) 438 { 439 last->messageDragged(point, B_EXITED_VIEW, message); 440 messageDragged(point, B_ENTERED_VIEW, message); 441 } 442 else 443 { 444 messageDragged(point, transit, message); 445 } 446 } 447 break; 448 } 449 } 450 } 451 else // no message at all -> redirect to mouseOver() 452 { 453 DiagramItem *last = lastItemUnder(); 454 if ((transit == B_EXITED_VIEW) || (transit == B_OUTSIDE_VIEW)) 455 { 456 if (last) 457 { 458 last->mouseOver(point, B_EXITED_VIEW); 459 resetItemUnder(); 460 mouseOver(point, B_EXITED_VIEW); 461 } 462 } 463 else 464 { 465 DiagramItem *item = itemUnder(point); 466 if (item) 467 { 468 if (item != last) 469 { 470 if (last) 471 last->mouseOver(point, B_EXITED_VIEW); 472 item->mouseOver(point, B_ENTERED_VIEW); 473 } 474 else 475 { 476 item->mouseOver(point, B_INSIDE_VIEW); 477 } 478 } 479 else if (last) 480 { 481 last->mouseOver(point, B_EXITED_VIEW); 482 mouseOver(point, B_ENTERED_VIEW); 483 } 484 } 485 } 486 } 487 488 void DiagramView::MouseUp( 489 BPoint point) 490 { 491 D_METHOD(("DiagramView::MouseUp()\n")); 492 if (multipleSelection()) 493 EndRectTracking(); 494 _endWireTracking(); 495 496 // [e.moon 16nov99] mark no button as down 497 m_pressedButton = 0; 498 } 499 500 // -------------------------------------------------------- // 501 // *** derived from DiagramItemGroup (public) 502 // -------------------------------------------------------- // 503 504 bool DiagramView::addItem( 505 DiagramItem *item) 506 { 507 D_METHOD(("DiagramBox::addItem()\n")); 508 if (item) 509 { 510 if (DiagramItemGroup::addItem(item)) 511 { 512 item->_setOwner(this); 513 item->attachedToDiagram(); 514 if (item->type() == DiagramItem::M_BOX) 515 { 516 updateDataRect(); 517 } 518 return true; 519 } 520 } 521 return false; 522 } 523 524 bool DiagramView::removeItem( 525 DiagramItem *item) 526 { 527 D_METHOD(("DiagramBox::removeItem()\n")); 528 if (item) 529 { 530 item->detachedFromDiagram(); 531 if (DiagramItemGroup::removeItem(item)) 532 { 533 item->_setOwner(0); 534 if (item->type() == DiagramItem::M_BOX) 535 { 536 updateDataRect(); 537 } 538 return true; 539 } 540 } 541 return false; 542 } 543 544 // -------------------------------------------------------- // 545 // *** operations (public) 546 // -------------------------------------------------------- // 547 548 void DiagramView::trackWire( 549 BPoint point) 550 { 551 D_MOUSE(("DiagramView::trackWire()\n")); 552 if (m_draggedWire) 553 { 554 BRegion region; 555 region.Include(m_draggedWire->frame()); 556 m_draggedWire->m_dragEndPoint = point; 557 m_draggedWire->endPointMoved(); 558 region.Include(m_draggedWire->frame()); 559 region.Exclude(&m_boxRegion); 560 for (int32 i = 0; i < region.CountRects(); i++) 561 { 562 Invalidate(region.RectAt(i)); 563 } 564 } 565 } 566 567 // -------------------------------------------------------- // 568 // *** internal operations (protected) 569 // -------------------------------------------------------- // 570 571 void DiagramView::drawBackground( 572 BRect updateRect) 573 { 574 D_METHOD(("DiagramView::drawBackground()\n")); 575 if (m_useBackgroundBitmap) 576 { 577 BRegion region; 578 region.Include(updateRect); 579 region.Exclude(&m_boxRegion); 580 BRect bounds = Bounds(); 581 PushState(); 582 { 583 ConstrainClippingRegion(®ion); 584 for (float y = 0; y < bounds.bottom; y += m_backgroundBitmap->Bounds().Height()) 585 { 586 for (float x = 0; x < bounds.right; x += m_backgroundBitmap->Bounds().Width()) 587 { 588 DrawBitmapAsync(m_backgroundBitmap, BPoint(x, y)); 589 } 590 } 591 } 592 PopState(); 593 } 594 else 595 { 596 BRegion region; 597 region.Include(updateRect); 598 region.Exclude(&m_boxRegion); 599 PushState(); 600 { 601 SetLowColor(m_backgroundColor); 602 FillRegion(®ion, B_SOLID_LOW); 603 } 604 PopState(); 605 } 606 } 607 608 void DiagramView::setBackgroundColor( 609 rgb_color color) 610 { 611 D_METHOD(("DiagramView::setBackgroundColor()\n")); 612 m_backgroundColor = color; 613 m_useBackgroundBitmap = false; 614 } 615 616 617 void 618 DiagramView::setBackgroundBitmap(BBitmap* bitmap) 619 { 620 D_METHOD(("DiagramView::setBackgroundBitmap()\n")); 621 if (m_backgroundBitmap) 622 delete m_backgroundBitmap; 623 624 m_backgroundBitmap = new BBitmap(bitmap); 625 m_useBackgroundBitmap = true; 626 } 627 628 629 void 630 DiagramView::updateDataRect() 631 { 632 D_METHOD(("DiagramView::updateDataRect()\n")); 633 // calculate the area in which boxes display 634 m_boxRegion.MakeEmpty(); 635 for (uint32 i = 0; i < countItems(DiagramItem::M_BOX); i++) { 636 m_boxRegion.Include(itemAt(i, DiagramItem::M_BOX)->frame()); 637 } 638 // adapt the data rect to the new region of boxes 639 BRect boxRect = m_boxRegion.Frame(); 640 m_dataRect.right = boxRect.right; 641 m_dataRect.bottom = boxRect.bottom; 642 // update the scroll bars 643 _updateScrollBars(); 644 } 645 646 647 // #pragma mark - internal operations (private) 648 649 650 void 651 DiagramView::_beginWireTracking(DiagramEndPoint *startPoint) 652 { 653 D_METHOD(("DiagramView::beginWireTracking()\n")); 654 m_draggedWire = createWire(startPoint); 655 addItem(m_draggedWire); 656 selectItem(m_draggedWire, true); 657 Invalidate(startPoint->frame()); 658 } 659 660 void DiagramView::_endWireTracking() 661 { 662 D_METHOD(("DiagramView::endWireTracking()\n")); 663 if (m_draggedWire) 664 { 665 removeItem(m_draggedWire); 666 Invalidate(m_draggedWire->frame()); 667 DiagramEndPoint *endPoint = m_draggedWire->startPoint(); 668 if (!endPoint) 669 { 670 endPoint = m_draggedWire->endPoint(); 671 } 672 if (endPoint) 673 { 674 Invalidate(endPoint->frame()); 675 } 676 delete m_draggedWire; 677 m_draggedWire = 0; 678 } 679 } 680 681 void DiagramView::_beginRectTracking( 682 BPoint origin) 683 { 684 D_METHOD(("DiagramView::beginRectTracking()\n")); 685 BMessage message(M_RECT_TRACKING); 686 message.AddPoint("origin", origin); 687 DragMessage(&message, BRect(0.0, 0.0, -1.0, -1.0)); 688 BView::BeginRectTracking(BRect(origin, origin), B_TRACK_RECT_CORNER); 689 } 690 691 692 void 693 DiagramView::_trackRect(BPoint origin, BPoint current) 694 { 695 D_METHOD(("DiagramView::trackRect()\n")); 696 bool changed = false; 697 BRect rect; 698 rect.left = origin.x < current.x ? origin.x : current.x; 699 rect.top = origin.y < current.y ? origin.y : current.y; 700 rect.right = origin.x < current.x ? current.x : origin.x; 701 rect.bottom = origin.y < current.y ? current.y : origin.y; 702 for (uint32 i = 0; i < countItems(DiagramItem::M_BOX); i++) { 703 DiagramBox *box = dynamic_cast<DiagramBox *>(itemAt(i, DiagramItem::M_BOX)); 704 if (box) { 705 if (rect.Intersects(box->frame())) 706 changed |= selectItem(box, false); 707 else 708 changed |= deselectItem(box); 709 } 710 } 711 712 if (changed) { 713 sortItems(DiagramItem::M_BOX, &compareSelectionTime); 714 selectionChanged(); 715 } 716 } 717 718 719 void 720 DiagramView::_updateScrollBars() 721 { 722 D_METHOD(("DiagramView::_updateScrollBars()\n")); 723 // fetch the vertical ScrollBar 724 BScrollBar *scrollBar = ScrollBar(B_VERTICAL); 725 if (scrollBar) 726 { 727 // Disable the ScrollBar if the view is large enough to display 728 // the entire data-rect 729 if (Bounds().Height() > m_dataRect.Height()) 730 { 731 scrollBar->SetRange(0.0, 0.0); 732 scrollBar->SetProportion(1.0); 733 } 734 else 735 { 736 scrollBar->SetRange(m_dataRect.top, m_dataRect.bottom - Bounds().Height()); 737 scrollBar->SetProportion(Bounds().Height() / m_dataRect.Height()); 738 } 739 } 740 scrollBar = ScrollBar(B_HORIZONTAL); 741 if (scrollBar) 742 { 743 // Disable the ScrollBar if the view is large enough to display 744 // the entire data-rect 745 if (Bounds().Width() > m_dataRect.Width()) 746 { 747 scrollBar->SetRange(0.0, 0.0); 748 scrollBar->SetProportion(1.0); 749 } 750 else 751 { 752 scrollBar->SetRange(m_dataRect.left, m_dataRect.right - Bounds().Width()); 753 scrollBar->SetProportion(Bounds().Width() / m_dataRect.Width()); 754 } 755 } 756 } 757 758 // END -- DiagramView.cpp -- 759