2 * Copyright (c) 1999-2000, Eric Moon.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions, and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "DiagramView.h"
35 #include "DiagramDefs.h"
36 #include "DiagramBox.h"
37 #include "DiagramEndPoint.h"
38 #include "DiagramWire.h"
42 #include <ScrollBar.h>
44 __USE_CORTEX_NAMESPACE
47 #define D_METHOD(x) //PRINT (x)
48 #define D_HOOK(x) //PRINT (x)
49 #define D_MESSAGE(x) //PRINT (x)
50 #define D_MOUSE(x) //PRINT (x)
51 #define D_DRAW(x) //PRINT (x)
53 // -------------------------------------------------------- //
55 // -------------------------------------------------------- //
57 DiagramView::DiagramView(
63 : BView(frame
, name
, resizingMode
, B_WILL_DRAW
| B_FRAME_EVENTS
| flags
),
64 DiagramItemGroup(DiagramItem::M_BOX
| DiagramItem::M_WIRE
),
67 m_lastClickPoint(-1.0, -1.0),
70 m_useBackgroundBitmap(false),
73 D_METHOD(("DiagramView::DiagramView()\n"));
74 SetViewColor(B_TRANSPARENT_COLOR
);
75 m_dataRect
= Bounds();
78 DiagramView::~DiagramView()
80 D_METHOD(("DiagramView::~DiagramView()\n"));
83 // -------------------------------------------------------- //
85 // -------------------------------------------------------- //
87 void DiagramView::MessageDragged(
90 const BMessage
*message
)
92 D_METHOD(("DiagramView::MessageDragged()\n"));
93 switch (message
->what
)
97 D_MESSAGE(("DiagramView::MessageDragged(M_WIRE_DROPPED)\n"));
100 DiagramEndPoint
*fromEndPoint
;
101 if (message
->FindPointer("from", reinterpret_cast<void **>(&fromEndPoint
)) == B_OK
)
103 _beginWireTracking(fromEndPoint
);
112 void DiagramView::MessageDropped(
116 D_METHOD(("DiagramView::MessageDropped()\n"));
117 switch (message
->what
)
121 D_MESSAGE(("DiagramView::MessageDropped(M_WIRE_DROPPED)\n"));
122 DiagramEndPoint
*fromWhich
= 0;
123 if (message
->FindPointer("from", reinterpret_cast<void **>(&fromWhich
)) == B_OK
)
125 connectionAborted(fromWhich
);
132 // -------------------------------------------------------- //
133 // *** derived from BView
134 // -------------------------------------------------------- //
136 // initial scrollbar update [e.moon 16nov99]
137 void DiagramView::AttachedToWindow()
139 D_METHOD(("DiagramView::AttachedToWindow()\n"));
143 void DiagramView::Draw(
146 D_METHOD(("DiagramView::Draw()\n"));
147 drawBackground(updateRect
);
148 DrawItems(updateRect
, DiagramItem::M_WIRE
);
149 DrawItems(updateRect
, DiagramItem::M_BOX
);
152 void DiagramView::FrameResized(
156 D_METHOD(("DiagramView::FrameResized()\n"));
160 void DiagramView::GetPreferredSize(
163 D_HOOK(("DiagramView::GetPreferredSize()\n"));
165 *width
= m_dataRect
.Width() + 10.0;
166 *height
= m_dataRect
.Height() + 10.0;
169 void DiagramView::MessageReceived(
172 D_METHOD(("DiagramView::MessageReceived()\n"));
173 switch (message
->what
)
175 case M_SELECTION_CHANGED
:
177 D_MESSAGE(("DiagramView::MessageReceived(M_SELECTION_CHANGED)\n"));
179 if (message
->FindPointer("item", reinterpret_cast<void **>(&item
)) == B_OK
)
181 bool deselectOthers
= true;
182 message
->FindBool("replace", &deselectOthers
);
183 if (item
->isSelected() && !deselectOthers
&& MultipleSelection())
185 if (DeselectItem(item
))
190 else if (SelectItem(item
, deselectOthers
))
192 SortItems(item
->type(), &compareSelectionTime
);
200 D_MESSAGE(("DiagramView::MessageReceived(M_WIRE_DROPPED)\n"));
201 DiagramEndPoint
*fromWhich
= 0;
202 DiagramEndPoint
*toWhich
= 0;
203 bool success
= false;
204 if (message
->FindPointer("from", reinterpret_cast<void **>(&fromWhich
)) == B_OK
)
206 if ((message
->FindPointer("to", reinterpret_cast<void **>(&toWhich
)) == B_OK
)
207 && (message
->FindBool("success", &success
) == B_OK
))
209 if (success
&& fromWhich
&& toWhich
)
212 DiagramWire
*wire
= createWire(fromWhich
, toWhich
);
213 if (wire
&& AddItem(wire
))
215 connectionEstablished(fromWhich
, toWhich
);
221 connectionAborted(fromWhich
);
226 if (message
->WasDropped())
228 BPoint point
= ConvertFromScreen(message
->DropPoint());
229 DiagramItem
*item
= ItemUnder(point
);
232 item
->MessageDropped(point
, message
);
237 MessageDropped(point
, message
);
242 BView::MessageReceived(message
);
248 void DiagramView::KeyDown(
252 D_METHOD(("DiagramView::KeyDown()\n"));
258 GetItemAlignment(&x
, 0);
259 BRegion updateRegion
;
260 DragSelectionBy(-x
, 0.0, &updateRegion
);
261 for (int32 i
= 0; i
< updateRegion
.CountRects(); i
++)
262 Invalidate(updateRegion
.RectAt(i
));
269 GetItemAlignment(&x
, 0);
270 BRegion updateRegion
;
271 DragSelectionBy(x
, 0.0, &updateRegion
);
272 for (int32 i
= 0; i
< updateRegion
.CountRects(); i
++)
273 Invalidate(updateRegion
.RectAt(i
));
280 GetItemAlignment(0, &y
);
281 BRegion updateRegion
;
282 DragSelectionBy(0.0, -y
, &updateRegion
);
283 for (int32 i
= 0; i
< updateRegion
.CountRects(); i
++)
284 Invalidate(updateRegion
.RectAt(i
));
291 GetItemAlignment(0, &y
);
292 BRegion updateRegion
;
293 DragSelectionBy(0.0, y
, &updateRegion
);
294 for (int32 i
= 0; i
< updateRegion
.CountRects(); i
++)
295 Invalidate(updateRegion
.RectAt(i
));
301 BView::KeyDown(bytes
, numBytes
);
307 void DiagramView::MouseDown(
310 D_METHOD(("DiagramView::MouseDown()\n"));
312 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
| B_NO_POINTER_HISTORY
);
314 // update click count
315 BMessage
* message
= Window()->CurrentMessage();
316 int32 clicks
= message
->FindInt32("clicks");
317 int32 buttons
= message
->FindInt32("buttons");
319 bool moved
= (fabs(point
.x
- m_lastClickPoint
.x
) > 2.0 || fabs(point
.y
- m_lastClickPoint
.y
) > 2.0);
320 if (!moved
&& (buttons
== m_lastButton
) && (clicks
> 1))
328 m_lastButton
= buttons
;
329 m_lastClickPoint
= point
;
330 m_lastDragPoint
= ConvertToScreen(point
);
332 // [e.moon 16nov99] scroll on 3rd button
333 m_pressedButton
= buttons
;
334 if(m_pressedButton
== B_TERTIARY_MOUSE_BUTTON
) {
338 // was an item clicked ?
339 DiagramItem
*item
= ItemUnder(point
);
342 item
->MouseDown(point
, m_lastButton
, m_clickCount
);
344 else // no, the background was clicked
346 if (!(modifiers() & B_SHIFT_KEY
) && DeselectAll(DiagramItem::M_ANY
))
348 if (MultipleSelection() && (m_lastButton
== B_PRIMARY_MOUSE_BUTTON
) && !(modifiers() & B_CONTROL_KEY
))
349 _beginRectTracking(point
);
350 BackgroundMouseDown(point
, m_lastButton
, m_clickCount
);
354 void DiagramView::MouseMoved(
357 const BMessage
*message
)
359 D_METHOD(("DiagramView::MouseMoved()\n"));
362 // [e.moon 16nov99] 3rd-button scrolling
363 if(m_pressedButton
== B_TERTIARY_MOUSE_BUTTON
) {
365 // fetch/store screen point; calculate distance travelled
366 ConvertToScreen(&point
);
367 float xDelta
= m_lastDragPoint
.x
- point
.x
;
368 float yDelta
= m_lastDragPoint
.y
- point
.y
;
369 m_lastDragPoint
= point
;
371 // constrain to scrollbar limits
372 BScrollBar
* hScroll
= ScrollBar(B_HORIZONTAL
);
373 if(xDelta
&& hScroll
) {
374 float val
= hScroll
->Value();
376 hScroll
->GetRange(&min
, &max
);
378 if(val
+ xDelta
< min
)
381 if(val
+ xDelta
> max
)
385 BScrollBar
* vScroll
= ScrollBar(B_VERTICAL
);
386 if(yDelta
&& vScroll
) {
387 float val
= vScroll
->Value();
389 vScroll
->GetRange(&min
, &max
);
391 if(val
+ yDelta
< min
)
394 if(val
+ yDelta
> max
)
399 if(xDelta
== 0.0 && yDelta
== 0.0)
402 ScrollBy(xDelta
, yDelta
);
408 switch (message
->what
)
410 case M_RECT_TRACKING
:
413 if (message
->FindPoint("origin", &origin
) == B_OK
)
415 _trackRect(origin
, point
);
423 if ((message
->FindPointer("item", reinterpret_cast<void **>(&box
)) == B_OK
)
424 && (message
->FindPoint("offset", &offset
) == B_OK
))
428 BRegion updateRegion
;
429 DragSelectionBy(point
.x
- box
->Frame().left
- offset
.x
,
430 point
.y
- box
->Frame().top
- offset
.y
,
433 for (int32 i
= 0; i
< updateRegion
.CountRects(); i
++)
435 Invalidate(updateRegion
.RectAt(i
));
441 default: // unkwown message -> redirect to MessageDragged()
443 DiagramItem
*last
= _LastItemUnder();
444 if (transit
== B_EXITED_VIEW
)
448 last
->MessageDragged(point
, B_EXITED_VIEW
, message
);
449 MessageDragged(point
, B_EXITED_VIEW
, message
);
454 DiagramItem
*item
= ItemUnder(point
);
460 last
->MessageDragged(point
, B_EXITED_VIEW
, message
);
461 item
->MessageDragged(point
, B_ENTERED_VIEW
, message
);
465 item
->MessageDragged(point
, B_INSIDE_VIEW
, message
);
470 last
->MessageDragged(point
, B_EXITED_VIEW
, message
);
471 MessageDragged(point
, B_ENTERED_VIEW
, message
);
475 MessageDragged(point
, transit
, message
);
482 else // no message at all -> redirect to MouseOver()
484 DiagramItem
*last
= _LastItemUnder();
485 if ((transit
== B_EXITED_VIEW
) || (transit
== B_OUTSIDE_VIEW
))
489 last
->MouseOver(point
, B_EXITED_VIEW
);
491 MouseOver(point
, B_EXITED_VIEW
);
496 DiagramItem
*item
= ItemUnder(point
);
502 last
->MouseOver(point
, B_EXITED_VIEW
);
503 item
->MouseOver(point
, B_ENTERED_VIEW
);
507 item
->MouseOver(point
, B_INSIDE_VIEW
);
512 last
->MouseOver(point
, B_EXITED_VIEW
);
513 MouseOver(point
, B_ENTERED_VIEW
);
519 void DiagramView::MouseUp(
522 D_METHOD(("DiagramView::MouseUp()\n"));
523 if (MultipleSelection())
527 // [e.moon 16nov99] mark no button as down
531 // -------------------------------------------------------- //
532 // *** derived from DiagramItemGroup (public)
533 // -------------------------------------------------------- //
535 bool DiagramView::AddItem(
538 D_METHOD(("DiagramBox::AddItem()\n"));
541 if (DiagramItemGroup::AddItem(item
))
543 item
->_SetOwner(this);
544 item
->attachedToDiagram();
545 if (item
->type() == DiagramItem::M_BOX
)
555 bool DiagramView::RemoveItem(
558 D_METHOD(("DiagramBox::RemoveItem()\n"));
561 item
->detachedFromDiagram();
562 if (DiagramItemGroup::RemoveItem(item
))
565 if (item
->type() == DiagramItem::M_BOX
)
575 // -------------------------------------------------------- //
576 // *** operations (public)
577 // -------------------------------------------------------- //
579 void DiagramView::trackWire(
582 D_MOUSE(("DiagramView::trackWire()\n"));
586 region
.Include(m_draggedWire
->Frame());
587 m_draggedWire
->m_dragEndPoint
= point
;
588 m_draggedWire
->endPointMoved();
589 region
.Include(m_draggedWire
->Frame());
590 region
.Exclude(&m_boxRegion
);
591 for (int32 i
= 0; i
< region
.CountRects(); i
++)
593 Invalidate(region
.RectAt(i
));
598 // -------------------------------------------------------- //
599 // *** internal operations (protected)
600 // -------------------------------------------------------- //
602 void DiagramView::drawBackground(
605 D_METHOD(("DiagramView::drawBackground()\n"));
606 if (m_useBackgroundBitmap
)
609 region
.Include(updateRect
);
610 region
.Exclude(&m_boxRegion
);
611 BRect bounds
= Bounds();
614 ConstrainClippingRegion(®ion
);
615 for (float y
= 0; y
< bounds
.bottom
; y
+= m_backgroundBitmap
->Bounds().Height())
617 for (float x
= 0; x
< bounds
.right
; x
+= m_backgroundBitmap
->Bounds().Width())
619 DrawBitmapAsync(m_backgroundBitmap
, BPoint(x
, y
));
628 region
.Include(updateRect
);
629 region
.Exclude(&m_boxRegion
);
632 SetLowColor(m_backgroundColor
);
633 FillRegion(®ion
, B_SOLID_LOW
);
639 void DiagramView::setBackgroundColor(
642 D_METHOD(("DiagramView::setBackgroundColor()\n"));
643 m_backgroundColor
= color
;
644 m_useBackgroundBitmap
= false;
649 DiagramView::setBackgroundBitmap(BBitmap
* bitmap
)
651 D_METHOD(("DiagramView::setBackgroundBitmap()\n"));
652 if (m_backgroundBitmap
)
653 delete m_backgroundBitmap
;
655 m_backgroundBitmap
= new BBitmap(bitmap
);
656 m_useBackgroundBitmap
= true;
661 DiagramView::updateDataRect()
663 D_METHOD(("DiagramView::updateDataRect()\n"));
664 // calculate the area in which boxes display
665 m_boxRegion
.MakeEmpty();
666 for (uint32 i
= 0; i
< CountItems(DiagramItem::M_BOX
); i
++) {
667 m_boxRegion
.Include(ItemAt(i
, DiagramItem::M_BOX
)->Frame());
669 // adapt the data rect to the new region of boxes
670 BRect boxRect
= m_boxRegion
.Frame();
671 m_dataRect
.right
= boxRect
.right
;
672 m_dataRect
.bottom
= boxRect
.bottom
;
673 // update the scroll bars
678 // #pragma mark - internal operations (private)
682 DiagramView::_beginWireTracking(DiagramEndPoint
*startPoint
)
684 D_METHOD(("DiagramView::beginWireTracking()\n"));
685 m_draggedWire
= createWire(startPoint
);
686 AddItem(m_draggedWire
);
687 SelectItem(m_draggedWire
, true);
688 Invalidate(startPoint
->Frame());
691 void DiagramView::_endWireTracking()
693 D_METHOD(("DiagramView::endWireTracking()\n"));
696 RemoveItem(m_draggedWire
);
697 Invalidate(m_draggedWire
->Frame());
698 DiagramEndPoint
*endPoint
= m_draggedWire
->startPoint();
701 endPoint
= m_draggedWire
->endPoint();
705 Invalidate(endPoint
->Frame());
707 delete m_draggedWire
;
712 void DiagramView::_beginRectTracking(
715 D_METHOD(("DiagramView::beginRectTracking()\n"));
716 BMessage
message(M_RECT_TRACKING
);
717 message
.AddPoint("origin", origin
);
718 DragMessage(&message
, BRect(0.0, 0.0, -1.0, -1.0));
719 BView::BeginRectTracking(BRect(origin
, origin
), B_TRACK_RECT_CORNER
);
724 DiagramView::_trackRect(BPoint origin
, BPoint current
)
726 D_METHOD(("DiagramView::trackRect()\n"));
727 bool changed
= false;
729 rect
.left
= origin
.x
< current
.x
? origin
.x
: current
.x
;
730 rect
.top
= origin
.y
< current
.y
? origin
.y
: current
.y
;
731 rect
.right
= origin
.x
< current
.x
? current
.x
: origin
.x
;
732 rect
.bottom
= origin
.y
< current
.y
? current
.y
: origin
.y
;
733 for (uint32 i
= 0; i
< CountItems(DiagramItem::M_BOX
); i
++) {
734 DiagramBox
*box
= dynamic_cast<DiagramBox
*>(ItemAt(i
, DiagramItem::M_BOX
));
736 if (rect
.Intersects(box
->Frame()))
737 changed
|= SelectItem(box
, false);
739 changed
|= DeselectItem(box
);
744 SortItems(DiagramItem::M_BOX
, &compareSelectionTime
);
751 DiagramView::_updateScrollBars()
753 D_METHOD(("DiagramView::_updateScrollBars()\n"));
754 // fetch the vertical ScrollBar
755 BScrollBar
*scrollBar
= ScrollBar(B_VERTICAL
);
758 // Disable the ScrollBar if the view is large enough to display
759 // the entire data-rect
760 if (Bounds().Height() > m_dataRect
.Height())
762 scrollBar
->SetRange(0.0, 0.0);
763 scrollBar
->SetProportion(1.0);
767 scrollBar
->SetRange(m_dataRect
.top
, m_dataRect
.bottom
- Bounds().Height());
768 scrollBar
->SetProportion(Bounds().Height() / m_dataRect
.Height());
771 scrollBar
= ScrollBar(B_HORIZONTAL
);
774 // Disable the ScrollBar if the view is large enough to display
775 // the entire data-rect
776 if (Bounds().Width() > m_dataRect
.Width())
778 scrollBar
->SetRange(0.0, 0.0);
779 scrollBar
->SetProportion(1.0);
783 scrollBar
->SetRange(m_dataRect
.left
, m_dataRect
.right
- Bounds().Width());
784 scrollBar
->SetProportion(Bounds().Width() / m_dataRect
.Width());
789 // END -- DiagramView.cpp --