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 void DiagramView::setBackgroundBitmap( 617 BBitmap *bitmap) 618 { 619 D_METHOD(("DiagramView::setBackgroundBitmap()\n")); 620 if (m_backgroundBitmap) 621 { 622 delete m_backgroundBitmap; 623 } 624 m_backgroundBitmap = new BBitmap(bitmap); 625 m_useBackgroundBitmap = true; 626 } 627 628 void DiagramView::updateDataRect() 629 { 630 D_METHOD(("DiagramView::updateDataRect()\n")); 631 // calculate the area in which boxes display 632 m_boxRegion.MakeEmpty(); 633 for (int32 i = 0; i < countItems(DiagramItem::M_BOX); i++) 634 { 635 m_boxRegion.Include(itemAt(i, DiagramItem::M_BOX)->frame()); 636 } 637 // adapt the data rect to the new region of boxes 638 BRect boxRect = m_boxRegion.Frame(); 639 m_dataRect.right = boxRect.right; 640 m_dataRect.bottom = boxRect.bottom; 641 // update the scroll bars 642 _updateScrollBars(); 643 } 644 645 // -------------------------------------------------------- // 646 // *** internal operations (private) 647 // -------------------------------------------------------- // 648 649 void DiagramView::_beginWireTracking( 650 DiagramEndPoint *startPoint) 651 { 652 D_METHOD(("DiagramView::beginWireTracking()\n")); 653 m_draggedWire = createWire(startPoint); 654 addItem(m_draggedWire); 655 selectItem(m_draggedWire, true); 656 Invalidate(startPoint->frame()); 657 } 658 659 void DiagramView::_endWireTracking() 660 { 661 D_METHOD(("DiagramView::endWireTracking()\n")); 662 if (m_draggedWire) 663 { 664 removeItem(m_draggedWire); 665 Invalidate(m_draggedWire->frame()); 666 DiagramEndPoint *endPoint = m_draggedWire->startPoint(); 667 if (!endPoint) 668 { 669 endPoint = m_draggedWire->endPoint(); 670 } 671 if (endPoint) 672 { 673 Invalidate(endPoint->frame()); 674 } 675 delete m_draggedWire; 676 m_draggedWire = 0; 677 } 678 } 679 680 void DiagramView::_beginRectTracking( 681 BPoint origin) 682 { 683 D_METHOD(("DiagramView::beginRectTracking()\n")); 684 BMessage message(M_RECT_TRACKING); 685 message.AddPoint("origin", origin); 686 DragMessage(&message, BRect(0.0, 0.0, -1.0, -1.0)); 687 BView::BeginRectTracking(BRect(origin, origin), B_TRACK_RECT_CORNER); 688 } 689 690 void DiagramView::_trackRect( 691 BPoint origin, 692 BPoint current) 693 { 694 D_METHOD(("DiagramView::trackRect()\n")); 695 bool changed = false; 696 BRect rect; 697 rect.left = origin.x < current.x ? origin.x : current.x; 698 rect.top = origin.y < current.y ? origin.y : current.y; 699 rect.right = origin.x < current.x ? current.x : origin.x; 700 rect.bottom = origin.y < current.y ? current.y : origin.y; 701 for (int32 i = 0; i < countItems(DiagramItem::M_BOX); i++) 702 { 703 DiagramBox *box = dynamic_cast<DiagramBox *>(itemAt(i, DiagramItem::M_BOX)); 704 if (box) 705 { 706 if (rect.Intersects(box->frame())) 707 { 708 changed |= selectItem(box, false); 709 } 710 else 711 { 712 changed |= deselectItem(box); 713 } 714 } 715 } 716 if (changed) 717 { 718 sortItems(DiagramItem::M_BOX, &compareSelectionTime); 719 selectionChanged(); 720 } 721 } 722 723 void DiagramView::_updateScrollBars() 724 { 725 D_METHOD(("DiagramView::_updateScrollBars()\n")); 726 // fetch the vertical ScrollBar 727 BScrollBar *scrollBar = ScrollBar(B_VERTICAL); 728 if (scrollBar) 729 { 730 // Disable the ScrollBar if the view is large enough to display 731 // the entire data-rect 732 if (Bounds().Height() > m_dataRect.Height()) 733 { 734 scrollBar->SetRange(0.0, 0.0); 735 scrollBar->SetProportion(1.0); 736 } 737 else 738 { 739 scrollBar->SetRange(m_dataRect.top, m_dataRect.bottom - Bounds().Height()); 740 scrollBar->SetProportion(Bounds().Height() / m_dataRect.Height()); 741 } 742 } 743 scrollBar = ScrollBar(B_HORIZONTAL); 744 if (scrollBar) 745 { 746 // Disable the ScrollBar if the view is large enough to display 747 // the entire data-rect 748 if (Bounds().Width() > m_dataRect.Width()) 749 { 750 scrollBar->SetRange(0.0, 0.0); 751 scrollBar->SetProportion(1.0); 752 } 753 else 754 { 755 scrollBar->SetRange(m_dataRect.left, m_dataRect.right - Bounds().Width()); 756 scrollBar->SetProportion(Bounds().Width() / m_dataRect.Width()); 757 } 758 } 759 } 760 761 // END -- DiagramView.cpp -- 762