xref: /haiku/src/apps/icon-o-matic/shape/PathManipulator.cpp (revision 7c4b3726d9477810a630f9c0d0604a36473df559)
1128277c9SStephan Aßmus /*
2*7c4b3726SStephan Aßmus  * Copyright 2006-2007, Haiku.
3128277c9SStephan Aßmus  * Distributed under the terms of the MIT License.
4128277c9SStephan Aßmus  *
5128277c9SStephan Aßmus  * Authors:
6128277c9SStephan Aßmus  *		Stephan Aßmus <superstippi@gmx.de>
7128277c9SStephan Aßmus  */
8128277c9SStephan Aßmus 
9128277c9SStephan Aßmus #include "PathManipulator.h"
10128277c9SStephan Aßmus 
11128277c9SStephan Aßmus #include <float.h>
12128277c9SStephan Aßmus #include <stdio.h>
13128277c9SStephan Aßmus 
14128277c9SStephan Aßmus #include <Cursor.h>
15128277c9SStephan Aßmus #include <Message.h>
16f4bd80a2SStephan Aßmus #include <MenuItem.h>
17f4bd80a2SStephan Aßmus #include <PopUpMenu.h>
18128277c9SStephan Aßmus #include <Window.h>
19128277c9SStephan Aßmus 
20128277c9SStephan Aßmus #include "cursors.h"
21128277c9SStephan Aßmus #include "support.h"
22128277c9SStephan Aßmus 
23f67876a0SStephan Aßmus #include "CanvasView.h"
24128277c9SStephan Aßmus 
25128277c9SStephan Aßmus #include "AddPointCommand.h"
26128277c9SStephan Aßmus #include "ChangePointCommand.h"
27128277c9SStephan Aßmus //#include "CloseCommand.h"
28128277c9SStephan Aßmus #include "InsertPointCommand.h"
29*7c4b3726SStephan Aßmus #include "FlipPointsCommand.h"
30128277c9SStephan Aßmus //#include "NewPathCommand.h"
310e1ba39fSStephan Aßmus #include "NudgePointsCommand.h"
32128277c9SStephan Aßmus //#include "RemovePathCommand.h"
33128277c9SStephan Aßmus #include "RemovePointsCommand.h"
34128277c9SStephan Aßmus //#include "ReversePathCommand.h"
35128277c9SStephan Aßmus //#include "SelectPathCommand.h"
36128277c9SStephan Aßmus //#include "SelectPointsCommand.h"
37f4bd80a2SStephan Aßmus #include "SplitPointsCommand.h"
380e1ba39fSStephan Aßmus #include "TransformPointsBox.h"
39128277c9SStephan Aßmus 
40128277c9SStephan Aßmus #define POINT_EXTEND 3.0
41128277c9SStephan Aßmus #define CONTROL_POINT_EXTEND 2.0
42128277c9SStephan Aßmus #define INSERT_DIST_THRESHOLD 7.0
43128277c9SStephan Aßmus #define MOVE_THRESHOLD 9.0
44128277c9SStephan Aßmus 
45128277c9SStephan Aßmus enum {
46128277c9SStephan Aßmus 	UNDEFINED,
47128277c9SStephan Aßmus 
48128277c9SStephan Aßmus 	NEW_PATH,
49128277c9SStephan Aßmus 
50128277c9SStephan Aßmus 	ADD_POINT,
51128277c9SStephan Aßmus 	INSERT_POINT,
52128277c9SStephan Aßmus 	MOVE_POINT,
53128277c9SStephan Aßmus 	MOVE_POINT_IN,
54128277c9SStephan Aßmus 	MOVE_POINT_OUT,
55128277c9SStephan Aßmus 	CLOSE_PATH,
56128277c9SStephan Aßmus 
57128277c9SStephan Aßmus 	TOGGLE_SHARP,
58128277c9SStephan Aßmus 	TOGGLE_SHARP_IN,
59128277c9SStephan Aßmus 	TOGGLE_SHARP_OUT,
60128277c9SStephan Aßmus 
61128277c9SStephan Aßmus 	REMOVE_POINT,
62128277c9SStephan Aßmus 	REMOVE_POINT_IN,
63128277c9SStephan Aßmus 	REMOVE_POINT_OUT,
64128277c9SStephan Aßmus 
65128277c9SStephan Aßmus 	SELECT_POINTS,
66128277c9SStephan Aßmus 	TRANSFORM_POINTS,
67128277c9SStephan Aßmus 	TRANSLATE_POINTS,
68128277c9SStephan Aßmus 
69128277c9SStephan Aßmus 	SELECT_SUB_PATH,
70128277c9SStephan Aßmus };
71128277c9SStephan Aßmus 
72f4bd80a2SStephan Aßmus enum {
73f4bd80a2SStephan Aßmus 	MSG_TRANSFORM				= 'strn',
74f4bd80a2SStephan Aßmus 	MSG_REMOVE_POINTS			= 'srmp',
75f4bd80a2SStephan Aßmus 	MSG_UPDATE_SHAPE_UI			= 'udsi',
76f4bd80a2SStephan Aßmus 
77f4bd80a2SStephan Aßmus 	MSG_SPLIT_POINTS			= 'splt',
78*7c4b3726SStephan Aßmus 	MSG_FLIP_POINTS				= 'flip',
79f4bd80a2SStephan Aßmus };
80f4bd80a2SStephan Aßmus 
81128277c9SStephan Aßmus inline const char*
82128277c9SStephan Aßmus string_for_mode(uint32 mode)
83128277c9SStephan Aßmus {
84128277c9SStephan Aßmus 	switch (mode) {
85128277c9SStephan Aßmus 		case UNDEFINED:
86128277c9SStephan Aßmus 			return "UNDEFINED";
87128277c9SStephan Aßmus 		case NEW_PATH:
88128277c9SStephan Aßmus 			return "NEW_PATH";
89128277c9SStephan Aßmus 		case ADD_POINT:
90128277c9SStephan Aßmus 			return "ADD_POINT";
91128277c9SStephan Aßmus 		case INSERT_POINT:
92128277c9SStephan Aßmus 			return "INSERT_POINT";
93128277c9SStephan Aßmus 		case MOVE_POINT:
94128277c9SStephan Aßmus 			return "MOVE_POINT";
95128277c9SStephan Aßmus 		case MOVE_POINT_IN:
96128277c9SStephan Aßmus 			return "MOVE_POINT_IN";
97128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
98128277c9SStephan Aßmus 			return "MOVE_POINT_OUT";
99128277c9SStephan Aßmus 		case CLOSE_PATH:
100128277c9SStephan Aßmus 			return "CLOSE_PATH";
101128277c9SStephan Aßmus 		case TOGGLE_SHARP:
102128277c9SStephan Aßmus 			return "TOGGLE_SHARP";
103128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
104128277c9SStephan Aßmus 			return "TOGGLE_SHARP_IN";
105128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
106128277c9SStephan Aßmus 			return "TOGGLE_SHARP_OUT";
107128277c9SStephan Aßmus 		case REMOVE_POINT:
108128277c9SStephan Aßmus 			return "REMOVE_POINT";
109128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
110128277c9SStephan Aßmus 			return "REMOVE_POINT_IN";
111128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
112128277c9SStephan Aßmus 			return "REMOVE_POINT_OUT";
113128277c9SStephan Aßmus 		case SELECT_POINTS:
114128277c9SStephan Aßmus 			return "SELECT_POINTS";
115128277c9SStephan Aßmus 		case TRANSFORM_POINTS:
116128277c9SStephan Aßmus 			return "TRANSFORM_POINTS";
117128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
118128277c9SStephan Aßmus 			return "TRANSLATE_POINTS";
119128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
120128277c9SStephan Aßmus 			return "SELECT_SUB_PATH";
121128277c9SStephan Aßmus 	}
122128277c9SStephan Aßmus 	return "<unknown mode>";
123128277c9SStephan Aßmus }
124128277c9SStephan Aßmus 
125ce181bb0SStephan Aßmus class PathManipulator::Selection : protected BList
126128277c9SStephan Aßmus {
127128277c9SStephan Aßmus public:
128128277c9SStephan Aßmus 	inline Selection(int32 count = 20)
129128277c9SStephan Aßmus 		: BList(count) {}
130128277c9SStephan Aßmus 	inline ~Selection() {}
131128277c9SStephan Aßmus 
132128277c9SStephan Aßmus 	inline void Add(int32 value)
133128277c9SStephan Aßmus 		{
134128277c9SStephan Aßmus 			if (value >= 0) {
135128277c9SStephan Aßmus 				// keep the list sorted
136128277c9SStephan Aßmus 				int32 count = CountItems();
137128277c9SStephan Aßmus 				int32 index = 0;
138128277c9SStephan Aßmus 				for (; index < count; index++) {
139128277c9SStephan Aßmus 					if (IndexAt(index) > value) {
140128277c9SStephan Aßmus 						break;
141128277c9SStephan Aßmus 					}
142128277c9SStephan Aßmus 				}
143128277c9SStephan Aßmus 				BList::AddItem((void*)value, index);
144128277c9SStephan Aßmus 			}
145128277c9SStephan Aßmus 		}
146128277c9SStephan Aßmus 
147128277c9SStephan Aßmus 	inline bool Remove(int32 value)
148128277c9SStephan Aßmus 		{ return BList::RemoveItem((void*)value); }
149128277c9SStephan Aßmus 
150128277c9SStephan Aßmus 	inline bool Contains(int32 value) const
151128277c9SStephan Aßmus 		{ return BList::HasItem((void*)value); }
152128277c9SStephan Aßmus 
153128277c9SStephan Aßmus 	inline bool IsEmpty() const
154128277c9SStephan Aßmus 		{ return BList::IsEmpty(); }
155128277c9SStephan Aßmus 
156128277c9SStephan Aßmus 	inline int32 IndexAt(int32 index) const
157128277c9SStephan Aßmus 		{ return (int32)BList::ItemAt(index); }
158128277c9SStephan Aßmus 
159128277c9SStephan Aßmus 	inline void MakeEmpty()
160128277c9SStephan Aßmus 		{ BList::MakeEmpty(); }
161128277c9SStephan Aßmus 
162128277c9SStephan Aßmus 	inline int32* Items() const
163128277c9SStephan Aßmus 		{ return (int32*)BList::Items(); }
164128277c9SStephan Aßmus 
165128277c9SStephan Aßmus 	inline const int32 CountItems() const
166128277c9SStephan Aßmus 		{ return BList::CountItems(); }
167128277c9SStephan Aßmus 
168128277c9SStephan Aßmus 	inline Selection& operator =(const Selection& other)
169128277c9SStephan Aßmus 		{
170128277c9SStephan Aßmus 			MakeEmpty();
171128277c9SStephan Aßmus 			int32 count = other.CountItems();
172128277c9SStephan Aßmus 			int32* items = other.Items();
173128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++) {
174128277c9SStephan Aßmus 				Add(items[i]);
175128277c9SStephan Aßmus 			}
176128277c9SStephan Aßmus 			return *this;
177128277c9SStephan Aßmus 		}
178128277c9SStephan Aßmus 
179128277c9SStephan Aßmus 	inline bool operator ==(const Selection& other)
180128277c9SStephan Aßmus 		{
181128277c9SStephan Aßmus 			if (other.CountItems() == CountItems()) {
182128277c9SStephan Aßmus 				int32* items = Items();
183128277c9SStephan Aßmus 				int32* otherItems = other.Items();
184128277c9SStephan Aßmus 				for (int32 i = 0; i < CountItems(); i++) {
185128277c9SStephan Aßmus 					if (items[i] != otherItems[i])
186128277c9SStephan Aßmus 						return false;
187128277c9SStephan Aßmus 					items++;
188128277c9SStephan Aßmus 					otherItems++;
189128277c9SStephan Aßmus 				}
190128277c9SStephan Aßmus 				return true;
191128277c9SStephan Aßmus 			} else
192128277c9SStephan Aßmus 				return false;
193128277c9SStephan Aßmus 		}
194128277c9SStephan Aßmus 
195128277c9SStephan Aßmus 	inline bool operator !=(const Selection& other)
196128277c9SStephan Aßmus 	{
197128277c9SStephan Aßmus 		return !(*this == other);
198128277c9SStephan Aßmus 	}
199128277c9SStephan Aßmus };
200128277c9SStephan Aßmus 
201128277c9SStephan Aßmus 
202128277c9SStephan Aßmus // constructor
203128277c9SStephan Aßmus PathManipulator::PathManipulator(VectorPath* path)
2040e1ba39fSStephan Aßmus 	: Manipulator(NULL),
205128277c9SStephan Aßmus 	  fCanvasView(NULL),
206128277c9SStephan Aßmus 
207128277c9SStephan Aßmus 	  fCommandDown(false),
208128277c9SStephan Aßmus 	  fOptionDown(false),
209128277c9SStephan Aßmus 	  fShiftDown(false),
210128277c9SStephan Aßmus 	  fAltDown(false),
211128277c9SStephan Aßmus 
212128277c9SStephan Aßmus 	  fClickToClose(false),
213128277c9SStephan Aßmus 
214128277c9SStephan Aßmus 	  fMode(NEW_PATH),
215128277c9SStephan Aßmus 	  fFallBackMode(SELECT_POINTS),
216128277c9SStephan Aßmus 
217128277c9SStephan Aßmus 	  fMouseDown(false),
218128277c9SStephan Aßmus 
219128277c9SStephan Aßmus 	  fPath(path),
220128277c9SStephan Aßmus 	  fCurrentPathPoint(-1),
221128277c9SStephan Aßmus 
222128277c9SStephan Aßmus 	  fChangePointCommand(NULL),
223128277c9SStephan Aßmus 	  fInsertPointCommand(NULL),
224128277c9SStephan Aßmus 	  fAddPointCommand(NULL),
225128277c9SStephan Aßmus 
226128277c9SStephan Aßmus 	  fSelection(new Selection()),
227128277c9SStephan Aßmus 	  fOldSelection(new Selection()),
2280e1ba39fSStephan Aßmus 	  fTransformBox(NULL),
229128277c9SStephan Aßmus 
230128277c9SStephan Aßmus 	  fNudgeOffset(0.0, 0.0),
2310e1ba39fSStephan Aßmus 	  fLastNudgeTime(system_time()),
2320e1ba39fSStephan Aßmus 	  fNudgeCommand(NULL)
233128277c9SStephan Aßmus {
2340e1ba39fSStephan Aßmus 	fPath->Acquire();
23505fd3818SStephan Aßmus 	fPath->AddListener(this);
2360e1ba39fSStephan Aßmus 	fPath->AddObserver(this);
237128277c9SStephan Aßmus }
238128277c9SStephan Aßmus 
239128277c9SStephan Aßmus // destructor
240128277c9SStephan Aßmus PathManipulator::~PathManipulator()
241128277c9SStephan Aßmus {
242128277c9SStephan Aßmus 	delete fChangePointCommand;
243128277c9SStephan Aßmus 	delete fInsertPointCommand;
244128277c9SStephan Aßmus 	delete fAddPointCommand;
245128277c9SStephan Aßmus 
246128277c9SStephan Aßmus 	delete fSelection;
247128277c9SStephan Aßmus 	delete fOldSelection;
2480e1ba39fSStephan Aßmus 	delete fTransformBox;
249128277c9SStephan Aßmus 
2500e1ba39fSStephan Aßmus 	delete fNudgeCommand;
2510e1ba39fSStephan Aßmus 
2520e1ba39fSStephan Aßmus 	fPath->RemoveObserver(this);
2530e1ba39fSStephan Aßmus 	fPath->RemoveListener(this);
2540e1ba39fSStephan Aßmus 	fPath->Release();
255128277c9SStephan Aßmus }
256128277c9SStephan Aßmus 
257128277c9SStephan Aßmus 
258128277c9SStephan Aßmus // #pragma mark -
259128277c9SStephan Aßmus 
260128277c9SStephan Aßmus class StrokePathIterator : public VectorPath::Iterator {
261128277c9SStephan Aßmus  public:
262f67876a0SStephan Aßmus 					StrokePathIterator(CanvasView* canvasView,
263f67876a0SStephan Aßmus 									   BView* drawingView)
264f67876a0SStephan Aßmus 						: fCanvasView(canvasView),
265f67876a0SStephan Aßmus 						  fDrawingView(drawingView)
266128277c9SStephan Aßmus 					{
267128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
268128277c9SStephan Aßmus 						fDrawingView->SetDrawingMode(B_OP_OVER);
269128277c9SStephan Aßmus 					}
270128277c9SStephan Aßmus 	virtual			~StrokePathIterator()
271128277c9SStephan Aßmus 					{}
272128277c9SStephan Aßmus 
273128277c9SStephan Aßmus 	virtual	void	MoveTo(BPoint point)
274128277c9SStephan Aßmus 					{
275128277c9SStephan Aßmus 						fBlack = true;
276f67876a0SStephan Aßmus 						fSkip = false;
277128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
278128277c9SStephan Aßmus 
279f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
280128277c9SStephan Aßmus 						fDrawingView->MovePenTo(point);
281128277c9SStephan Aßmus 					}
282128277c9SStephan Aßmus 	virtual	void	LineTo(BPoint point)
283128277c9SStephan Aßmus 					{
284f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
285f67876a0SStephan Aßmus 						if (!fSkip) {
286128277c9SStephan Aßmus 							if (fBlack)
287128277c9SStephan Aßmus 								fDrawingView->SetHighColor(255, 255, 255, 255);
288128277c9SStephan Aßmus 							else
289128277c9SStephan Aßmus 								fDrawingView->SetHighColor(0, 0, 0, 255);
290128277c9SStephan Aßmus 							fBlack = !fBlack;
291128277c9SStephan Aßmus 
292128277c9SStephan Aßmus 							fDrawingView->StrokeLine(point);
293f67876a0SStephan Aßmus 						} else {
294f67876a0SStephan Aßmus 							fDrawingView->MovePenTo(point);
295f67876a0SStephan Aßmus 						}
296f67876a0SStephan Aßmus 						fSkip = !fSkip;
297128277c9SStephan Aßmus 					}
298128277c9SStephan Aßmus 
299128277c9SStephan Aßmus  private:
300f67876a0SStephan Aßmus 	CanvasView*		fCanvasView;
301128277c9SStephan Aßmus 	BView*			fDrawingView;
302128277c9SStephan Aßmus 	bool			fBlack;
303f67876a0SStephan Aßmus 	bool			fSkip;
304128277c9SStephan Aßmus };
305128277c9SStephan Aßmus 
306128277c9SStephan Aßmus // Draw
307128277c9SStephan Aßmus void
308128277c9SStephan Aßmus PathManipulator::Draw(BView* into, BRect updateRect)
309128277c9SStephan Aßmus {
31064dd737cSStephan Aßmus 	// draw the Bezier curve, but only if not "editing",
31164dd737cSStephan Aßmus 	// the path is actually on top all other modifiers
312128277c9SStephan Aßmus 	// TODO: make this customizable in the GUI
31364dd737cSStephan Aßmus 
31464dd737cSStephan Aßmus 	#if __HAIKU__
31564dd737cSStephan Aßmus 	uint32 flags = into->Flags();
31664dd737cSStephan Aßmus 	into->SetFlags(flags | B_SUBPIXEL_PRECISE);
31764dd737cSStephan Aßmus 	#endif // __HAIKU__
31864dd737cSStephan Aßmus 
319f67876a0SStephan Aßmus 	StrokePathIterator iterator(fCanvasView, into);
320f67876a0SStephan Aßmus 	fPath->Iterate(&iterator, fCanvasView->ZoomLevel());
321128277c9SStephan Aßmus 
32264dd737cSStephan Aßmus 	#if __HAIKU__
32364dd737cSStephan Aßmus 	into->SetFlags(flags);
32464dd737cSStephan Aßmus 	#endif // __HAIKU__
32564dd737cSStephan Aßmus 
326128277c9SStephan Aßmus 	into->SetLowColor(0, 0, 0, 255);
327128277c9SStephan Aßmus 	BPoint point;
328128277c9SStephan Aßmus 	BPoint pointIn;
329128277c9SStephan Aßmus 	BPoint pointOut;
330128277c9SStephan Aßmus 	rgb_color focusColor = (rgb_color){ 255, 0, 0, 255 };
331128277c9SStephan Aßmus 	rgb_color highlightColor = (rgb_color){ 60, 60, 255, 255 };
332128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut); i++) {
333128277c9SStephan Aßmus 		bool highlight = fCurrentPathPoint == i;
334128277c9SStephan Aßmus 		bool selected = fSelection->Contains(i);
335128277c9SStephan Aßmus 		rgb_color normal = selected ? focusColor : (rgb_color){ 0, 0, 0, 255 };
336128277c9SStephan Aßmus 		into->SetLowColor(normal);
337128277c9SStephan Aßmus 		into->SetHighColor(255, 255, 255, 255);
338128277c9SStephan Aßmus 		// convert to view coordinate space
339f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&point);
340f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointIn);
341f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointOut);
342128277c9SStephan Aßmus 		// connect the points belonging to one control point
343128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_INVERT);
344128277c9SStephan Aßmus 		into->StrokeLine(point, pointIn);
345128277c9SStephan Aßmus 		into->StrokeLine(point, pointOut);
346128277c9SStephan Aßmus 		// draw main control point
347128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT ||
348128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP ||
349128277c9SStephan Aßmus 						  fMode == REMOVE_POINT ||
350128277c9SStephan Aßmus 						  fMode == SELECT_POINTS ||
351128277c9SStephan Aßmus 						  fMode == CLOSE_PATH)) {
352128277c9SStephan Aßmus 
353128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
354128277c9SStephan Aßmus 		}
355128277c9SStephan Aßmus 
356128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_COPY);
357128277c9SStephan Aßmus 		BRect r(point, point);
358128277c9SStephan Aßmus 		r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
359128277c9SStephan Aßmus 		into->StrokeRect(r, B_SOLID_LOW);
360128277c9SStephan Aßmus 		r.InsetBy(1.0, 1.0);
361128277c9SStephan Aßmus 		into->FillRect(r, B_SOLID_HIGH);
362128277c9SStephan Aßmus 		// draw in control point
363128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_IN ||
364128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_IN ||
365128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_IN ||
366128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
367128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
368128277c9SStephan Aßmus 		else
369128277c9SStephan Aßmus 			into->SetLowColor(normal);
370128277c9SStephan Aßmus 		if (selected) {
371128277c9SStephan Aßmus 			into->SetHighColor(220, 220, 220, 255);
372128277c9SStephan Aßmus 		} else {
373128277c9SStephan Aßmus 			into->SetHighColor(170, 170, 170, 255);
374128277c9SStephan Aßmus 		}
375128277c9SStephan Aßmus 		if (pointIn != point) {
376128277c9SStephan Aßmus 			r.Set(pointIn.x - CONTROL_POINT_EXTEND, pointIn.y - CONTROL_POINT_EXTEND,
377128277c9SStephan Aßmus 				  pointIn.x + CONTROL_POINT_EXTEND, pointIn.y + CONTROL_POINT_EXTEND);
378128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
379128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
380128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
381128277c9SStephan Aßmus 		}
382128277c9SStephan Aßmus 		// draw out control point
383128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_OUT ||
384128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_OUT ||
385128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_OUT ||
386128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
387128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
388128277c9SStephan Aßmus 		else
389128277c9SStephan Aßmus 			into->SetLowColor(normal);
390128277c9SStephan Aßmus 		if (pointOut != point) {
391128277c9SStephan Aßmus 			r.Set(pointOut.x - CONTROL_POINT_EXTEND, pointOut.y - CONTROL_POINT_EXTEND,
392128277c9SStephan Aßmus 				  pointOut.x + CONTROL_POINT_EXTEND, pointOut.y + CONTROL_POINT_EXTEND);
393128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
394128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
395128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
396128277c9SStephan Aßmus 		}
397128277c9SStephan Aßmus 	}
3980e1ba39fSStephan Aßmus 
3990e1ba39fSStephan Aßmus 	if (fTransformBox) {
4000e1ba39fSStephan Aßmus 		fTransformBox->Draw(into, updateRect);
4010e1ba39fSStephan Aßmus 	}
402128277c9SStephan Aßmus }
403128277c9SStephan Aßmus 
404128277c9SStephan Aßmus // #pragma mark -
405128277c9SStephan Aßmus 
406128277c9SStephan Aßmus // MouseDown
407128277c9SStephan Aßmus bool
408128277c9SStephan Aßmus PathManipulator::MouseDown(BPoint where)
409128277c9SStephan Aßmus {
410128277c9SStephan Aßmus 	fMouseDown = true;
411128277c9SStephan Aßmus 
4120e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
4130e1ba39fSStephan Aßmus 		if (fTransformBox) {
4140e1ba39fSStephan Aßmus 			fTransformBox->MouseDown(where);
4150e1ba39fSStephan Aßmus 
4160e1ba39fSStephan Aßmus //			if (!fTransformBox->IsRotating())
4170e1ba39fSStephan Aßmus //				fCanvasView->SetAutoScrolling(true);
4180e1ba39fSStephan Aßmus 		}
4190e1ba39fSStephan Aßmus 		return true;
4200e1ba39fSStephan Aßmus 	}
4210e1ba39fSStephan Aßmus 
422128277c9SStephan Aßmus 	if (fMode == MOVE_POINT &&
423128277c9SStephan Aßmus 		fSelection->CountItems() > 1 &&
424128277c9SStephan Aßmus 		fSelection->Contains(fCurrentPathPoint)) {
425128277c9SStephan Aßmus 		fMode = TRANSLATE_POINTS;
426128277c9SStephan Aßmus 	}
427128277c9SStephan Aßmus 
4284fac07a0SStephan Aßmus 	// apply the canvas view mouse filter depending on current mode
4294fac07a0SStephan Aßmus 	if (fMode == ADD_POINT || fMode == TRANSLATE_POINTS)
4304fac07a0SStephan Aßmus 		fCanvasView->FilterMouse(&where);
4314fac07a0SStephan Aßmus 
432128277c9SStephan Aßmus 	BPoint canvasWhere = where;
433f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
434128277c9SStephan Aßmus 
435128277c9SStephan Aßmus 	// maybe we're changing some point, so we construct the
436128277c9SStephan Aßmus 	// "ChangePointCommand" here so that the point is remembered
437128277c9SStephan Aßmus 	// in its current state
4384fac07a0SStephan Aßmus 	// apply the canvas view mouse filter depending on current mode
439128277c9SStephan Aßmus 	delete fChangePointCommand;
440128277c9SStephan Aßmus 	fChangePointCommand = NULL;
441128277c9SStephan Aßmus 	switch (fMode) {
442128277c9SStephan Aßmus 		case TOGGLE_SHARP:
443128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
444128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
445128277c9SStephan Aßmus 		case MOVE_POINT:
446128277c9SStephan Aßmus 		case MOVE_POINT_IN:
447128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
448128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
449128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
450128277c9SStephan Aßmus 			fChangePointCommand = new ChangePointCommand(fPath,
451128277c9SStephan Aßmus 														 fCurrentPathPoint,
452128277c9SStephan Aßmus 														 fSelection->Items(),
453128277c9SStephan Aßmus 														 fSelection->CountItems());
454128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
455128277c9SStephan Aßmus 			break;
456128277c9SStephan Aßmus 	}
457128277c9SStephan Aßmus 
458128277c9SStephan Aßmus 	// at this point we init doing something
459128277c9SStephan Aßmus 	switch (fMode) {
460128277c9SStephan Aßmus 		case ADD_POINT:
461128277c9SStephan Aßmus 			_AddPoint(canvasWhere);
462128277c9SStephan Aßmus 			break;
463128277c9SStephan Aßmus 		case INSERT_POINT:
464128277c9SStephan Aßmus 			_InsertPoint(canvasWhere, fCurrentPathPoint);
465128277c9SStephan Aßmus 			break;
466128277c9SStephan Aßmus 
467128277c9SStephan Aßmus 		case TOGGLE_SHARP:
468128277c9SStephan Aßmus 			_SetSharp(fCurrentPathPoint);
469128277c9SStephan Aßmus 			// continue by dragging out the _connected_ in/out points
470128277c9SStephan Aßmus 			break;
471128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
472128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
473128277c9SStephan Aßmus 			// continue by moving the "in" point
474128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_IN);
475128277c9SStephan Aßmus 			break;
476128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
477128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
478128277c9SStephan Aßmus 			// continue by moving the "out" point
479128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
480128277c9SStephan Aßmus 			break;
481128277c9SStephan Aßmus 
482128277c9SStephan Aßmus 		case MOVE_POINT:
483128277c9SStephan Aßmus 		case MOVE_POINT_IN:
484128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
485128277c9SStephan Aßmus 			// the right thing happens since "fCurrentPathPoint"
486128277c9SStephan Aßmus 			// points to the correct index
487128277c9SStephan Aßmus 			break;
488128277c9SStephan Aßmus 
489128277c9SStephan Aßmus 		case CLOSE_PATH:
490128277c9SStephan Aßmus //			SetClosed(true, true);
491128277c9SStephan Aßmus 			break;
492128277c9SStephan Aßmus 
493128277c9SStephan Aßmus 		case REMOVE_POINT:
494128277c9SStephan Aßmus 			if (fPath->CountPoints() == 1) {
495128277c9SStephan Aßmus //				fCanvasView->Perform(new RemovePathCommand(this, fPath));
496128277c9SStephan Aßmus 			} else {
497128277c9SStephan Aßmus 				fCanvasView->Perform(new RemovePointsCommand(fPath,
498128277c9SStephan Aßmus 															 fCurrentPathPoint,
499128277c9SStephan Aßmus 															 fSelection->Items(),
500128277c9SStephan Aßmus 															 fSelection->CountItems()));
501128277c9SStephan Aßmus 				_RemovePoint(fCurrentPathPoint);
502128277c9SStephan Aßmus 			}
503128277c9SStephan Aßmus 			break;
504128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
505128277c9SStephan Aßmus 			_RemovePointIn(fCurrentPathPoint);
506128277c9SStephan Aßmus 			break;
507128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
508128277c9SStephan Aßmus 			_RemovePointOut(fCurrentPathPoint);
509128277c9SStephan Aßmus 			break;
510128277c9SStephan Aßmus 
511f4bd80a2SStephan Aßmus 		case SELECT_POINTS: {
512f4bd80a2SStephan Aßmus 			// TODO: this works so that you can deselect all points
513f4bd80a2SStephan Aßmus 			// when clicking outside the path even if pressing shift
514f4bd80a2SStephan Aßmus 			// in case the path is open... a better way would be
515f4bd80a2SStephan Aßmus 			// to deselect all on mouse up, if the mouse has not moved
516f4bd80a2SStephan Aßmus 			bool appendSelection;
517f4bd80a2SStephan Aßmus 			if (fPath->IsClosed())
518f4bd80a2SStephan Aßmus 				appendSelection = fShiftDown;
519f4bd80a2SStephan Aßmus 			else
520f4bd80a2SStephan Aßmus 				appendSelection = fShiftDown && fCurrentPathPoint >= 0;
521f4bd80a2SStephan Aßmus 
522f4bd80a2SStephan Aßmus 			if (!appendSelection) {
523128277c9SStephan Aßmus 				fSelection->MakeEmpty();
524128277c9SStephan Aßmus 				_UpdateSelection();
525128277c9SStephan Aßmus 			}
526128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
527128277c9SStephan Aßmus 			if (fCurrentPathPoint >= 0) {
528f4bd80a2SStephan Aßmus 				_Select(fCurrentPathPoint, appendSelection);
529128277c9SStephan Aßmus 			}
530128277c9SStephan Aßmus 			fCanvasView->BeginRectTracking(BRect(where, where),
531128277c9SStephan Aßmus 										   B_TRACK_RECT_CORNER);
532128277c9SStephan Aßmus 			break;
533128277c9SStephan Aßmus 		}
534f4bd80a2SStephan Aßmus 	}
535128277c9SStephan Aßmus 
536128277c9SStephan Aßmus 	fTrackingStart = canvasWhere;
537128277c9SStephan Aßmus 	// remember the subpixel position
538128277c9SStephan Aßmus 	// so that MouseMoved() will work even before
539128277c9SStephan Aßmus 	// the integer position becomes different
540128277c9SStephan Aßmus 	fLastCanvasPos = where;
541f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&fLastCanvasPos);
542128277c9SStephan Aßmus 
543128277c9SStephan Aßmus 	// the reason to exclude the select mode
544128277c9SStephan Aßmus 	// is that the BView rect tracking does not
545128277c9SStephan Aßmus 	// scroll the rect starting point along with us
546128277c9SStephan Aßmus 	// (since we're doing no real scrolling)
547128277c9SStephan Aßmus //	if (fMode != SELECT_POINTS)
548128277c9SStephan Aßmus //		fCanvasView->SetAutoScrolling(true);
549128277c9SStephan Aßmus 
550128277c9SStephan Aßmus 	UpdateCursor();
551128277c9SStephan Aßmus 
552128277c9SStephan Aßmus 	return true;
553128277c9SStephan Aßmus }
554128277c9SStephan Aßmus 
555128277c9SStephan Aßmus // MouseMoved
556128277c9SStephan Aßmus void
557128277c9SStephan Aßmus PathManipulator::MouseMoved(BPoint where)
558128277c9SStephan Aßmus {
5594fac07a0SStephan Aßmus 	fCanvasView->FilterMouse(&where);
5604fac07a0SStephan Aßmus 		// NOTE: only filter mouse coords in mouse moved, no other
5614fac07a0SStephan Aßmus 		// mouse function
562128277c9SStephan Aßmus 	BPoint canvasWhere = where;
563f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
564128277c9SStephan Aßmus 
565128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
566128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
567128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
568128277c9SStephan Aßmus 	if (fLastCanvasPos == canvasWhere)
569128277c9SStephan Aßmus 		return;
570128277c9SStephan Aßmus 
571128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
572128277c9SStephan Aßmus 
5730e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
5740e1ba39fSStephan Aßmus 		if (fTransformBox) {
5750e1ba39fSStephan Aßmus 			fTransformBox->MouseMoved(where);
5760e1ba39fSStephan Aßmus 		}
5770e1ba39fSStephan Aßmus 		return;
5780e1ba39fSStephan Aßmus 	}
5790e1ba39fSStephan Aßmus 
580128277c9SStephan Aßmus 	if (fMode == CLOSE_PATH) {
581128277c9SStephan Aßmus 		// continue by moving the point
582128277c9SStephan Aßmus 		_SetMode(MOVE_POINT);
583128277c9SStephan Aßmus 		delete fChangePointCommand;
584128277c9SStephan Aßmus 		fChangePointCommand = new ChangePointCommand(fPath,
585128277c9SStephan Aßmus 													 fCurrentPathPoint,
586128277c9SStephan Aßmus 													 fSelection->Items(),
587128277c9SStephan Aßmus 													 fSelection->CountItems());
588128277c9SStephan Aßmus 	}
589128277c9SStephan Aßmus 
590128277c9SStephan Aßmus //	if (!fPrecise) {
591128277c9SStephan Aßmus //		float offset = fmod(fOutlineWidth, 2.0) / 2.0;
592128277c9SStephan Aßmus //		canvasWhere.point += BPoint(offset, offset);
593128277c9SStephan Aßmus //	}
594128277c9SStephan Aßmus 
595128277c9SStephan Aßmus 	switch (fMode) {
596128277c9SStephan Aßmus 		case ADD_POINT:
597128277c9SStephan Aßmus 		case INSERT_POINT:
598128277c9SStephan Aßmus 		case TOGGLE_SHARP:
599128277c9SStephan Aßmus 			// drag the "out" control point, mirror the "in" control point
600128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere, true);
601128277c9SStephan Aßmus 			break;
602128277c9SStephan Aßmus 		case MOVE_POINT:
603128277c9SStephan Aßmus 			// drag all three control points at once
604128277c9SStephan Aßmus 			fPath->SetPoint(fCurrentPathPoint, canvasWhere);
605128277c9SStephan Aßmus 			break;
606128277c9SStephan Aßmus 		case MOVE_POINT_IN:
607128277c9SStephan Aßmus 			// drag in control point
608128277c9SStephan Aßmus 			fPath->SetPointIn(fCurrentPathPoint, canvasWhere);
609128277c9SStephan Aßmus 			break;
610128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
611128277c9SStephan Aßmus 			// drag out control point
612128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere);
613128277c9SStephan Aßmus 			break;
614128277c9SStephan Aßmus 
615128277c9SStephan Aßmus 		case SELECT_POINTS: {
616128277c9SStephan Aßmus 			// change the selection
617128277c9SStephan Aßmus 			BRect r;
618128277c9SStephan Aßmus 			r.left = min_c(fTrackingStart.x, canvasWhere.x);
619128277c9SStephan Aßmus 			r.top = min_c(fTrackingStart.y, canvasWhere.y);
620128277c9SStephan Aßmus 			r.right = max_c(fTrackingStart.x, canvasWhere.x);
621128277c9SStephan Aßmus 			r.bottom = max_c(fTrackingStart.y, canvasWhere.y);
622128277c9SStephan Aßmus 			_Select(r);
623128277c9SStephan Aßmus 			break;
624128277c9SStephan Aßmus 		}
625128277c9SStephan Aßmus 
626128277c9SStephan Aßmus 		case TRANSLATE_POINTS: {
627128277c9SStephan Aßmus 			BPoint offset = canvasWhere - fTrackingStart;
628128277c9SStephan Aßmus 			_Nudge(offset);
629128277c9SStephan Aßmus 			fTrackingStart = canvasWhere;
630128277c9SStephan Aßmus 			break;
631128277c9SStephan Aßmus 		}
632128277c9SStephan Aßmus 	}
633128277c9SStephan Aßmus }
634128277c9SStephan Aßmus 
635128277c9SStephan Aßmus // MouseUp
636128277c9SStephan Aßmus Command*
637128277c9SStephan Aßmus PathManipulator::MouseUp()
638128277c9SStephan Aßmus {
639128277c9SStephan Aßmus 	// prevent carrying out actions more than once by only
640128277c9SStephan Aßmus 	// doing it if "fMouseDown" is true at the point of
641128277c9SStephan Aßmus 	// entering this function
642128277c9SStephan Aßmus 	if (!fMouseDown)
643128277c9SStephan Aßmus 		return NULL;
644128277c9SStephan Aßmus 	fMouseDown = false;
645128277c9SStephan Aßmus 
6460e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
6470e1ba39fSStephan Aßmus 		if (fTransformBox) {
6480e1ba39fSStephan Aßmus 			return fTransformBox->MouseUp();
6490e1ba39fSStephan Aßmus 		}
6500e1ba39fSStephan Aßmus 		return NULL;
6510e1ba39fSStephan Aßmus 	}
6520e1ba39fSStephan Aßmus 
653128277c9SStephan Aßmus 	Command* command = NULL;
654128277c9SStephan Aßmus 
655128277c9SStephan Aßmus 	switch (fMode) {
656128277c9SStephan Aßmus 
657128277c9SStephan Aßmus 		case ADD_POINT:
658128277c9SStephan Aßmus 			command = fAddPointCommand;
659128277c9SStephan Aßmus 			fAddPointCommand = NULL;
660128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
661128277c9SStephan Aßmus 			break;
662128277c9SStephan Aßmus 
663128277c9SStephan Aßmus 		case INSERT_POINT:
664128277c9SStephan Aßmus 			command = fInsertPointCommand;
665128277c9SStephan Aßmus 			fInsertPointCommand = NULL;
666128277c9SStephan Aßmus 			break;
667128277c9SStephan Aßmus 
668128277c9SStephan Aßmus 		case SELECT_POINTS:
669128277c9SStephan Aßmus 			if (*fSelection != *fOldSelection) {
670128277c9SStephan Aßmus //				command = new SelectPointsCommand(this, fPath,
671128277c9SStephan Aßmus //												  fOldSelection->Items(),
672128277c9SStephan Aßmus //												  fOldSelection->CountItems(),
673128277c9SStephan Aßmus //												  fSelection->Items(),
674128277c9SStephan Aßmus //												  fSelection->CountItems()));
675128277c9SStephan Aßmus 			}
676128277c9SStephan Aßmus 			fCanvasView->EndRectTracking();
677128277c9SStephan Aßmus 			break;
678128277c9SStephan Aßmus 
679128277c9SStephan Aßmus 		case TOGGLE_SHARP:
680128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
681128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
682128277c9SStephan Aßmus 		case MOVE_POINT:
683128277c9SStephan Aßmus 		case MOVE_POINT_IN:
684128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
685128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
686128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
687128277c9SStephan Aßmus 			command = fChangePointCommand;
688128277c9SStephan Aßmus 			fChangePointCommand = NULL;
689128277c9SStephan Aßmus 			break;
690128277c9SStephan Aßmus 
691128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
6920e1ba39fSStephan Aßmus 			if (!fNudgeCommand) {
693128277c9SStephan Aßmus 				// select just the point that was clicked
694128277c9SStephan Aßmus 				*fOldSelection = *fSelection;
695128277c9SStephan Aßmus 				if (fCurrentPathPoint >= 0) {
696128277c9SStephan Aßmus 					_Select(fCurrentPathPoint, fShiftDown);
697128277c9SStephan Aßmus 				}
698128277c9SStephan Aßmus 				if (*fSelection != *fOldSelection) {
699128277c9SStephan Aßmus //					command = new SelectPointsCommand(this, fPath,
700128277c9SStephan Aßmus //													  fOldSelection->Items(),
701128277c9SStephan Aßmus //													  fOldSelection->CountItems(),
702128277c9SStephan Aßmus //													  fSelection->Items(),
703128277c9SStephan Aßmus //													  fSelection->CountItems()));
704128277c9SStephan Aßmus 				}
7050e1ba39fSStephan Aßmus 			} else {
7060e1ba39fSStephan Aßmus 				command = _FinishNudging();
7070e1ba39fSStephan Aßmus 			}
708128277c9SStephan Aßmus 			break;
709128277c9SStephan Aßmus 	}
710128277c9SStephan Aßmus 
711128277c9SStephan Aßmus 	return command;
712128277c9SStephan Aßmus }
713128277c9SStephan Aßmus 
714128277c9SStephan Aßmus // MouseOver
715128277c9SStephan Aßmus bool
716128277c9SStephan Aßmus PathManipulator::MouseOver(BPoint where)
717128277c9SStephan Aßmus {
7180e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
7190e1ba39fSStephan Aßmus 		if (fTransformBox) {
7200e1ba39fSStephan Aßmus 			return fTransformBox->MouseOver(where);
7210e1ba39fSStephan Aßmus 		}
7220e1ba39fSStephan Aßmus 		return false;
7230e1ba39fSStephan Aßmus 	}
7240e1ba39fSStephan Aßmus 
725128277c9SStephan Aßmus 	BPoint canvasWhere = where;
726f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
727128277c9SStephan Aßmus 
728128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
729128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
730128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
731128277c9SStephan Aßmus 	if (fMouseDown && fLastCanvasPos == canvasWhere)
732128277c9SStephan Aßmus 		return false;
733128277c9SStephan Aßmus 
734128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
735128277c9SStephan Aßmus 
736128277c9SStephan Aßmus 	// hit testing
737128277c9SStephan Aßmus 	// (use a subpixel mouse pos)
738f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&where);
739128277c9SStephan Aßmus 	_SetModeForMousePos(where);
740128277c9SStephan Aßmus 
741128277c9SStephan Aßmus 	// TODO: always true?
742128277c9SStephan Aßmus 	return true;
743128277c9SStephan Aßmus }
744128277c9SStephan Aßmus 
745128277c9SStephan Aßmus // DoubleClicked
746128277c9SStephan Aßmus bool
747128277c9SStephan Aßmus PathManipulator::DoubleClicked(BPoint where)
748128277c9SStephan Aßmus {
749128277c9SStephan Aßmus 	return false;
750128277c9SStephan Aßmus }
751128277c9SStephan Aßmus 
752f4bd80a2SStephan Aßmus // ShowContextMenu
753f4bd80a2SStephan Aßmus bool
754f4bd80a2SStephan Aßmus PathManipulator::ShowContextMenu(BPoint where)
755f4bd80a2SStephan Aßmus {
756f4bd80a2SStephan Aßmus 	BPopUpMenu* menu = new BPopUpMenu("context menu", false, false);
757f4bd80a2SStephan Aßmus 	BMessage* message;
758f4bd80a2SStephan Aßmus 	BMenuItem* item;
759f4bd80a2SStephan Aßmus 
760f4bd80a2SStephan Aßmus 	bool hasSelection = fSelection->CountItems() > 0;
761f4bd80a2SStephan Aßmus 
762f4bd80a2SStephan Aßmus 	message = new BMessage(B_SELECT_ALL);
763f4bd80a2SStephan Aßmus 	item = new BMenuItem("Select All", message, 'A');
764f4bd80a2SStephan Aßmus 	menu->AddItem(item);
765f4bd80a2SStephan Aßmus 
766f4bd80a2SStephan Aßmus 	menu->AddSeparatorItem();
767f4bd80a2SStephan Aßmus 
768f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_TRANSFORM);
769f4bd80a2SStephan Aßmus 	item = new BMenuItem("Transform", message);
770f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
771f4bd80a2SStephan Aßmus 	menu->AddItem(item);
772f4bd80a2SStephan Aßmus 
773f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_SPLIT_POINTS);
774f4bd80a2SStephan Aßmus 	item = new BMenuItem("Split", message);
775f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
776f4bd80a2SStephan Aßmus 	menu->AddItem(item);
777f4bd80a2SStephan Aßmus 
778*7c4b3726SStephan Aßmus 	message = new BMessage(MSG_FLIP_POINTS);
779*7c4b3726SStephan Aßmus 	item = new BMenuItem("Flip", message);
780*7c4b3726SStephan Aßmus 	item->SetEnabled(hasSelection);
781*7c4b3726SStephan Aßmus 	menu->AddItem(item);
782*7c4b3726SStephan Aßmus 
783f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_REMOVE_POINTS);
784f4bd80a2SStephan Aßmus 	item = new BMenuItem("Remove", message, 'A');
785f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
786f4bd80a2SStephan Aßmus 	menu->AddItem(item);
787f4bd80a2SStephan Aßmus 
788f4bd80a2SStephan Aßmus 	// go
789f4bd80a2SStephan Aßmus 	menu->SetTargetForItems(fCanvasView);
790f4bd80a2SStephan Aßmus 	menu->SetAsyncAutoDestruct(true);
791f4bd80a2SStephan Aßmus 	menu->SetFont(be_plain_font);
792f4bd80a2SStephan Aßmus 	where = fCanvasView->ConvertToScreen(where);
793f4bd80a2SStephan Aßmus 	BRect mouseRect(where, where);
794f4bd80a2SStephan Aßmus 	mouseRect.InsetBy(-10.0, -10.0);
795f4bd80a2SStephan Aßmus 	where += BPoint(5.0, 5.0);
796f4bd80a2SStephan Aßmus 	menu->Go(where, true, false, mouseRect, true);
797f4bd80a2SStephan Aßmus 
798f4bd80a2SStephan Aßmus 	return true;
799f4bd80a2SStephan Aßmus }
800f4bd80a2SStephan Aßmus 
801f4bd80a2SStephan Aßmus // #pragma mark -
802f4bd80a2SStephan Aßmus 
803128277c9SStephan Aßmus // Bounds
804128277c9SStephan Aßmus BRect
805128277c9SStephan Aßmus PathManipulator::Bounds()
806128277c9SStephan Aßmus {
807f67876a0SStephan Aßmus 	BRect r = _ControlPointRect();
808f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&r);
809f67876a0SStephan Aßmus 	return r;
810128277c9SStephan Aßmus }
811128277c9SStephan Aßmus 
812128277c9SStephan Aßmus // TrackingBounds
813128277c9SStephan Aßmus BRect
814128277c9SStephan Aßmus PathManipulator::TrackingBounds(BView* withinView)
815128277c9SStephan Aßmus {
816128277c9SStephan Aßmus 	return withinView->Bounds();
817128277c9SStephan Aßmus }
818128277c9SStephan Aßmus 
819128277c9SStephan Aßmus // #pragma mark -
820128277c9SStephan Aßmus 
821128277c9SStephan Aßmus // MessageReceived
822128277c9SStephan Aßmus bool
823128277c9SStephan Aßmus PathManipulator::MessageReceived(BMessage* message, Command** _command)
824128277c9SStephan Aßmus {
825128277c9SStephan Aßmus 	bool result = true;
826128277c9SStephan Aßmus 	switch (message->what) {
827f4bd80a2SStephan Aßmus 		case MSG_TRANSFORM:
828128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
829128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
830128277c9SStephan Aßmus 			break;
831f4bd80a2SStephan Aßmus 		case MSG_REMOVE_POINTS:
832128277c9SStephan Aßmus 			*_command = _Delete();
833128277c9SStephan Aßmus 			break;
834f4bd80a2SStephan Aßmus 		case MSG_SPLIT_POINTS:
835f4bd80a2SStephan Aßmus 			*_command = new SplitPointsCommand(fPath,
836f4bd80a2SStephan Aßmus 											   fSelection->Items(),
837f4bd80a2SStephan Aßmus 											   fSelection->CountItems());
838f4bd80a2SStephan Aßmus 			break;
839*7c4b3726SStephan Aßmus 		case MSG_FLIP_POINTS:
840*7c4b3726SStephan Aßmus 			*_command = new FlipPointsCommand(fPath,
841*7c4b3726SStephan Aßmus 											  fSelection->Items(),
842*7c4b3726SStephan Aßmus 											  fSelection->CountItems());
843*7c4b3726SStephan Aßmus 			break;
844128277c9SStephan Aßmus 		case B_SELECT_ALL: {
845128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
846128277c9SStephan Aßmus 			fSelection->MakeEmpty();
847128277c9SStephan Aßmus 			int32 count = fPath->CountPoints();
848128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++)
849128277c9SStephan Aßmus 				fSelection->Add(i);
850128277c9SStephan Aßmus 			if (*fOldSelection != *fSelection) {
851128277c9SStephan Aßmus //				*_command = new SelectPointsCommand(this, fPath,
852128277c9SStephan Aßmus //												   fOldSelection->Items(),
853128277c9SStephan Aßmus //												   fOldSelection->CountItems(),
854128277c9SStephan Aßmus //												   fSelection->Items(),
855128277c9SStephan Aßmus //												   fSelection->CountItems()));
856128277c9SStephan Aßmus 				count = fSelection->CountItems();
857128277c9SStephan Aßmus 				int32 indices[count];
858128277c9SStephan Aßmus 				memcpy(indices, fSelection->Items(), count * sizeof(int32));
859128277c9SStephan Aßmus 				_Select(indices, count);
860128277c9SStephan Aßmus 			}
861128277c9SStephan Aßmus 			break;
862128277c9SStephan Aßmus 		}
863128277c9SStephan Aßmus 		default:
864128277c9SStephan Aßmus 			result = false;
865128277c9SStephan Aßmus 			break;
866128277c9SStephan Aßmus 	}
867128277c9SStephan Aßmus 	return result;
868128277c9SStephan Aßmus }
869128277c9SStephan Aßmus 
870128277c9SStephan Aßmus 
871128277c9SStephan Aßmus // ModifiersChanged
872128277c9SStephan Aßmus void
873128277c9SStephan Aßmus PathManipulator::ModifiersChanged(uint32 modifiers)
874128277c9SStephan Aßmus {
875128277c9SStephan Aßmus 	fCommandDown = modifiers & B_COMMAND_KEY;
876128277c9SStephan Aßmus 	fOptionDown = modifiers & B_CONTROL_KEY;
877128277c9SStephan Aßmus 	fShiftDown = modifiers & B_SHIFT_KEY;
878128277c9SStephan Aßmus 	fAltDown = modifiers & B_OPTION_KEY;
879128277c9SStephan Aßmus 
8800e1ba39fSStephan Aßmus 	if (fTransformBox) {
8810e1ba39fSStephan Aßmus 		fTransformBox->ModifiersChanged(modifiers);
8820e1ba39fSStephan Aßmus 		return;
8830e1ba39fSStephan Aßmus 	}
884128277c9SStephan Aßmus 	// reevaluate mode
885128277c9SStephan Aßmus 	if (!fMouseDown)
886128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
887128277c9SStephan Aßmus }
888128277c9SStephan Aßmus 
889128277c9SStephan Aßmus // HandleKeyDown
890128277c9SStephan Aßmus bool
891128277c9SStephan Aßmus PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
892128277c9SStephan Aßmus {
893128277c9SStephan Aßmus 	bool result = true;
894128277c9SStephan Aßmus 
895128277c9SStephan Aßmus 	float nudgeDist = 1.0;
896f67876a0SStephan Aßmus 	if (modifiers & B_SHIFT_KEY)
897f67876a0SStephan Aßmus 		nudgeDist /= fCanvasView->ZoomLevel();
898128277c9SStephan Aßmus 
899128277c9SStephan Aßmus 	switch (key) {
900128277c9SStephan Aßmus 		// commit
901128277c9SStephan Aßmus 		case B_RETURN:
9020e1ba39fSStephan Aßmus 			if (fTransformBox) {
9034fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9044fac07a0SStephan Aßmus 			}// else
905128277c9SStephan Aßmus //				_Perform();
906128277c9SStephan Aßmus 			break;
907128277c9SStephan Aßmus 		// cancel
908128277c9SStephan Aßmus 		case B_ESCAPE:
9090e1ba39fSStephan Aßmus 			if (fTransformBox) {
9100e1ba39fSStephan Aßmus 				fTransformBox->Cancel();
9114fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9120e1ba39fSStephan Aßmus 			} else if (fFallBackMode == NEW_PATH) {
913128277c9SStephan Aßmus 				fFallBackMode = SELECT_POINTS;
9144fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9154fac07a0SStephan Aßmus 			}// else
916128277c9SStephan Aßmus //				_Cancel();
917128277c9SStephan Aßmus 			break;
918128277c9SStephan Aßmus 		case 't':
919128277c9SStephan Aßmus 		case 'T':
920128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
921128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
922128277c9SStephan Aßmus 			else
923128277c9SStephan Aßmus 				result = false;
924128277c9SStephan Aßmus 			break;
925128277c9SStephan Aßmus 		// nudging
926128277c9SStephan Aßmus 		case B_UP_ARROW:
927128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, -nudgeDist));
928128277c9SStephan Aßmus 			break;
929128277c9SStephan Aßmus 		case B_DOWN_ARROW:
930128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, nudgeDist));
931128277c9SStephan Aßmus 			break;
932128277c9SStephan Aßmus 		case B_LEFT_ARROW:
933128277c9SStephan Aßmus 			_Nudge(BPoint(-nudgeDist, 0.0));
934128277c9SStephan Aßmus 			break;
935128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
936128277c9SStephan Aßmus 			_Nudge(BPoint(nudgeDist, 0.0));
937128277c9SStephan Aßmus 			break;
938128277c9SStephan Aßmus 
939128277c9SStephan Aßmus 		case B_DELETE:
940128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
941128277c9SStephan Aßmus 				*_command = _Delete();
942128277c9SStephan Aßmus 			else
943128277c9SStephan Aßmus 				result = false;
944128277c9SStephan Aßmus 			break;
945128277c9SStephan Aßmus 
946128277c9SStephan Aßmus 		default:
947128277c9SStephan Aßmus 			result = false;
948128277c9SStephan Aßmus 	}
949128277c9SStephan Aßmus 	return result;
950128277c9SStephan Aßmus }
951128277c9SStephan Aßmus 
952128277c9SStephan Aßmus // HandleKeyUp
953128277c9SStephan Aßmus bool
954128277c9SStephan Aßmus PathManipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
955128277c9SStephan Aßmus {
956128277c9SStephan Aßmus 	bool handled = true;
957128277c9SStephan Aßmus 	switch (key) {
958128277c9SStephan Aßmus 		// nudging
959128277c9SStephan Aßmus 		case B_UP_ARROW:
960128277c9SStephan Aßmus 		case B_DOWN_ARROW:
961128277c9SStephan Aßmus 		case B_LEFT_ARROW:
962128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
9630e1ba39fSStephan Aßmus 			*_command = _FinishNudging();
964128277c9SStephan Aßmus 			break;
965128277c9SStephan Aßmus 		default:
966128277c9SStephan Aßmus 			handled = false;
967128277c9SStephan Aßmus 			break;
968128277c9SStephan Aßmus 	}
969128277c9SStephan Aßmus 	return handled;
970128277c9SStephan Aßmus }
971128277c9SStephan Aßmus 
972128277c9SStephan Aßmus // UpdateCursor
973*7c4b3726SStephan Aßmus bool
974128277c9SStephan Aßmus PathManipulator::UpdateCursor()
975128277c9SStephan Aßmus {
976*7c4b3726SStephan Aßmus 	if (fTransformBox)
977*7c4b3726SStephan Aßmus 		return fTransformBox->UpdateCursor();
978*7c4b3726SStephan Aßmus 
979128277c9SStephan Aßmus 	const uchar* cursorData;
980128277c9SStephan Aßmus 	switch (fMode) {
981128277c9SStephan Aßmus 		case ADD_POINT:
982128277c9SStephan Aßmus 			cursorData = kPathAddCursor;
983128277c9SStephan Aßmus 			break;
984128277c9SStephan Aßmus 		case INSERT_POINT:
985128277c9SStephan Aßmus 			cursorData = kPathInsertCursor;
986128277c9SStephan Aßmus 			break;
987128277c9SStephan Aßmus 		case MOVE_POINT:
988128277c9SStephan Aßmus 		case MOVE_POINT_IN:
989128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
990128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
991128277c9SStephan Aßmus 			cursorData = kPathMoveCursor;
992128277c9SStephan Aßmus 			break;
993128277c9SStephan Aßmus 		case CLOSE_PATH:
994128277c9SStephan Aßmus 			cursorData = kPathCloseCursor;
995128277c9SStephan Aßmus 			break;
996128277c9SStephan Aßmus 		case TOGGLE_SHARP:
997128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
998128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
999128277c9SStephan Aßmus 			cursorData = kPathSharpCursor;
1000128277c9SStephan Aßmus 			break;
1001128277c9SStephan Aßmus 		case REMOVE_POINT:
1002128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
1003128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
1004128277c9SStephan Aßmus 			cursorData = kPathRemoveCursor;
1005128277c9SStephan Aßmus 			break;
1006128277c9SStephan Aßmus 		case SELECT_POINTS:
1007128277c9SStephan Aßmus 			cursorData = kPathSelectCursor;
1008128277c9SStephan Aßmus 			break;
1009128277c9SStephan Aßmus 
1010128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
1011128277c9SStephan Aßmus 			cursorData = B_HAND_CURSOR;
1012128277c9SStephan Aßmus 			break;
1013128277c9SStephan Aßmus 
1014128277c9SStephan Aßmus 		case UNDEFINED:
1015128277c9SStephan Aßmus 		default:
1016128277c9SStephan Aßmus 			cursorData = kStopCursor;
1017128277c9SStephan Aßmus 			break;
1018128277c9SStephan Aßmus 	}
1019128277c9SStephan Aßmus 	BCursor cursor(cursorData);
1020128277c9SStephan Aßmus 	fCanvasView->SetViewCursor(&cursor, true);
1021128277c9SStephan Aßmus 	fCanvasView->Sync();
1022*7c4b3726SStephan Aßmus 
1023*7c4b3726SStephan Aßmus 	return true;
1024128277c9SStephan Aßmus }
1025128277c9SStephan Aßmus 
1026128277c9SStephan Aßmus // AttachedToView
1027128277c9SStephan Aßmus void
1028128277c9SStephan Aßmus PathManipulator::AttachedToView(BView* view)
1029128277c9SStephan Aßmus {
1030f67876a0SStephan Aßmus 	fCanvasView = dynamic_cast<CanvasView*>(view);
1031128277c9SStephan Aßmus }
1032128277c9SStephan Aßmus 
1033128277c9SStephan Aßmus // DetachedFromView
1034128277c9SStephan Aßmus void
1035128277c9SStephan Aßmus PathManipulator::DetachedFromView(BView* view)
1036128277c9SStephan Aßmus {
1037128277c9SStephan Aßmus 	fCanvasView = NULL;
1038128277c9SStephan Aßmus }
1039128277c9SStephan Aßmus 
1040128277c9SStephan Aßmus // #pragma mark -
1041128277c9SStephan Aßmus 
1042128277c9SStephan Aßmus // ObjectChanged
1043128277c9SStephan Aßmus void
1044128277c9SStephan Aßmus PathManipulator::ObjectChanged(const Observable* object)
1045128277c9SStephan Aßmus {
1046128277c9SStephan Aßmus 	// TODO: refine VectorPath listener interface and
1047128277c9SStephan Aßmus 	// implement more efficiently
1048128277c9SStephan Aßmus 	BRect currentBounds = _ControlPointRect();
1049128277c9SStephan Aßmus 	_InvalidateCanvas(currentBounds | fPreviousBounds);
1050128277c9SStephan Aßmus 	fPreviousBounds = currentBounds;
1051128277c9SStephan Aßmus 
1052128277c9SStephan Aßmus 	// reevaluate mode
10530e1ba39fSStephan Aßmus 	if (!fMouseDown && !fTransformBox)
1054128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1055128277c9SStephan Aßmus }
1056128277c9SStephan Aßmus 
1057128277c9SStephan Aßmus // #pragma mark -
1058128277c9SStephan Aßmus 
105905fd3818SStephan Aßmus // PointAdded
106005fd3818SStephan Aßmus void
106105fd3818SStephan Aßmus PathManipulator::PointAdded(int32 index)
106205fd3818SStephan Aßmus {
106305fd3818SStephan Aßmus 	ObjectChanged(fPath);
106405fd3818SStephan Aßmus }
106505fd3818SStephan Aßmus 
106605fd3818SStephan Aßmus // PointRemoved
106705fd3818SStephan Aßmus void
106805fd3818SStephan Aßmus PathManipulator::PointRemoved(int32 index)
106905fd3818SStephan Aßmus {
10700e1ba39fSStephan Aßmus 	fSelection->Remove(index);
107105fd3818SStephan Aßmus 	ObjectChanged(fPath);
107205fd3818SStephan Aßmus }
107305fd3818SStephan Aßmus 
107405fd3818SStephan Aßmus // PointChanged
107505fd3818SStephan Aßmus void
107605fd3818SStephan Aßmus PathManipulator::PointChanged(int32 index)
107705fd3818SStephan Aßmus {
107805fd3818SStephan Aßmus 	ObjectChanged(fPath);
107905fd3818SStephan Aßmus }
108005fd3818SStephan Aßmus 
108105fd3818SStephan Aßmus // PathChanged
108205fd3818SStephan Aßmus void
108305fd3818SStephan Aßmus PathManipulator::PathChanged()
108405fd3818SStephan Aßmus {
108505fd3818SStephan Aßmus 	ObjectChanged(fPath);
108605fd3818SStephan Aßmus }
108705fd3818SStephan Aßmus 
108805fd3818SStephan Aßmus // PathClosedChanged
108905fd3818SStephan Aßmus void
109005fd3818SStephan Aßmus PathManipulator::PathClosedChanged()
109105fd3818SStephan Aßmus {
109205fd3818SStephan Aßmus 	ObjectChanged(fPath);
109305fd3818SStephan Aßmus }
109405fd3818SStephan Aßmus 
109505fd3818SStephan Aßmus // PathReversed
109605fd3818SStephan Aßmus void
109705fd3818SStephan Aßmus PathManipulator::PathReversed()
109805fd3818SStephan Aßmus {
10990e1ba39fSStephan Aßmus 	// reverse selection along with path
11000e1ba39fSStephan Aßmus 	int32 count = fSelection->CountItems();
11010e1ba39fSStephan Aßmus 	int32 pointCount = fPath->CountPoints();
11020e1ba39fSStephan Aßmus 	if (count > 0) {
11030e1ba39fSStephan Aßmus 		Selection temp;
11040e1ba39fSStephan Aßmus 		for (int32 i = 0; i < count; i++) {
11050e1ba39fSStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
11060e1ba39fSStephan Aßmus 		}
11070e1ba39fSStephan Aßmus 		*fSelection = temp;
11080e1ba39fSStephan Aßmus 	}
11090e1ba39fSStephan Aßmus 
111005fd3818SStephan Aßmus 	ObjectChanged(fPath);
111105fd3818SStephan Aßmus }
111205fd3818SStephan Aßmus 
111305fd3818SStephan Aßmus // #pragma mark -
111405fd3818SStephan Aßmus 
1115128277c9SStephan Aßmus // ControlFlags
1116128277c9SStephan Aßmus uint32
1117128277c9SStephan Aßmus PathManipulator::ControlFlags() const
1118128277c9SStephan Aßmus {
1119128277c9SStephan Aßmus 	uint32 flags = 0;
1120128277c9SStephan Aßmus 
1121128277c9SStephan Aßmus //	flags |= SHAPE_UI_FLAGS_CAN_REVERSE_PATH;
1122128277c9SStephan Aßmus //
1123128277c9SStephan Aßmus //	if (!fSelection->IsEmpty())
1124128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_HAS_SELECTION;
1125128277c9SStephan Aßmus //	if (fPath->CountPoints() > 1)
1126128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_CAN_CLOSE_PATH;
1127128277c9SStephan Aßmus //	if (fPath->IsClosed())
1128128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_PATH_IS_CLOSED;
11290e1ba39fSStephan Aßmus //	if (fTransformBox)
11300e1ba39fSStephan Aßmus //		flags |= SHAPE_UI_FLAGS_IS_TRANSFORMING;
1131128277c9SStephan Aßmus 
1132128277c9SStephan Aßmus 	return flags;
1133128277c9SStephan Aßmus }
1134128277c9SStephan Aßmus 
1135128277c9SStephan Aßmus // ReversePath
1136128277c9SStephan Aßmus void
1137128277c9SStephan Aßmus PathManipulator::ReversePath()
1138128277c9SStephan Aßmus {
1139128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1140128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1141128277c9SStephan Aßmus 	if (count > 0) {
1142128277c9SStephan Aßmus 		Selection temp;
1143128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1144128277c9SStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
1145128277c9SStephan Aßmus 		}
1146128277c9SStephan Aßmus 		*fSelection = temp;
1147128277c9SStephan Aßmus 	}
1148128277c9SStephan Aßmus 	fPath->Reverse();
1149128277c9SStephan Aßmus }
1150128277c9SStephan Aßmus 
1151128277c9SStephan Aßmus // #pragma mark -
1152128277c9SStephan Aßmus 
1153128277c9SStephan Aßmus // _SetMode
1154128277c9SStephan Aßmus void
1155128277c9SStephan Aßmus PathManipulator::_SetMode(uint32 mode)
1156128277c9SStephan Aßmus {
1157128277c9SStephan Aßmus 	if (fMode != mode) {
1158128277c9SStephan Aßmus //printf("switching mode: %s -> %s\n", string_for_mode(fMode), string_for_mode(mode));
1159128277c9SStephan Aßmus 		fMode = mode;
1160128277c9SStephan Aßmus 
11610e1ba39fSStephan Aßmus 		if (fMode == TRANSFORM_POINTS) {
11620e1ba39fSStephan Aßmus 			_SetTransformBox(new TransformPointsBox(fCanvasView,
11630e1ba39fSStephan Aßmus 													this,
11640e1ba39fSStephan Aßmus 													fPath,
11650e1ba39fSStephan Aßmus 													fSelection->Items(),
11660e1ba39fSStephan Aßmus 													fSelection->CountItems()));
11670e1ba39fSStephan Aßmus //			fCanvasView->Perform(new EnterTransformPointsCommand(this,
11680e1ba39fSStephan Aßmus //														  fSelection->Items(),
11690e1ba39fSStephan Aßmus //														  fSelection->CountItems()));
11700e1ba39fSStephan Aßmus 		} else {
11710e1ba39fSStephan Aßmus 			if (fTransformBox)
11720e1ba39fSStephan Aßmus 				_SetTransformBox(NULL);
11730e1ba39fSStephan Aßmus 		}
11740e1ba39fSStephan Aßmus 
1175128277c9SStephan Aßmus 		if (BWindow* window = fCanvasView->Window()) {
1176128277c9SStephan Aßmus 			window->PostMessage(MSG_UPDATE_SHAPE_UI);
1177128277c9SStephan Aßmus 		}
1178128277c9SStephan Aßmus 		UpdateCursor();
1179128277c9SStephan Aßmus 	}
1180128277c9SStephan Aßmus }
1181128277c9SStephan Aßmus 
11820e1ba39fSStephan Aßmus 
11830e1ba39fSStephan Aßmus // _SetTransformBox
11840e1ba39fSStephan Aßmus void
11850e1ba39fSStephan Aßmus PathManipulator::_SetTransformBox(TransformPointsBox* transformBox)
11860e1ba39fSStephan Aßmus {
11870e1ba39fSStephan Aßmus 	if (fTransformBox == transformBox)
11880e1ba39fSStephan Aßmus 		return;
11890e1ba39fSStephan Aßmus 
11900e1ba39fSStephan Aßmus 	BRect dirty(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN);
11910e1ba39fSStephan Aßmus 	if (fTransformBox) {
11920e1ba39fSStephan Aßmus 		// get rid of transform box display
11930e1ba39fSStephan Aßmus 		dirty = fTransformBox->Bounds();
11940e1ba39fSStephan Aßmus 		delete fTransformBox;
11950e1ba39fSStephan Aßmus 	}
11960e1ba39fSStephan Aßmus 
11970e1ba39fSStephan Aßmus 	fTransformBox = transformBox;
11980e1ba39fSStephan Aßmus 
11994fac07a0SStephan Aßmus 	// TODO: this is weird, fMode should only be set in _SetMode, not
12004fac07a0SStephan Aßmus 	// here as well, also this method could be called this way
12014fac07a0SStephan Aßmus 	// _SetModeForMousePos -> _SetMode -> _SetTransformBox
12024fac07a0SStephan Aßmus 	// and then below it does _SetModeForMousePos again...
12030e1ba39fSStephan Aßmus 	if (fTransformBox) {
12040e1ba39fSStephan Aßmus 		fTransformBox->MouseMoved(fLastCanvasPos);
12050e1ba39fSStephan Aßmus 		if (fMode != TRANSFORM_POINTS) {
12060e1ba39fSStephan Aßmus 			fMode = TRANSFORM_POINTS;
12070e1ba39fSStephan Aßmus 		}
12080e1ba39fSStephan Aßmus 		dirty = dirty | fTransformBox->Bounds();
12090e1ba39fSStephan Aßmus 	} else {
12100e1ba39fSStephan Aßmus 		if (fMode == TRANSFORM_POINTS) {
12110e1ba39fSStephan Aßmus 			_SetModeForMousePos(fLastCanvasPos);
12120e1ba39fSStephan Aßmus 		}
12130e1ba39fSStephan Aßmus 	}
12140e1ba39fSStephan Aßmus 
12150e1ba39fSStephan Aßmus 	if (dirty.IsValid()) {
12160e1ba39fSStephan Aßmus 		dirty.InsetBy(-8, -8);
12170e1ba39fSStephan Aßmus 		fCanvasView->Invalidate(dirty);
12180e1ba39fSStephan Aßmus 	}
12190e1ba39fSStephan Aßmus }
12200e1ba39fSStephan Aßmus 
1221128277c9SStephan Aßmus // _AddPoint
1222128277c9SStephan Aßmus void
1223128277c9SStephan Aßmus PathManipulator::_AddPoint(BPoint where)
1224128277c9SStephan Aßmus {
1225128277c9SStephan Aßmus 	if (fPath->AddPoint(where)) {
1226128277c9SStephan Aßmus 		fCurrentPathPoint = fPath->CountPoints() - 1;
1227128277c9SStephan Aßmus 
1228128277c9SStephan Aßmus 		delete fAddPointCommand;
1229128277c9SStephan Aßmus 		fAddPointCommand = new AddPointCommand(fPath, fCurrentPathPoint,
1230128277c9SStephan Aßmus 											   fSelection->Items(),
1231128277c9SStephan Aßmus 											   fSelection->CountItems());
1232128277c9SStephan Aßmus 
1233128277c9SStephan Aßmus 		_Select(fCurrentPathPoint, fShiftDown);
1234128277c9SStephan Aßmus 	}
1235128277c9SStephan Aßmus }
1236128277c9SStephan Aßmus 
1237128277c9SStephan Aßmus // scale_point
1238128277c9SStephan Aßmus BPoint
1239128277c9SStephan Aßmus scale_point(BPoint a, BPoint b, float scale)
1240128277c9SStephan Aßmus {
1241128277c9SStephan Aßmus 	return BPoint(a.x + (b.x - a.x) * scale,
1242128277c9SStephan Aßmus 				  a.y + (b.y - a.y) * scale);
1243128277c9SStephan Aßmus }
1244128277c9SStephan Aßmus 
1245128277c9SStephan Aßmus // _InsertPoint
1246128277c9SStephan Aßmus void
1247128277c9SStephan Aßmus PathManipulator::_InsertPoint(BPoint where, int32 index)
1248128277c9SStephan Aßmus {
1249128277c9SStephan Aßmus 	double scale;
1250128277c9SStephan Aßmus 
1251128277c9SStephan Aßmus 	BPoint point;
1252128277c9SStephan Aßmus 	BPoint pointIn;
1253128277c9SStephan Aßmus 	BPoint pointOut;
1254128277c9SStephan Aßmus 
1255128277c9SStephan Aßmus 	BPoint previous;
1256128277c9SStephan Aßmus 	BPoint previousOut;
1257128277c9SStephan Aßmus 	BPoint next;
1258128277c9SStephan Aßmus 	BPoint nextIn;
1259128277c9SStephan Aßmus 
1260128277c9SStephan Aßmus 	if (fPath->FindBezierScale(index - 1, where, &scale)
1261128277c9SStephan Aßmus 		&& scale >= 0.0 && scale <= 1.0
1262128277c9SStephan Aßmus 		&& fPath->GetPoint(index - 1, scale, point)) {
1263128277c9SStephan Aßmus 
1264128277c9SStephan Aßmus 		fPath->GetPointAt(index - 1, previous);
1265128277c9SStephan Aßmus 		fPath->GetPointOutAt(index - 1, previousOut);
1266128277c9SStephan Aßmus 		fPath->GetPointAt(index, next);
1267128277c9SStephan Aßmus 		fPath->GetPointInAt(index, nextIn);
1268128277c9SStephan Aßmus 
1269128277c9SStephan Aßmus 		where = scale_point(previousOut, nextIn, scale);
1270128277c9SStephan Aßmus 
1271128277c9SStephan Aßmus 		previousOut = scale_point(previous, previousOut, scale);
1272128277c9SStephan Aßmus 		nextIn = scale_point(next, nextIn, 1 - scale);
1273128277c9SStephan Aßmus 		pointIn = scale_point(previousOut, where, scale);
1274128277c9SStephan Aßmus 		pointOut = scale_point(nextIn, where, 1 - scale);
1275128277c9SStephan Aßmus 
1276128277c9SStephan Aßmus 		if (fPath->AddPoint(point, index)) {
1277128277c9SStephan Aßmus 
1278128277c9SStephan Aßmus 			fPath->SetPointIn(index, pointIn);
1279128277c9SStephan Aßmus 			fPath->SetPointOut(index, pointOut);
1280128277c9SStephan Aßmus 
1281128277c9SStephan Aßmus 			delete fInsertPointCommand;
1282128277c9SStephan Aßmus 			fInsertPointCommand = new InsertPointCommand(fPath, index,
1283128277c9SStephan Aßmus 														 fSelection->Items(),
1284128277c9SStephan Aßmus 														 fSelection->CountItems());
1285128277c9SStephan Aßmus 
1286128277c9SStephan Aßmus 			fPath->SetPointOut(index - 1, previousOut);
1287128277c9SStephan Aßmus 			fPath->SetPointIn(index + 1, nextIn);
1288128277c9SStephan Aßmus 
1289128277c9SStephan Aßmus 			fCurrentPathPoint = index;
1290128277c9SStephan Aßmus 			_ShiftSelection(fCurrentPathPoint, 1);
1291128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
1292128277c9SStephan Aßmus 		}
1293128277c9SStephan Aßmus 	}
1294128277c9SStephan Aßmus }
1295128277c9SStephan Aßmus 
1296128277c9SStephan Aßmus // _SetInOutConnected
1297128277c9SStephan Aßmus void
1298128277c9SStephan Aßmus PathManipulator::_SetInOutConnected(int32 index, bool connected)
1299128277c9SStephan Aßmus {
1300128277c9SStephan Aßmus 	fPath->SetInOutConnected(index, connected);
1301128277c9SStephan Aßmus }
1302128277c9SStephan Aßmus 
1303128277c9SStephan Aßmus // _SetSharp
1304128277c9SStephan Aßmus void
1305128277c9SStephan Aßmus PathManipulator::_SetSharp(int32 index)
1306128277c9SStephan Aßmus {
1307128277c9SStephan Aßmus 	BPoint p;
1308128277c9SStephan Aßmus 	fPath->GetPointAt(index, p);
1309128277c9SStephan Aßmus 	fPath->SetPoint(index, p, p, p, true);
1310128277c9SStephan Aßmus }
1311128277c9SStephan Aßmus 
1312128277c9SStephan Aßmus // _RemoveSelection
1313128277c9SStephan Aßmus void
1314128277c9SStephan Aßmus PathManipulator::_RemoveSelection()
1315128277c9SStephan Aßmus {
13160e1ba39fSStephan Aßmus 	// NOTE: copy selection since removing points will
13170e1ba39fSStephan Aßmus 	// trigger notifications, and that will influence the
13180e1ba39fSStephan Aßmus 	// selection
13190e1ba39fSStephan Aßmus 	Selection selection = *fSelection;
13200e1ba39fSStephan Aßmus 	int32 count = selection.CountItems();
1321128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
13220e1ba39fSStephan Aßmus 		if (!fPath->RemovePoint(selection.IndexAt(i) - i))
1323128277c9SStephan Aßmus 			break;
1324128277c9SStephan Aßmus 	}
1325128277c9SStephan Aßmus 
13260e1ba39fSStephan Aßmus 	fPath->SetClosed(fPath->IsClosed() && fPath->CountPoints() > 1);
1327128277c9SStephan Aßmus 
1328128277c9SStephan Aßmus 	fSelection->MakeEmpty();
1329128277c9SStephan Aßmus }
1330128277c9SStephan Aßmus 
1331128277c9SStephan Aßmus 
1332128277c9SStephan Aßmus // _RemovePoint
1333128277c9SStephan Aßmus void
1334128277c9SStephan Aßmus PathManipulator::_RemovePoint(int32 index)
1335128277c9SStephan Aßmus {
1336128277c9SStephan Aßmus 	if (fPath->RemovePoint(index)) {
1337128277c9SStephan Aßmus 		_Deselect(index);
1338128277c9SStephan Aßmus 		_ShiftSelection(index + 1, -1);
1339128277c9SStephan Aßmus 	}
1340128277c9SStephan Aßmus }
1341128277c9SStephan Aßmus 
1342128277c9SStephan Aßmus // _RemovePointIn
1343128277c9SStephan Aßmus void
1344128277c9SStephan Aßmus PathManipulator::_RemovePointIn(int32 index)
1345128277c9SStephan Aßmus {
1346128277c9SStephan Aßmus 	BPoint p;
1347128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1348128277c9SStephan Aßmus 		fPath->SetPointIn(index, p);
1349128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1350128277c9SStephan Aßmus 	}
1351128277c9SStephan Aßmus }
1352128277c9SStephan Aßmus 
1353128277c9SStephan Aßmus // _RemovePointOut
1354128277c9SStephan Aßmus void
1355128277c9SStephan Aßmus PathManipulator::_RemovePointOut(int32 index)
1356128277c9SStephan Aßmus {
1357128277c9SStephan Aßmus 	BPoint p;
1358128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1359128277c9SStephan Aßmus 		fPath->SetPointOut(index, p);
1360128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1361128277c9SStephan Aßmus 	}
1362128277c9SStephan Aßmus }
1363128277c9SStephan Aßmus 
1364128277c9SStephan Aßmus // _Delete
1365128277c9SStephan Aßmus Command*
1366128277c9SStephan Aßmus PathManipulator::_Delete()
1367128277c9SStephan Aßmus {
1368128277c9SStephan Aßmus 	Command* command = NULL;
1369128277c9SStephan Aßmus 	if (!fMouseDown) {
13700e1ba39fSStephan Aßmus 		// make sure we apply an on-going transformation before we proceed
13710e1ba39fSStephan Aßmus 		if (fTransformBox) {
13720e1ba39fSStephan Aßmus 			_SetTransformBox(NULL);
13730e1ba39fSStephan Aßmus 		}
13740e1ba39fSStephan Aßmus 
1375128277c9SStephan Aßmus 		if (fSelection->CountItems() == fPath->CountPoints()) {
1376128277c9SStephan Aßmus //			command = new RemovePathCommand(fPath);
1377128277c9SStephan Aßmus 		} else {
1378128277c9SStephan Aßmus 			command = new RemovePointsCommand(fPath,
1379128277c9SStephan Aßmus 											  fSelection->Items(),
1380128277c9SStephan Aßmus 											  fSelection->CountItems());
1381128277c9SStephan Aßmus 			_RemoveSelection();
1382128277c9SStephan Aßmus 		}
1383128277c9SStephan Aßmus 
1384128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1385128277c9SStephan Aßmus 	}
1386128277c9SStephan Aßmus 
1387128277c9SStephan Aßmus 	return command;
1388128277c9SStephan Aßmus }
1389128277c9SStephan Aßmus 
1390128277c9SStephan Aßmus // #pragma mark -
1391128277c9SStephan Aßmus 
1392128277c9SStephan Aßmus // _Select
1393128277c9SStephan Aßmus void
1394128277c9SStephan Aßmus PathManipulator::_Select(BRect r)
1395128277c9SStephan Aßmus {
1396128277c9SStephan Aßmus 	BPoint p;
1397f4bd80a2SStephan Aßmus 	BPoint pIn;
1398f4bd80a2SStephan Aßmus 	BPoint pOut;
1399128277c9SStephan Aßmus 	int32 count = fPath->CountPoints();
1400128277c9SStephan Aßmus 	Selection temp;
1401f4bd80a2SStephan Aßmus 	for (int32 i = 0; i < count && fPath->GetPointsAt(i, p, pIn, pOut); i++) {
1402f4bd80a2SStephan Aßmus 		if (r.Contains(p) || r.Contains(pIn) || r.Contains(pOut)) {
1403128277c9SStephan Aßmus 			temp.Add(i);
1404128277c9SStephan Aßmus 		}
1405128277c9SStephan Aßmus 	}
1406128277c9SStephan Aßmus 	// merge old and new selection
1407128277c9SStephan Aßmus 	count = fOldSelection->CountItems();
1408128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
1409128277c9SStephan Aßmus 		int32 index = fOldSelection->IndexAt(i);
1410128277c9SStephan Aßmus 		if (temp.Contains(index))
1411128277c9SStephan Aßmus 			temp.Remove(index);
1412128277c9SStephan Aßmus 		else
1413128277c9SStephan Aßmus 			temp.Add(index);
1414128277c9SStephan Aßmus 	}
1415128277c9SStephan Aßmus 	if (temp != *fSelection) {
1416128277c9SStephan Aßmus 		*fSelection = temp;
1417128277c9SStephan Aßmus 		_UpdateSelection();
1418128277c9SStephan Aßmus 	}
1419128277c9SStephan Aßmus }
1420128277c9SStephan Aßmus 
1421128277c9SStephan Aßmus // _Select
1422128277c9SStephan Aßmus void
1423128277c9SStephan Aßmus PathManipulator::_Select(int32 index, bool extend)
1424128277c9SStephan Aßmus {
1425128277c9SStephan Aßmus 	if (!extend)
1426128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1427128277c9SStephan Aßmus 	if (fSelection->Contains(index))
1428128277c9SStephan Aßmus 		fSelection->Remove(index);
1429128277c9SStephan Aßmus 	else
1430128277c9SStephan Aßmus 		fSelection->Add(index);
1431128277c9SStephan Aßmus 	// TODO: this can lead to unnecessary invalidation (maybe need to investigate)
1432128277c9SStephan Aßmus 	_UpdateSelection();
1433128277c9SStephan Aßmus }
1434128277c9SStephan Aßmus 
1435128277c9SStephan Aßmus // _Select
1436128277c9SStephan Aßmus void
1437128277c9SStephan Aßmus PathManipulator::_Select(const int32* indices, int32 count, bool extend)
1438128277c9SStephan Aßmus {
1439128277c9SStephan Aßmus 	if (extend) {
1440128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1441128277c9SStephan Aßmus 			if (!fSelection->Contains(indices[i]))
1442128277c9SStephan Aßmus 				fSelection->Add(indices[i]);
1443128277c9SStephan Aßmus 		}
1444128277c9SStephan Aßmus 	} else {
1445128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1446128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1447128277c9SStephan Aßmus 			fSelection->Add(indices[i]);
1448128277c9SStephan Aßmus 		}
1449128277c9SStephan Aßmus 	}
1450128277c9SStephan Aßmus 	_UpdateSelection();
1451128277c9SStephan Aßmus }
1452128277c9SStephan Aßmus 
1453128277c9SStephan Aßmus // _Deselect
1454128277c9SStephan Aßmus void
1455128277c9SStephan Aßmus PathManipulator::_Deselect(int32 index)
1456128277c9SStephan Aßmus {
1457128277c9SStephan Aßmus 	if (fSelection->Contains(index)) {
1458128277c9SStephan Aßmus 		fSelection->Remove(index);
1459128277c9SStephan Aßmus 		_UpdateSelection();
1460128277c9SStephan Aßmus 	}
1461128277c9SStephan Aßmus }
1462128277c9SStephan Aßmus 
1463128277c9SStephan Aßmus // _ShiftSelection
1464128277c9SStephan Aßmus void
1465128277c9SStephan Aßmus PathManipulator::_ShiftSelection(int32 startIndex, int32 direction)
1466128277c9SStephan Aßmus {
1467128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1468128277c9SStephan Aßmus 	if (count > 0) {
1469128277c9SStephan Aßmus 		int32* selection = fSelection->Items();
1470128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1471128277c9SStephan Aßmus 			if (selection[i] >= startIndex) {
1472128277c9SStephan Aßmus 				selection[i] += direction;
1473128277c9SStephan Aßmus 			}
1474128277c9SStephan Aßmus 		}
1475128277c9SStephan Aßmus 	}
1476128277c9SStephan Aßmus 	_UpdateSelection();
1477128277c9SStephan Aßmus }
1478128277c9SStephan Aßmus 
1479128277c9SStephan Aßmus // _IsSelected
1480128277c9SStephan Aßmus bool
1481128277c9SStephan Aßmus PathManipulator::_IsSelected(int32 index) const
1482128277c9SStephan Aßmus {
1483128277c9SStephan Aßmus 	return fSelection->Contains(index);
1484128277c9SStephan Aßmus }
1485128277c9SStephan Aßmus 
1486128277c9SStephan Aßmus // #pragma mark -
1487128277c9SStephan Aßmus 
1488128277c9SStephan Aßmus // _InvalidateCanvas
1489128277c9SStephan Aßmus void
1490128277c9SStephan Aßmus PathManipulator::_InvalidateCanvas(BRect rect) const
1491128277c9SStephan Aßmus {
1492f67876a0SStephan Aßmus 	// convert from canvas to view space
1493f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&rect);
1494128277c9SStephan Aßmus 	fCanvasView->Invalidate(rect);
1495128277c9SStephan Aßmus }
1496128277c9SStephan Aßmus 
1497128277c9SStephan Aßmus // _InvalidateHighlightPoints
1498128277c9SStephan Aßmus void
1499128277c9SStephan Aßmus PathManipulator::_InvalidateHighlightPoints(int32 newIndex, uint32 newMode)
1500128277c9SStephan Aßmus {
1501128277c9SStephan Aßmus 	BRect oldRect = _ControlPointRect(fCurrentPathPoint, fMode);
1502128277c9SStephan Aßmus 	BRect newRect = _ControlPointRect(newIndex, newMode);
1503128277c9SStephan Aßmus 	if (oldRect.IsValid())
1504128277c9SStephan Aßmus 		_InvalidateCanvas(oldRect);
1505128277c9SStephan Aßmus 	if (newRect.IsValid())
1506128277c9SStephan Aßmus 		_InvalidateCanvas(newRect);
1507128277c9SStephan Aßmus }
1508128277c9SStephan Aßmus 
1509128277c9SStephan Aßmus // _UpdateSelection
1510128277c9SStephan Aßmus void
1511128277c9SStephan Aßmus PathManipulator::_UpdateSelection() const
1512128277c9SStephan Aßmus {
1513128277c9SStephan Aßmus 	_InvalidateCanvas(_ControlPointRect());
1514128277c9SStephan Aßmus 	if (BWindow* window = fCanvasView->Window()) {
1515128277c9SStephan Aßmus 		window->PostMessage(MSG_UPDATE_SHAPE_UI);
1516128277c9SStephan Aßmus 	}
1517128277c9SStephan Aßmus }
1518128277c9SStephan Aßmus 
1519128277c9SStephan Aßmus // _ControlPointRect
1520128277c9SStephan Aßmus BRect
1521128277c9SStephan Aßmus PathManipulator::_ControlPointRect() const
1522128277c9SStephan Aßmus {
1523128277c9SStephan Aßmus 	BRect r = fPath->ControlPointBounds();
1524128277c9SStephan Aßmus 	r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1525128277c9SStephan Aßmus 	return r;
1526128277c9SStephan Aßmus }
1527128277c9SStephan Aßmus 
1528128277c9SStephan Aßmus // _ControlPointRect
1529128277c9SStephan Aßmus BRect
1530128277c9SStephan Aßmus PathManipulator::_ControlPointRect(int32 index, uint32 mode) const
1531128277c9SStephan Aßmus {
1532128277c9SStephan Aßmus 	BRect rect(0.0, 0.0, -1.0, -1.0);
1533128277c9SStephan Aßmus 	if (index >= 0) {
1534128277c9SStephan Aßmus 		BPoint p, pIn, pOut;
1535128277c9SStephan Aßmus 		fPath->GetPointsAt(index, p, pIn, pOut);
1536128277c9SStephan Aßmus 		switch (mode) {
1537128277c9SStephan Aßmus 			case MOVE_POINT:
1538128277c9SStephan Aßmus 			case TOGGLE_SHARP:
1539128277c9SStephan Aßmus 			case REMOVE_POINT:
1540128277c9SStephan Aßmus 			case CLOSE_PATH:
1541128277c9SStephan Aßmus 				rect.Set(p.x, p.y, p.x, p.y);
1542128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1543128277c9SStephan Aßmus 				break;
1544128277c9SStephan Aßmus 			case MOVE_POINT_IN:
1545128277c9SStephan Aßmus 			case TOGGLE_SHARP_IN:
1546128277c9SStephan Aßmus 			case REMOVE_POINT_IN:
1547128277c9SStephan Aßmus 				rect.Set(pIn.x, pIn.y, pIn.x, pIn.y);
1548128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1549128277c9SStephan Aßmus 				break;
1550128277c9SStephan Aßmus 			case MOVE_POINT_OUT:
1551128277c9SStephan Aßmus 			case TOGGLE_SHARP_OUT:
1552128277c9SStephan Aßmus 			case REMOVE_POINT_OUT:
1553128277c9SStephan Aßmus 				rect.Set(pOut.x, pOut.y, pOut.x, pOut.y);
1554128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1555128277c9SStephan Aßmus 				break;
1556128277c9SStephan Aßmus 			case SELECT_POINTS:
1557128277c9SStephan Aßmus 				rect.Set(min4(p.x, pIn.x, pOut.x, pOut.x),
1558128277c9SStephan Aßmus 						 min4(p.y, pIn.y, pOut.y, pOut.y),
1559128277c9SStephan Aßmus 						 max4(p.x, pIn.x, pOut.x, pOut.x),
1560128277c9SStephan Aßmus 						 max4(p.y, pIn.y, pOut.y, pOut.y));
1561128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1562128277c9SStephan Aßmus 				break;
1563128277c9SStephan Aßmus 		}
1564128277c9SStephan Aßmus 	}
1565128277c9SStephan Aßmus 	return rect;
1566128277c9SStephan Aßmus }
1567128277c9SStephan Aßmus 
1568128277c9SStephan Aßmus // #pragma mark -
1569128277c9SStephan Aßmus 
1570128277c9SStephan Aßmus // _SetModeForMousePos
1571128277c9SStephan Aßmus void
1572128277c9SStephan Aßmus PathManipulator::_SetModeForMousePos(BPoint where)
1573128277c9SStephan Aßmus {
1574128277c9SStephan Aßmus 	uint32 mode = UNDEFINED;
1575128277c9SStephan Aßmus 	int32 index = -1;
1576128277c9SStephan Aßmus 
1577f67876a0SStephan Aßmus 	float zoomLevel = fCanvasView->ZoomLevel();
1578128277c9SStephan Aßmus 
1579128277c9SStephan Aßmus 	// see if we're close enough at a control point
1580128277c9SStephan Aßmus 	BPoint point;
1581128277c9SStephan Aßmus 	BPoint pointIn;
1582128277c9SStephan Aßmus 	BPoint pointOut;
1583128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut)
1584128277c9SStephan Aßmus 					  && mode == UNDEFINED; i++) {
1585128277c9SStephan Aßmus 
1586128277c9SStephan Aßmus 		float distM = point_point_distance(point, where) * zoomLevel;
1587128277c9SStephan Aßmus 		float distIn = point_point_distance(pointIn, where) * zoomLevel;
1588128277c9SStephan Aßmus 		float distOut = point_point_distance(pointOut, where) * zoomLevel;
1589128277c9SStephan Aßmus 
1590128277c9SStephan Aßmus 		if (distM < MOVE_THRESHOLD) {
1591128277c9SStephan Aßmus 			if (i == 0 && fClickToClose
1592128277c9SStephan Aßmus 				&& !fPath->IsClosed() && fPath->CountPoints() > 1) {
1593128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1594128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : CLOSE_PATH);
1595128277c9SStephan Aßmus 				index = i;
1596128277c9SStephan Aßmus 			} else {
1597128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1598128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : MOVE_POINT);
1599128277c9SStephan Aßmus 				index = i;
1600128277c9SStephan Aßmus 			}
1601128277c9SStephan Aßmus 		}
16020e1ba39fSStephan Aßmus 		if (distM - distIn > 0.00001
16030e1ba39fSStephan Aßmus 			&& distIn < MOVE_THRESHOLD) {
1604128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_IN :
1605128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_IN : MOVE_POINT_IN);
1606128277c9SStephan Aßmus 			index = i;
1607128277c9SStephan Aßmus 		}
16080e1ba39fSStephan Aßmus 		if (distIn - distOut > 0.00001
16090e1ba39fSStephan Aßmus 			&& distOut < distM && distOut < MOVE_THRESHOLD) {
1610128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_OUT :
1611128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_OUT : MOVE_POINT_OUT);
1612128277c9SStephan Aßmus 			index = i;
1613128277c9SStephan Aßmus 		}
1614128277c9SStephan Aßmus 	}
1615128277c9SStephan Aßmus 	// selection mode overrides any other mode,
1616128277c9SStephan Aßmus 	// but we need to check for it after we know
1617128277c9SStephan Aßmus 	// the index of the point under the mouse (code above)
1618128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1619128277c9SStephan Aßmus 	if (fShiftDown && pointCount > 0) {
1620128277c9SStephan Aßmus 		mode = SELECT_POINTS;
1621128277c9SStephan Aßmus 	}
1622128277c9SStephan Aßmus 
1623128277c9SStephan Aßmus 	// see if user wants to start new sub path
1624128277c9SStephan Aßmus 	if (fAltDown) {
1625128277c9SStephan Aßmus 		mode = NEW_PATH;
1626128277c9SStephan Aßmus 		index = -1;
1627128277c9SStephan Aßmus 	}
1628128277c9SStephan Aßmus 
1629128277c9SStephan Aßmus 	// see if we're close enough at a line
1630128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1631128277c9SStephan Aßmus 		float distance;
1632128277c9SStephan Aßmus 		if (fPath->GetDistance(where, &distance, &index)) {
1633128277c9SStephan Aßmus 			if (distance < (INSERT_DIST_THRESHOLD / zoomLevel)) {
1634128277c9SStephan Aßmus 				mode = INSERT_POINT;
1635128277c9SStephan Aßmus 			}
1636128277c9SStephan Aßmus 		} else {
1637128277c9SStephan Aßmus 			// restore index, since it was changed by call above
1638128277c9SStephan Aßmus 			index = fCurrentPathPoint;
1639128277c9SStephan Aßmus 		}
1640128277c9SStephan Aßmus 	}
1641128277c9SStephan Aßmus 
1642128277c9SStephan Aßmus 	// nope, still undefined mode, last fall back
1643128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1644128277c9SStephan Aßmus 		if (fFallBackMode == SELECT_POINTS) {
1645128277c9SStephan Aßmus 			if (fPath->IsClosed() && pointCount > 0) {
1646128277c9SStephan Aßmus 				mode = SELECT_POINTS;
1647128277c9SStephan Aßmus 				index = -1;
1648128277c9SStephan Aßmus 			} else {
1649128277c9SStephan Aßmus 				mode = ADD_POINT;
1650128277c9SStephan Aßmus 				index = pointCount - 1;
1651128277c9SStephan Aßmus 			}
1652128277c9SStephan Aßmus 		} else {
1653128277c9SStephan Aßmus 			// user had clicked "New Path" icon
1654128277c9SStephan Aßmus 			mode = fFallBackMode;
1655128277c9SStephan Aßmus 		}
1656128277c9SStephan Aßmus 	}
1657128277c9SStephan Aßmus 	// switch mode if necessary
1658128277c9SStephan Aßmus 	if (mode != fMode || index != fCurrentPathPoint) {
1659128277c9SStephan Aßmus 		// invalidate path display (to highlight the respective point)
1660128277c9SStephan Aßmus 		_InvalidateHighlightPoints(index, mode);
1661128277c9SStephan Aßmus 		_SetMode(mode);
1662128277c9SStephan Aßmus 		fCurrentPathPoint = index;
1663128277c9SStephan Aßmus 	}
1664128277c9SStephan Aßmus }
1665128277c9SStephan Aßmus 
1666128277c9SStephan Aßmus // #pragma mark -
1667128277c9SStephan Aßmus 
1668128277c9SStephan Aßmus // _Nudge
1669128277c9SStephan Aßmus void
1670128277c9SStephan Aßmus PathManipulator::_Nudge(BPoint direction)
1671128277c9SStephan Aßmus {
1672128277c9SStephan Aßmus 	bigtime_t now = system_time();
1673128277c9SStephan Aßmus 	if (now - fLastNudgeTime > 500000) {
16740e1ba39fSStephan Aßmus 		fCanvasView->Perform(_FinishNudging());
1675128277c9SStephan Aßmus 	}
1676128277c9SStephan Aßmus 	fLastNudgeTime = now;
1677128277c9SStephan Aßmus 	fNudgeOffset += direction;
1678128277c9SStephan Aßmus 
16790e1ba39fSStephan Aßmus 	if (fTransformBox) {
16800e1ba39fSStephan Aßmus 		fTransformBox->NudgeBy(direction);
16810e1ba39fSStephan Aßmus 		return;
16820e1ba39fSStephan Aßmus 	}
16830e1ba39fSStephan Aßmus 
16840e1ba39fSStephan Aßmus 	if (!fNudgeCommand) {
16850e1ba39fSStephan Aßmus 
16860e1ba39fSStephan Aßmus 		bool fromSelection = !fSelection->IsEmpty();
16870e1ba39fSStephan Aßmus 
16880e1ba39fSStephan Aßmus 		int32 count = fromSelection ? fSelection->CountItems()
16890e1ba39fSStephan Aßmus 									: fPath->CountPoints();
16900e1ba39fSStephan Aßmus 		int32 indices[count];
16910e1ba39fSStephan Aßmus 		control_point points[count];
16920e1ba39fSStephan Aßmus 
16930e1ba39fSStephan Aßmus 		// init indices and points
16940e1ba39fSStephan Aßmus 		for (int32 i = 0; i < count; i++) {
16950e1ba39fSStephan Aßmus 			indices[i] = fromSelection ? fSelection->IndexAt(i) : i;
16960e1ba39fSStephan Aßmus 			fPath->GetPointsAt(indices[i],
16970e1ba39fSStephan Aßmus 							   points[i].point,
16980e1ba39fSStephan Aßmus 							   points[i].point_in,
16990e1ba39fSStephan Aßmus 							   points[i].point_out,
17000e1ba39fSStephan Aßmus 							   &points[i].connected);
17010e1ba39fSStephan Aßmus 		}
17020e1ba39fSStephan Aßmus 
17030e1ba39fSStephan Aßmus 		fNudgeCommand = new NudgePointsCommand(fPath, indices, points, count);
17040e1ba39fSStephan Aßmus 
17050e1ba39fSStephan Aßmus 		fNudgeCommand->SetNewTranslation(fNudgeOffset);
17060e1ba39fSStephan Aßmus 		fNudgeCommand->Redo();
17070e1ba39fSStephan Aßmus 
17080e1ba39fSStephan Aßmus 	} else {
17090e1ba39fSStephan Aßmus 		fNudgeCommand->SetNewTranslation(fNudgeOffset);
17100e1ba39fSStephan Aßmus 		fNudgeCommand->Redo();
17110e1ba39fSStephan Aßmus 	}
1712128277c9SStephan Aßmus 
1713128277c9SStephan Aßmus 	if (!fMouseDown)
1714128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1715128277c9SStephan Aßmus }
1716128277c9SStephan Aßmus 
1717128277c9SStephan Aßmus // _FinishNudging
17180e1ba39fSStephan Aßmus Command*
1719128277c9SStephan Aßmus PathManipulator::_FinishNudging()
1720128277c9SStephan Aßmus {
1721128277c9SStephan Aßmus 	fNudgeOffset = BPoint(0.0, 0.0);
1722128277c9SStephan Aßmus 
17230e1ba39fSStephan Aßmus 	Command* command;
17240e1ba39fSStephan Aßmus 
17250e1ba39fSStephan Aßmus 	if (fTransformBox) {
17260e1ba39fSStephan Aßmus 		command = fTransformBox->FinishNudging();
17270e1ba39fSStephan Aßmus 	} else {
17280e1ba39fSStephan Aßmus 		command = fNudgeCommand;
17290e1ba39fSStephan Aßmus 		fNudgeCommand = NULL;
17300e1ba39fSStephan Aßmus 	}
17310e1ba39fSStephan Aßmus 
17320e1ba39fSStephan Aßmus 	return command;
1733128277c9SStephan Aßmus }
1734128277c9SStephan Aßmus 
1735128277c9SStephan Aßmus 
1736