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