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// DiagramEndPoint.cpp
33
34#include "DiagramEndPoint.h"
35#include "DiagramDefs.h"
36#include "DiagramWire.h"
37#include "DiagramView.h"
38
39#include <Message.h>
40#include <Messenger.h>
41__USE_CORTEX_NAMESPACE
42
43#include <Debug.h>
44#define D_METHOD(x) //PRINT (x)
45#define D_MESSAGE(x) //PRINT (x)
46#define D_MOUSE(x) //PRINT (x)
47
48// -------------------------------------------------------- //
49// *** ctor/dtor
50// -------------------------------------------------------- //
51
52DiagramEndPoint::DiagramEndPoint(
53	BRect frame)
54	: DiagramItem(DiagramItem::M_ENDPOINT),
55	  m_frame(frame),
56	  m_wire(0),
57	  m_connected(false),
58	  m_connecting(false)
59{
60	D_METHOD(("DiagramEndPoint::DiagramEndPoint()\n"));
61	makeDraggable(true);
62}
63
64DiagramEndPoint::~DiagramEndPoint()
65{
66	D_METHOD(("DiagramEndPoint::~DiagramEndPoint()\n"));
67}
68
69// -------------------------------------------------------- //
70// *** hook functions
71// -------------------------------------------------------- //
72
73BPoint DiagramEndPoint::connectionPoint() const
74{
75	D_METHOD(("DiagramEndPoint::connectionPoint()\n"));
76	return BPoint(Frame().left + Frame().Height() / 2.0, Frame().top + Frame().Width() / 2.0);
77}
78
79bool DiagramEndPoint::connectionRequested(
80	DiagramEndPoint *fromWhich)
81{
82	D_METHOD(("DiagramEndPoint::connectionRequested()\n"));
83	if (!isConnected())
84	{
85		return true;
86	}
87	return false;
88}
89
90// -------------------------------------------------------- //
91// *** derived from DiagramItem
92// -------------------------------------------------------- //
93
94void DiagramEndPoint::Draw(
95	BRect updateRect)
96{
97	D_METHOD(("DiagramEndPoint::Draw()\n"));
98	if (view())
99	{
100		view()->PushState();
101		{
102			BRegion region;
103			region.Include(Frame() & updateRect);
104			view()->ConstrainClippingRegion(&region);
105			drawEndPoint();
106		}
107		view()->PopState();
108	}
109}
110
111void DiagramEndPoint::MouseDown(
112	BPoint point,
113	uint32 buttons,
114	uint32 clicks)
115{
116	D_MOUSE(("DiagramEndPoint::MouseDown()\n"));
117	if (clicks == 1)
118	{
119		if (isSelectable())
120		{
121			BMessage selectMsg(M_SELECTION_CHANGED);
122			if (modifiers() & B_SHIFT_KEY)
123			{
124				selectMsg.AddBool("replace", false);
125			}
126			else
127			{
128				selectMsg.AddBool("replace", true);
129			}
130			selectMsg.AddPointer("item", reinterpret_cast<void *>(this));
131			DiagramView* v = view();
132			BMessenger(v).SendMessage(&selectMsg);
133		}
134		if (isDraggable() && (buttons == B_PRIMARY_MOUSE_BUTTON))
135		{
136			BMessage dragMsg(M_WIRE_DRAGGED);
137			dragMsg.AddPointer("from", static_cast<void *>(this));
138			view()->DragMessage(&dragMsg, BRect(0.0, 0.0, -1.0, -1.0), view());
139			view()->MessageDragged(point, B_INSIDE_VIEW, &dragMsg);
140		}
141	}
142}
143
144void DiagramEndPoint::MessageDragged(
145	BPoint point,
146	uint32 transit,
147	const BMessage *message)
148{
149	D_MOUSE(("DiagramEndPoint::MessageDragged()\n"));
150	switch (message->what)
151	{
152		case M_WIRE_DRAGGED:
153		{
154			D_MESSAGE(("DiagramEndPoint::MessageDragged(M_WIRE_DRAGGED)\n"));
155			switch (transit)
156			{
157				case B_INSIDE_VIEW:
158				{
159					//PRINT((" -> transit: B_INSIDE_VIEW\n"));
160					// this is a WORK-AROUND caused by the unreliability
161					// of BViews DragMessage() routine !!
162					if (isConnecting())
163					{
164						break;
165					}
166					else if (isConnected())
167					{
168						view()->trackWire(point);
169					}
170					/* this should be enough in theory:
171					if (!isConnecting())
172					{
173						view()->trackWire(point);
174					}
175					//break;*/
176				}
177				case B_ENTERED_VIEW:
178				{
179					//PRINT((" -> transit: B_ENTERED_VIEW\n"));
180					DiagramEndPoint *endPoint;
181					if ((message->FindPointer("from", reinterpret_cast<void **>(&endPoint)) == B_OK)
182					 && (endPoint != this))
183					{
184						if (connectionRequested(endPoint))
185						{
186							view()->trackWire(connectionPoint());
187							if (!isConnecting())
188							{
189								m_connecting = true;
190								connected();
191							}
192						}
193						else
194						{
195							view()->trackWire(point);
196							if (isConnecting())
197							{
198								m_connecting = false;
199								disconnected();
200							}
201						}
202					}
203					break;
204				}
205				case B_EXITED_VIEW:
206				{
207					//PRINT((" -> transit: B_EXITED_VIEW\n"));
208					if (isConnecting())
209					{
210						m_connecting = false;
211						disconnected();
212					}
213					break;
214				}
215			}
216			break;
217		}
218		default:
219		{
220			DiagramItem::MessageDragged(point, transit, message);
221		}
222	}
223}
224
225void DiagramEndPoint::MessageDropped(
226	BPoint point,
227	BMessage *message)
228{
229	D_MESSAGE(("DiagramEndPoint::MessageDropped()\n"));
230	switch (message->what)
231	{
232		case M_WIRE_DRAGGED:
233		{
234			D_MESSAGE(("DiagramEndPoint::MessageDropped(M_WIRE_DRAGGED)\n"));
235			DiagramEndPoint *endPoint;
236			if ((message->FindPointer("from", reinterpret_cast<void **>(&endPoint)) == B_OK)
237			 && (endPoint != this))
238			{
239				bool success = connectionRequested(endPoint);
240				BMessage dropMsg(M_WIRE_DROPPED);
241				dropMsg.AddPointer("from", reinterpret_cast<void *>(endPoint));
242				dropMsg.AddPointer("to", reinterpret_cast<void *>(this));
243				dropMsg.AddBool("success", success);
244				DiagramView* v = view();
245				BMessenger(v).SendMessage(&dropMsg);
246				if (isConnecting())
247				{
248					m_connecting = false;
249					disconnected();
250				}
251			}
252			return;
253		}
254		default:
255		{
256			DiagramItem::MessageDropped(point, message);
257		}
258	}
259}
260
261// -------------------------------------------------------- //
262// *** frame related operations
263// -------------------------------------------------------- //
264
265void DiagramEndPoint::MoveBy(
266	float x,
267	float y,
268	BRegion *updateRegion)
269{
270	D_METHOD(("DiagramEndPoint::MoveBy()\n"));
271	if (isConnected() && m_wire && updateRegion)
272	{
273		updateRegion->Include(m_wire->Frame());
274		m_frame.OffsetBy(x, y);
275		m_wire->endPointMoved(this);
276		updateRegion->Include(m_wire->Frame());
277	}
278	else
279	{
280		m_frame.OffsetBy(x, y);
281		if (m_wire)
282		{
283			m_wire->endPointMoved(this);
284		}
285	}
286}
287
288void DiagramEndPoint::ResizeBy(
289	float horizontal,
290	float vertical)
291{
292	D_METHOD(("DiagramEndPoint::ResizeBy()\n"));
293	m_frame.right += horizontal;
294	m_frame.bottom += vertical;
295}
296
297// -------------------------------------------------------- //
298// *** connection methods
299// -------------------------------------------------------- //
300
301void DiagramEndPoint::connect(
302	DiagramWire *wire)
303{
304	D_METHOD(("DiagramEndPoint::connect()\n"));
305	if (!m_connected && wire)
306	{
307		m_connected = true;
308		m_wire = wire;
309		makeDraggable(false);
310		connected();
311	}
312}
313
314void DiagramEndPoint::disconnect()
315{
316	D_METHOD(("DiagramEndPoint::disconnect()\n"));
317	if (m_connected)
318	{
319		m_connected = false;
320		m_wire = 0;
321		makeDraggable(true);
322		disconnected();
323	}
324}
325
326// END -- DiagramEndPoint.cpp --
327