xref: /haiku/src/apps/icon-o-matic/shape/PathManipulator.cpp (revision ce181bb0cd9855442f7a1db340e7e9d51f231e33)
1128277c9SStephan Aßmus /*
2128277c9SStephan Aßmus  * Copyright 2006, 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>
16128277c9SStephan Aßmus #include <Window.h>
17128277c9SStephan Aßmus 
18128277c9SStephan Aßmus #include "cursors.h"
19128277c9SStephan Aßmus #include "support.h"
20128277c9SStephan Aßmus 
21f67876a0SStephan Aßmus #include "CanvasView.h"
22128277c9SStephan Aßmus #include "VectorPath.h"
23128277c9SStephan Aßmus 
24128277c9SStephan Aßmus #include "AddPointCommand.h"
25128277c9SStephan Aßmus #include "ChangePointCommand.h"
26128277c9SStephan Aßmus //#include "CloseCommand.h"
27128277c9SStephan Aßmus #include "InsertPointCommand.h"
28128277c9SStephan Aßmus //#include "NewPathCommand.h"
29128277c9SStephan Aßmus //#include "NudgePointsCommand.h"
30128277c9SStephan Aßmus //#include "RemovePathCommand.h"
31128277c9SStephan Aßmus #include "RemovePointsCommand.h"
32128277c9SStephan Aßmus //#include "ReversePathCommand.h"
33128277c9SStephan Aßmus //#include "SelectPathCommand.h"
34128277c9SStephan Aßmus //#include "SelectPointsCommand.h"
35128277c9SStephan Aßmus 
36128277c9SStephan Aßmus #define POINT_EXTEND 3.0
37128277c9SStephan Aßmus #define CONTROL_POINT_EXTEND 2.0
38128277c9SStephan Aßmus #define INSERT_DIST_THRESHOLD 7.0
39128277c9SStephan Aßmus #define MOVE_THRESHOLD 9.0
40128277c9SStephan Aßmus 
41128277c9SStephan Aßmus enum {
42128277c9SStephan Aßmus 	UNDEFINED,
43128277c9SStephan Aßmus 
44128277c9SStephan Aßmus 	NEW_PATH,
45128277c9SStephan Aßmus 
46128277c9SStephan Aßmus 	ADD_POINT,
47128277c9SStephan Aßmus 	INSERT_POINT,
48128277c9SStephan Aßmus 	MOVE_POINT,
49128277c9SStephan Aßmus 	MOVE_POINT_IN,
50128277c9SStephan Aßmus 	MOVE_POINT_OUT,
51128277c9SStephan Aßmus 	CLOSE_PATH,
52128277c9SStephan Aßmus 
53128277c9SStephan Aßmus 	TOGGLE_SHARP,
54128277c9SStephan Aßmus 	TOGGLE_SHARP_IN,
55128277c9SStephan Aßmus 	TOGGLE_SHARP_OUT,
56128277c9SStephan Aßmus 
57128277c9SStephan Aßmus 	REMOVE_POINT,
58128277c9SStephan Aßmus 	REMOVE_POINT_IN,
59128277c9SStephan Aßmus 	REMOVE_POINT_OUT,
60128277c9SStephan Aßmus 
61128277c9SStephan Aßmus 	SELECT_POINTS,
62128277c9SStephan Aßmus 	TRANSFORM_POINTS,
63128277c9SStephan Aßmus 	TRANSLATE_POINTS,
64128277c9SStephan Aßmus 
65128277c9SStephan Aßmus 	SELECT_SUB_PATH,
66128277c9SStephan Aßmus };
67128277c9SStephan Aßmus 
68128277c9SStephan Aßmus inline const char*
69128277c9SStephan Aßmus string_for_mode(uint32 mode)
70128277c9SStephan Aßmus {
71128277c9SStephan Aßmus 	switch (mode) {
72128277c9SStephan Aßmus 		case UNDEFINED:
73128277c9SStephan Aßmus 			return "UNDEFINED";
74128277c9SStephan Aßmus 		case NEW_PATH:
75128277c9SStephan Aßmus 			return "NEW_PATH";
76128277c9SStephan Aßmus 		case ADD_POINT:
77128277c9SStephan Aßmus 			return "ADD_POINT";
78128277c9SStephan Aßmus 		case INSERT_POINT:
79128277c9SStephan Aßmus 			return "INSERT_POINT";
80128277c9SStephan Aßmus 		case MOVE_POINT:
81128277c9SStephan Aßmus 			return "MOVE_POINT";
82128277c9SStephan Aßmus 		case MOVE_POINT_IN:
83128277c9SStephan Aßmus 			return "MOVE_POINT_IN";
84128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
85128277c9SStephan Aßmus 			return "MOVE_POINT_OUT";
86128277c9SStephan Aßmus 		case CLOSE_PATH:
87128277c9SStephan Aßmus 			return "CLOSE_PATH";
88128277c9SStephan Aßmus 		case TOGGLE_SHARP:
89128277c9SStephan Aßmus 			return "TOGGLE_SHARP";
90128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
91128277c9SStephan Aßmus 			return "TOGGLE_SHARP_IN";
92128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
93128277c9SStephan Aßmus 			return "TOGGLE_SHARP_OUT";
94128277c9SStephan Aßmus 		case REMOVE_POINT:
95128277c9SStephan Aßmus 			return "REMOVE_POINT";
96128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
97128277c9SStephan Aßmus 			return "REMOVE_POINT_IN";
98128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
99128277c9SStephan Aßmus 			return "REMOVE_POINT_OUT";
100128277c9SStephan Aßmus 		case SELECT_POINTS:
101128277c9SStephan Aßmus 			return "SELECT_POINTS";
102128277c9SStephan Aßmus 		case TRANSFORM_POINTS:
103128277c9SStephan Aßmus 			return "TRANSFORM_POINTS";
104128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
105128277c9SStephan Aßmus 			return "TRANSLATE_POINTS";
106128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
107128277c9SStephan Aßmus 			return "SELECT_SUB_PATH";
108128277c9SStephan Aßmus 	}
109128277c9SStephan Aßmus 	return "<unknown mode>";
110128277c9SStephan Aßmus }
111128277c9SStephan Aßmus 
112*ce181bb0SStephan Aßmus class PathManipulator::Selection : protected BList
113128277c9SStephan Aßmus {
114128277c9SStephan Aßmus public:
115128277c9SStephan Aßmus 	inline Selection(int32 count = 20)
116128277c9SStephan Aßmus 		: BList(count) {}
117128277c9SStephan Aßmus 	inline ~Selection() {}
118128277c9SStephan Aßmus 
119128277c9SStephan Aßmus 	inline void Add(int32 value)
120128277c9SStephan Aßmus 		{
121128277c9SStephan Aßmus 			if (value >= 0) {
122128277c9SStephan Aßmus 				// keep the list sorted
123128277c9SStephan Aßmus 				int32 count = CountItems();
124128277c9SStephan Aßmus 				int32 index = 0;
125128277c9SStephan Aßmus 				for (; index < count; index++) {
126128277c9SStephan Aßmus 					if (IndexAt(index) > value) {
127128277c9SStephan Aßmus 						break;
128128277c9SStephan Aßmus 					}
129128277c9SStephan Aßmus 				}
130128277c9SStephan Aßmus 				BList::AddItem((void*)value, index);
131128277c9SStephan Aßmus 			}
132128277c9SStephan Aßmus 		}
133128277c9SStephan Aßmus 
134128277c9SStephan Aßmus 	inline bool Remove(int32 value)
135128277c9SStephan Aßmus 		{ return BList::RemoveItem((void*)value); }
136128277c9SStephan Aßmus 
137128277c9SStephan Aßmus 	inline bool Contains(int32 value) const
138128277c9SStephan Aßmus 		{ return BList::HasItem((void*)value); }
139128277c9SStephan Aßmus 
140128277c9SStephan Aßmus 	inline bool IsEmpty() const
141128277c9SStephan Aßmus 		{ return BList::IsEmpty(); }
142128277c9SStephan Aßmus 
143128277c9SStephan Aßmus 	inline int32 IndexAt(int32 index) const
144128277c9SStephan Aßmus 		{ return (int32)BList::ItemAt(index); }
145128277c9SStephan Aßmus 
146128277c9SStephan Aßmus 	inline void MakeEmpty()
147128277c9SStephan Aßmus 		{ BList::MakeEmpty(); }
148128277c9SStephan Aßmus 
149128277c9SStephan Aßmus 	inline int32* Items() const
150128277c9SStephan Aßmus 		{ return (int32*)BList::Items(); }
151128277c9SStephan Aßmus 
152128277c9SStephan Aßmus 	inline const int32 CountItems() const
153128277c9SStephan Aßmus 		{ return BList::CountItems(); }
154128277c9SStephan Aßmus 
155128277c9SStephan Aßmus 	inline Selection& operator =(const Selection& other)
156128277c9SStephan Aßmus 		{
157128277c9SStephan Aßmus 			MakeEmpty();
158128277c9SStephan Aßmus 			int32 count = other.CountItems();
159128277c9SStephan Aßmus 			int32* items = other.Items();
160128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++) {
161128277c9SStephan Aßmus 				Add(items[i]);
162128277c9SStephan Aßmus 			}
163128277c9SStephan Aßmus 			return *this;
164128277c9SStephan Aßmus 		}
165128277c9SStephan Aßmus 
166128277c9SStephan Aßmus 	inline bool operator ==(const Selection& other)
167128277c9SStephan Aßmus 		{
168128277c9SStephan Aßmus 			if (other.CountItems() == CountItems()) {
169128277c9SStephan Aßmus 				int32* items = Items();
170128277c9SStephan Aßmus 				int32* otherItems = other.Items();
171128277c9SStephan Aßmus 				for (int32 i = 0; i < CountItems(); i++) {
172128277c9SStephan Aßmus 					if (items[i] != otherItems[i])
173128277c9SStephan Aßmus 						return false;
174128277c9SStephan Aßmus 					items++;
175128277c9SStephan Aßmus 					otherItems++;
176128277c9SStephan Aßmus 				}
177128277c9SStephan Aßmus 				return true;
178128277c9SStephan Aßmus 			} else
179128277c9SStephan Aßmus 				return false;
180128277c9SStephan Aßmus 		}
181128277c9SStephan Aßmus 
182128277c9SStephan Aßmus 	inline bool operator !=(const Selection& other)
183128277c9SStephan Aßmus 	{
184128277c9SStephan Aßmus 		return !(*this == other);
185128277c9SStephan Aßmus 	}
186128277c9SStephan Aßmus };
187128277c9SStephan Aßmus 
188128277c9SStephan Aßmus 
189128277c9SStephan Aßmus // constructor
190128277c9SStephan Aßmus PathManipulator::PathManipulator(VectorPath* path)
191128277c9SStephan Aßmus 	: Manipulator(path),
192128277c9SStephan Aßmus 	  fCanvasView(NULL),
193128277c9SStephan Aßmus 
194128277c9SStephan Aßmus 	  fCommandDown(false),
195128277c9SStephan Aßmus 	  fOptionDown(false),
196128277c9SStephan Aßmus 	  fShiftDown(false),
197128277c9SStephan Aßmus 	  fAltDown(false),
198128277c9SStephan Aßmus 
199128277c9SStephan Aßmus 	  fClickToClose(false),
200128277c9SStephan Aßmus 
201128277c9SStephan Aßmus 	  fMode(NEW_PATH),
202128277c9SStephan Aßmus 	  fFallBackMode(SELECT_POINTS),
203128277c9SStephan Aßmus 
204128277c9SStephan Aßmus 	  fMouseDown(false),
205128277c9SStephan Aßmus 
206128277c9SStephan Aßmus 	  fPath(path),
207128277c9SStephan Aßmus 	  fCurrentPathPoint(-1),
208128277c9SStephan Aßmus 
209128277c9SStephan Aßmus 	  fChangePointCommand(NULL),
210128277c9SStephan Aßmus 	  fInsertPointCommand(NULL),
211128277c9SStephan Aßmus 	  fAddPointCommand(NULL),
212128277c9SStephan Aßmus 
213128277c9SStephan Aßmus 	  fSelection(new Selection()),
214128277c9SStephan Aßmus 	  fOldSelection(new Selection()),
215128277c9SStephan Aßmus 
216128277c9SStephan Aßmus 	  fNudgeOffset(0.0, 0.0),
217128277c9SStephan Aßmus 	  fLastNudgeTime(system_time())//,
218128277c9SStephan Aßmus //	  fNudgeCommand(NULL)
219128277c9SStephan Aßmus {
220128277c9SStephan Aßmus }
221128277c9SStephan Aßmus 
222128277c9SStephan Aßmus // destructor
223128277c9SStephan Aßmus PathManipulator::~PathManipulator()
224128277c9SStephan Aßmus {
225128277c9SStephan Aßmus 	delete fChangePointCommand;
226128277c9SStephan Aßmus 	delete fInsertPointCommand;
227128277c9SStephan Aßmus 	delete fAddPointCommand;
228128277c9SStephan Aßmus 
229128277c9SStephan Aßmus 	delete fSelection;
230128277c9SStephan Aßmus 	delete fOldSelection;
231128277c9SStephan Aßmus 
232128277c9SStephan Aßmus //	delete fNudgeCommand;
233128277c9SStephan Aßmus }
234128277c9SStephan Aßmus 
235128277c9SStephan Aßmus 
236128277c9SStephan Aßmus // #pragma mark -
237128277c9SStephan Aßmus 
238128277c9SStephan Aßmus class StrokePathIterator : public VectorPath::Iterator {
239128277c9SStephan Aßmus  public:
240f67876a0SStephan Aßmus 					StrokePathIterator(CanvasView* canvasView,
241f67876a0SStephan Aßmus 									   BView* drawingView)
242f67876a0SStephan Aßmus 						: fCanvasView(canvasView),
243f67876a0SStephan Aßmus 						  fDrawingView(drawingView)
244128277c9SStephan Aßmus 					{
245128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
246128277c9SStephan Aßmus 						fDrawingView->SetDrawingMode(B_OP_OVER);
247128277c9SStephan Aßmus 					}
248128277c9SStephan Aßmus 	virtual			~StrokePathIterator()
249128277c9SStephan Aßmus 					{}
250128277c9SStephan Aßmus 
251128277c9SStephan Aßmus 	virtual	void	MoveTo(BPoint point)
252128277c9SStephan Aßmus 					{
253128277c9SStephan Aßmus 						fBlack = true;
254f67876a0SStephan Aßmus 						fSkip = false;
255128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
256128277c9SStephan Aßmus 
257f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
258128277c9SStephan Aßmus 						fDrawingView->MovePenTo(point);
259128277c9SStephan Aßmus 					}
260128277c9SStephan Aßmus 	virtual	void	LineTo(BPoint point)
261128277c9SStephan Aßmus 					{
262f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
263f67876a0SStephan Aßmus 						if (!fSkip) {
264128277c9SStephan Aßmus 							if (fBlack)
265128277c9SStephan Aßmus 								fDrawingView->SetHighColor(255, 255, 255, 255);
266128277c9SStephan Aßmus 							else
267128277c9SStephan Aßmus 								fDrawingView->SetHighColor(0, 0, 0, 255);
268128277c9SStephan Aßmus 							fBlack = !fBlack;
269128277c9SStephan Aßmus 
270128277c9SStephan Aßmus 							fDrawingView->StrokeLine(point);
271f67876a0SStephan Aßmus 						} else {
272f67876a0SStephan Aßmus 							fDrawingView->MovePenTo(point);
273f67876a0SStephan Aßmus 						}
274f67876a0SStephan Aßmus 						fSkip = !fSkip;
275128277c9SStephan Aßmus 					}
276128277c9SStephan Aßmus 
277128277c9SStephan Aßmus  private:
278f67876a0SStephan Aßmus 	CanvasView*		fCanvasView;
279128277c9SStephan Aßmus 	BView*			fDrawingView;
280128277c9SStephan Aßmus 	bool			fBlack;
281f67876a0SStephan Aßmus 	bool			fSkip;
282128277c9SStephan Aßmus };
283128277c9SStephan Aßmus 
284128277c9SStephan Aßmus // Draw
285128277c9SStephan Aßmus void
286128277c9SStephan Aßmus PathManipulator::Draw(BView* into, BRect updateRect)
287128277c9SStephan Aßmus {
288128277c9SStephan Aßmus 	// draw the Bezier curve, but only if editing
289128277c9SStephan Aßmus 	// if not "editing", the path is actually on top all other modifiers
290128277c9SStephan Aßmus 	// TODO: make this customizable in the GUI
291f67876a0SStephan Aßmus 	StrokePathIterator iterator(fCanvasView, into);
292f67876a0SStephan Aßmus 	fPath->Iterate(&iterator, fCanvasView->ZoomLevel());
293128277c9SStephan Aßmus 
294128277c9SStephan Aßmus 	into->SetLowColor(0, 0, 0, 255);
295128277c9SStephan Aßmus 	BPoint point;
296128277c9SStephan Aßmus 	BPoint pointIn;
297128277c9SStephan Aßmus 	BPoint pointOut;
298128277c9SStephan Aßmus 	rgb_color focusColor = (rgb_color){ 255, 0, 0, 255 };
299128277c9SStephan Aßmus 	rgb_color highlightColor = (rgb_color){ 60, 60, 255, 255 };
300128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut); i++) {
301128277c9SStephan Aßmus 		bool highlight = fCurrentPathPoint == i;
302128277c9SStephan Aßmus 		bool selected = fSelection->Contains(i);
303128277c9SStephan Aßmus 		rgb_color normal = selected ? focusColor : (rgb_color){ 0, 0, 0, 255 };
304128277c9SStephan Aßmus 		into->SetLowColor(normal);
305128277c9SStephan Aßmus 		into->SetHighColor(255, 255, 255, 255);
306128277c9SStephan Aßmus 		// convert to view coordinate space
307f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&point);
308f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointIn);
309f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointOut);
310128277c9SStephan Aßmus 		// connect the points belonging to one control point
311128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_INVERT);
312128277c9SStephan Aßmus 		into->StrokeLine(point, pointIn);
313128277c9SStephan Aßmus 		into->StrokeLine(point, pointOut);
314128277c9SStephan Aßmus 		// draw main control point
315128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT ||
316128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP ||
317128277c9SStephan Aßmus 						  fMode == REMOVE_POINT ||
318128277c9SStephan Aßmus 						  fMode == SELECT_POINTS ||
319128277c9SStephan Aßmus 						  fMode == CLOSE_PATH)) {
320128277c9SStephan Aßmus 
321128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
322128277c9SStephan Aßmus 		}
323128277c9SStephan Aßmus 
324128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_COPY);
325128277c9SStephan Aßmus 		BRect r(point, point);
326128277c9SStephan Aßmus 		r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
327128277c9SStephan Aßmus 		into->StrokeRect(r, B_SOLID_LOW);
328128277c9SStephan Aßmus 		r.InsetBy(1.0, 1.0);
329128277c9SStephan Aßmus 		into->FillRect(r, B_SOLID_HIGH);
330128277c9SStephan Aßmus 		// draw in control point
331128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_IN ||
332128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_IN ||
333128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_IN ||
334128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
335128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
336128277c9SStephan Aßmus 		else
337128277c9SStephan Aßmus 			into->SetLowColor(normal);
338128277c9SStephan Aßmus 		if (selected) {
339128277c9SStephan Aßmus 			into->SetHighColor(220, 220, 220, 255);
340128277c9SStephan Aßmus 		} else {
341128277c9SStephan Aßmus 			into->SetHighColor(170, 170, 170, 255);
342128277c9SStephan Aßmus 		}
343128277c9SStephan Aßmus 		if (pointIn != point) {
344128277c9SStephan Aßmus 			r.Set(pointIn.x - CONTROL_POINT_EXTEND, pointIn.y - CONTROL_POINT_EXTEND,
345128277c9SStephan Aßmus 				  pointIn.x + CONTROL_POINT_EXTEND, pointIn.y + CONTROL_POINT_EXTEND);
346128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
347128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
348128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
349128277c9SStephan Aßmus 		}
350128277c9SStephan Aßmus 		// draw out control point
351128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_OUT ||
352128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_OUT ||
353128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_OUT ||
354128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
355128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
356128277c9SStephan Aßmus 		else
357128277c9SStephan Aßmus 			into->SetLowColor(normal);
358128277c9SStephan Aßmus 		if (pointOut != point) {
359128277c9SStephan Aßmus 			r.Set(pointOut.x - CONTROL_POINT_EXTEND, pointOut.y - CONTROL_POINT_EXTEND,
360128277c9SStephan Aßmus 				  pointOut.x + CONTROL_POINT_EXTEND, pointOut.y + CONTROL_POINT_EXTEND);
361128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
362128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
363128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
364128277c9SStephan Aßmus 		}
365128277c9SStephan Aßmus 	}
366128277c9SStephan Aßmus }
367128277c9SStephan Aßmus 
368128277c9SStephan Aßmus // #pragma mark -
369128277c9SStephan Aßmus 
370128277c9SStephan Aßmus // MouseDown
371128277c9SStephan Aßmus bool
372128277c9SStephan Aßmus PathManipulator::MouseDown(BPoint where)
373128277c9SStephan Aßmus {
374128277c9SStephan Aßmus 	fMouseDown = true;
375128277c9SStephan Aßmus 
376128277c9SStephan Aßmus 	if (fMode == MOVE_POINT &&
377128277c9SStephan Aßmus 		fSelection->CountItems() > 1 &&
378128277c9SStephan Aßmus 		fSelection->Contains(fCurrentPathPoint)) {
379128277c9SStephan Aßmus 		fMode = TRANSLATE_POINTS;
380128277c9SStephan Aßmus 	}
381128277c9SStephan Aßmus 
382128277c9SStephan Aßmus 	BPoint canvasWhere = where;
383f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
384128277c9SStephan Aßmus 
385128277c9SStephan Aßmus 	// maybe we're changing some point, so we construct the
386128277c9SStephan Aßmus 	// "ChangePointCommand" here so that the point is remembered
387128277c9SStephan Aßmus 	// in its current state
388128277c9SStephan Aßmus 	delete fChangePointCommand;
389128277c9SStephan Aßmus 	fChangePointCommand = NULL;
390128277c9SStephan Aßmus 	switch (fMode) {
391128277c9SStephan Aßmus 		case TOGGLE_SHARP:
392128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
393128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
394128277c9SStephan Aßmus 		case MOVE_POINT:
395128277c9SStephan Aßmus 		case MOVE_POINT_IN:
396128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
397128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
398128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
399128277c9SStephan Aßmus 			fChangePointCommand = new ChangePointCommand(fPath,
400128277c9SStephan Aßmus 														 fCurrentPathPoint,
401128277c9SStephan Aßmus 														 fSelection->Items(),
402128277c9SStephan Aßmus 														 fSelection->CountItems());
403128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
404128277c9SStephan Aßmus 			break;
405128277c9SStephan Aßmus 	}
406128277c9SStephan Aßmus 
407128277c9SStephan Aßmus 	// at this point we init doing something
408128277c9SStephan Aßmus 	switch (fMode) {
409128277c9SStephan Aßmus 		case ADD_POINT:
410128277c9SStephan Aßmus 			_AddPoint(canvasWhere);
411128277c9SStephan Aßmus 			break;
412128277c9SStephan Aßmus 		case INSERT_POINT:
413128277c9SStephan Aßmus 			_InsertPoint(canvasWhere, fCurrentPathPoint);
414128277c9SStephan Aßmus 			break;
415128277c9SStephan Aßmus 
416128277c9SStephan Aßmus 		case TOGGLE_SHARP:
417128277c9SStephan Aßmus 			_SetSharp(fCurrentPathPoint);
418128277c9SStephan Aßmus 			// continue by dragging out the _connected_ in/out points
419128277c9SStephan Aßmus 			break;
420128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
421128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
422128277c9SStephan Aßmus 			// continue by moving the "in" point
423128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_IN);
424128277c9SStephan Aßmus 			break;
425128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
426128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
427128277c9SStephan Aßmus 			// continue by moving the "out" point
428128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
429128277c9SStephan Aßmus 			break;
430128277c9SStephan Aßmus 
431128277c9SStephan Aßmus 		case MOVE_POINT:
432128277c9SStephan Aßmus 		case MOVE_POINT_IN:
433128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
434128277c9SStephan Aßmus 			// the right thing happens since "fCurrentPathPoint"
435128277c9SStephan Aßmus 			// points to the correct index
436128277c9SStephan Aßmus 			break;
437128277c9SStephan Aßmus 
438128277c9SStephan Aßmus 		case CLOSE_PATH:
439128277c9SStephan Aßmus //			SetClosed(true, true);
440128277c9SStephan Aßmus 			break;
441128277c9SStephan Aßmus 
442128277c9SStephan Aßmus 		case REMOVE_POINT:
443128277c9SStephan Aßmus 			if (fPath->CountPoints() == 1) {
444128277c9SStephan Aßmus //				fCanvasView->Perform(new RemovePathCommand(this, fPath));
445128277c9SStephan Aßmus 			} else {
446128277c9SStephan Aßmus 				fCanvasView->Perform(new RemovePointsCommand(fPath,
447128277c9SStephan Aßmus 															 fCurrentPathPoint,
448128277c9SStephan Aßmus 															 fSelection->Items(),
449128277c9SStephan Aßmus 															 fSelection->CountItems()));
450128277c9SStephan Aßmus 				_RemovePoint(fCurrentPathPoint);
451128277c9SStephan Aßmus 			}
452128277c9SStephan Aßmus 			break;
453128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
454128277c9SStephan Aßmus 			_RemovePointIn(fCurrentPathPoint);
455128277c9SStephan Aßmus 			break;
456128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
457128277c9SStephan Aßmus 			_RemovePointOut(fCurrentPathPoint);
458128277c9SStephan Aßmus 			break;
459128277c9SStephan Aßmus 
460128277c9SStephan Aßmus 		case SELECT_POINTS:
461128277c9SStephan Aßmus 			if (!fShiftDown) {
462128277c9SStephan Aßmus 				fSelection->MakeEmpty();
463128277c9SStephan Aßmus 				_UpdateSelection();
464128277c9SStephan Aßmus 			}
465128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
466128277c9SStephan Aßmus 			if (fCurrentPathPoint >= 0) {
467128277c9SStephan Aßmus 				_Select(fCurrentPathPoint, fShiftDown);
468128277c9SStephan Aßmus 			}
469128277c9SStephan Aßmus 			fCanvasView->BeginRectTracking(BRect(where, where),
470128277c9SStephan Aßmus 										   B_TRACK_RECT_CORNER);
471128277c9SStephan Aßmus 			break;
472128277c9SStephan Aßmus 	}
473128277c9SStephan Aßmus 
474128277c9SStephan Aßmus 	fTrackingStart = canvasWhere;
475128277c9SStephan Aßmus 	// remember the subpixel position
476128277c9SStephan Aßmus 	// so that MouseMoved() will work even before
477128277c9SStephan Aßmus 	// the integer position becomes different
478128277c9SStephan Aßmus 	fLastCanvasPos = where;
479f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&fLastCanvasPos);
480128277c9SStephan Aßmus 
481128277c9SStephan Aßmus 	// the reason to exclude the select mode
482128277c9SStephan Aßmus 	// is that the BView rect tracking does not
483128277c9SStephan Aßmus 	// scroll the rect starting point along with us
484128277c9SStephan Aßmus 	// (since we're doing no real scrolling)
485128277c9SStephan Aßmus //	if (fMode != SELECT_POINTS)
486128277c9SStephan Aßmus //		fCanvasView->SetAutoScrolling(true);
487128277c9SStephan Aßmus 
488128277c9SStephan Aßmus 	UpdateCursor();
489128277c9SStephan Aßmus 
490128277c9SStephan Aßmus 	return true;
491128277c9SStephan Aßmus }
492128277c9SStephan Aßmus 
493128277c9SStephan Aßmus // MouseMoved
494128277c9SStephan Aßmus void
495128277c9SStephan Aßmus PathManipulator::MouseMoved(BPoint where)
496128277c9SStephan Aßmus {
497128277c9SStephan Aßmus 	BPoint canvasWhere = where;
498f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
499128277c9SStephan Aßmus 
500128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
501128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
502128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
503128277c9SStephan Aßmus 	if (fLastCanvasPos == canvasWhere)
504128277c9SStephan Aßmus 		return;
505128277c9SStephan Aßmus 
506128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
507128277c9SStephan Aßmus 
508128277c9SStephan Aßmus 	if (fMode == CLOSE_PATH) {
509128277c9SStephan Aßmus 		// continue by moving the point
510128277c9SStephan Aßmus 		_SetMode(MOVE_POINT);
511128277c9SStephan Aßmus 		delete fChangePointCommand;
512128277c9SStephan Aßmus 		fChangePointCommand = new ChangePointCommand(fPath,
513128277c9SStephan Aßmus 													 fCurrentPathPoint,
514128277c9SStephan Aßmus 													 fSelection->Items(),
515128277c9SStephan Aßmus 													 fSelection->CountItems());
516128277c9SStephan Aßmus 	}
517128277c9SStephan Aßmus 
518128277c9SStephan Aßmus //	if (!fPrecise) {
519128277c9SStephan Aßmus //		float offset = fmod(fOutlineWidth, 2.0) / 2.0;
520128277c9SStephan Aßmus //		canvasWhere.point += BPoint(offset, offset);
521128277c9SStephan Aßmus //	}
522128277c9SStephan Aßmus 
523128277c9SStephan Aßmus 	switch (fMode) {
524128277c9SStephan Aßmus 		case ADD_POINT:
525128277c9SStephan Aßmus 		case INSERT_POINT:
526128277c9SStephan Aßmus 		case TOGGLE_SHARP:
527128277c9SStephan Aßmus 			// drag the "out" control point, mirror the "in" control point
528128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere, true);
529128277c9SStephan Aßmus 			break;
530128277c9SStephan Aßmus 		case MOVE_POINT:
531128277c9SStephan Aßmus 			// drag all three control points at once
532128277c9SStephan Aßmus 			fPath->SetPoint(fCurrentPathPoint, canvasWhere);
533128277c9SStephan Aßmus 			break;
534128277c9SStephan Aßmus 		case MOVE_POINT_IN:
535128277c9SStephan Aßmus 			// drag in control point
536128277c9SStephan Aßmus 			fPath->SetPointIn(fCurrentPathPoint, canvasWhere);
537128277c9SStephan Aßmus 			break;
538128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
539128277c9SStephan Aßmus 			// drag out control point
540128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere);
541128277c9SStephan Aßmus 			break;
542128277c9SStephan Aßmus 
543128277c9SStephan Aßmus 		case SELECT_POINTS: {
544128277c9SStephan Aßmus 			// change the selection
545128277c9SStephan Aßmus 			BRect r;
546128277c9SStephan Aßmus 			r.left = min_c(fTrackingStart.x, canvasWhere.x);
547128277c9SStephan Aßmus 			r.top = min_c(fTrackingStart.y, canvasWhere.y);
548128277c9SStephan Aßmus 			r.right = max_c(fTrackingStart.x, canvasWhere.x);
549128277c9SStephan Aßmus 			r.bottom = max_c(fTrackingStart.y, canvasWhere.y);
550128277c9SStephan Aßmus 			_Select(r);
551128277c9SStephan Aßmus 			break;
552128277c9SStephan Aßmus 		}
553128277c9SStephan Aßmus 
554128277c9SStephan Aßmus 		case TRANSLATE_POINTS: {
555128277c9SStephan Aßmus 			BPoint offset = canvasWhere - fTrackingStart;
556128277c9SStephan Aßmus 			_Nudge(offset);
557128277c9SStephan Aßmus 			fTrackingStart = canvasWhere;
558128277c9SStephan Aßmus 			break;
559128277c9SStephan Aßmus 		}
560128277c9SStephan Aßmus 	}
561128277c9SStephan Aßmus }
562128277c9SStephan Aßmus 
563128277c9SStephan Aßmus // MouseUp
564128277c9SStephan Aßmus Command*
565128277c9SStephan Aßmus PathManipulator::MouseUp()
566128277c9SStephan Aßmus {
567128277c9SStephan Aßmus 	// prevent carrying out actions more than once by only
568128277c9SStephan Aßmus 	// doing it if "fMouseDown" is true at the point of
569128277c9SStephan Aßmus 	// entering this function
570128277c9SStephan Aßmus 	if (!fMouseDown)
571128277c9SStephan Aßmus 		return NULL;
572128277c9SStephan Aßmus 	fMouseDown = false;
573128277c9SStephan Aßmus 
574128277c9SStephan Aßmus 	Command* command = NULL;
575128277c9SStephan Aßmus 
576128277c9SStephan Aßmus 	switch (fMode) {
577128277c9SStephan Aßmus 
578128277c9SStephan Aßmus 		case ADD_POINT:
579128277c9SStephan Aßmus 			command = fAddPointCommand;
580128277c9SStephan Aßmus 			fAddPointCommand = NULL;
581128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
582128277c9SStephan Aßmus 			break;
583128277c9SStephan Aßmus 
584128277c9SStephan Aßmus 		case INSERT_POINT:
585128277c9SStephan Aßmus 			command = fInsertPointCommand;
586128277c9SStephan Aßmus 			fInsertPointCommand = NULL;
587128277c9SStephan Aßmus 			break;
588128277c9SStephan Aßmus 
589128277c9SStephan Aßmus 		case SELECT_POINTS:
590128277c9SStephan Aßmus 			if (*fSelection != *fOldSelection) {
591128277c9SStephan Aßmus //				command = new SelectPointsCommand(this, fPath,
592128277c9SStephan Aßmus //												  fOldSelection->Items(),
593128277c9SStephan Aßmus //												  fOldSelection->CountItems(),
594128277c9SStephan Aßmus //												  fSelection->Items(),
595128277c9SStephan Aßmus //												  fSelection->CountItems()));
596128277c9SStephan Aßmus 			}
597128277c9SStephan Aßmus 			fCanvasView->EndRectTracking();
598128277c9SStephan Aßmus 			break;
599128277c9SStephan Aßmus 
600128277c9SStephan Aßmus 		case TOGGLE_SHARP:
601128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
602128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
603128277c9SStephan Aßmus 		case MOVE_POINT:
604128277c9SStephan Aßmus 		case MOVE_POINT_IN:
605128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
606128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
607128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
608128277c9SStephan Aßmus 			command = fChangePointCommand;
609128277c9SStephan Aßmus 			fChangePointCommand = NULL;
610128277c9SStephan Aßmus 			break;
611128277c9SStephan Aßmus 
612128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
613128277c9SStephan Aßmus //			if (!fNudgeCommand) {
614128277c9SStephan Aßmus 				// select just the point that was clicked
615128277c9SStephan Aßmus 				*fOldSelection = *fSelection;
616128277c9SStephan Aßmus 				if (fCurrentPathPoint >= 0) {
617128277c9SStephan Aßmus 					_Select(fCurrentPathPoint, fShiftDown);
618128277c9SStephan Aßmus 				}
619128277c9SStephan Aßmus 				if (*fSelection != *fOldSelection) {
620128277c9SStephan Aßmus //					command = new SelectPointsCommand(this, fPath,
621128277c9SStephan Aßmus //													  fOldSelection->Items(),
622128277c9SStephan Aßmus //													  fOldSelection->CountItems(),
623128277c9SStephan Aßmus //													  fSelection->Items(),
624128277c9SStephan Aßmus //													  fSelection->CountItems()));
625128277c9SStephan Aßmus 				}
626128277c9SStephan Aßmus //			} else {
627128277c9SStephan Aßmus //				_FinishNudging();
628128277c9SStephan Aßmus //			}
629128277c9SStephan Aßmus 			break;
630128277c9SStephan Aßmus 	}
631128277c9SStephan Aßmus 
632128277c9SStephan Aßmus 	return command;
633128277c9SStephan Aßmus }
634128277c9SStephan Aßmus 
635128277c9SStephan Aßmus // MouseOver
636128277c9SStephan Aßmus bool
637128277c9SStephan Aßmus PathManipulator::MouseOver(BPoint where)
638128277c9SStephan Aßmus {
639128277c9SStephan Aßmus 	BPoint canvasWhere = where;
640f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
641128277c9SStephan Aßmus 
642128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
643128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
644128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
645128277c9SStephan Aßmus 	if (fMouseDown && fLastCanvasPos == canvasWhere)
646128277c9SStephan Aßmus 		return false;
647128277c9SStephan Aßmus 
648128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
649128277c9SStephan Aßmus 
650128277c9SStephan Aßmus 	// hit testing
651128277c9SStephan Aßmus 	// (use a subpixel mouse pos)
652f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&where);
653128277c9SStephan Aßmus 	_SetModeForMousePos(where);
654128277c9SStephan Aßmus 
655128277c9SStephan Aßmus 	// TODO: always true?
656128277c9SStephan Aßmus 	return true;
657128277c9SStephan Aßmus }
658128277c9SStephan Aßmus 
659128277c9SStephan Aßmus // DoubleClicked
660128277c9SStephan Aßmus bool
661128277c9SStephan Aßmus PathManipulator::DoubleClicked(BPoint where)
662128277c9SStephan Aßmus {
663128277c9SStephan Aßmus 	return false;
664128277c9SStephan Aßmus }
665128277c9SStephan Aßmus 
666128277c9SStephan Aßmus // Bounds
667128277c9SStephan Aßmus BRect
668128277c9SStephan Aßmus PathManipulator::Bounds()
669128277c9SStephan Aßmus {
670f67876a0SStephan Aßmus 	BRect r = _ControlPointRect();
671f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&r);
672f67876a0SStephan Aßmus 	return r;
673128277c9SStephan Aßmus }
674128277c9SStephan Aßmus 
675128277c9SStephan Aßmus // TrackingBounds
676128277c9SStephan Aßmus BRect
677128277c9SStephan Aßmus PathManipulator::TrackingBounds(BView* withinView)
678128277c9SStephan Aßmus {
679128277c9SStephan Aßmus 	return withinView->Bounds();
680128277c9SStephan Aßmus }
681128277c9SStephan Aßmus 
682128277c9SStephan Aßmus // #pragma mark -
683128277c9SStephan Aßmus 
684128277c9SStephan Aßmus enum {
685128277c9SStephan Aßmus 	MSG_SHAPE_TRANSFORM			= 'strn',
686128277c9SStephan Aßmus 	MSG_SHAPE_REMOVE_POINTS		= 'srmp',
687128277c9SStephan Aßmus 	MSG_UPDATE_SHAPE_UI			= 'udsi',
688128277c9SStephan Aßmus };
689128277c9SStephan Aßmus 
690128277c9SStephan Aßmus // MessageReceived
691128277c9SStephan Aßmus bool
692128277c9SStephan Aßmus PathManipulator::MessageReceived(BMessage* message, Command** _command)
693128277c9SStephan Aßmus {
694128277c9SStephan Aßmus 	bool result = true;
695128277c9SStephan Aßmus 	switch (message->what) {
696128277c9SStephan Aßmus 		case MSG_SHAPE_TRANSFORM:
697128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
698128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
699128277c9SStephan Aßmus 			break;
700128277c9SStephan Aßmus 		case MSG_SHAPE_REMOVE_POINTS:
701128277c9SStephan Aßmus 			*_command = _Delete();
702128277c9SStephan Aßmus 			break;
703128277c9SStephan Aßmus 		case B_SELECT_ALL: {
704128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
705128277c9SStephan Aßmus 			fSelection->MakeEmpty();
706128277c9SStephan Aßmus 			int32 count = fPath->CountPoints();
707128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++)
708128277c9SStephan Aßmus 				fSelection->Add(i);
709128277c9SStephan Aßmus 			if (*fOldSelection != *fSelection) {
710128277c9SStephan Aßmus //				*_command = new SelectPointsCommand(this, fPath,
711128277c9SStephan Aßmus //												   fOldSelection->Items(),
712128277c9SStephan Aßmus //												   fOldSelection->CountItems(),
713128277c9SStephan Aßmus //												   fSelection->Items(),
714128277c9SStephan Aßmus //												   fSelection->CountItems()));
715128277c9SStephan Aßmus 				count = fSelection->CountItems();
716128277c9SStephan Aßmus 				int32 indices[count];
717128277c9SStephan Aßmus 				memcpy(indices, fSelection->Items(), count * sizeof(int32));
718128277c9SStephan Aßmus 				_Select(indices, count);
719128277c9SStephan Aßmus 			}
720128277c9SStephan Aßmus 			break;
721128277c9SStephan Aßmus 		}
722128277c9SStephan Aßmus 		default:
723128277c9SStephan Aßmus 			result = false;
724128277c9SStephan Aßmus 			break;
725128277c9SStephan Aßmus 	}
726128277c9SStephan Aßmus 	return result;
727128277c9SStephan Aßmus }
728128277c9SStephan Aßmus 
729128277c9SStephan Aßmus 
730128277c9SStephan Aßmus // ModifiersChanged
731128277c9SStephan Aßmus void
732128277c9SStephan Aßmus PathManipulator::ModifiersChanged(uint32 modifiers)
733128277c9SStephan Aßmus {
734128277c9SStephan Aßmus 	fCommandDown = modifiers & B_COMMAND_KEY;
735128277c9SStephan Aßmus 	fOptionDown = modifiers & B_CONTROL_KEY;
736128277c9SStephan Aßmus 	fShiftDown = modifiers & B_SHIFT_KEY;
737128277c9SStephan Aßmus 	fAltDown = modifiers & B_OPTION_KEY;
738128277c9SStephan Aßmus 
739128277c9SStephan Aßmus 	// reevaluate mode
740128277c9SStephan Aßmus 	if (!fMouseDown)
741128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
742128277c9SStephan Aßmus }
743128277c9SStephan Aßmus 
744128277c9SStephan Aßmus // HandleKeyDown
745128277c9SStephan Aßmus bool
746128277c9SStephan Aßmus PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
747128277c9SStephan Aßmus {
748128277c9SStephan Aßmus 	bool result = true;
749128277c9SStephan Aßmus 
750128277c9SStephan Aßmus 	float nudgeDist = 1.0;
751f67876a0SStephan Aßmus 	if (modifiers & B_SHIFT_KEY)
752f67876a0SStephan Aßmus 		nudgeDist /= fCanvasView->ZoomLevel();
753128277c9SStephan Aßmus 
754128277c9SStephan Aßmus 	switch (key) {
755128277c9SStephan Aßmus 		// commit
756128277c9SStephan Aßmus 		case B_RETURN:
757128277c9SStephan Aßmus //			_Perform();
758128277c9SStephan Aßmus 			break;
759128277c9SStephan Aßmus 		// cancel
760128277c9SStephan Aßmus 		case B_ESCAPE:
761128277c9SStephan Aßmus 			if (fFallBackMode == NEW_PATH) {
762128277c9SStephan Aßmus 				fFallBackMode = SELECT_POINTS;
763128277c9SStephan Aßmus 				_SetModeForMousePos(fLastCanvasPos);
764128277c9SStephan Aßmus 			} else
765128277c9SStephan Aßmus //				_Cancel();
766128277c9SStephan Aßmus 			break;
767128277c9SStephan Aßmus 		case 't':
768128277c9SStephan Aßmus 		case 'T':
769128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
770128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
771128277c9SStephan Aßmus 			else
772128277c9SStephan Aßmus 				result = false;
773128277c9SStephan Aßmus 			break;
774128277c9SStephan Aßmus 		// nudging
775128277c9SStephan Aßmus 		case B_UP_ARROW:
776128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, -nudgeDist));
777128277c9SStephan Aßmus 			break;
778128277c9SStephan Aßmus 		case B_DOWN_ARROW:
779128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, nudgeDist));
780128277c9SStephan Aßmus 			break;
781128277c9SStephan Aßmus 		case B_LEFT_ARROW:
782128277c9SStephan Aßmus 			_Nudge(BPoint(-nudgeDist, 0.0));
783128277c9SStephan Aßmus 			break;
784128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
785128277c9SStephan Aßmus 			_Nudge(BPoint(nudgeDist, 0.0));
786128277c9SStephan Aßmus 			break;
787128277c9SStephan Aßmus 
788128277c9SStephan Aßmus 		case B_DELETE:
789128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
790128277c9SStephan Aßmus 				*_command = _Delete();
791128277c9SStephan Aßmus 			else
792128277c9SStephan Aßmus 				result = false;
793128277c9SStephan Aßmus 			break;
794128277c9SStephan Aßmus 
795128277c9SStephan Aßmus 		default:
796128277c9SStephan Aßmus 			result = false;
797128277c9SStephan Aßmus 	}
798128277c9SStephan Aßmus 	return result;
799128277c9SStephan Aßmus }
800128277c9SStephan Aßmus 
801128277c9SStephan Aßmus // HandleKeyUp
802128277c9SStephan Aßmus bool
803128277c9SStephan Aßmus PathManipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
804128277c9SStephan Aßmus {
805128277c9SStephan Aßmus 	bool handled = true;
806128277c9SStephan Aßmus 	switch (key) {
807128277c9SStephan Aßmus 		// nudging
808128277c9SStephan Aßmus 		case B_UP_ARROW:
809128277c9SStephan Aßmus 		case B_DOWN_ARROW:
810128277c9SStephan Aßmus 		case B_LEFT_ARROW:
811128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
812128277c9SStephan Aßmus 			_FinishNudging();
813128277c9SStephan Aßmus 			break;
814128277c9SStephan Aßmus 		default:
815128277c9SStephan Aßmus 			handled = false;
816128277c9SStephan Aßmus 			break;
817128277c9SStephan Aßmus 	}
818128277c9SStephan Aßmus 	return handled;
819128277c9SStephan Aßmus }
820128277c9SStephan Aßmus 
821128277c9SStephan Aßmus // UpdateCursor
822128277c9SStephan Aßmus void
823128277c9SStephan Aßmus PathManipulator::UpdateCursor()
824128277c9SStephan Aßmus {
825128277c9SStephan Aßmus 	const uchar* cursorData;
826128277c9SStephan Aßmus 	switch (fMode) {
827128277c9SStephan Aßmus 		case ADD_POINT:
828128277c9SStephan Aßmus 			cursorData = kPathAddCursor;
829128277c9SStephan Aßmus 			break;
830128277c9SStephan Aßmus 		case INSERT_POINT:
831128277c9SStephan Aßmus 			cursorData = kPathInsertCursor;
832128277c9SStephan Aßmus 			break;
833128277c9SStephan Aßmus 		case MOVE_POINT:
834128277c9SStephan Aßmus 		case MOVE_POINT_IN:
835128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
836128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
837128277c9SStephan Aßmus 			cursorData = kPathMoveCursor;
838128277c9SStephan Aßmus 			break;
839128277c9SStephan Aßmus 		case CLOSE_PATH:
840128277c9SStephan Aßmus 			cursorData = kPathCloseCursor;
841128277c9SStephan Aßmus 			break;
842128277c9SStephan Aßmus 		case TOGGLE_SHARP:
843128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
844128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
845128277c9SStephan Aßmus 			cursorData = kPathSharpCursor;
846128277c9SStephan Aßmus 			break;
847128277c9SStephan Aßmus 		case REMOVE_POINT:
848128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
849128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
850128277c9SStephan Aßmus 			cursorData = kPathRemoveCursor;
851128277c9SStephan Aßmus 			break;
852128277c9SStephan Aßmus 		case SELECT_POINTS:
853128277c9SStephan Aßmus 			cursorData = kPathSelectCursor;
854128277c9SStephan Aßmus 			break;
855128277c9SStephan Aßmus 
856128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
857128277c9SStephan Aßmus 			cursorData = B_HAND_CURSOR;
858128277c9SStephan Aßmus 			break;
859128277c9SStephan Aßmus 
860128277c9SStephan Aßmus 		case UNDEFINED:
861128277c9SStephan Aßmus 		default:
862128277c9SStephan Aßmus 			cursorData = kStopCursor;
863128277c9SStephan Aßmus 			break;
864128277c9SStephan Aßmus 	}
865128277c9SStephan Aßmus 	BCursor cursor(cursorData);
866128277c9SStephan Aßmus 	fCanvasView->SetViewCursor(&cursor, true);
867128277c9SStephan Aßmus 	fCanvasView->Sync();
868128277c9SStephan Aßmus }
869128277c9SStephan Aßmus 
870128277c9SStephan Aßmus // AttachedToView
871128277c9SStephan Aßmus void
872128277c9SStephan Aßmus PathManipulator::AttachedToView(BView* view)
873128277c9SStephan Aßmus {
874f67876a0SStephan Aßmus 	fCanvasView = dynamic_cast<CanvasView*>(view);
875128277c9SStephan Aßmus }
876128277c9SStephan Aßmus 
877128277c9SStephan Aßmus // DetachedFromView
878128277c9SStephan Aßmus void
879128277c9SStephan Aßmus PathManipulator::DetachedFromView(BView* view)
880128277c9SStephan Aßmus {
881128277c9SStephan Aßmus 	fCanvasView = NULL;
882128277c9SStephan Aßmus }
883128277c9SStephan Aßmus 
884128277c9SStephan Aßmus // #pragma mark -
885128277c9SStephan Aßmus 
886128277c9SStephan Aßmus // ObjectChanged
887128277c9SStephan Aßmus void
888128277c9SStephan Aßmus PathManipulator::ObjectChanged(const Observable* object)
889128277c9SStephan Aßmus {
890128277c9SStephan Aßmus 	// TODO: refine VectorPath listener interface and
891128277c9SStephan Aßmus 	// implement more efficiently
892128277c9SStephan Aßmus 	BRect currentBounds = _ControlPointRect();
893128277c9SStephan Aßmus 	_InvalidateCanvas(currentBounds | fPreviousBounds);
894128277c9SStephan Aßmus 	fPreviousBounds = currentBounds;
895128277c9SStephan Aßmus 
896128277c9SStephan Aßmus 	// reevaluate mode
897128277c9SStephan Aßmus 	if (!fMouseDown)
898128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
899128277c9SStephan Aßmus }
900128277c9SStephan Aßmus 
901128277c9SStephan Aßmus // #pragma mark -
902128277c9SStephan Aßmus 
903128277c9SStephan Aßmus // ControlFlags
904128277c9SStephan Aßmus uint32
905128277c9SStephan Aßmus PathManipulator::ControlFlags() const
906128277c9SStephan Aßmus {
907128277c9SStephan Aßmus 	uint32 flags = 0;
908128277c9SStephan Aßmus 
909128277c9SStephan Aßmus //	flags |= SHAPE_UI_FLAGS_CAN_REVERSE_PATH;
910128277c9SStephan Aßmus //
911128277c9SStephan Aßmus //	if (!fSelection->IsEmpty())
912128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_HAS_SELECTION;
913128277c9SStephan Aßmus //	if (fPath->CountPoints() > 1)
914128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_CAN_CLOSE_PATH;
915128277c9SStephan Aßmus //	if (fPath->IsClosed())
916128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_PATH_IS_CLOSED;
917128277c9SStephan Aßmus 
918128277c9SStephan Aßmus 	return flags;
919128277c9SStephan Aßmus }
920128277c9SStephan Aßmus 
921128277c9SStephan Aßmus // ReversePath
922128277c9SStephan Aßmus void
923128277c9SStephan Aßmus PathManipulator::ReversePath()
924128277c9SStephan Aßmus {
925128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
926128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
927128277c9SStephan Aßmus 	if (count > 0) {
928128277c9SStephan Aßmus 		Selection temp;
929128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
930128277c9SStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
931128277c9SStephan Aßmus 		}
932128277c9SStephan Aßmus 		*fSelection = temp;
933128277c9SStephan Aßmus 	}
934128277c9SStephan Aßmus 	fPath->Reverse();
935128277c9SStephan Aßmus }
936128277c9SStephan Aßmus 
937128277c9SStephan Aßmus // #pragma mark -
938128277c9SStephan Aßmus 
939128277c9SStephan Aßmus // _SetMode
940128277c9SStephan Aßmus void
941128277c9SStephan Aßmus PathManipulator::_SetMode(uint32 mode)
942128277c9SStephan Aßmus {
943128277c9SStephan Aßmus 	if (fMode != mode) {
944128277c9SStephan Aßmus //printf("switching mode: %s -> %s\n", string_for_mode(fMode), string_for_mode(mode));
945128277c9SStephan Aßmus 		fMode = mode;
946128277c9SStephan Aßmus 
947128277c9SStephan Aßmus 		if (BWindow* window = fCanvasView->Window()) {
948128277c9SStephan Aßmus 			window->PostMessage(MSG_UPDATE_SHAPE_UI);
949128277c9SStephan Aßmus 		}
950128277c9SStephan Aßmus 		UpdateCursor();
951128277c9SStephan Aßmus 	}
952128277c9SStephan Aßmus }
953128277c9SStephan Aßmus 
954128277c9SStephan Aßmus // _AddPoint
955128277c9SStephan Aßmus void
956128277c9SStephan Aßmus PathManipulator::_AddPoint(BPoint where)
957128277c9SStephan Aßmus {
958128277c9SStephan Aßmus 	if (fPath->AddPoint(where)) {
959128277c9SStephan Aßmus 		fCurrentPathPoint = fPath->CountPoints() - 1;
960128277c9SStephan Aßmus 
961128277c9SStephan Aßmus 		delete fAddPointCommand;
962128277c9SStephan Aßmus 		fAddPointCommand = new AddPointCommand(fPath, fCurrentPathPoint,
963128277c9SStephan Aßmus 											   fSelection->Items(),
964128277c9SStephan Aßmus 											   fSelection->CountItems());
965128277c9SStephan Aßmus 
966128277c9SStephan Aßmus 		_Select(fCurrentPathPoint, fShiftDown);
967128277c9SStephan Aßmus 	}
968128277c9SStephan Aßmus }
969128277c9SStephan Aßmus 
970128277c9SStephan Aßmus // scale_point
971128277c9SStephan Aßmus BPoint
972128277c9SStephan Aßmus scale_point(BPoint a, BPoint b, float scale)
973128277c9SStephan Aßmus {
974128277c9SStephan Aßmus 	return BPoint(a.x + (b.x - a.x) * scale,
975128277c9SStephan Aßmus 				  a.y + (b.y - a.y) * scale);
976128277c9SStephan Aßmus }
977128277c9SStephan Aßmus 
978128277c9SStephan Aßmus // _InsertPoint
979128277c9SStephan Aßmus void
980128277c9SStephan Aßmus PathManipulator::_InsertPoint(BPoint where, int32 index)
981128277c9SStephan Aßmus {
982128277c9SStephan Aßmus 	double scale;
983128277c9SStephan Aßmus 
984128277c9SStephan Aßmus 	BPoint point;
985128277c9SStephan Aßmus 	BPoint pointIn;
986128277c9SStephan Aßmus 	BPoint pointOut;
987128277c9SStephan Aßmus 
988128277c9SStephan Aßmus 	BPoint previous;
989128277c9SStephan Aßmus 	BPoint previousOut;
990128277c9SStephan Aßmus 	BPoint next;
991128277c9SStephan Aßmus 	BPoint nextIn;
992128277c9SStephan Aßmus 
993128277c9SStephan Aßmus 	if (fPath->FindBezierScale(index - 1, where, &scale)
994128277c9SStephan Aßmus 		&& scale >= 0.0 && scale <= 1.0
995128277c9SStephan Aßmus 		&& fPath->GetPoint(index - 1, scale, point)) {
996128277c9SStephan Aßmus 
997128277c9SStephan Aßmus 		fPath->GetPointAt(index - 1, previous);
998128277c9SStephan Aßmus 		fPath->GetPointOutAt(index - 1, previousOut);
999128277c9SStephan Aßmus 		fPath->GetPointAt(index, next);
1000128277c9SStephan Aßmus 		fPath->GetPointInAt(index, nextIn);
1001128277c9SStephan Aßmus 
1002128277c9SStephan Aßmus 		where = scale_point(previousOut, nextIn, scale);
1003128277c9SStephan Aßmus 
1004128277c9SStephan Aßmus 		previousOut = scale_point(previous, previousOut, scale);
1005128277c9SStephan Aßmus 		nextIn = scale_point(next, nextIn, 1 - scale);
1006128277c9SStephan Aßmus 		pointIn = scale_point(previousOut, where, scale);
1007128277c9SStephan Aßmus 		pointOut = scale_point(nextIn, where, 1 - scale);
1008128277c9SStephan Aßmus 
1009128277c9SStephan Aßmus 		if (fPath->AddPoint(point, index)) {
1010128277c9SStephan Aßmus 
1011128277c9SStephan Aßmus 			fPath->SetPointIn(index, pointIn);
1012128277c9SStephan Aßmus 			fPath->SetPointOut(index, pointOut);
1013128277c9SStephan Aßmus 
1014128277c9SStephan Aßmus 			delete fInsertPointCommand;
1015128277c9SStephan Aßmus 			fInsertPointCommand = new InsertPointCommand(fPath, index,
1016128277c9SStephan Aßmus 														 fSelection->Items(),
1017128277c9SStephan Aßmus 														 fSelection->CountItems());
1018128277c9SStephan Aßmus 
1019128277c9SStephan Aßmus 			fPath->SetPointOut(index - 1, previousOut);
1020128277c9SStephan Aßmus 			fPath->SetPointIn(index + 1, nextIn);
1021128277c9SStephan Aßmus 
1022128277c9SStephan Aßmus 			fCurrentPathPoint = index;
1023128277c9SStephan Aßmus 			_ShiftSelection(fCurrentPathPoint, 1);
1024128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
1025128277c9SStephan Aßmus 		}
1026128277c9SStephan Aßmus 	}
1027128277c9SStephan Aßmus }
1028128277c9SStephan Aßmus 
1029128277c9SStephan Aßmus // _SetInOutConnected
1030128277c9SStephan Aßmus void
1031128277c9SStephan Aßmus PathManipulator::_SetInOutConnected(int32 index, bool connected)
1032128277c9SStephan Aßmus {
1033128277c9SStephan Aßmus 	fPath->SetInOutConnected(index, connected);
1034128277c9SStephan Aßmus }
1035128277c9SStephan Aßmus 
1036128277c9SStephan Aßmus // _SetSharp
1037128277c9SStephan Aßmus void
1038128277c9SStephan Aßmus PathManipulator::_SetSharp(int32 index)
1039128277c9SStephan Aßmus {
1040128277c9SStephan Aßmus 	BPoint p;
1041128277c9SStephan Aßmus 	fPath->GetPointAt(index, p);
1042128277c9SStephan Aßmus 	fPath->SetPoint(index, p, p, p, true);
1043128277c9SStephan Aßmus }
1044128277c9SStephan Aßmus 
1045128277c9SStephan Aßmus // _RemoveSelection
1046128277c9SStephan Aßmus void
1047128277c9SStephan Aßmus PathManipulator::_RemoveSelection()
1048128277c9SStephan Aßmus {
1049128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1050128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
1051128277c9SStephan Aßmus 		if (!fPath->RemovePoint(fSelection->IndexAt(i) - i))
1052128277c9SStephan Aßmus 			break;
1053128277c9SStephan Aßmus 	}
1054128277c9SStephan Aßmus 
1055128277c9SStephan Aßmus //	SetClosed(fPath->IsClosed() && fPath->CountPoints() > 1);
1056128277c9SStephan Aßmus 
1057128277c9SStephan Aßmus 	fSelection->MakeEmpty();
1058128277c9SStephan Aßmus }
1059128277c9SStephan Aßmus 
1060128277c9SStephan Aßmus 
1061128277c9SStephan Aßmus // _RemovePoint
1062128277c9SStephan Aßmus void
1063128277c9SStephan Aßmus PathManipulator::_RemovePoint(int32 index)
1064128277c9SStephan Aßmus {
1065128277c9SStephan Aßmus 	if (fPath->RemovePoint(index)) {
1066128277c9SStephan Aßmus 		_Deselect(index);
1067128277c9SStephan Aßmus 		_ShiftSelection(index + 1, -1);
1068128277c9SStephan Aßmus 	}
1069128277c9SStephan Aßmus }
1070128277c9SStephan Aßmus 
1071128277c9SStephan Aßmus // _RemovePointIn
1072128277c9SStephan Aßmus void
1073128277c9SStephan Aßmus PathManipulator::_RemovePointIn(int32 index)
1074128277c9SStephan Aßmus {
1075128277c9SStephan Aßmus 	BPoint p;
1076128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1077128277c9SStephan Aßmus 		fPath->SetPointIn(index, p);
1078128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1079128277c9SStephan Aßmus 	}
1080128277c9SStephan Aßmus }
1081128277c9SStephan Aßmus 
1082128277c9SStephan Aßmus // _RemovePointOut
1083128277c9SStephan Aßmus void
1084128277c9SStephan Aßmus PathManipulator::_RemovePointOut(int32 index)
1085128277c9SStephan Aßmus {
1086128277c9SStephan Aßmus 	BPoint p;
1087128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1088128277c9SStephan Aßmus 		fPath->SetPointOut(index, p);
1089128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1090128277c9SStephan Aßmus 	}
1091128277c9SStephan Aßmus }
1092128277c9SStephan Aßmus 
1093128277c9SStephan Aßmus // _Delete
1094128277c9SStephan Aßmus Command*
1095128277c9SStephan Aßmus PathManipulator::_Delete()
1096128277c9SStephan Aßmus {
1097128277c9SStephan Aßmus 	Command* command = NULL;
1098128277c9SStephan Aßmus 	if (!fMouseDown) {
1099128277c9SStephan Aßmus 		if (fSelection->CountItems() == fPath->CountPoints()) {
1100128277c9SStephan Aßmus //			command = new RemovePathCommand(fPath);
1101128277c9SStephan Aßmus 		} else {
1102128277c9SStephan Aßmus 			command = new RemovePointsCommand(fPath,
1103128277c9SStephan Aßmus 											  fSelection->Items(),
1104128277c9SStephan Aßmus 											  fSelection->CountItems());
1105128277c9SStephan Aßmus 			_RemoveSelection();
1106128277c9SStephan Aßmus 		}
1107128277c9SStephan Aßmus 
1108128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1109128277c9SStephan Aßmus 	}
1110128277c9SStephan Aßmus 
1111128277c9SStephan Aßmus 	return command;
1112128277c9SStephan Aßmus }
1113128277c9SStephan Aßmus 
1114128277c9SStephan Aßmus // #pragma mark -
1115128277c9SStephan Aßmus 
1116128277c9SStephan Aßmus // _Select
1117128277c9SStephan Aßmus void
1118128277c9SStephan Aßmus PathManipulator::_Select(BRect r)
1119128277c9SStephan Aßmus {
1120128277c9SStephan Aßmus 	BPoint p;
1121128277c9SStephan Aßmus 	int32 count = fPath->CountPoints();
1122128277c9SStephan Aßmus 	Selection temp;
1123128277c9SStephan Aßmus 	for (int32 i = 0; i < count && fPath->GetPointAt(i, p); i++) {
1124128277c9SStephan Aßmus 		if (r.Contains(p)) {
1125128277c9SStephan Aßmus 			temp.Add(i);
1126128277c9SStephan Aßmus 		}
1127128277c9SStephan Aßmus 	}
1128128277c9SStephan Aßmus 	// merge old and new selection
1129128277c9SStephan Aßmus 	count = fOldSelection->CountItems();
1130128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
1131128277c9SStephan Aßmus 		int32 index = fOldSelection->IndexAt(i);
1132128277c9SStephan Aßmus 		if (temp.Contains(index))
1133128277c9SStephan Aßmus 			temp.Remove(index);
1134128277c9SStephan Aßmus 		else
1135128277c9SStephan Aßmus 			temp.Add(index);
1136128277c9SStephan Aßmus 	}
1137128277c9SStephan Aßmus 	if (temp != *fSelection) {
1138128277c9SStephan Aßmus 		*fSelection = temp;
1139128277c9SStephan Aßmus 		_UpdateSelection();
1140128277c9SStephan Aßmus 	}
1141128277c9SStephan Aßmus }
1142128277c9SStephan Aßmus 
1143128277c9SStephan Aßmus // _Select
1144128277c9SStephan Aßmus void
1145128277c9SStephan Aßmus PathManipulator::_Select(int32 index, bool extend)
1146128277c9SStephan Aßmus {
1147128277c9SStephan Aßmus 	if (!extend)
1148128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1149128277c9SStephan Aßmus 	if (fSelection->Contains(index))
1150128277c9SStephan Aßmus 		fSelection->Remove(index);
1151128277c9SStephan Aßmus 	else
1152128277c9SStephan Aßmus 		fSelection->Add(index);
1153128277c9SStephan Aßmus 	// TODO: this can lead to unnecessary invalidation (maybe need to investigate)
1154128277c9SStephan Aßmus 	_UpdateSelection();
1155128277c9SStephan Aßmus }
1156128277c9SStephan Aßmus 
1157128277c9SStephan Aßmus // _Select
1158128277c9SStephan Aßmus void
1159128277c9SStephan Aßmus PathManipulator::_Select(const int32* indices, int32 count, bool extend)
1160128277c9SStephan Aßmus {
1161128277c9SStephan Aßmus 	if (extend) {
1162128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1163128277c9SStephan Aßmus 			if (!fSelection->Contains(indices[i]))
1164128277c9SStephan Aßmus 				fSelection->Add(indices[i]);
1165128277c9SStephan Aßmus 		}
1166128277c9SStephan Aßmus 	} else {
1167128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1168128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1169128277c9SStephan Aßmus 			fSelection->Add(indices[i]);
1170128277c9SStephan Aßmus 		}
1171128277c9SStephan Aßmus 	}
1172128277c9SStephan Aßmus 	_UpdateSelection();
1173128277c9SStephan Aßmus }
1174128277c9SStephan Aßmus 
1175128277c9SStephan Aßmus // _Deselect
1176128277c9SStephan Aßmus void
1177128277c9SStephan Aßmus PathManipulator::_Deselect(int32 index)
1178128277c9SStephan Aßmus {
1179128277c9SStephan Aßmus 	if (fSelection->Contains(index)) {
1180128277c9SStephan Aßmus 		fSelection->Remove(index);
1181128277c9SStephan Aßmus 		_UpdateSelection();
1182128277c9SStephan Aßmus 	}
1183128277c9SStephan Aßmus }
1184128277c9SStephan Aßmus 
1185128277c9SStephan Aßmus // _ShiftSelection
1186128277c9SStephan Aßmus void
1187128277c9SStephan Aßmus PathManipulator::_ShiftSelection(int32 startIndex, int32 direction)
1188128277c9SStephan Aßmus {
1189128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1190128277c9SStephan Aßmus 	if (count > 0) {
1191128277c9SStephan Aßmus 		int32* selection = fSelection->Items();
1192128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1193128277c9SStephan Aßmus 			if (selection[i] >= startIndex) {
1194128277c9SStephan Aßmus 				selection[i] += direction;
1195128277c9SStephan Aßmus 			}
1196128277c9SStephan Aßmus 		}
1197128277c9SStephan Aßmus 	}
1198128277c9SStephan Aßmus 	_UpdateSelection();
1199128277c9SStephan Aßmus }
1200128277c9SStephan Aßmus 
1201128277c9SStephan Aßmus // _IsSelected
1202128277c9SStephan Aßmus bool
1203128277c9SStephan Aßmus PathManipulator::_IsSelected(int32 index) const
1204128277c9SStephan Aßmus {
1205128277c9SStephan Aßmus 	return fSelection->Contains(index);
1206128277c9SStephan Aßmus }
1207128277c9SStephan Aßmus 
1208128277c9SStephan Aßmus // #pragma mark -
1209128277c9SStephan Aßmus 
1210128277c9SStephan Aßmus // _InvalidateCanvas
1211128277c9SStephan Aßmus void
1212128277c9SStephan Aßmus PathManipulator::_InvalidateCanvas(BRect rect) const
1213128277c9SStephan Aßmus {
1214f67876a0SStephan Aßmus 	// convert from canvas to view space
1215f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&rect);
1216128277c9SStephan Aßmus 	fCanvasView->Invalidate(rect);
1217128277c9SStephan Aßmus }
1218128277c9SStephan Aßmus 
1219128277c9SStephan Aßmus // _InvalidateHighlightPoints
1220128277c9SStephan Aßmus void
1221128277c9SStephan Aßmus PathManipulator::_InvalidateHighlightPoints(int32 newIndex, uint32 newMode)
1222128277c9SStephan Aßmus {
1223128277c9SStephan Aßmus 	BRect oldRect = _ControlPointRect(fCurrentPathPoint, fMode);
1224128277c9SStephan Aßmus 	BRect newRect = _ControlPointRect(newIndex, newMode);
1225128277c9SStephan Aßmus 	if (oldRect.IsValid())
1226128277c9SStephan Aßmus 		_InvalidateCanvas(oldRect);
1227128277c9SStephan Aßmus 	if (newRect.IsValid())
1228128277c9SStephan Aßmus 		_InvalidateCanvas(newRect);
1229128277c9SStephan Aßmus }
1230128277c9SStephan Aßmus 
1231128277c9SStephan Aßmus // _UpdateSelection
1232128277c9SStephan Aßmus void
1233128277c9SStephan Aßmus PathManipulator::_UpdateSelection() const
1234128277c9SStephan Aßmus {
1235128277c9SStephan Aßmus 	_InvalidateCanvas(_ControlPointRect());
1236128277c9SStephan Aßmus 	if (BWindow* window = fCanvasView->Window()) {
1237128277c9SStephan Aßmus 		window->PostMessage(MSG_UPDATE_SHAPE_UI);
1238128277c9SStephan Aßmus 	}
1239128277c9SStephan Aßmus }
1240128277c9SStephan Aßmus 
1241128277c9SStephan Aßmus // _ControlPointRect
1242128277c9SStephan Aßmus BRect
1243128277c9SStephan Aßmus PathManipulator::_ControlPointRect() const
1244128277c9SStephan Aßmus {
1245128277c9SStephan Aßmus 	BRect r = fPath->ControlPointBounds();
1246128277c9SStephan Aßmus 	r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1247128277c9SStephan Aßmus 	return r;
1248128277c9SStephan Aßmus }
1249128277c9SStephan Aßmus 
1250128277c9SStephan Aßmus // _ControlPointRect
1251128277c9SStephan Aßmus BRect
1252128277c9SStephan Aßmus PathManipulator::_ControlPointRect(int32 index, uint32 mode) const
1253128277c9SStephan Aßmus {
1254128277c9SStephan Aßmus 	BRect rect(0.0, 0.0, -1.0, -1.0);
1255128277c9SStephan Aßmus 	if (index >= 0) {
1256128277c9SStephan Aßmus 		BPoint p, pIn, pOut;
1257128277c9SStephan Aßmus 		fPath->GetPointsAt(index, p, pIn, pOut);
1258128277c9SStephan Aßmus 		switch (mode) {
1259128277c9SStephan Aßmus 			case MOVE_POINT:
1260128277c9SStephan Aßmus 			case TOGGLE_SHARP:
1261128277c9SStephan Aßmus 			case REMOVE_POINT:
1262128277c9SStephan Aßmus 			case CLOSE_PATH:
1263128277c9SStephan Aßmus 				rect.Set(p.x, p.y, p.x, p.y);
1264128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1265128277c9SStephan Aßmus 				break;
1266128277c9SStephan Aßmus 			case MOVE_POINT_IN:
1267128277c9SStephan Aßmus 			case TOGGLE_SHARP_IN:
1268128277c9SStephan Aßmus 			case REMOVE_POINT_IN:
1269128277c9SStephan Aßmus 				rect.Set(pIn.x, pIn.y, pIn.x, pIn.y);
1270128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1271128277c9SStephan Aßmus 				break;
1272128277c9SStephan Aßmus 			case MOVE_POINT_OUT:
1273128277c9SStephan Aßmus 			case TOGGLE_SHARP_OUT:
1274128277c9SStephan Aßmus 			case REMOVE_POINT_OUT:
1275128277c9SStephan Aßmus 				rect.Set(pOut.x, pOut.y, pOut.x, pOut.y);
1276128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1277128277c9SStephan Aßmus 				break;
1278128277c9SStephan Aßmus 			case SELECT_POINTS:
1279128277c9SStephan Aßmus 				rect.Set(min4(p.x, pIn.x, pOut.x, pOut.x),
1280128277c9SStephan Aßmus 						 min4(p.y, pIn.y, pOut.y, pOut.y),
1281128277c9SStephan Aßmus 						 max4(p.x, pIn.x, pOut.x, pOut.x),
1282128277c9SStephan Aßmus 						 max4(p.y, pIn.y, pOut.y, pOut.y));
1283128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1284128277c9SStephan Aßmus 				break;
1285128277c9SStephan Aßmus 		}
1286128277c9SStephan Aßmus 	}
1287128277c9SStephan Aßmus 	return rect;
1288128277c9SStephan Aßmus }
1289128277c9SStephan Aßmus 
1290128277c9SStephan Aßmus // #pragma mark -
1291128277c9SStephan Aßmus 
1292128277c9SStephan Aßmus // _SetModeForMousePos
1293128277c9SStephan Aßmus void
1294128277c9SStephan Aßmus PathManipulator::_SetModeForMousePos(BPoint where)
1295128277c9SStephan Aßmus {
1296128277c9SStephan Aßmus 	uint32 mode = UNDEFINED;
1297128277c9SStephan Aßmus 	int32 index = -1;
1298128277c9SStephan Aßmus 
1299f67876a0SStephan Aßmus 	float zoomLevel = fCanvasView->ZoomLevel();
1300128277c9SStephan Aßmus 
1301128277c9SStephan Aßmus 	// see if we're close enough at a control point
1302128277c9SStephan Aßmus 	BPoint point;
1303128277c9SStephan Aßmus 	BPoint pointIn;
1304128277c9SStephan Aßmus 	BPoint pointOut;
1305128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut)
1306128277c9SStephan Aßmus 					  && mode == UNDEFINED; i++) {
1307128277c9SStephan Aßmus 
1308128277c9SStephan Aßmus 		float distM = point_point_distance(point, where) * zoomLevel;
1309128277c9SStephan Aßmus 		float distIn = point_point_distance(pointIn, where) * zoomLevel;
1310128277c9SStephan Aßmus 		float distOut = point_point_distance(pointOut, where) * zoomLevel;
1311128277c9SStephan Aßmus 
1312128277c9SStephan Aßmus 		if (distM < MOVE_THRESHOLD) {
1313128277c9SStephan Aßmus 			if (i == 0 && fClickToClose
1314128277c9SStephan Aßmus 				&& !fPath->IsClosed() && fPath->CountPoints() > 1) {
1315128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1316128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : CLOSE_PATH);
1317128277c9SStephan Aßmus 				index = i;
1318128277c9SStephan Aßmus 			} else {
1319128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1320128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : MOVE_POINT);
1321128277c9SStephan Aßmus 				index = i;
1322128277c9SStephan Aßmus 			}
1323128277c9SStephan Aßmus 		}
1324128277c9SStephan Aßmus 		if (distIn < distM && distIn < MOVE_THRESHOLD) {
1325128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_IN :
1326128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_IN : MOVE_POINT_IN);
1327128277c9SStephan Aßmus 			index = i;
1328128277c9SStephan Aßmus 		}
1329128277c9SStephan Aßmus 		if (distOut < distIn && distOut < distM && distOut < MOVE_THRESHOLD) {
1330128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_OUT :
1331128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_OUT : MOVE_POINT_OUT);
1332128277c9SStephan Aßmus 			index = i;
1333128277c9SStephan Aßmus 		}
1334128277c9SStephan Aßmus 	}
1335128277c9SStephan Aßmus 	// selection mode overrides any other mode,
1336128277c9SStephan Aßmus 	// but we need to check for it after we know
1337128277c9SStephan Aßmus 	// the index of the point under the mouse (code above)
1338128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1339128277c9SStephan Aßmus 	if (fShiftDown && pointCount > 0) {
1340128277c9SStephan Aßmus 		mode = SELECT_POINTS;
1341128277c9SStephan Aßmus 	}
1342128277c9SStephan Aßmus 
1343128277c9SStephan Aßmus 	// see if user wants to start new sub path
1344128277c9SStephan Aßmus 	if (fAltDown) {
1345128277c9SStephan Aßmus 		mode = NEW_PATH;
1346128277c9SStephan Aßmus 		index = -1;
1347128277c9SStephan Aßmus 	}
1348128277c9SStephan Aßmus 
1349128277c9SStephan Aßmus 	// see if we're close enough at a line
1350128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1351128277c9SStephan Aßmus 		float distance;
1352128277c9SStephan Aßmus 		if (fPath->GetDistance(where, &distance, &index)) {
1353128277c9SStephan Aßmus 			if (distance < (INSERT_DIST_THRESHOLD / zoomLevel)) {
1354128277c9SStephan Aßmus 				mode = INSERT_POINT;
1355128277c9SStephan Aßmus 			}
1356128277c9SStephan Aßmus 		} else {
1357128277c9SStephan Aßmus 			// restore index, since it was changed by call above
1358128277c9SStephan Aßmus 			index = fCurrentPathPoint;
1359128277c9SStephan Aßmus 		}
1360128277c9SStephan Aßmus 	}
1361128277c9SStephan Aßmus 
1362128277c9SStephan Aßmus 	// nope, still undefined mode, last fall back
1363128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1364128277c9SStephan Aßmus 		if (fFallBackMode == SELECT_POINTS) {
1365128277c9SStephan Aßmus 			if (fPath->IsClosed() && pointCount > 0) {
1366128277c9SStephan Aßmus 				mode = SELECT_POINTS;
1367128277c9SStephan Aßmus 				index = -1;
1368128277c9SStephan Aßmus 			} else {
1369128277c9SStephan Aßmus 				mode = ADD_POINT;
1370128277c9SStephan Aßmus 				index = pointCount - 1;
1371128277c9SStephan Aßmus 			}
1372128277c9SStephan Aßmus 		} else {
1373128277c9SStephan Aßmus 			// user had clicked "New Path" icon
1374128277c9SStephan Aßmus 			mode = fFallBackMode;
1375128277c9SStephan Aßmus 		}
1376128277c9SStephan Aßmus 	}
1377128277c9SStephan Aßmus 	// switch mode if necessary
1378128277c9SStephan Aßmus 	if (mode != fMode || index != fCurrentPathPoint) {
1379128277c9SStephan Aßmus 		// invalidate path display (to highlight the respective point)
1380128277c9SStephan Aßmus 		_InvalidateHighlightPoints(index, mode);
1381128277c9SStephan Aßmus 		_SetMode(mode);
1382128277c9SStephan Aßmus 		fCurrentPathPoint = index;
1383128277c9SStephan Aßmus 	}
1384128277c9SStephan Aßmus }
1385128277c9SStephan Aßmus 
1386128277c9SStephan Aßmus // #pragma mark -
1387128277c9SStephan Aßmus 
1388128277c9SStephan Aßmus // _Nudge
1389128277c9SStephan Aßmus void
1390128277c9SStephan Aßmus PathManipulator::_Nudge(BPoint direction)
1391128277c9SStephan Aßmus {
1392128277c9SStephan Aßmus 	bigtime_t now = system_time();
1393128277c9SStephan Aßmus 	if (now - fLastNudgeTime > 500000) {
1394128277c9SStephan Aßmus 		_FinishNudging();
1395128277c9SStephan Aßmus 	}
1396128277c9SStephan Aßmus 	fLastNudgeTime = now;
1397128277c9SStephan Aßmus 	fNudgeOffset += direction;
1398128277c9SStephan Aßmus 
1399128277c9SStephan Aßmus //	if (!fNudgeCommand) {
1400128277c9SStephan Aßmus //
1401128277c9SStephan Aßmus //		bool fromSelection = !fSelection->IsEmpty();
1402128277c9SStephan Aßmus //
1403128277c9SStephan Aßmus //		int32 count = fromSelection ? fSelection->CountItems()
1404128277c9SStephan Aßmus //									: fPath->CountPoints();
1405128277c9SStephan Aßmus //		int32* indices = new int32[count];
1406128277c9SStephan Aßmus //		control_point* points = new control_point[count];
1407128277c9SStephan Aßmus //
1408128277c9SStephan Aßmus //		// init indices and points
1409128277c9SStephan Aßmus //		for (int32 i = 0; i < count; i++) {
1410128277c9SStephan Aßmus //			indices[i] = fromSelection ? fSelection->IndexAt(i) : i;
1411128277c9SStephan Aßmus //			fPath->GetPointsAt(indices[i],
1412128277c9SStephan Aßmus //							   points[i].point,
1413128277c9SStephan Aßmus //							   points[i].point_in,
1414128277c9SStephan Aßmus //							   points[i].point_out,
1415128277c9SStephan Aßmus //							   &points[i].connected);
1416128277c9SStephan Aßmus //		}
1417128277c9SStephan Aßmus //
1418128277c9SStephan Aßmus //			fNudgeCommand = new NudgePointsCommand(this, fPath,
1419128277c9SStephan Aßmus //												 indices, points, count);
1420128277c9SStephan Aßmus //
1421128277c9SStephan Aßmus //			fNudgeCommand->SetNewTranslation(fNudgeOffset);
1422128277c9SStephan Aßmus //			fNudgeCommand->Redo(fCanvasView);
1423128277c9SStephan Aßmus //
1424128277c9SStephan Aßmus //		delete[] indices;
1425128277c9SStephan Aßmus //		delete[] points;
1426128277c9SStephan Aßmus //
1427128277c9SStephan Aßmus //	} else {
1428128277c9SStephan Aßmus //		fNudgeCommand->SetNewTranslation(fNudgeOffset);
1429128277c9SStephan Aßmus //		fNudgeCommand->Redo(fCanvasView);
1430128277c9SStephan Aßmus //	}
1431128277c9SStephan Aßmus 
1432128277c9SStephan Aßmus 	if (!fMouseDown)
1433128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1434128277c9SStephan Aßmus }
1435128277c9SStephan Aßmus 
1436128277c9SStephan Aßmus // _FinishNudging
1437128277c9SStephan Aßmus void
1438128277c9SStephan Aßmus PathManipulator::_FinishNudging()
1439128277c9SStephan Aßmus {
1440128277c9SStephan Aßmus 	fNudgeOffset = BPoint(0.0, 0.0);
1441128277c9SStephan Aßmus 
1442128277c9SStephan Aßmus //	if (fNudgeCommand) {
1443128277c9SStephan Aßmus //		fCanvasView->Perform(fNudgeCommand);
1444128277c9SStephan Aßmus //		fNudgeCommand = NULL;
1445128277c9SStephan Aßmus //	}
1446128277c9SStephan Aßmus }
1447128277c9SStephan Aßmus 
1448128277c9SStephan Aßmus 
1449