/*
* Copyright (c) 1999-2000, Eric Moon.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions, and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// DiagramView.cpp
#include "DiagramView.h"
#include "DiagramDefs.h"
#include "DiagramBox.h"
#include "DiagramEndPoint.h"
#include "DiagramWire.h"
#include <Bitmap.h>
#include <Message.h>
#include <ScrollBar.h>
__USE_CORTEX_NAMESPACE
#include <Debug.h>
#define D_METHOD(x) //PRINT (x)
#define D_HOOK(x) //PRINT (x)
#define D_MESSAGE(x) //PRINT (x)
#define D_MOUSE(x) //PRINT (x)
#define D_DRAW(x) //PRINT (x)
// -------------------------------------------------------- //
// *** ctor/dtor
// -------------------------------------------------------- //
DiagramView::DiagramView(
BRect frame,
const char *name,
bool multiSelection,
uint32 resizingMode,
uint32 flags)
: BView(frame, name, resizingMode, B_WILL_DRAW | B_FRAME_EVENTS | flags),
DiagramItemGroup(DiagramItem::M_BOX | DiagramItem::M_WIRE),
m_lastButton(0),
m_clickCount(0),
m_lastClickPoint(-1.0, -1.0),
m_pressedButton(0),
m_draggedWire(0),
m_useBackgroundBitmap(false),
m_backgroundBitmap(0)
{
D_METHOD(("DiagramView::DiagramView()\n"));
SetViewColor(B_TRANSPARENT_COLOR);
m_dataRect = Bounds();
}
DiagramView::~DiagramView()
{
D_METHOD(("DiagramView::~DiagramView()\n"));
}
// -------------------------------------------------------- //
// *** hook functions
// -------------------------------------------------------- //
void DiagramView::MessageDragged(
BPoint point,
uint32 transit,
const BMessage *message)
{
D_METHOD(("DiagramView::MessageDragged()\n"));
switch (message->what)
{
case M_WIRE_DRAGGED:
{
D_MESSAGE(("DiagramView::MessageDragged(M_WIRE_DROPPED)\n"));
if (!m_draggedWire)
{
DiagramEndPoint *fromEndPoint;
if (message->FindPointer("from", reinterpret_cast<void **>(&fromEndPoint)) == B_OK)
{
_beginWireTracking(fromEndPoint);
}
}
trackWire(point);
break;
}
}
}
void DiagramView::MessageDropped(
BPoint point,
BMessage *message)
{
D_METHOD(("DiagramView::MessageDropped()\n"));
switch (message->what)
{
case M_WIRE_DRAGGED:
{
D_MESSAGE(("DiagramView::MessageDropped(M_WIRE_DROPPED)\n"));
DiagramEndPoint *fromWhich = 0;
if (message->FindPointer("from", reinterpret_cast<void **>(&fromWhich)) == B_OK)
{
connectionAborted(fromWhich);
}
break;
}
}
}
// -------------------------------------------------------- //
// *** derived from BView
// -------------------------------------------------------- //
// initial scrollbar update [e.moon 16nov99]
void DiagramView::AttachedToWindow()
{
D_METHOD(("DiagramView::AttachedToWindow()\n"));
_updateScrollBars();
}
void DiagramView::Draw(
BRect updateRect)
{
D_METHOD(("DiagramView::Draw()\n"));
drawBackground(updateRect);
DrawItems(updateRect, DiagramItem::M_WIRE);
DrawItems(updateRect, DiagramItem::M_BOX);
}
void DiagramView::FrameResized(
float width,
float height)
{
D_METHOD(("DiagramView::FrameResized()\n"));
_updateScrollBars();
}
void DiagramView::GetPreferredSize(
float *width,
float *height) {
D_HOOK(("DiagramView::GetPreferredSize()\n"));
*width = m_dataRect.Width() + 10.0;
*height = m_dataRect.Height() + 10.0;
}
void DiagramView::MessageReceived(
BMessage *message)
{
D_METHOD(("DiagramView::MessageReceived()\n"));
switch (message->what)
{
case M_SELECTION_CHANGED:
{
D_MESSAGE(("DiagramView::MessageReceived(M_SELECTION_CHANGED)\n"));
DiagramItem *item;
if (message->FindPointer("item", reinterpret_cast<void **>(&item)) == B_OK)
{
bool deselectOthers = true;
message->FindBool("replace", &deselectOthers);
if (item->isSelected() && !deselectOthers && MultipleSelection())
{
if (DeselectItem(item))
{
SelectionChanged();
}
}
else if (SelectItem(item, deselectOthers))
{
SortItems(item->type(), &compareSelectionTime);
SelectionChanged();
}
}
break;
}
case M_WIRE_DROPPED:
{
D_MESSAGE(("DiagramView::MessageReceived(M_WIRE_DROPPED)\n"));
DiagramEndPoint *fromWhich = 0;
DiagramEndPoint *toWhich = 0;
bool success = false;
if (message->FindPointer("from", reinterpret_cast<void **>(&fromWhich)) == B_OK)
{
if ((message->FindPointer("to", reinterpret_cast<void **>(&toWhich)) == B_OK)
&& (message->FindBool("success", &success) == B_OK))
{
if (success && fromWhich && toWhich)
{
_endWireTracking();
DiagramWire *wire = createWire(fromWhich, toWhich);
if (wire && AddItem(wire))
{
connectionEstablished(fromWhich, toWhich);
break;
}
}
}
}
connectionAborted(fromWhich);
break;
}
default:
{
if (message->WasDropped())
{
BPoint point = ConvertFromScreen(message->DropPoint());
DiagramItem *item = ItemUnder(point);
if (item)
{
item->MessageDropped(point, message);
return;
}
else
{
MessageDropped(point, message);
}
}
else
{
BView::MessageReceived(message);
}
}
}
}
void DiagramView::KeyDown(
const char *bytes,
int32 numBytes)
{
D_METHOD(("DiagramView::KeyDown()\n"));
switch (bytes[0])
{
case B_LEFT_ARROW:
{
float x;
GetItemAlignment(&x, 0);
BRegion updateRegion;
DragSelectionBy(-x, 0.0, &updateRegion);
for (int32 i = 0; i < updateRegion.CountRects(); i++)
Invalidate(updateRegion.RectAt(i));
updateDataRect();
break;
}
case B_RIGHT_ARROW:
{
float x;
GetItemAlignment(&x, 0);
BRegion updateRegion;
DragSelectionBy(x, 0.0, &updateRegion);
for (int32 i = 0; i < updateRegion.CountRects(); i++)
Invalidate(updateRegion.RectAt(i));
updateDataRect();
break;
}
case B_UP_ARROW:
{
float y;
GetItemAlignment(0, &y);
BRegion updateRegion;
DragSelectionBy(0.0, -y, &updateRegion);
for (int32 i = 0; i < updateRegion.CountRects(); i++)
Invalidate(updateRegion.RectAt(i));
updateDataRect();
break;
}
case B_DOWN_ARROW:
{
float y;
GetItemAlignment(0, &y);
BRegion updateRegion;
DragSelectionBy(0.0, y, &updateRegion);
for (int32 i = 0; i < updateRegion.CountRects(); i++)
Invalidate(updateRegion.RectAt(i));
updateDataRect();
break;
}
default:
{
BView::KeyDown(bytes, numBytes);
break;
}
}
}
void DiagramView::MouseDown(
BPoint point)
{
D_METHOD(("DiagramView::MouseDown()\n"));
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
// update click count
BMessage* message = Window()->CurrentMessage();
int32 clicks = message->FindInt32("clicks");
int32 buttons = message->FindInt32("buttons");
bool moved = (fabs(point.x - m_lastClickPoint.x) > 2.0 || fabs(point.y - m_lastClickPoint.y) > 2.0);
if (!moved && (buttons == m_lastButton) && (clicks > 1))
{
m_clickCount++;
}
else
{
m_clickCount = 1;
}
m_lastButton = buttons;
m_lastClickPoint = point;
m_lastDragPoint = ConvertToScreen(point);
// [e.moon 16nov99] scroll on 3rd button
m_pressedButton = buttons;
if(m_pressedButton == B_TERTIARY_MOUSE_BUTTON) {
return;
}
// was an item clicked ?
DiagramItem *item = ItemUnder(point);
if (item)
{
item->MouseDown(point, m_lastButton, m_clickCount);
}
else // no, the background was clicked
{
if (!(modifiers() & B_SHIFT_KEY) && DeselectAll(DiagramItem::M_ANY))
SelectionChanged();
if (MultipleSelection() && (m_lastButton == B_PRIMARY_MOUSE_BUTTON) && !(modifiers() & B_CONTROL_KEY))
_beginRectTracking(point);
BackgroundMouseDown(point, m_lastButton, m_clickCount);
}
}
void DiagramView::MouseMoved(
BPoint point,
uint32 transit,
const BMessage *message)
{
D_METHOD(("DiagramView::MouseMoved()\n"));
// [e.moon 16nov99] 3rd-button scrolling
if(m_pressedButton == B_TERTIARY_MOUSE_BUTTON) {
// fetch/store screen point; calculate distance travelled
ConvertToScreen(&point);
float xDelta = m_lastDragPoint.x - point.x;
float yDelta = m_lastDragPoint.y - point.y;
m_lastDragPoint = point;
// constrain to scrollbar limits
BScrollBar* hScroll = ScrollBar(B_HORIZONTAL);
if(xDelta && hScroll) {
float val = hScroll->Value();
float min, max;
hScroll->GetRange(&min, &max);
if(val + xDelta < min)
xDelta = 0;
if(val + xDelta > max)
xDelta = 0;
}
BScrollBar* vScroll = ScrollBar(B_VERTICAL);
if(yDelta && vScroll) {
float val = vScroll->Value();
float min, max;
vScroll->GetRange(&min, &max);
if(val + yDelta < min)
yDelta = 0;
if(val + yDelta > max)
yDelta = 0;
}
// scroll
if(xDelta == 0.0 && yDelta == 0.0)
return;
ScrollBy(xDelta, yDelta);
return;
}
if (message)
{
switch (message->what)
{
case M_RECT_TRACKING:
{
BPoint origin;
if (message->FindPoint("origin", &origin) == B_OK)
{
_trackRect(origin, point);
}
break;
}
case M_BOX_DRAGGED:
{
DiagramBox *box;
BPoint offset;
if ((message->FindPointer("item", reinterpret_cast<void **>(&box)) == B_OK)
&& (message->FindPoint("offset", &offset) == B_OK))
{
if (box)
{
BRegion updateRegion;
DragSelectionBy(point.x - box->Frame().left - offset.x,
point.y - box->Frame().top - offset.y,
&updateRegion);
updateDataRect();
for (int32 i = 0; i < updateRegion.CountRects(); i++)
{
Invalidate(updateRegion.RectAt(i));
}
}
}
break;
}
default: // unkwown message -> redirect to MessageDragged()
{
DiagramItem *last = _LastItemUnder();
if (transit == B_EXITED_VIEW)
{
if (last)
{
last->MessageDragged(point, B_EXITED_VIEW, message);
MessageDragged(point, B_EXITED_VIEW, message);
}
}
else
{
DiagramItem *item = ItemUnder(point);
if (item)
{
if (item != last)
{
if (last)
last->MessageDragged(point, B_EXITED_VIEW, message);
item->MessageDragged(point, B_ENTERED_VIEW, message);
}
else
{
item->MessageDragged(point, B_INSIDE_VIEW, message);
}
}
else if (last)
{
last->MessageDragged(point, B_EXITED_VIEW, message);
MessageDragged(point, B_ENTERED_VIEW, message);
}
else
{
MessageDragged(point, transit, message);
}
}
break;
}
}
}
else // no message at all -> redirect to MouseOver()
{
DiagramItem *last = _LastItemUnder();
if ((transit == B_EXITED_VIEW) || (transit == B_OUTSIDE_VIEW))
{
if (last)
{
last->MouseOver(point, B_EXITED_VIEW);
_ResetItemUnder();
MouseOver(point, B_EXITED_VIEW);
}
}
else
{
DiagramItem *item = ItemUnder(point);
if (item)
{
if (item != last)
{
if (last)
last->MouseOver(point, B_EXITED_VIEW);
item->MouseOver(point, B_ENTERED_VIEW);
}
else
{
item->MouseOver(point, B_INSIDE_VIEW);
}
}
else if (last)
{
last->MouseOver(point, B_EXITED_VIEW);
MouseOver(point, B_ENTERED_VIEW);
}
}
}
}
void DiagramView::MouseUp(
BPoint point)
{
D_METHOD(("DiagramView::MouseUp()\n"));
if (MultipleSelection())
EndRectTracking();
_endWireTracking();
// [e.moon 16nov99] mark no button as down
m_pressedButton = 0;
}
// -------------------------------------------------------- //
// *** derived from DiagramItemGroup (public)
// -------------------------------------------------------- //
bool DiagramView::AddItem(
DiagramItem *item)
{
D_METHOD(("DiagramBox::AddItem()\n"));
if (item)
{
if (DiagramItemGroup::AddItem(item))
{
item->_SetOwner(this);
item->attachedToDiagram();
if (item->type() == DiagramItem::M_BOX)
{
updateDataRect();
}
return true;
}
}
return false;
}
bool DiagramView::RemoveItem(
DiagramItem *item)
{
D_METHOD(("DiagramBox::RemoveItem()\n"));
if (item)
{
item->detachedFromDiagram();
if (DiagramItemGroup::RemoveItem(item))
{
item->_SetOwner(0);
if (item->type() == DiagramItem::M_BOX)
{
updateDataRect();
}
return true;
}
}
return false;
}
// -------------------------------------------------------- //
// *** operations (public)
// -------------------------------------------------------- //
void DiagramView::trackWire(
BPoint point)
{
D_MOUSE(("DiagramView::trackWire()\n"));
if (m_draggedWire)
{
BRegion region;
region.Include(m_draggedWire->Frame());
m_draggedWire->m_dragEndPoint = point;
m_draggedWire->endPointMoved();
region.Include(m_draggedWire->Frame());
region.Exclude(&m_boxRegion);
for (int32 i = 0; i < region.CountRects(); i++)
{
Invalidate(region.RectAt(i));
}
}
}
// -------------------------------------------------------- //
// *** internal operations (protected)
// -------------------------------------------------------- //
void DiagramView::drawBackground(
BRect updateRect)
{
D_METHOD(("DiagramView::drawBackground()\n"));
if (m_useBackgroundBitmap)
{
BRegion region;
region.Include(updateRect);
region.Exclude(&m_boxRegion);
BRect bounds = Bounds();
PushState();
{
ConstrainClippingRegion(®ion);
for (float y = 0; y < bounds.bottom; y += m_backgroundBitmap->Bounds().Height())
{
for (float x = 0; x < bounds.right; x += m_backgroundBitmap->Bounds().Width())
{
DrawBitmapAsync(m_backgroundBitmap, BPoint(x, y));
}
}
}
PopState();
}
else
{
BRegion region;
region.Include(updateRect);
region.Exclude(&m_boxRegion);
PushState();
{
SetLowColor(m_backgroundColor);
FillRegion(®ion, B_SOLID_LOW);
}
PopState();
}
}
void DiagramView::setBackgroundColor(
rgb_color color)
{
D_METHOD(("DiagramView::setBackgroundColor()\n"));
m_backgroundColor = color;
m_useBackgroundBitmap = false;
}
void
DiagramView::setBackgroundBitmap(BBitmap* bitmap)
{
D_METHOD(("DiagramView::setBackgroundBitmap()\n"));
if (m_backgroundBitmap)
delete m_backgroundBitmap;
m_backgroundBitmap = new BBitmap(bitmap);
m_useBackgroundBitmap = true;
}
void
DiagramView::updateDataRect()
{
D_METHOD(("DiagramView::updateDataRect()\n"));
// calculate the area in which boxes display
m_boxRegion.MakeEmpty();
for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++) {
m_boxRegion.Include(ItemAt(i, DiagramItem::M_BOX)->Frame());
}
// adapt the data rect to the new region of boxes
BRect boxRect = m_boxRegion.Frame();
m_dataRect.right = boxRect.right;
m_dataRect.bottom = boxRect.bottom;
// update the scroll bars
_updateScrollBars();
}
// #pragma mark - internal operations (private)
void
DiagramView::_beginWireTracking(DiagramEndPoint *startPoint)
{
D_METHOD(("DiagramView::beginWireTracking()\n"));
m_draggedWire = createWire(startPoint);
AddItem(m_draggedWire);
SelectItem(m_draggedWire, true);
Invalidate(startPoint->Frame());
}
void DiagramView::_endWireTracking()
{
D_METHOD(("DiagramView::endWireTracking()\n"));
if (m_draggedWire)
{
RemoveItem(m_draggedWire);
Invalidate(m_draggedWire->Frame());
DiagramEndPoint *endPoint = m_draggedWire->startPoint();
if (!endPoint)
{
endPoint = m_draggedWire->endPoint();
}
if (endPoint)
{
Invalidate(endPoint->Frame());
}
delete m_draggedWire;
m_draggedWire = 0;
}
}
void DiagramView::_beginRectTracking(
BPoint origin)
{
D_METHOD(("DiagramView::beginRectTracking()\n"));
BMessage message(M_RECT_TRACKING);
message.AddPoint("origin", origin);
DragMessage(&message, BRect(0.0, 0.0, -1.0, -1.0));
BView::BeginRectTracking(BRect(origin, origin), B_TRACK_RECT_CORNER);
}
void
DiagramView::_trackRect(BPoint origin, BPoint current)
{
D_METHOD(("DiagramView::trackRect()\n"));
bool changed = false;
BRect rect;
rect.left = origin.x < current.x ? origin.x : current.x;
rect.top = origin.y < current.y ? origin.y : current.y;
rect.right = origin.x < current.x ? current.x : origin.x;
rect.bottom = origin.y < current.y ? current.y : origin.y;
for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++) {
DiagramBox *box = dynamic_cast<DiagramBox *>(ItemAt(i, DiagramItem::M_BOX));
if (box) {
if (rect.Intersects(box->Frame()))
changed |= SelectItem(box, false);
else
changed |= DeselectItem(box);
}
}
if (changed) {
SortItems(DiagramItem::M_BOX, &compareSelectionTime);
SelectionChanged();
}
}
void
DiagramView::_updateScrollBars()
{
D_METHOD(("DiagramView::_updateScrollBars()\n"));
// fetch the vertical ScrollBar
BScrollBar *scrollBar = ScrollBar(B_VERTICAL);
if (scrollBar)
{
// Disable the ScrollBar if the view is large enough to display
// the entire data-rect
if (Bounds().Height() > m_dataRect.Height())
{
scrollBar->SetRange(0.0, 0.0);
scrollBar->SetProportion(1.0);
}
else
{
scrollBar->SetRange(m_dataRect.top, m_dataRect.bottom - Bounds().Height());
scrollBar->SetProportion(Bounds().Height() / m_dataRect.Height());
}
}
scrollBar = ScrollBar(B_HORIZONTAL);
if (scrollBar)
{
// Disable the ScrollBar if the view is large enough to display
// the entire data-rect
if (Bounds().Width() > m_dataRect.Width())
{
scrollBar->SetRange(0.0, 0.0);
scrollBar->SetProportion(1.0);
}
else
{
scrollBar->SetRange(m_dataRect.left, m_dataRect.right - Bounds().Width());
scrollBar->SetProportion(Bounds().Width() / m_dataRect.Width());
}
}
}
// END -- DiagramView.cpp --
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: m_backgroundColor.