1 // DiagramBox.cpp 2 3 /*! \class DiagramBox 4 DiagramItem subclass providing a basic framework for 5 boxes in diagrams, i.e. objects that can contain various 6 endpoints 7 */ 8 9 #include "DiagramBox.h" 10 #include "DiagramDefs.h" 11 #include "DiagramEndPoint.h" 12 #include "DiagramView.h" 13 14 #include <Message.h> 15 #include <Messenger.h> 16 17 __USE_CORTEX_NAMESPACE 18 19 #include <Debug.h> 20 #define D_METHOD(x) //PRINT (x) 21 #define D_MESSAGE(x) //PRINT (x) 22 #define D_MOUSE(x) //PRINT (x) 23 #define D_DRAW(x) //PRINT (x) 24 25 26 // #pragma mark - 27 28 29 DiagramBox::DiagramBox(BRect frame, uint32 flags) 30 : DiagramItem(DiagramItem::M_BOX), 31 DiagramItemGroup(DiagramItem::M_ENDPOINT), 32 fFrame(frame), 33 fFlags(flags) 34 { 35 D_METHOD(("DiagramBox::DiagramBox()\n")); 36 makeDraggable(true); 37 } 38 39 40 DiagramBox::~DiagramBox() 41 { 42 D_METHOD(("DiagramBox::~DiagramBox()\n")); 43 } 44 45 46 // #pragma mark - derived from DiagramItemGroup (public) 47 48 49 /*! Extends the DiagramItemGroup implementation by setting 50 the items owner and calling the attachedToDiagram() hook 51 on it 52 */ 53 bool 54 DiagramBox::AddItem(DiagramItem *item) 55 { 56 D_METHOD(("DiagramBox::AddItem()\n")); 57 if (item) { 58 if (DiagramItemGroup::AddItem(item)) { 59 if (m_view) { 60 item->_SetOwner(m_view); 61 item->attachedToDiagram(); 62 } 63 return true; 64 } 65 } 66 return false; 67 } 68 69 70 /*! Extends the DiagramItemGroup implementation by calling 71 the detachedToDiagram() hook on the \a item 72 */ 73 bool 74 DiagramBox::RemoveItem(DiagramItem *item) 75 { 76 D_METHOD(("DiagramBox::RemoveItem()\n")); 77 if (item) { 78 item->detachedFromDiagram(); 79 if (DiagramItemGroup::RemoveItem(item)) { 80 item->_SetOwner(0); 81 return true; 82 } 83 } 84 return false; 85 } 86 87 88 // #pragma mark - derived from DiagramItem (public) 89 90 91 /*! Prepares the drawing stack and clipping region, then 92 calls drawBox 93 */ 94 void 95 DiagramBox::Draw(BRect updateRect) 96 { 97 D_DRAW(("DiagramBox::Draw()\n")); 98 if (view()) { 99 view()->PushState(); 100 { 101 if (fFlags & M_DRAW_UNDER_ENDPOINTS) { 102 BRegion region, clipping; 103 region.Include(Frame()); 104 if (group()->GetClippingAbove(this, &clipping)) 105 region.Exclude(&clipping); 106 view()->ConstrainClippingRegion(®ion); 107 DrawBox(); 108 for (uint32 i = 0; i < CountItems(); i++) { 109 DiagramItem *item = ItemAt(i); 110 if (region.Intersects(item->Frame())) 111 item->Draw(item->Frame()); 112 } 113 } else { 114 BRegion region, clipping; 115 region.Include(Frame()); 116 if (view()->GetClippingAbove(this, &clipping)) 117 region.Exclude(&clipping); 118 for (uint32 i = 0; i < CountItems(); i++) { 119 DiagramItem *item = ItemAt(i); 120 BRect r; 121 if (region.Intersects(r = item->Frame())) { 122 item->Draw(r); 123 region.Exclude(r); 124 } 125 } 126 view()->ConstrainClippingRegion(®ion); 127 DrawBox(); 128 } 129 } 130 view()->PopState(); 131 } 132 } 133 134 135 /*! Is called from the parent DiagramViews MouseDown() implementation 136 if the Box was hit; this version initiates selection and dragging 137 */ 138 void 139 DiagramBox::MouseDown(BPoint point, uint32 buttons, uint32 clicks) 140 { 141 D_MOUSE(("DiagramBox::MouseDown()\n")); 142 DiagramItem *item = ItemUnder(point); 143 if (item) 144 item->MouseDown(point, buttons, clicks); 145 else if (clicks == 1) { 146 if (isSelectable()) { 147 BMessage selectMsg(M_SELECTION_CHANGED); 148 if (modifiers() & B_SHIFT_KEY) 149 selectMsg.AddBool("replace", false); 150 else 151 selectMsg.AddBool("replace", true); 152 selectMsg.AddPointer("item", reinterpret_cast<void *>(this)); 153 DiagramView* v = view(); 154 BMessenger(v).SendMessage(&selectMsg); 155 } 156 if (isDraggable() && (buttons == B_PRIMARY_MOUSE_BUTTON)) { 157 BMessage dragMsg(M_BOX_DRAGGED); 158 dragMsg.AddPointer("item", static_cast<void *>(this)); 159 dragMsg.AddPoint("offset", point - Frame().LeftTop()); 160 view()->DragMessage(&dragMsg, BRect(0.0, 0.0, -1.0, -1.0), view()); 161 } 162 } 163 } 164 165 166 167 /*! Is called from the DiagramViews MouseMoved() when no message is being 168 dragged, but the mouse has moved above the box 169 */ 170 void 171 DiagramBox::MouseOver(BPoint point, uint32 transit) 172 { 173 D_MOUSE(("DiagramBox::MouseOver()\n")); 174 DiagramItem *last = _LastItemUnder(); 175 if (last && (transit == B_EXITED_VIEW)) { 176 last->MouseOver(point, B_EXITED_VIEW); 177 _ResetItemUnder(); 178 } else { 179 DiagramItem *item = ItemUnder(point); 180 if (item) { 181 if (item != last) { 182 if (last) 183 last->MouseOver(point, B_EXITED_VIEW); 184 item->MouseOver(point, B_ENTERED_VIEW); 185 } else 186 item->MouseOver(point, B_INSIDE_VIEW); 187 } 188 else if (last) 189 last->MouseOver(point, B_EXITED_VIEW); 190 } 191 } 192 193 194 /*! Is called from the DiagramViews MouseMoved() when a message is being 195 dragged over the DiagramBox 196 */ 197 void 198 DiagramBox::MessageDragged(BPoint point, uint32 transit, const BMessage *message) 199 { 200 D_MOUSE(("DiagramBox::MessageDragged()\n")); 201 DiagramItem *last = _LastItemUnder(); 202 if (last && (transit == B_EXITED_VIEW)) { 203 last->MessageDragged(point, B_EXITED_VIEW, message); 204 _ResetItemUnder(); 205 } else { 206 DiagramItem *item = ItemUnder(point); 207 if (item) { 208 if (item != last) { 209 if (last) 210 last->MessageDragged(point, B_EXITED_VIEW, message); 211 item->MessageDragged(point, B_ENTERED_VIEW, message); 212 } else 213 item->MessageDragged(point, B_INSIDE_VIEW, message); 214 return; 215 } else if (last) 216 last->MessageDragged(point, B_EXITED_VIEW, message); 217 if (message->what == M_WIRE_DRAGGED) 218 view()->trackWire(point); 219 } 220 } 221 222 223 /*! Is called from the DiagramViews MessageReceived() function when a 224 message has been dropped on the DiagramBox 225 */ 226 void 227 DiagramBox::MessageDropped(BPoint point, BMessage *message) 228 { 229 D_METHOD(("DiagramBox::MessageDropped()\n")); 230 DiagramItem *item = ItemUnder(point); 231 if (item) { 232 item->MessageDropped(point, message); 233 return; 234 } 235 } 236 237 238 // #pragma mark - operations (public) 239 240 241 /*! Moves the box by a given amount, and returns in updateRegion the 242 frames of wires impacted by the move 243 */ 244 void 245 DiagramBox::MoveBy(float x, float y, BRegion *wireRegion) 246 { 247 D_METHOD(("DiagramBox::MoveBy()\n")); 248 if (view()) { 249 view()->PushState(); 250 { 251 for (uint32 i = 0; i < CountItems(); i++) { 252 DiagramEndPoint *endPoint = dynamic_cast<DiagramEndPoint *>(ItemAt(i)); 253 if (endPoint) 254 endPoint->MoveBy(x, y, wireRegion); 255 } 256 if (wireRegion) { 257 wireRegion->Include(fFrame); 258 fFrame.OffsetBy(x, y); 259 wireRegion->Include(fFrame); 260 } 261 else 262 fFrame.OffsetBy(x, y); 263 } 264 view()->PopState(); 265 } 266 } 267 268 269 //! Resizes the boxes frame without doing any updating 270 void 271 DiagramBox::ResizeBy(float horizontal, float vertical) 272 { 273 D_METHOD(("DiagramBox::ResizeBy()\n")); 274 fFrame.right += horizontal; 275 fFrame.bottom += vertical; 276 } 277 278 279 // #pragma mark - internal operations (private) 280 281 282 //! Is called by the DiagramView when added 283 void 284 DiagramBox::_SetOwner(DiagramView *owner) 285 { 286 D_METHOD(("DiagramBox::_SetOwner()\n")); 287 m_view = owner; 288 for (uint32 i = 0; i < CountItems(DiagramItem::M_ENDPOINT); i++) { 289 DiagramItem *item = ItemAt(i); 290 item->_SetOwner(m_view); 291 if (m_view) 292 item->attachedToDiagram(); 293 } 294 } 295