/* * 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. */ // DiagramBox.cpp /*! \class DiagramBox DiagramItem subclass providing a basic framework for boxes in diagrams, i.e. objects that can contain various endpoints */ #include "DiagramBox.h" #include "DiagramDefs.h" #include "DiagramEndPoint.h" #include "DiagramView.h" #include #include __USE_CORTEX_NAMESPACE #include #define D_METHOD(x) //PRINT (x) #define D_MESSAGE(x) //PRINT (x) #define D_MOUSE(x) //PRINT (x) #define D_DRAW(x) //PRINT (x) // #pragma mark - DiagramBox::DiagramBox(BRect frame, uint32 flags) : DiagramItem(DiagramItem::M_BOX), DiagramItemGroup(DiagramItem::M_ENDPOINT), fFrame(frame), fFlags(flags) { D_METHOD(("DiagramBox::DiagramBox()\n")); makeDraggable(true); } DiagramBox::~DiagramBox() { D_METHOD(("DiagramBox::~DiagramBox()\n")); } // #pragma mark - derived from DiagramItemGroup (public) /*! Extends the DiagramItemGroup implementation by setting the items owner and calling the attachedToDiagram() hook on it */ bool DiagramBox::AddItem(DiagramItem *item) { D_METHOD(("DiagramBox::AddItem()\n")); if (item) { if (DiagramItemGroup::AddItem(item)) { if (m_view) { item->_SetOwner(m_view); item->attachedToDiagram(); } return true; } } return false; } /*! Extends the DiagramItemGroup implementation by calling the detachedToDiagram() hook on the \a item */ bool DiagramBox::RemoveItem(DiagramItem *item) { D_METHOD(("DiagramBox::RemoveItem()\n")); if (item) { item->detachedFromDiagram(); if (DiagramItemGroup::RemoveItem(item)) { item->_SetOwner(0); return true; } } return false; } // #pragma mark - derived from DiagramItem (public) /*! Prepares the drawing stack and clipping region, then calls drawBox */ void DiagramBox::Draw(BRect updateRect) { D_DRAW(("DiagramBox::Draw()\n")); if (view()) { view()->PushState(); { if (fFlags & M_DRAW_UNDER_ENDPOINTS) { BRegion region, clipping; region.Include(Frame()); if (group()->GetClippingAbove(this, &clipping)) region.Exclude(&clipping); view()->ConstrainClippingRegion(®ion); DrawBox(); for (uint32 i = 0; i < CountItems(); i++) { DiagramItem *item = ItemAt(i); if (region.Intersects(item->Frame())) item->Draw(item->Frame()); } } else { BRegion region, clipping; region.Include(Frame()); if (view()->GetClippingAbove(this, &clipping)) region.Exclude(&clipping); for (uint32 i = 0; i < CountItems(); i++) { DiagramItem *item = ItemAt(i); BRect r; if (region.Intersects(r = item->Frame())) { item->Draw(r); region.Exclude(r); } } view()->ConstrainClippingRegion(®ion); DrawBox(); } } view()->PopState(); } } /*! Is called from the parent DiagramViews MouseDown() implementation if the Box was hit; this version initiates selection and dragging */ void DiagramBox::MouseDown(BPoint point, uint32 buttons, uint32 clicks) { D_MOUSE(("DiagramBox::MouseDown()\n")); DiagramItem *item = ItemUnder(point); if (item) item->MouseDown(point, buttons, clicks); else if (clicks == 1) { if (isSelectable()) { BMessage selectMsg(M_SELECTION_CHANGED); if (modifiers() & B_SHIFT_KEY) selectMsg.AddBool("replace", false); else selectMsg.AddBool("replace", true); selectMsg.AddPointer("item", reinterpret_cast(this)); DiagramView* v = view(); BMessenger(v).SendMessage(&selectMsg); } if (isDraggable() && (buttons == B_PRIMARY_MOUSE_BUTTON)) { BMessage dragMsg(M_BOX_DRAGGED); dragMsg.AddPointer("item", static_cast(this)); dragMsg.AddPoint("offset", point - Frame().LeftTop()); view()->DragMessage(&dragMsg, BRect(0.0, 0.0, -1.0, -1.0), view()); } } } /*! Is called from the DiagramViews MouseMoved() when no message is being dragged, but the mouse has moved above the box */ void DiagramBox::MouseOver(BPoint point, uint32 transit) { D_MOUSE(("DiagramBox::MouseOver()\n")); DiagramItem *last = _LastItemUnder(); if (last && (transit == B_EXITED_VIEW)) { last->MouseOver(point, B_EXITED_VIEW); _ResetItemUnder(); } 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); } } /*! Is called from the DiagramViews MouseMoved() when a message is being dragged over the DiagramBox */ void DiagramBox::MessageDragged(BPoint point, uint32 transit, const BMessage *message) { D_MOUSE(("DiagramBox::MessageDragged()\n")); DiagramItem *last = _LastItemUnder(); if (last && (transit == B_EXITED_VIEW)) { last->MessageDragged(point, B_EXITED_VIEW, message); _ResetItemUnder(); } 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); return; } else if (last) last->MessageDragged(point, B_EXITED_VIEW, message); if (message->what == M_WIRE_DRAGGED) view()->trackWire(point); } } /*! Is called from the DiagramViews MessageReceived() function when a message has been dropped on the DiagramBox */ void DiagramBox::MessageDropped(BPoint point, BMessage *message) { D_METHOD(("DiagramBox::MessageDropped()\n")); DiagramItem *item = ItemUnder(point); if (item) { item->MessageDropped(point, message); return; } } // #pragma mark - operations (public) /*! Moves the box by a given amount, and returns in updateRegion the frames of wires impacted by the move */ void DiagramBox::MoveBy(float x, float y, BRegion *wireRegion) { D_METHOD(("DiagramBox::MoveBy()\n")); if (view()) { view()->PushState(); { for (uint32 i = 0; i < CountItems(); i++) { DiagramEndPoint *endPoint = dynamic_cast(ItemAt(i)); if (endPoint) endPoint->MoveBy(x, y, wireRegion); } if (wireRegion) { wireRegion->Include(fFrame); fFrame.OffsetBy(x, y); wireRegion->Include(fFrame); } else fFrame.OffsetBy(x, y); } view()->PopState(); } } //! Resizes the boxes frame without doing any updating void DiagramBox::ResizeBy(float horizontal, float vertical) { D_METHOD(("DiagramBox::ResizeBy()\n")); fFrame.right += horizontal; fFrame.bottom += vertical; } // #pragma mark - internal operations (private) //! Is called by the DiagramView when added void DiagramBox::_SetOwner(DiagramView *owner) { D_METHOD(("DiagramBox::_SetOwner()\n")); m_view = owner; for (uint32 i = 0; i < CountItems(DiagramItem::M_ENDPOINT); i++) { DiagramItem *item = ItemAt(i); item->_SetOwner(m_view); if (m_view) item->attachedToDiagram(); } }