xref: /haiku/src/apps/icon-o-matic/shape/PathManipulator.cpp (revision 4fac07a0887406d944d78403f8a3b01a4e6154be)
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>
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"
29128277c9SStephan Aßmus //#include "NewPathCommand.h"
300e1ba39fSStephan Aßmus #include "NudgePointsCommand.h"
31128277c9SStephan Aßmus //#include "RemovePathCommand.h"
32128277c9SStephan Aßmus #include "RemovePointsCommand.h"
33128277c9SStephan Aßmus //#include "ReversePathCommand.h"
34128277c9SStephan Aßmus //#include "SelectPathCommand.h"
35128277c9SStephan Aßmus //#include "SelectPointsCommand.h"
36f4bd80a2SStephan Aßmus #include "SplitPointsCommand.h"
370e1ba39fSStephan Aßmus #include "TransformPointsBox.h"
38128277c9SStephan Aßmus 
39128277c9SStephan Aßmus #define POINT_EXTEND 3.0
40128277c9SStephan Aßmus #define CONTROL_POINT_EXTEND 2.0
41128277c9SStephan Aßmus #define INSERT_DIST_THRESHOLD 7.0
42128277c9SStephan Aßmus #define MOVE_THRESHOLD 9.0
43128277c9SStephan Aßmus 
44128277c9SStephan Aßmus enum {
45128277c9SStephan Aßmus 	UNDEFINED,
46128277c9SStephan Aßmus 
47128277c9SStephan Aßmus 	NEW_PATH,
48128277c9SStephan Aßmus 
49128277c9SStephan Aßmus 	ADD_POINT,
50128277c9SStephan Aßmus 	INSERT_POINT,
51128277c9SStephan Aßmus 	MOVE_POINT,
52128277c9SStephan Aßmus 	MOVE_POINT_IN,
53128277c9SStephan Aßmus 	MOVE_POINT_OUT,
54128277c9SStephan Aßmus 	CLOSE_PATH,
55128277c9SStephan Aßmus 
56128277c9SStephan Aßmus 	TOGGLE_SHARP,
57128277c9SStephan Aßmus 	TOGGLE_SHARP_IN,
58128277c9SStephan Aßmus 	TOGGLE_SHARP_OUT,
59128277c9SStephan Aßmus 
60128277c9SStephan Aßmus 	REMOVE_POINT,
61128277c9SStephan Aßmus 	REMOVE_POINT_IN,
62128277c9SStephan Aßmus 	REMOVE_POINT_OUT,
63128277c9SStephan Aßmus 
64128277c9SStephan Aßmus 	SELECT_POINTS,
65128277c9SStephan Aßmus 	TRANSFORM_POINTS,
66128277c9SStephan Aßmus 	TRANSLATE_POINTS,
67128277c9SStephan Aßmus 
68128277c9SStephan Aßmus 	SELECT_SUB_PATH,
69128277c9SStephan Aßmus };
70128277c9SStephan Aßmus 
71f4bd80a2SStephan Aßmus enum {
72f4bd80a2SStephan Aßmus 	MSG_TRANSFORM				= 'strn',
73f4bd80a2SStephan Aßmus 	MSG_REMOVE_POINTS			= 'srmp',
74f4bd80a2SStephan Aßmus 	MSG_UPDATE_SHAPE_UI			= 'udsi',
75f4bd80a2SStephan Aßmus 
76f4bd80a2SStephan Aßmus 	MSG_SPLIT_POINTS			= 'splt',
77f4bd80a2SStephan Aßmus };
78f4bd80a2SStephan Aßmus 
79128277c9SStephan Aßmus inline const char*
80128277c9SStephan Aßmus string_for_mode(uint32 mode)
81128277c9SStephan Aßmus {
82128277c9SStephan Aßmus 	switch (mode) {
83128277c9SStephan Aßmus 		case UNDEFINED:
84128277c9SStephan Aßmus 			return "UNDEFINED";
85128277c9SStephan Aßmus 		case NEW_PATH:
86128277c9SStephan Aßmus 			return "NEW_PATH";
87128277c9SStephan Aßmus 		case ADD_POINT:
88128277c9SStephan Aßmus 			return "ADD_POINT";
89128277c9SStephan Aßmus 		case INSERT_POINT:
90128277c9SStephan Aßmus 			return "INSERT_POINT";
91128277c9SStephan Aßmus 		case MOVE_POINT:
92128277c9SStephan Aßmus 			return "MOVE_POINT";
93128277c9SStephan Aßmus 		case MOVE_POINT_IN:
94128277c9SStephan Aßmus 			return "MOVE_POINT_IN";
95128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
96128277c9SStephan Aßmus 			return "MOVE_POINT_OUT";
97128277c9SStephan Aßmus 		case CLOSE_PATH:
98128277c9SStephan Aßmus 			return "CLOSE_PATH";
99128277c9SStephan Aßmus 		case TOGGLE_SHARP:
100128277c9SStephan Aßmus 			return "TOGGLE_SHARP";
101128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
102128277c9SStephan Aßmus 			return "TOGGLE_SHARP_IN";
103128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
104128277c9SStephan Aßmus 			return "TOGGLE_SHARP_OUT";
105128277c9SStephan Aßmus 		case REMOVE_POINT:
106128277c9SStephan Aßmus 			return "REMOVE_POINT";
107128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
108128277c9SStephan Aßmus 			return "REMOVE_POINT_IN";
109128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
110128277c9SStephan Aßmus 			return "REMOVE_POINT_OUT";
111128277c9SStephan Aßmus 		case SELECT_POINTS:
112128277c9SStephan Aßmus 			return "SELECT_POINTS";
113128277c9SStephan Aßmus 		case TRANSFORM_POINTS:
114128277c9SStephan Aßmus 			return "TRANSFORM_POINTS";
115128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
116128277c9SStephan Aßmus 			return "TRANSLATE_POINTS";
117128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
118128277c9SStephan Aßmus 			return "SELECT_SUB_PATH";
119128277c9SStephan Aßmus 	}
120128277c9SStephan Aßmus 	return "<unknown mode>";
121128277c9SStephan Aßmus }
122128277c9SStephan Aßmus 
123ce181bb0SStephan Aßmus class PathManipulator::Selection : protected BList
124128277c9SStephan Aßmus {
125128277c9SStephan Aßmus public:
126128277c9SStephan Aßmus 	inline Selection(int32 count = 20)
127128277c9SStephan Aßmus 		: BList(count) {}
128128277c9SStephan Aßmus 	inline ~Selection() {}
129128277c9SStephan Aßmus 
130128277c9SStephan Aßmus 	inline void Add(int32 value)
131128277c9SStephan Aßmus 		{
132128277c9SStephan Aßmus 			if (value >= 0) {
133128277c9SStephan Aßmus 				// keep the list sorted
134128277c9SStephan Aßmus 				int32 count = CountItems();
135128277c9SStephan Aßmus 				int32 index = 0;
136128277c9SStephan Aßmus 				for (; index < count; index++) {
137128277c9SStephan Aßmus 					if (IndexAt(index) > value) {
138128277c9SStephan Aßmus 						break;
139128277c9SStephan Aßmus 					}
140128277c9SStephan Aßmus 				}
141128277c9SStephan Aßmus 				BList::AddItem((void*)value, index);
142128277c9SStephan Aßmus 			}
143128277c9SStephan Aßmus 		}
144128277c9SStephan Aßmus 
145128277c9SStephan Aßmus 	inline bool Remove(int32 value)
146128277c9SStephan Aßmus 		{ return BList::RemoveItem((void*)value); }
147128277c9SStephan Aßmus 
148128277c9SStephan Aßmus 	inline bool Contains(int32 value) const
149128277c9SStephan Aßmus 		{ return BList::HasItem((void*)value); }
150128277c9SStephan Aßmus 
151128277c9SStephan Aßmus 	inline bool IsEmpty() const
152128277c9SStephan Aßmus 		{ return BList::IsEmpty(); }
153128277c9SStephan Aßmus 
154128277c9SStephan Aßmus 	inline int32 IndexAt(int32 index) const
155128277c9SStephan Aßmus 		{ return (int32)BList::ItemAt(index); }
156128277c9SStephan Aßmus 
157128277c9SStephan Aßmus 	inline void MakeEmpty()
158128277c9SStephan Aßmus 		{ BList::MakeEmpty(); }
159128277c9SStephan Aßmus 
160128277c9SStephan Aßmus 	inline int32* Items() const
161128277c9SStephan Aßmus 		{ return (int32*)BList::Items(); }
162128277c9SStephan Aßmus 
163128277c9SStephan Aßmus 	inline const int32 CountItems() const
164128277c9SStephan Aßmus 		{ return BList::CountItems(); }
165128277c9SStephan Aßmus 
166128277c9SStephan Aßmus 	inline Selection& operator =(const Selection& other)
167128277c9SStephan Aßmus 		{
168128277c9SStephan Aßmus 			MakeEmpty();
169128277c9SStephan Aßmus 			int32 count = other.CountItems();
170128277c9SStephan Aßmus 			int32* items = other.Items();
171128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++) {
172128277c9SStephan Aßmus 				Add(items[i]);
173128277c9SStephan Aßmus 			}
174128277c9SStephan Aßmus 			return *this;
175128277c9SStephan Aßmus 		}
176128277c9SStephan Aßmus 
177128277c9SStephan Aßmus 	inline bool operator ==(const Selection& other)
178128277c9SStephan Aßmus 		{
179128277c9SStephan Aßmus 			if (other.CountItems() == CountItems()) {
180128277c9SStephan Aßmus 				int32* items = Items();
181128277c9SStephan Aßmus 				int32* otherItems = other.Items();
182128277c9SStephan Aßmus 				for (int32 i = 0; i < CountItems(); i++) {
183128277c9SStephan Aßmus 					if (items[i] != otherItems[i])
184128277c9SStephan Aßmus 						return false;
185128277c9SStephan Aßmus 					items++;
186128277c9SStephan Aßmus 					otherItems++;
187128277c9SStephan Aßmus 				}
188128277c9SStephan Aßmus 				return true;
189128277c9SStephan Aßmus 			} else
190128277c9SStephan Aßmus 				return false;
191128277c9SStephan Aßmus 		}
192128277c9SStephan Aßmus 
193128277c9SStephan Aßmus 	inline bool operator !=(const Selection& other)
194128277c9SStephan Aßmus 	{
195128277c9SStephan Aßmus 		return !(*this == other);
196128277c9SStephan Aßmus 	}
197128277c9SStephan Aßmus };
198128277c9SStephan Aßmus 
199128277c9SStephan Aßmus 
200128277c9SStephan Aßmus // constructor
201128277c9SStephan Aßmus PathManipulator::PathManipulator(VectorPath* path)
2020e1ba39fSStephan Aßmus 	: Manipulator(NULL),
203128277c9SStephan Aßmus 	  fCanvasView(NULL),
204128277c9SStephan Aßmus 
205128277c9SStephan Aßmus 	  fCommandDown(false),
206128277c9SStephan Aßmus 	  fOptionDown(false),
207128277c9SStephan Aßmus 	  fShiftDown(false),
208128277c9SStephan Aßmus 	  fAltDown(false),
209128277c9SStephan Aßmus 
210128277c9SStephan Aßmus 	  fClickToClose(false),
211128277c9SStephan Aßmus 
212128277c9SStephan Aßmus 	  fMode(NEW_PATH),
213128277c9SStephan Aßmus 	  fFallBackMode(SELECT_POINTS),
214128277c9SStephan Aßmus 
215128277c9SStephan Aßmus 	  fMouseDown(false),
216128277c9SStephan Aßmus 
217128277c9SStephan Aßmus 	  fPath(path),
218128277c9SStephan Aßmus 	  fCurrentPathPoint(-1),
219128277c9SStephan Aßmus 
220128277c9SStephan Aßmus 	  fChangePointCommand(NULL),
221128277c9SStephan Aßmus 	  fInsertPointCommand(NULL),
222128277c9SStephan Aßmus 	  fAddPointCommand(NULL),
223128277c9SStephan Aßmus 
224128277c9SStephan Aßmus 	  fSelection(new Selection()),
225128277c9SStephan Aßmus 	  fOldSelection(new Selection()),
2260e1ba39fSStephan Aßmus 	  fTransformBox(NULL),
227128277c9SStephan Aßmus 
228128277c9SStephan Aßmus 	  fNudgeOffset(0.0, 0.0),
2290e1ba39fSStephan Aßmus 	  fLastNudgeTime(system_time()),
2300e1ba39fSStephan Aßmus 	  fNudgeCommand(NULL)
231128277c9SStephan Aßmus {
2320e1ba39fSStephan Aßmus 	fPath->Acquire();
23305fd3818SStephan Aßmus 	fPath->AddListener(this);
2340e1ba39fSStephan Aßmus 	fPath->AddObserver(this);
235128277c9SStephan Aßmus }
236128277c9SStephan Aßmus 
237128277c9SStephan Aßmus // destructor
238128277c9SStephan Aßmus PathManipulator::~PathManipulator()
239128277c9SStephan Aßmus {
240128277c9SStephan Aßmus 	delete fChangePointCommand;
241128277c9SStephan Aßmus 	delete fInsertPointCommand;
242128277c9SStephan Aßmus 	delete fAddPointCommand;
243128277c9SStephan Aßmus 
244128277c9SStephan Aßmus 	delete fSelection;
245128277c9SStephan Aßmus 	delete fOldSelection;
2460e1ba39fSStephan Aßmus 	delete fTransformBox;
247128277c9SStephan Aßmus 
2480e1ba39fSStephan Aßmus 	delete fNudgeCommand;
2490e1ba39fSStephan Aßmus 
2500e1ba39fSStephan Aßmus 	fPath->RemoveObserver(this);
2510e1ba39fSStephan Aßmus 	fPath->RemoveListener(this);
2520e1ba39fSStephan Aßmus 	fPath->Release();
253128277c9SStephan Aßmus }
254128277c9SStephan Aßmus 
255128277c9SStephan Aßmus 
256128277c9SStephan Aßmus // #pragma mark -
257128277c9SStephan Aßmus 
258128277c9SStephan Aßmus class StrokePathIterator : public VectorPath::Iterator {
259128277c9SStephan Aßmus  public:
260f67876a0SStephan Aßmus 					StrokePathIterator(CanvasView* canvasView,
261f67876a0SStephan Aßmus 									   BView* drawingView)
262f67876a0SStephan Aßmus 						: fCanvasView(canvasView),
263f67876a0SStephan Aßmus 						  fDrawingView(drawingView)
264128277c9SStephan Aßmus 					{
265128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
266128277c9SStephan Aßmus 						fDrawingView->SetDrawingMode(B_OP_OVER);
267128277c9SStephan Aßmus 					}
268128277c9SStephan Aßmus 	virtual			~StrokePathIterator()
269128277c9SStephan Aßmus 					{}
270128277c9SStephan Aßmus 
271128277c9SStephan Aßmus 	virtual	void	MoveTo(BPoint point)
272128277c9SStephan Aßmus 					{
273128277c9SStephan Aßmus 						fBlack = true;
274f67876a0SStephan Aßmus 						fSkip = false;
275128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
276128277c9SStephan Aßmus 
277f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
278128277c9SStephan Aßmus 						fDrawingView->MovePenTo(point);
279128277c9SStephan Aßmus 					}
280128277c9SStephan Aßmus 	virtual	void	LineTo(BPoint point)
281128277c9SStephan Aßmus 					{
282f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
283f67876a0SStephan Aßmus 						if (!fSkip) {
284128277c9SStephan Aßmus 							if (fBlack)
285128277c9SStephan Aßmus 								fDrawingView->SetHighColor(255, 255, 255, 255);
286128277c9SStephan Aßmus 							else
287128277c9SStephan Aßmus 								fDrawingView->SetHighColor(0, 0, 0, 255);
288128277c9SStephan Aßmus 							fBlack = !fBlack;
289128277c9SStephan Aßmus 
290128277c9SStephan Aßmus 							fDrawingView->StrokeLine(point);
291f67876a0SStephan Aßmus 						} else {
292f67876a0SStephan Aßmus 							fDrawingView->MovePenTo(point);
293f67876a0SStephan Aßmus 						}
294f67876a0SStephan Aßmus 						fSkip = !fSkip;
295128277c9SStephan Aßmus 					}
296128277c9SStephan Aßmus 
297128277c9SStephan Aßmus  private:
298f67876a0SStephan Aßmus 	CanvasView*		fCanvasView;
299128277c9SStephan Aßmus 	BView*			fDrawingView;
300128277c9SStephan Aßmus 	bool			fBlack;
301f67876a0SStephan Aßmus 	bool			fSkip;
302128277c9SStephan Aßmus };
303128277c9SStephan Aßmus 
304128277c9SStephan Aßmus // Draw
305128277c9SStephan Aßmus void
306128277c9SStephan Aßmus PathManipulator::Draw(BView* into, BRect updateRect)
307128277c9SStephan Aßmus {
308128277c9SStephan Aßmus 	// draw the Bezier curve, but only if editing
309128277c9SStephan Aßmus 	// if not "editing", the path is actually on top all other modifiers
310128277c9SStephan Aßmus 	// TODO: make this customizable in the GUI
311f67876a0SStephan Aßmus 	StrokePathIterator iterator(fCanvasView, into);
312f67876a0SStephan Aßmus 	fPath->Iterate(&iterator, fCanvasView->ZoomLevel());
313128277c9SStephan Aßmus 
314128277c9SStephan Aßmus 	into->SetLowColor(0, 0, 0, 255);
315128277c9SStephan Aßmus 	BPoint point;
316128277c9SStephan Aßmus 	BPoint pointIn;
317128277c9SStephan Aßmus 	BPoint pointOut;
318128277c9SStephan Aßmus 	rgb_color focusColor = (rgb_color){ 255, 0, 0, 255 };
319128277c9SStephan Aßmus 	rgb_color highlightColor = (rgb_color){ 60, 60, 255, 255 };
320128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut); i++) {
321128277c9SStephan Aßmus 		bool highlight = fCurrentPathPoint == i;
322128277c9SStephan Aßmus 		bool selected = fSelection->Contains(i);
323128277c9SStephan Aßmus 		rgb_color normal = selected ? focusColor : (rgb_color){ 0, 0, 0, 255 };
324128277c9SStephan Aßmus 		into->SetLowColor(normal);
325128277c9SStephan Aßmus 		into->SetHighColor(255, 255, 255, 255);
326128277c9SStephan Aßmus 		// convert to view coordinate space
327f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&point);
328f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointIn);
329f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointOut);
330128277c9SStephan Aßmus 		// connect the points belonging to one control point
331128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_INVERT);
332128277c9SStephan Aßmus 		into->StrokeLine(point, pointIn);
333128277c9SStephan Aßmus 		into->StrokeLine(point, pointOut);
334128277c9SStephan Aßmus 		// draw main control point
335128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT ||
336128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP ||
337128277c9SStephan Aßmus 						  fMode == REMOVE_POINT ||
338128277c9SStephan Aßmus 						  fMode == SELECT_POINTS ||
339128277c9SStephan Aßmus 						  fMode == CLOSE_PATH)) {
340128277c9SStephan Aßmus 
341128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
342128277c9SStephan Aßmus 		}
343128277c9SStephan Aßmus 
344128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_COPY);
345128277c9SStephan Aßmus 		BRect r(point, point);
346128277c9SStephan Aßmus 		r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
347128277c9SStephan Aßmus 		into->StrokeRect(r, B_SOLID_LOW);
348128277c9SStephan Aßmus 		r.InsetBy(1.0, 1.0);
349128277c9SStephan Aßmus 		into->FillRect(r, B_SOLID_HIGH);
350128277c9SStephan Aßmus 		// draw in control point
351128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_IN ||
352128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_IN ||
353128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_IN ||
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 (selected) {
359128277c9SStephan Aßmus 			into->SetHighColor(220, 220, 220, 255);
360128277c9SStephan Aßmus 		} else {
361128277c9SStephan Aßmus 			into->SetHighColor(170, 170, 170, 255);
362128277c9SStephan Aßmus 		}
363128277c9SStephan Aßmus 		if (pointIn != point) {
364128277c9SStephan Aßmus 			r.Set(pointIn.x - CONTROL_POINT_EXTEND, pointIn.y - CONTROL_POINT_EXTEND,
365128277c9SStephan Aßmus 				  pointIn.x + CONTROL_POINT_EXTEND, pointIn.y + CONTROL_POINT_EXTEND);
366128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
367128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
368128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
369128277c9SStephan Aßmus 		}
370128277c9SStephan Aßmus 		// draw out control point
371128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_OUT ||
372128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_OUT ||
373128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_OUT ||
374128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
375128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
376128277c9SStephan Aßmus 		else
377128277c9SStephan Aßmus 			into->SetLowColor(normal);
378128277c9SStephan Aßmus 		if (pointOut != point) {
379128277c9SStephan Aßmus 			r.Set(pointOut.x - CONTROL_POINT_EXTEND, pointOut.y - CONTROL_POINT_EXTEND,
380128277c9SStephan Aßmus 				  pointOut.x + CONTROL_POINT_EXTEND, pointOut.y + CONTROL_POINT_EXTEND);
381128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
382128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
383128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
384128277c9SStephan Aßmus 		}
385128277c9SStephan Aßmus 	}
3860e1ba39fSStephan Aßmus 
3870e1ba39fSStephan Aßmus 	if (fTransformBox) {
3880e1ba39fSStephan Aßmus 		fTransformBox->Draw(into, updateRect);
3890e1ba39fSStephan Aßmus 	}
390128277c9SStephan Aßmus }
391128277c9SStephan Aßmus 
392128277c9SStephan Aßmus // #pragma mark -
393128277c9SStephan Aßmus 
394128277c9SStephan Aßmus // MouseDown
395128277c9SStephan Aßmus bool
396128277c9SStephan Aßmus PathManipulator::MouseDown(BPoint where)
397128277c9SStephan Aßmus {
398128277c9SStephan Aßmus 	fMouseDown = true;
399128277c9SStephan Aßmus 
4000e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
4010e1ba39fSStephan Aßmus 		if (fTransformBox) {
4020e1ba39fSStephan Aßmus 			fTransformBox->MouseDown(where);
4030e1ba39fSStephan Aßmus 
4040e1ba39fSStephan Aßmus //			if (!fTransformBox->IsRotating())
4050e1ba39fSStephan Aßmus //				fCanvasView->SetAutoScrolling(true);
4060e1ba39fSStephan Aßmus 		}
4070e1ba39fSStephan Aßmus 		return true;
4080e1ba39fSStephan Aßmus 	}
4090e1ba39fSStephan Aßmus 
410128277c9SStephan Aßmus 	if (fMode == MOVE_POINT &&
411128277c9SStephan Aßmus 		fSelection->CountItems() > 1 &&
412128277c9SStephan Aßmus 		fSelection->Contains(fCurrentPathPoint)) {
413128277c9SStephan Aßmus 		fMode = TRANSLATE_POINTS;
414128277c9SStephan Aßmus 	}
415128277c9SStephan Aßmus 
416*4fac07a0SStephan Aßmus 	// apply the canvas view mouse filter depending on current mode
417*4fac07a0SStephan Aßmus 	if (fMode == ADD_POINT || fMode == TRANSLATE_POINTS)
418*4fac07a0SStephan Aßmus 		fCanvasView->FilterMouse(&where);
419*4fac07a0SStephan Aßmus 
420128277c9SStephan Aßmus 	BPoint canvasWhere = where;
421f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
422128277c9SStephan Aßmus 
423128277c9SStephan Aßmus 	// maybe we're changing some point, so we construct the
424128277c9SStephan Aßmus 	// "ChangePointCommand" here so that the point is remembered
425128277c9SStephan Aßmus 	// in its current state
426*4fac07a0SStephan Aßmus 	// apply the canvas view mouse filter depending on current mode
427128277c9SStephan Aßmus 	delete fChangePointCommand;
428128277c9SStephan Aßmus 	fChangePointCommand = NULL;
429128277c9SStephan Aßmus 	switch (fMode) {
430128277c9SStephan Aßmus 		case TOGGLE_SHARP:
431128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
432128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
433128277c9SStephan Aßmus 		case MOVE_POINT:
434128277c9SStephan Aßmus 		case MOVE_POINT_IN:
435128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
436128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
437128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
438128277c9SStephan Aßmus 			fChangePointCommand = new ChangePointCommand(fPath,
439128277c9SStephan Aßmus 														 fCurrentPathPoint,
440128277c9SStephan Aßmus 														 fSelection->Items(),
441128277c9SStephan Aßmus 														 fSelection->CountItems());
442128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
443128277c9SStephan Aßmus 			break;
444128277c9SStephan Aßmus 	}
445128277c9SStephan Aßmus 
446128277c9SStephan Aßmus 	// at this point we init doing something
447128277c9SStephan Aßmus 	switch (fMode) {
448128277c9SStephan Aßmus 		case ADD_POINT:
449128277c9SStephan Aßmus 			_AddPoint(canvasWhere);
450128277c9SStephan Aßmus 			break;
451128277c9SStephan Aßmus 		case INSERT_POINT:
452128277c9SStephan Aßmus 			_InsertPoint(canvasWhere, fCurrentPathPoint);
453128277c9SStephan Aßmus 			break;
454128277c9SStephan Aßmus 
455128277c9SStephan Aßmus 		case TOGGLE_SHARP:
456128277c9SStephan Aßmus 			_SetSharp(fCurrentPathPoint);
457128277c9SStephan Aßmus 			// continue by dragging out the _connected_ in/out points
458128277c9SStephan Aßmus 			break;
459128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
460128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
461128277c9SStephan Aßmus 			// continue by moving the "in" point
462128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_IN);
463128277c9SStephan Aßmus 			break;
464128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
465128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
466128277c9SStephan Aßmus 			// continue by moving the "out" point
467128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
468128277c9SStephan Aßmus 			break;
469128277c9SStephan Aßmus 
470128277c9SStephan Aßmus 		case MOVE_POINT:
471128277c9SStephan Aßmus 		case MOVE_POINT_IN:
472128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
473128277c9SStephan Aßmus 			// the right thing happens since "fCurrentPathPoint"
474128277c9SStephan Aßmus 			// points to the correct index
475128277c9SStephan Aßmus 			break;
476128277c9SStephan Aßmus 
477128277c9SStephan Aßmus 		case CLOSE_PATH:
478128277c9SStephan Aßmus //			SetClosed(true, true);
479128277c9SStephan Aßmus 			break;
480128277c9SStephan Aßmus 
481128277c9SStephan Aßmus 		case REMOVE_POINT:
482128277c9SStephan Aßmus 			if (fPath->CountPoints() == 1) {
483128277c9SStephan Aßmus //				fCanvasView->Perform(new RemovePathCommand(this, fPath));
484128277c9SStephan Aßmus 			} else {
485128277c9SStephan Aßmus 				fCanvasView->Perform(new RemovePointsCommand(fPath,
486128277c9SStephan Aßmus 															 fCurrentPathPoint,
487128277c9SStephan Aßmus 															 fSelection->Items(),
488128277c9SStephan Aßmus 															 fSelection->CountItems()));
489128277c9SStephan Aßmus 				_RemovePoint(fCurrentPathPoint);
490128277c9SStephan Aßmus 			}
491128277c9SStephan Aßmus 			break;
492128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
493128277c9SStephan Aßmus 			_RemovePointIn(fCurrentPathPoint);
494128277c9SStephan Aßmus 			break;
495128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
496128277c9SStephan Aßmus 			_RemovePointOut(fCurrentPathPoint);
497128277c9SStephan Aßmus 			break;
498128277c9SStephan Aßmus 
499f4bd80a2SStephan Aßmus 		case SELECT_POINTS: {
500f4bd80a2SStephan Aßmus 			// TODO: this works so that you can deselect all points
501f4bd80a2SStephan Aßmus 			// when clicking outside the path even if pressing shift
502f4bd80a2SStephan Aßmus 			// in case the path is open... a better way would be
503f4bd80a2SStephan Aßmus 			// to deselect all on mouse up, if the mouse has not moved
504f4bd80a2SStephan Aßmus 			bool appendSelection;
505f4bd80a2SStephan Aßmus 			if (fPath->IsClosed())
506f4bd80a2SStephan Aßmus 				appendSelection = fShiftDown;
507f4bd80a2SStephan Aßmus 			else
508f4bd80a2SStephan Aßmus 				appendSelection = fShiftDown && fCurrentPathPoint >= 0;
509f4bd80a2SStephan Aßmus 
510f4bd80a2SStephan Aßmus 			if (!appendSelection) {
511128277c9SStephan Aßmus 				fSelection->MakeEmpty();
512128277c9SStephan Aßmus 				_UpdateSelection();
513128277c9SStephan Aßmus 			}
514128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
515128277c9SStephan Aßmus 			if (fCurrentPathPoint >= 0) {
516f4bd80a2SStephan Aßmus 				_Select(fCurrentPathPoint, appendSelection);
517128277c9SStephan Aßmus 			}
518128277c9SStephan Aßmus 			fCanvasView->BeginRectTracking(BRect(where, where),
519128277c9SStephan Aßmus 										   B_TRACK_RECT_CORNER);
520128277c9SStephan Aßmus 			break;
521128277c9SStephan Aßmus 		}
522f4bd80a2SStephan Aßmus 	}
523128277c9SStephan Aßmus 
524128277c9SStephan Aßmus 	fTrackingStart = canvasWhere;
525128277c9SStephan Aßmus 	// remember the subpixel position
526128277c9SStephan Aßmus 	// so that MouseMoved() will work even before
527128277c9SStephan Aßmus 	// the integer position becomes different
528128277c9SStephan Aßmus 	fLastCanvasPos = where;
529f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&fLastCanvasPos);
530128277c9SStephan Aßmus 
531128277c9SStephan Aßmus 	// the reason to exclude the select mode
532128277c9SStephan Aßmus 	// is that the BView rect tracking does not
533128277c9SStephan Aßmus 	// scroll the rect starting point along with us
534128277c9SStephan Aßmus 	// (since we're doing no real scrolling)
535128277c9SStephan Aßmus //	if (fMode != SELECT_POINTS)
536128277c9SStephan Aßmus //		fCanvasView->SetAutoScrolling(true);
537128277c9SStephan Aßmus 
538128277c9SStephan Aßmus 	UpdateCursor();
539128277c9SStephan Aßmus 
540128277c9SStephan Aßmus 	return true;
541128277c9SStephan Aßmus }
542128277c9SStephan Aßmus 
543128277c9SStephan Aßmus // MouseMoved
544128277c9SStephan Aßmus void
545128277c9SStephan Aßmus PathManipulator::MouseMoved(BPoint where)
546128277c9SStephan Aßmus {
547*4fac07a0SStephan Aßmus 	fCanvasView->FilterMouse(&where);
548*4fac07a0SStephan Aßmus 		// NOTE: only filter mouse coords in mouse moved, no other
549*4fac07a0SStephan Aßmus 		// mouse function
550128277c9SStephan Aßmus 	BPoint canvasWhere = where;
551f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
552128277c9SStephan Aßmus 
553128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
554128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
555128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
556128277c9SStephan Aßmus 	if (fLastCanvasPos == canvasWhere)
557128277c9SStephan Aßmus 		return;
558128277c9SStephan Aßmus 
559128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
560128277c9SStephan Aßmus 
5610e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
5620e1ba39fSStephan Aßmus 		if (fTransformBox) {
5630e1ba39fSStephan Aßmus 			fTransformBox->MouseMoved(where);
5640e1ba39fSStephan Aßmus 		}
5650e1ba39fSStephan Aßmus 		return;
5660e1ba39fSStephan Aßmus 	}
5670e1ba39fSStephan Aßmus 
568128277c9SStephan Aßmus 	if (fMode == CLOSE_PATH) {
569128277c9SStephan Aßmus 		// continue by moving the point
570128277c9SStephan Aßmus 		_SetMode(MOVE_POINT);
571128277c9SStephan Aßmus 		delete fChangePointCommand;
572128277c9SStephan Aßmus 		fChangePointCommand = new ChangePointCommand(fPath,
573128277c9SStephan Aßmus 													 fCurrentPathPoint,
574128277c9SStephan Aßmus 													 fSelection->Items(),
575128277c9SStephan Aßmus 													 fSelection->CountItems());
576128277c9SStephan Aßmus 	}
577128277c9SStephan Aßmus 
578128277c9SStephan Aßmus //	if (!fPrecise) {
579128277c9SStephan Aßmus //		float offset = fmod(fOutlineWidth, 2.0) / 2.0;
580128277c9SStephan Aßmus //		canvasWhere.point += BPoint(offset, offset);
581128277c9SStephan Aßmus //	}
582128277c9SStephan Aßmus 
583128277c9SStephan Aßmus 	switch (fMode) {
584128277c9SStephan Aßmus 		case ADD_POINT:
585128277c9SStephan Aßmus 		case INSERT_POINT:
586128277c9SStephan Aßmus 		case TOGGLE_SHARP:
587128277c9SStephan Aßmus 			// drag the "out" control point, mirror the "in" control point
588128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere, true);
589128277c9SStephan Aßmus 			break;
590128277c9SStephan Aßmus 		case MOVE_POINT:
591128277c9SStephan Aßmus 			// drag all three control points at once
592128277c9SStephan Aßmus 			fPath->SetPoint(fCurrentPathPoint, canvasWhere);
593128277c9SStephan Aßmus 			break;
594128277c9SStephan Aßmus 		case MOVE_POINT_IN:
595128277c9SStephan Aßmus 			// drag in control point
596128277c9SStephan Aßmus 			fPath->SetPointIn(fCurrentPathPoint, canvasWhere);
597128277c9SStephan Aßmus 			break;
598128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
599128277c9SStephan Aßmus 			// drag out control point
600128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere);
601128277c9SStephan Aßmus 			break;
602128277c9SStephan Aßmus 
603128277c9SStephan Aßmus 		case SELECT_POINTS: {
604128277c9SStephan Aßmus 			// change the selection
605128277c9SStephan Aßmus 			BRect r;
606128277c9SStephan Aßmus 			r.left = min_c(fTrackingStart.x, canvasWhere.x);
607128277c9SStephan Aßmus 			r.top = min_c(fTrackingStart.y, canvasWhere.y);
608128277c9SStephan Aßmus 			r.right = max_c(fTrackingStart.x, canvasWhere.x);
609128277c9SStephan Aßmus 			r.bottom = max_c(fTrackingStart.y, canvasWhere.y);
610128277c9SStephan Aßmus 			_Select(r);
611128277c9SStephan Aßmus 			break;
612128277c9SStephan Aßmus 		}
613128277c9SStephan Aßmus 
614128277c9SStephan Aßmus 		case TRANSLATE_POINTS: {
615128277c9SStephan Aßmus 			BPoint offset = canvasWhere - fTrackingStart;
616128277c9SStephan Aßmus 			_Nudge(offset);
617128277c9SStephan Aßmus 			fTrackingStart = canvasWhere;
618128277c9SStephan Aßmus 			break;
619128277c9SStephan Aßmus 		}
620128277c9SStephan Aßmus 	}
621128277c9SStephan Aßmus }
622128277c9SStephan Aßmus 
623128277c9SStephan Aßmus // MouseUp
624128277c9SStephan Aßmus Command*
625128277c9SStephan Aßmus PathManipulator::MouseUp()
626128277c9SStephan Aßmus {
627128277c9SStephan Aßmus 	// prevent carrying out actions more than once by only
628128277c9SStephan Aßmus 	// doing it if "fMouseDown" is true at the point of
629128277c9SStephan Aßmus 	// entering this function
630128277c9SStephan Aßmus 	if (!fMouseDown)
631128277c9SStephan Aßmus 		return NULL;
632128277c9SStephan Aßmus 	fMouseDown = false;
633128277c9SStephan Aßmus 
6340e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
6350e1ba39fSStephan Aßmus 		if (fTransformBox) {
6360e1ba39fSStephan Aßmus 			return fTransformBox->MouseUp();
6370e1ba39fSStephan Aßmus 		}
6380e1ba39fSStephan Aßmus 		return NULL;
6390e1ba39fSStephan Aßmus 	}
6400e1ba39fSStephan Aßmus 
641128277c9SStephan Aßmus 	Command* command = NULL;
642128277c9SStephan Aßmus 
643128277c9SStephan Aßmus 	switch (fMode) {
644128277c9SStephan Aßmus 
645128277c9SStephan Aßmus 		case ADD_POINT:
646128277c9SStephan Aßmus 			command = fAddPointCommand;
647128277c9SStephan Aßmus 			fAddPointCommand = NULL;
648128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
649128277c9SStephan Aßmus 			break;
650128277c9SStephan Aßmus 
651128277c9SStephan Aßmus 		case INSERT_POINT:
652128277c9SStephan Aßmus 			command = fInsertPointCommand;
653128277c9SStephan Aßmus 			fInsertPointCommand = NULL;
654128277c9SStephan Aßmus 			break;
655128277c9SStephan Aßmus 
656128277c9SStephan Aßmus 		case SELECT_POINTS:
657128277c9SStephan Aßmus 			if (*fSelection != *fOldSelection) {
658128277c9SStephan Aßmus //				command = new SelectPointsCommand(this, fPath,
659128277c9SStephan Aßmus //												  fOldSelection->Items(),
660128277c9SStephan Aßmus //												  fOldSelection->CountItems(),
661128277c9SStephan Aßmus //												  fSelection->Items(),
662128277c9SStephan Aßmus //												  fSelection->CountItems()));
663128277c9SStephan Aßmus 			}
664128277c9SStephan Aßmus 			fCanvasView->EndRectTracking();
665128277c9SStephan Aßmus 			break;
666128277c9SStephan Aßmus 
667128277c9SStephan Aßmus 		case TOGGLE_SHARP:
668128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
669128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
670128277c9SStephan Aßmus 		case MOVE_POINT:
671128277c9SStephan Aßmus 		case MOVE_POINT_IN:
672128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
673128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
674128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
675128277c9SStephan Aßmus 			command = fChangePointCommand;
676128277c9SStephan Aßmus 			fChangePointCommand = NULL;
677128277c9SStephan Aßmus 			break;
678128277c9SStephan Aßmus 
679128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
6800e1ba39fSStephan Aßmus 			if (!fNudgeCommand) {
681128277c9SStephan Aßmus 				// select just the point that was clicked
682128277c9SStephan Aßmus 				*fOldSelection = *fSelection;
683128277c9SStephan Aßmus 				if (fCurrentPathPoint >= 0) {
684128277c9SStephan Aßmus 					_Select(fCurrentPathPoint, fShiftDown);
685128277c9SStephan Aßmus 				}
686128277c9SStephan Aßmus 				if (*fSelection != *fOldSelection) {
687128277c9SStephan Aßmus //					command = new SelectPointsCommand(this, fPath,
688128277c9SStephan Aßmus //													  fOldSelection->Items(),
689128277c9SStephan Aßmus //													  fOldSelection->CountItems(),
690128277c9SStephan Aßmus //													  fSelection->Items(),
691128277c9SStephan Aßmus //													  fSelection->CountItems()));
692128277c9SStephan Aßmus 				}
6930e1ba39fSStephan Aßmus 			} else {
6940e1ba39fSStephan Aßmus 				command = _FinishNudging();
6950e1ba39fSStephan Aßmus 			}
696128277c9SStephan Aßmus 			break;
697128277c9SStephan Aßmus 	}
698128277c9SStephan Aßmus 
699128277c9SStephan Aßmus 	return command;
700128277c9SStephan Aßmus }
701128277c9SStephan Aßmus 
702128277c9SStephan Aßmus // MouseOver
703128277c9SStephan Aßmus bool
704128277c9SStephan Aßmus PathManipulator::MouseOver(BPoint where)
705128277c9SStephan Aßmus {
7060e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
7070e1ba39fSStephan Aßmus 		if (fTransformBox) {
7080e1ba39fSStephan Aßmus 			return fTransformBox->MouseOver(where);
7090e1ba39fSStephan Aßmus 		}
7100e1ba39fSStephan Aßmus 		return false;
7110e1ba39fSStephan Aßmus 	}
7120e1ba39fSStephan Aßmus 
713128277c9SStephan Aßmus 	BPoint canvasWhere = where;
714f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
715128277c9SStephan Aßmus 
716128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
717128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
718128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
719128277c9SStephan Aßmus 	if (fMouseDown && fLastCanvasPos == canvasWhere)
720128277c9SStephan Aßmus 		return false;
721128277c9SStephan Aßmus 
722128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
723128277c9SStephan Aßmus 
724128277c9SStephan Aßmus 	// hit testing
725128277c9SStephan Aßmus 	// (use a subpixel mouse pos)
726f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&where);
727128277c9SStephan Aßmus 	_SetModeForMousePos(where);
728128277c9SStephan Aßmus 
729128277c9SStephan Aßmus 	// TODO: always true?
730128277c9SStephan Aßmus 	return true;
731128277c9SStephan Aßmus }
732128277c9SStephan Aßmus 
733128277c9SStephan Aßmus // DoubleClicked
734128277c9SStephan Aßmus bool
735128277c9SStephan Aßmus PathManipulator::DoubleClicked(BPoint where)
736128277c9SStephan Aßmus {
737128277c9SStephan Aßmus 	return false;
738128277c9SStephan Aßmus }
739128277c9SStephan Aßmus 
740f4bd80a2SStephan Aßmus // ShowContextMenu
741f4bd80a2SStephan Aßmus bool
742f4bd80a2SStephan Aßmus PathManipulator::ShowContextMenu(BPoint where)
743f4bd80a2SStephan Aßmus {
744f4bd80a2SStephan Aßmus 	BPopUpMenu* menu = new BPopUpMenu("context menu", false, false);
745f4bd80a2SStephan Aßmus 	BMessage* message;
746f4bd80a2SStephan Aßmus 	BMenuItem* item;
747f4bd80a2SStephan Aßmus 
748f4bd80a2SStephan Aßmus 	bool hasSelection = fSelection->CountItems() > 0;
749f4bd80a2SStephan Aßmus 
750f4bd80a2SStephan Aßmus 	message = new BMessage(B_SELECT_ALL);
751f4bd80a2SStephan Aßmus 	item = new BMenuItem("Select All", message, 'A');
752f4bd80a2SStephan Aßmus 	menu->AddItem(item);
753f4bd80a2SStephan Aßmus 
754f4bd80a2SStephan Aßmus 	menu->AddSeparatorItem();
755f4bd80a2SStephan Aßmus 
756f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_TRANSFORM);
757f4bd80a2SStephan Aßmus 	item = new BMenuItem("Transform", message);
758f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
759f4bd80a2SStephan Aßmus 	menu->AddItem(item);
760f4bd80a2SStephan Aßmus 
761f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_SPLIT_POINTS);
762f4bd80a2SStephan Aßmus 	item = new BMenuItem("Split", message);
763f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
764f4bd80a2SStephan Aßmus 	menu->AddItem(item);
765f4bd80a2SStephan Aßmus 
766f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_REMOVE_POINTS);
767f4bd80a2SStephan Aßmus 	item = new BMenuItem("Remove", message, 'A');
768f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
769f4bd80a2SStephan Aßmus 	menu->AddItem(item);
770f4bd80a2SStephan Aßmus 
771f4bd80a2SStephan Aßmus 	// go
772f4bd80a2SStephan Aßmus 	menu->SetTargetForItems(fCanvasView);
773f4bd80a2SStephan Aßmus 	menu->SetAsyncAutoDestruct(true);
774f4bd80a2SStephan Aßmus 	menu->SetFont(be_plain_font);
775f4bd80a2SStephan Aßmus 	where = fCanvasView->ConvertToScreen(where);
776f4bd80a2SStephan Aßmus 	BRect mouseRect(where, where);
777f4bd80a2SStephan Aßmus 	mouseRect.InsetBy(-10.0, -10.0);
778f4bd80a2SStephan Aßmus 	where += BPoint(5.0, 5.0);
779f4bd80a2SStephan Aßmus 	menu->Go(where, true, false, mouseRect, true);
780f4bd80a2SStephan Aßmus 
781f4bd80a2SStephan Aßmus 	return true;
782f4bd80a2SStephan Aßmus }
783f4bd80a2SStephan Aßmus 
784f4bd80a2SStephan Aßmus // #pragma mark -
785f4bd80a2SStephan Aßmus 
786128277c9SStephan Aßmus // Bounds
787128277c9SStephan Aßmus BRect
788128277c9SStephan Aßmus PathManipulator::Bounds()
789128277c9SStephan Aßmus {
790f67876a0SStephan Aßmus 	BRect r = _ControlPointRect();
791f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&r);
792f67876a0SStephan Aßmus 	return r;
793128277c9SStephan Aßmus }
794128277c9SStephan Aßmus 
795128277c9SStephan Aßmus // TrackingBounds
796128277c9SStephan Aßmus BRect
797128277c9SStephan Aßmus PathManipulator::TrackingBounds(BView* withinView)
798128277c9SStephan Aßmus {
799128277c9SStephan Aßmus 	return withinView->Bounds();
800128277c9SStephan Aßmus }
801128277c9SStephan Aßmus 
802128277c9SStephan Aßmus // #pragma mark -
803128277c9SStephan Aßmus 
804128277c9SStephan Aßmus // MessageReceived
805128277c9SStephan Aßmus bool
806128277c9SStephan Aßmus PathManipulator::MessageReceived(BMessage* message, Command** _command)
807128277c9SStephan Aßmus {
808128277c9SStephan Aßmus 	bool result = true;
809128277c9SStephan Aßmus 	switch (message->what) {
810f4bd80a2SStephan Aßmus 		case MSG_TRANSFORM:
811128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
812128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
813128277c9SStephan Aßmus 			break;
814f4bd80a2SStephan Aßmus 		case MSG_REMOVE_POINTS:
815128277c9SStephan Aßmus 			*_command = _Delete();
816128277c9SStephan Aßmus 			break;
817f4bd80a2SStephan Aßmus 		case MSG_SPLIT_POINTS:
818f4bd80a2SStephan Aßmus 			*_command = new SplitPointsCommand(fPath,
819f4bd80a2SStephan Aßmus 											   fSelection->Items(),
820f4bd80a2SStephan Aßmus 											   fSelection->CountItems());
821f4bd80a2SStephan Aßmus 			break;
822128277c9SStephan Aßmus 		case B_SELECT_ALL: {
823128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
824128277c9SStephan Aßmus 			fSelection->MakeEmpty();
825128277c9SStephan Aßmus 			int32 count = fPath->CountPoints();
826128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++)
827128277c9SStephan Aßmus 				fSelection->Add(i);
828128277c9SStephan Aßmus 			if (*fOldSelection != *fSelection) {
829128277c9SStephan Aßmus //				*_command = new SelectPointsCommand(this, fPath,
830128277c9SStephan Aßmus //												   fOldSelection->Items(),
831128277c9SStephan Aßmus //												   fOldSelection->CountItems(),
832128277c9SStephan Aßmus //												   fSelection->Items(),
833128277c9SStephan Aßmus //												   fSelection->CountItems()));
834128277c9SStephan Aßmus 				count = fSelection->CountItems();
835128277c9SStephan Aßmus 				int32 indices[count];
836128277c9SStephan Aßmus 				memcpy(indices, fSelection->Items(), count * sizeof(int32));
837128277c9SStephan Aßmus 				_Select(indices, count);
838128277c9SStephan Aßmus 			}
839128277c9SStephan Aßmus 			break;
840128277c9SStephan Aßmus 		}
841128277c9SStephan Aßmus 		default:
842128277c9SStephan Aßmus 			result = false;
843128277c9SStephan Aßmus 			break;
844128277c9SStephan Aßmus 	}
845128277c9SStephan Aßmus 	return result;
846128277c9SStephan Aßmus }
847128277c9SStephan Aßmus 
848128277c9SStephan Aßmus 
849128277c9SStephan Aßmus // ModifiersChanged
850128277c9SStephan Aßmus void
851128277c9SStephan Aßmus PathManipulator::ModifiersChanged(uint32 modifiers)
852128277c9SStephan Aßmus {
853128277c9SStephan Aßmus 	fCommandDown = modifiers & B_COMMAND_KEY;
854128277c9SStephan Aßmus 	fOptionDown = modifiers & B_CONTROL_KEY;
855128277c9SStephan Aßmus 	fShiftDown = modifiers & B_SHIFT_KEY;
856128277c9SStephan Aßmus 	fAltDown = modifiers & B_OPTION_KEY;
857128277c9SStephan Aßmus 
8580e1ba39fSStephan Aßmus 	if (fTransformBox) {
8590e1ba39fSStephan Aßmus 		fTransformBox->ModifiersChanged(modifiers);
8600e1ba39fSStephan Aßmus 		return;
8610e1ba39fSStephan Aßmus 	}
862128277c9SStephan Aßmus 	// reevaluate mode
863128277c9SStephan Aßmus 	if (!fMouseDown)
864128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
865128277c9SStephan Aßmus }
866128277c9SStephan Aßmus 
867128277c9SStephan Aßmus // HandleKeyDown
868128277c9SStephan Aßmus bool
869128277c9SStephan Aßmus PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
870128277c9SStephan Aßmus {
871128277c9SStephan Aßmus 	bool result = true;
872128277c9SStephan Aßmus 
873128277c9SStephan Aßmus 	float nudgeDist = 1.0;
874f67876a0SStephan Aßmus 	if (modifiers & B_SHIFT_KEY)
875f67876a0SStephan Aßmus 		nudgeDist /= fCanvasView->ZoomLevel();
876128277c9SStephan Aßmus 
877128277c9SStephan Aßmus 	switch (key) {
878128277c9SStephan Aßmus 		// commit
879128277c9SStephan Aßmus 		case B_RETURN:
8800e1ba39fSStephan Aßmus 			if (fTransformBox) {
881*4fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
882*4fac07a0SStephan Aßmus 			}// else
883128277c9SStephan Aßmus //				_Perform();
884128277c9SStephan Aßmus 			break;
885128277c9SStephan Aßmus 		// cancel
886128277c9SStephan Aßmus 		case B_ESCAPE:
8870e1ba39fSStephan Aßmus 			if (fTransformBox) {
8880e1ba39fSStephan Aßmus 				fTransformBox->Cancel();
889*4fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
8900e1ba39fSStephan Aßmus 			} else if (fFallBackMode == NEW_PATH) {
891128277c9SStephan Aßmus 				fFallBackMode = SELECT_POINTS;
892*4fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
893*4fac07a0SStephan Aßmus 			}// else
894128277c9SStephan Aßmus //				_Cancel();
895128277c9SStephan Aßmus 			break;
896128277c9SStephan Aßmus 		case 't':
897128277c9SStephan Aßmus 		case 'T':
898128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
899128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
900128277c9SStephan Aßmus 			else
901128277c9SStephan Aßmus 				result = false;
902128277c9SStephan Aßmus 			break;
903128277c9SStephan Aßmus 		// nudging
904128277c9SStephan Aßmus 		case B_UP_ARROW:
905128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, -nudgeDist));
906128277c9SStephan Aßmus 			break;
907128277c9SStephan Aßmus 		case B_DOWN_ARROW:
908128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, nudgeDist));
909128277c9SStephan Aßmus 			break;
910128277c9SStephan Aßmus 		case B_LEFT_ARROW:
911128277c9SStephan Aßmus 			_Nudge(BPoint(-nudgeDist, 0.0));
912128277c9SStephan Aßmus 			break;
913128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
914128277c9SStephan Aßmus 			_Nudge(BPoint(nudgeDist, 0.0));
915128277c9SStephan Aßmus 			break;
916128277c9SStephan Aßmus 
917128277c9SStephan Aßmus 		case B_DELETE:
918128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
919128277c9SStephan Aßmus 				*_command = _Delete();
920128277c9SStephan Aßmus 			else
921128277c9SStephan Aßmus 				result = false;
922128277c9SStephan Aßmus 			break;
923128277c9SStephan Aßmus 
924128277c9SStephan Aßmus 		default:
925128277c9SStephan Aßmus 			result = false;
926128277c9SStephan Aßmus 	}
927128277c9SStephan Aßmus 	return result;
928128277c9SStephan Aßmus }
929128277c9SStephan Aßmus 
930128277c9SStephan Aßmus // HandleKeyUp
931128277c9SStephan Aßmus bool
932128277c9SStephan Aßmus PathManipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
933128277c9SStephan Aßmus {
934128277c9SStephan Aßmus 	bool handled = true;
935128277c9SStephan Aßmus 	switch (key) {
936128277c9SStephan Aßmus 		// nudging
937128277c9SStephan Aßmus 		case B_UP_ARROW:
938128277c9SStephan Aßmus 		case B_DOWN_ARROW:
939128277c9SStephan Aßmus 		case B_LEFT_ARROW:
940128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
9410e1ba39fSStephan Aßmus 			*_command = _FinishNudging();
942128277c9SStephan Aßmus 			break;
943128277c9SStephan Aßmus 		default:
944128277c9SStephan Aßmus 			handled = false;
945128277c9SStephan Aßmus 			break;
946128277c9SStephan Aßmus 	}
947128277c9SStephan Aßmus 	return handled;
948128277c9SStephan Aßmus }
949128277c9SStephan Aßmus 
950128277c9SStephan Aßmus // UpdateCursor
951128277c9SStephan Aßmus void
952128277c9SStephan Aßmus PathManipulator::UpdateCursor()
953128277c9SStephan Aßmus {
9540e1ba39fSStephan Aßmus 	if (fTransformBox) {
9550e1ba39fSStephan Aßmus 		fTransformBox->UpdateCursor();
9560e1ba39fSStephan Aßmus 		return;
9570e1ba39fSStephan Aßmus 	}
958128277c9SStephan Aßmus 	const uchar* cursorData;
959128277c9SStephan Aßmus 	switch (fMode) {
960128277c9SStephan Aßmus 		case ADD_POINT:
961128277c9SStephan Aßmus 			cursorData = kPathAddCursor;
962128277c9SStephan Aßmus 			break;
963128277c9SStephan Aßmus 		case INSERT_POINT:
964128277c9SStephan Aßmus 			cursorData = kPathInsertCursor;
965128277c9SStephan Aßmus 			break;
966128277c9SStephan Aßmus 		case MOVE_POINT:
967128277c9SStephan Aßmus 		case MOVE_POINT_IN:
968128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
969128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
970128277c9SStephan Aßmus 			cursorData = kPathMoveCursor;
971128277c9SStephan Aßmus 			break;
972128277c9SStephan Aßmus 		case CLOSE_PATH:
973128277c9SStephan Aßmus 			cursorData = kPathCloseCursor;
974128277c9SStephan Aßmus 			break;
975128277c9SStephan Aßmus 		case TOGGLE_SHARP:
976128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
977128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
978128277c9SStephan Aßmus 			cursorData = kPathSharpCursor;
979128277c9SStephan Aßmus 			break;
980128277c9SStephan Aßmus 		case REMOVE_POINT:
981128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
982128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
983128277c9SStephan Aßmus 			cursorData = kPathRemoveCursor;
984128277c9SStephan Aßmus 			break;
985128277c9SStephan Aßmus 		case SELECT_POINTS:
986128277c9SStephan Aßmus 			cursorData = kPathSelectCursor;
987128277c9SStephan Aßmus 			break;
988128277c9SStephan Aßmus 
989128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
990128277c9SStephan Aßmus 			cursorData = B_HAND_CURSOR;
991128277c9SStephan Aßmus 			break;
992128277c9SStephan Aßmus 
993128277c9SStephan Aßmus 		case UNDEFINED:
994128277c9SStephan Aßmus 		default:
995128277c9SStephan Aßmus 			cursorData = kStopCursor;
996128277c9SStephan Aßmus 			break;
997128277c9SStephan Aßmus 	}
998128277c9SStephan Aßmus 	BCursor cursor(cursorData);
999128277c9SStephan Aßmus 	fCanvasView->SetViewCursor(&cursor, true);
1000128277c9SStephan Aßmus 	fCanvasView->Sync();
1001128277c9SStephan Aßmus }
1002128277c9SStephan Aßmus 
1003128277c9SStephan Aßmus // AttachedToView
1004128277c9SStephan Aßmus void
1005128277c9SStephan Aßmus PathManipulator::AttachedToView(BView* view)
1006128277c9SStephan Aßmus {
1007f67876a0SStephan Aßmus 	fCanvasView = dynamic_cast<CanvasView*>(view);
1008128277c9SStephan Aßmus }
1009128277c9SStephan Aßmus 
1010128277c9SStephan Aßmus // DetachedFromView
1011128277c9SStephan Aßmus void
1012128277c9SStephan Aßmus PathManipulator::DetachedFromView(BView* view)
1013128277c9SStephan Aßmus {
1014128277c9SStephan Aßmus 	fCanvasView = NULL;
1015128277c9SStephan Aßmus }
1016128277c9SStephan Aßmus 
1017128277c9SStephan Aßmus // #pragma mark -
1018128277c9SStephan Aßmus 
1019128277c9SStephan Aßmus // ObjectChanged
1020128277c9SStephan Aßmus void
1021128277c9SStephan Aßmus PathManipulator::ObjectChanged(const Observable* object)
1022128277c9SStephan Aßmus {
1023128277c9SStephan Aßmus 	// TODO: refine VectorPath listener interface and
1024128277c9SStephan Aßmus 	// implement more efficiently
1025128277c9SStephan Aßmus 	BRect currentBounds = _ControlPointRect();
1026128277c9SStephan Aßmus 	_InvalidateCanvas(currentBounds | fPreviousBounds);
1027128277c9SStephan Aßmus 	fPreviousBounds = currentBounds;
1028128277c9SStephan Aßmus 
1029128277c9SStephan Aßmus 	// reevaluate mode
10300e1ba39fSStephan Aßmus 	if (!fMouseDown && !fTransformBox)
1031128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1032128277c9SStephan Aßmus }
1033128277c9SStephan Aßmus 
1034128277c9SStephan Aßmus // #pragma mark -
1035128277c9SStephan Aßmus 
103605fd3818SStephan Aßmus // PointAdded
103705fd3818SStephan Aßmus void
103805fd3818SStephan Aßmus PathManipulator::PointAdded(int32 index)
103905fd3818SStephan Aßmus {
104005fd3818SStephan Aßmus 	ObjectChanged(fPath);
104105fd3818SStephan Aßmus }
104205fd3818SStephan Aßmus 
104305fd3818SStephan Aßmus // PointRemoved
104405fd3818SStephan Aßmus void
104505fd3818SStephan Aßmus PathManipulator::PointRemoved(int32 index)
104605fd3818SStephan Aßmus {
10470e1ba39fSStephan Aßmus 	fSelection->Remove(index);
104805fd3818SStephan Aßmus 	ObjectChanged(fPath);
104905fd3818SStephan Aßmus }
105005fd3818SStephan Aßmus 
105105fd3818SStephan Aßmus // PointChanged
105205fd3818SStephan Aßmus void
105305fd3818SStephan Aßmus PathManipulator::PointChanged(int32 index)
105405fd3818SStephan Aßmus {
105505fd3818SStephan Aßmus 	ObjectChanged(fPath);
105605fd3818SStephan Aßmus }
105705fd3818SStephan Aßmus 
105805fd3818SStephan Aßmus // PathChanged
105905fd3818SStephan Aßmus void
106005fd3818SStephan Aßmus PathManipulator::PathChanged()
106105fd3818SStephan Aßmus {
106205fd3818SStephan Aßmus 	ObjectChanged(fPath);
106305fd3818SStephan Aßmus }
106405fd3818SStephan Aßmus 
106505fd3818SStephan Aßmus // PathClosedChanged
106605fd3818SStephan Aßmus void
106705fd3818SStephan Aßmus PathManipulator::PathClosedChanged()
106805fd3818SStephan Aßmus {
106905fd3818SStephan Aßmus 	ObjectChanged(fPath);
107005fd3818SStephan Aßmus }
107105fd3818SStephan Aßmus 
107205fd3818SStephan Aßmus // PathReversed
107305fd3818SStephan Aßmus void
107405fd3818SStephan Aßmus PathManipulator::PathReversed()
107505fd3818SStephan Aßmus {
10760e1ba39fSStephan Aßmus 	// reverse selection along with path
10770e1ba39fSStephan Aßmus 	int32 count = fSelection->CountItems();
10780e1ba39fSStephan Aßmus 	int32 pointCount = fPath->CountPoints();
10790e1ba39fSStephan Aßmus 	if (count > 0) {
10800e1ba39fSStephan Aßmus 		Selection temp;
10810e1ba39fSStephan Aßmus 		for (int32 i = 0; i < count; i++) {
10820e1ba39fSStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
10830e1ba39fSStephan Aßmus 		}
10840e1ba39fSStephan Aßmus 		*fSelection = temp;
10850e1ba39fSStephan Aßmus 	}
10860e1ba39fSStephan Aßmus 
108705fd3818SStephan Aßmus 	ObjectChanged(fPath);
108805fd3818SStephan Aßmus }
108905fd3818SStephan Aßmus 
109005fd3818SStephan Aßmus // #pragma mark -
109105fd3818SStephan Aßmus 
1092128277c9SStephan Aßmus // ControlFlags
1093128277c9SStephan Aßmus uint32
1094128277c9SStephan Aßmus PathManipulator::ControlFlags() const
1095128277c9SStephan Aßmus {
1096128277c9SStephan Aßmus 	uint32 flags = 0;
1097128277c9SStephan Aßmus 
1098128277c9SStephan Aßmus //	flags |= SHAPE_UI_FLAGS_CAN_REVERSE_PATH;
1099128277c9SStephan Aßmus //
1100128277c9SStephan Aßmus //	if (!fSelection->IsEmpty())
1101128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_HAS_SELECTION;
1102128277c9SStephan Aßmus //	if (fPath->CountPoints() > 1)
1103128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_CAN_CLOSE_PATH;
1104128277c9SStephan Aßmus //	if (fPath->IsClosed())
1105128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_PATH_IS_CLOSED;
11060e1ba39fSStephan Aßmus //	if (fTransformBox)
11070e1ba39fSStephan Aßmus //		flags |= SHAPE_UI_FLAGS_IS_TRANSFORMING;
1108128277c9SStephan Aßmus 
1109128277c9SStephan Aßmus 	return flags;
1110128277c9SStephan Aßmus }
1111128277c9SStephan Aßmus 
1112128277c9SStephan Aßmus // ReversePath
1113128277c9SStephan Aßmus void
1114128277c9SStephan Aßmus PathManipulator::ReversePath()
1115128277c9SStephan Aßmus {
1116128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1117128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1118128277c9SStephan Aßmus 	if (count > 0) {
1119128277c9SStephan Aßmus 		Selection temp;
1120128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1121128277c9SStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
1122128277c9SStephan Aßmus 		}
1123128277c9SStephan Aßmus 		*fSelection = temp;
1124128277c9SStephan Aßmus 	}
1125128277c9SStephan Aßmus 	fPath->Reverse();
1126128277c9SStephan Aßmus }
1127128277c9SStephan Aßmus 
1128128277c9SStephan Aßmus // #pragma mark -
1129128277c9SStephan Aßmus 
1130128277c9SStephan Aßmus // _SetMode
1131128277c9SStephan Aßmus void
1132128277c9SStephan Aßmus PathManipulator::_SetMode(uint32 mode)
1133128277c9SStephan Aßmus {
1134128277c9SStephan Aßmus 	if (fMode != mode) {
1135128277c9SStephan Aßmus //printf("switching mode: %s -> %s\n", string_for_mode(fMode), string_for_mode(mode));
1136128277c9SStephan Aßmus 		fMode = mode;
1137128277c9SStephan Aßmus 
11380e1ba39fSStephan Aßmus 		if (fMode == TRANSFORM_POINTS) {
11390e1ba39fSStephan Aßmus 			_SetTransformBox(new TransformPointsBox(fCanvasView,
11400e1ba39fSStephan Aßmus 													this,
11410e1ba39fSStephan Aßmus 													fPath,
11420e1ba39fSStephan Aßmus 													fSelection->Items(),
11430e1ba39fSStephan Aßmus 													fSelection->CountItems()));
11440e1ba39fSStephan Aßmus //			fCanvasView->Perform(new EnterTransformPointsCommand(this,
11450e1ba39fSStephan Aßmus //														  fSelection->Items(),
11460e1ba39fSStephan Aßmus //														  fSelection->CountItems()));
11470e1ba39fSStephan Aßmus 		} else {
11480e1ba39fSStephan Aßmus 			if (fTransformBox)
11490e1ba39fSStephan Aßmus 				_SetTransformBox(NULL);
11500e1ba39fSStephan Aßmus 		}
11510e1ba39fSStephan Aßmus 
1152128277c9SStephan Aßmus 		if (BWindow* window = fCanvasView->Window()) {
1153128277c9SStephan Aßmus 			window->PostMessage(MSG_UPDATE_SHAPE_UI);
1154128277c9SStephan Aßmus 		}
1155128277c9SStephan Aßmus 		UpdateCursor();
1156128277c9SStephan Aßmus 	}
1157128277c9SStephan Aßmus }
1158128277c9SStephan Aßmus 
11590e1ba39fSStephan Aßmus 
11600e1ba39fSStephan Aßmus // _SetTransformBox
11610e1ba39fSStephan Aßmus void
11620e1ba39fSStephan Aßmus PathManipulator::_SetTransformBox(TransformPointsBox* transformBox)
11630e1ba39fSStephan Aßmus {
11640e1ba39fSStephan Aßmus 	if (fTransformBox == transformBox)
11650e1ba39fSStephan Aßmus 		return;
11660e1ba39fSStephan Aßmus 
11670e1ba39fSStephan Aßmus 	BRect dirty(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN);
11680e1ba39fSStephan Aßmus 	if (fTransformBox) {
11690e1ba39fSStephan Aßmus 		// get rid of transform box display
11700e1ba39fSStephan Aßmus 		dirty = fTransformBox->Bounds();
11710e1ba39fSStephan Aßmus 		delete fTransformBox;
11720e1ba39fSStephan Aßmus 	}
11730e1ba39fSStephan Aßmus 
11740e1ba39fSStephan Aßmus 	fTransformBox = transformBox;
11750e1ba39fSStephan Aßmus 
1176*4fac07a0SStephan Aßmus 	// TODO: this is weird, fMode should only be set in _SetMode, not
1177*4fac07a0SStephan Aßmus 	// here as well, also this method could be called this way
1178*4fac07a0SStephan Aßmus 	// _SetModeForMousePos -> _SetMode -> _SetTransformBox
1179*4fac07a0SStephan Aßmus 	// and then below it does _SetModeForMousePos again...
11800e1ba39fSStephan Aßmus 	if (fTransformBox) {
11810e1ba39fSStephan Aßmus 		fTransformBox->MouseMoved(fLastCanvasPos);
11820e1ba39fSStephan Aßmus 		if (fMode != TRANSFORM_POINTS) {
11830e1ba39fSStephan Aßmus 			fMode = TRANSFORM_POINTS;
11840e1ba39fSStephan Aßmus 		}
11850e1ba39fSStephan Aßmus 		dirty = dirty | fTransformBox->Bounds();
11860e1ba39fSStephan Aßmus 	} else {
11870e1ba39fSStephan Aßmus 		if (fMode == TRANSFORM_POINTS) {
11880e1ba39fSStephan Aßmus 			_SetModeForMousePos(fLastCanvasPos);
11890e1ba39fSStephan Aßmus 		}
11900e1ba39fSStephan Aßmus 	}
11910e1ba39fSStephan Aßmus 
11920e1ba39fSStephan Aßmus 	if (dirty.IsValid()) {
11930e1ba39fSStephan Aßmus 		dirty.InsetBy(-8, -8);
11940e1ba39fSStephan Aßmus 		fCanvasView->Invalidate(dirty);
11950e1ba39fSStephan Aßmus 	}
11960e1ba39fSStephan Aßmus }
11970e1ba39fSStephan Aßmus 
1198128277c9SStephan Aßmus // _AddPoint
1199128277c9SStephan Aßmus void
1200128277c9SStephan Aßmus PathManipulator::_AddPoint(BPoint where)
1201128277c9SStephan Aßmus {
1202128277c9SStephan Aßmus 	if (fPath->AddPoint(where)) {
1203128277c9SStephan Aßmus 		fCurrentPathPoint = fPath->CountPoints() - 1;
1204128277c9SStephan Aßmus 
1205128277c9SStephan Aßmus 		delete fAddPointCommand;
1206128277c9SStephan Aßmus 		fAddPointCommand = new AddPointCommand(fPath, fCurrentPathPoint,
1207128277c9SStephan Aßmus 											   fSelection->Items(),
1208128277c9SStephan Aßmus 											   fSelection->CountItems());
1209128277c9SStephan Aßmus 
1210128277c9SStephan Aßmus 		_Select(fCurrentPathPoint, fShiftDown);
1211128277c9SStephan Aßmus 	}
1212128277c9SStephan Aßmus }
1213128277c9SStephan Aßmus 
1214128277c9SStephan Aßmus // scale_point
1215128277c9SStephan Aßmus BPoint
1216128277c9SStephan Aßmus scale_point(BPoint a, BPoint b, float scale)
1217128277c9SStephan Aßmus {
1218128277c9SStephan Aßmus 	return BPoint(a.x + (b.x - a.x) * scale,
1219128277c9SStephan Aßmus 				  a.y + (b.y - a.y) * scale);
1220128277c9SStephan Aßmus }
1221128277c9SStephan Aßmus 
1222128277c9SStephan Aßmus // _InsertPoint
1223128277c9SStephan Aßmus void
1224128277c9SStephan Aßmus PathManipulator::_InsertPoint(BPoint where, int32 index)
1225128277c9SStephan Aßmus {
1226128277c9SStephan Aßmus 	double scale;
1227128277c9SStephan Aßmus 
1228128277c9SStephan Aßmus 	BPoint point;
1229128277c9SStephan Aßmus 	BPoint pointIn;
1230128277c9SStephan Aßmus 	BPoint pointOut;
1231128277c9SStephan Aßmus 
1232128277c9SStephan Aßmus 	BPoint previous;
1233128277c9SStephan Aßmus 	BPoint previousOut;
1234128277c9SStephan Aßmus 	BPoint next;
1235128277c9SStephan Aßmus 	BPoint nextIn;
1236128277c9SStephan Aßmus 
1237128277c9SStephan Aßmus 	if (fPath->FindBezierScale(index - 1, where, &scale)
1238128277c9SStephan Aßmus 		&& scale >= 0.0 && scale <= 1.0
1239128277c9SStephan Aßmus 		&& fPath->GetPoint(index - 1, scale, point)) {
1240128277c9SStephan Aßmus 
1241128277c9SStephan Aßmus 		fPath->GetPointAt(index - 1, previous);
1242128277c9SStephan Aßmus 		fPath->GetPointOutAt(index - 1, previousOut);
1243128277c9SStephan Aßmus 		fPath->GetPointAt(index, next);
1244128277c9SStephan Aßmus 		fPath->GetPointInAt(index, nextIn);
1245128277c9SStephan Aßmus 
1246128277c9SStephan Aßmus 		where = scale_point(previousOut, nextIn, scale);
1247128277c9SStephan Aßmus 
1248128277c9SStephan Aßmus 		previousOut = scale_point(previous, previousOut, scale);
1249128277c9SStephan Aßmus 		nextIn = scale_point(next, nextIn, 1 - scale);
1250128277c9SStephan Aßmus 		pointIn = scale_point(previousOut, where, scale);
1251128277c9SStephan Aßmus 		pointOut = scale_point(nextIn, where, 1 - scale);
1252128277c9SStephan Aßmus 
1253128277c9SStephan Aßmus 		if (fPath->AddPoint(point, index)) {
1254128277c9SStephan Aßmus 
1255128277c9SStephan Aßmus 			fPath->SetPointIn(index, pointIn);
1256128277c9SStephan Aßmus 			fPath->SetPointOut(index, pointOut);
1257128277c9SStephan Aßmus 
1258128277c9SStephan Aßmus 			delete fInsertPointCommand;
1259128277c9SStephan Aßmus 			fInsertPointCommand = new InsertPointCommand(fPath, index,
1260128277c9SStephan Aßmus 														 fSelection->Items(),
1261128277c9SStephan Aßmus 														 fSelection->CountItems());
1262128277c9SStephan Aßmus 
1263128277c9SStephan Aßmus 			fPath->SetPointOut(index - 1, previousOut);
1264128277c9SStephan Aßmus 			fPath->SetPointIn(index + 1, nextIn);
1265128277c9SStephan Aßmus 
1266128277c9SStephan Aßmus 			fCurrentPathPoint = index;
1267128277c9SStephan Aßmus 			_ShiftSelection(fCurrentPathPoint, 1);
1268128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
1269128277c9SStephan Aßmus 		}
1270128277c9SStephan Aßmus 	}
1271128277c9SStephan Aßmus }
1272128277c9SStephan Aßmus 
1273128277c9SStephan Aßmus // _SetInOutConnected
1274128277c9SStephan Aßmus void
1275128277c9SStephan Aßmus PathManipulator::_SetInOutConnected(int32 index, bool connected)
1276128277c9SStephan Aßmus {
1277128277c9SStephan Aßmus 	fPath->SetInOutConnected(index, connected);
1278128277c9SStephan Aßmus }
1279128277c9SStephan Aßmus 
1280128277c9SStephan Aßmus // _SetSharp
1281128277c9SStephan Aßmus void
1282128277c9SStephan Aßmus PathManipulator::_SetSharp(int32 index)
1283128277c9SStephan Aßmus {
1284128277c9SStephan Aßmus 	BPoint p;
1285128277c9SStephan Aßmus 	fPath->GetPointAt(index, p);
1286128277c9SStephan Aßmus 	fPath->SetPoint(index, p, p, p, true);
1287128277c9SStephan Aßmus }
1288128277c9SStephan Aßmus 
1289128277c9SStephan Aßmus // _RemoveSelection
1290128277c9SStephan Aßmus void
1291128277c9SStephan Aßmus PathManipulator::_RemoveSelection()
1292128277c9SStephan Aßmus {
12930e1ba39fSStephan Aßmus 	// NOTE: copy selection since removing points will
12940e1ba39fSStephan Aßmus 	// trigger notifications, and that will influence the
12950e1ba39fSStephan Aßmus 	// selection
12960e1ba39fSStephan Aßmus 	Selection selection = *fSelection;
12970e1ba39fSStephan Aßmus 	int32 count = selection.CountItems();
1298128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
12990e1ba39fSStephan Aßmus 		if (!fPath->RemovePoint(selection.IndexAt(i) - i))
1300128277c9SStephan Aßmus 			break;
1301128277c9SStephan Aßmus 	}
1302128277c9SStephan Aßmus 
13030e1ba39fSStephan Aßmus 	fPath->SetClosed(fPath->IsClosed() && fPath->CountPoints() > 1);
1304128277c9SStephan Aßmus 
1305128277c9SStephan Aßmus 	fSelection->MakeEmpty();
1306128277c9SStephan Aßmus }
1307128277c9SStephan Aßmus 
1308128277c9SStephan Aßmus 
1309128277c9SStephan Aßmus // _RemovePoint
1310128277c9SStephan Aßmus void
1311128277c9SStephan Aßmus PathManipulator::_RemovePoint(int32 index)
1312128277c9SStephan Aßmus {
1313128277c9SStephan Aßmus 	if (fPath->RemovePoint(index)) {
1314128277c9SStephan Aßmus 		_Deselect(index);
1315128277c9SStephan Aßmus 		_ShiftSelection(index + 1, -1);
1316128277c9SStephan Aßmus 	}
1317128277c9SStephan Aßmus }
1318128277c9SStephan Aßmus 
1319128277c9SStephan Aßmus // _RemovePointIn
1320128277c9SStephan Aßmus void
1321128277c9SStephan Aßmus PathManipulator::_RemovePointIn(int32 index)
1322128277c9SStephan Aßmus {
1323128277c9SStephan Aßmus 	BPoint p;
1324128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1325128277c9SStephan Aßmus 		fPath->SetPointIn(index, p);
1326128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1327128277c9SStephan Aßmus 	}
1328128277c9SStephan Aßmus }
1329128277c9SStephan Aßmus 
1330128277c9SStephan Aßmus // _RemovePointOut
1331128277c9SStephan Aßmus void
1332128277c9SStephan Aßmus PathManipulator::_RemovePointOut(int32 index)
1333128277c9SStephan Aßmus {
1334128277c9SStephan Aßmus 	BPoint p;
1335128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1336128277c9SStephan Aßmus 		fPath->SetPointOut(index, p);
1337128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1338128277c9SStephan Aßmus 	}
1339128277c9SStephan Aßmus }
1340128277c9SStephan Aßmus 
1341128277c9SStephan Aßmus // _Delete
1342128277c9SStephan Aßmus Command*
1343128277c9SStephan Aßmus PathManipulator::_Delete()
1344128277c9SStephan Aßmus {
1345128277c9SStephan Aßmus 	Command* command = NULL;
1346128277c9SStephan Aßmus 	if (!fMouseDown) {
13470e1ba39fSStephan Aßmus 		// make sure we apply an on-going transformation before we proceed
13480e1ba39fSStephan Aßmus 		if (fTransformBox) {
13490e1ba39fSStephan Aßmus 			_SetTransformBox(NULL);
13500e1ba39fSStephan Aßmus 		}
13510e1ba39fSStephan Aßmus 
1352128277c9SStephan Aßmus 		if (fSelection->CountItems() == fPath->CountPoints()) {
1353128277c9SStephan Aßmus //			command = new RemovePathCommand(fPath);
1354128277c9SStephan Aßmus 		} else {
1355128277c9SStephan Aßmus 			command = new RemovePointsCommand(fPath,
1356128277c9SStephan Aßmus 											  fSelection->Items(),
1357128277c9SStephan Aßmus 											  fSelection->CountItems());
1358128277c9SStephan Aßmus 			_RemoveSelection();
1359128277c9SStephan Aßmus 		}
1360128277c9SStephan Aßmus 
1361128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1362128277c9SStephan Aßmus 	}
1363128277c9SStephan Aßmus 
1364128277c9SStephan Aßmus 	return command;
1365128277c9SStephan Aßmus }
1366128277c9SStephan Aßmus 
1367128277c9SStephan Aßmus // #pragma mark -
1368128277c9SStephan Aßmus 
1369128277c9SStephan Aßmus // _Select
1370128277c9SStephan Aßmus void
1371128277c9SStephan Aßmus PathManipulator::_Select(BRect r)
1372128277c9SStephan Aßmus {
1373128277c9SStephan Aßmus 	BPoint p;
1374f4bd80a2SStephan Aßmus 	BPoint pIn;
1375f4bd80a2SStephan Aßmus 	BPoint pOut;
1376128277c9SStephan Aßmus 	int32 count = fPath->CountPoints();
1377128277c9SStephan Aßmus 	Selection temp;
1378f4bd80a2SStephan Aßmus 	for (int32 i = 0; i < count && fPath->GetPointsAt(i, p, pIn, pOut); i++) {
1379f4bd80a2SStephan Aßmus 		if (r.Contains(p) || r.Contains(pIn) || r.Contains(pOut)) {
1380128277c9SStephan Aßmus 			temp.Add(i);
1381128277c9SStephan Aßmus 		}
1382128277c9SStephan Aßmus 	}
1383128277c9SStephan Aßmus 	// merge old and new selection
1384128277c9SStephan Aßmus 	count = fOldSelection->CountItems();
1385128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
1386128277c9SStephan Aßmus 		int32 index = fOldSelection->IndexAt(i);
1387128277c9SStephan Aßmus 		if (temp.Contains(index))
1388128277c9SStephan Aßmus 			temp.Remove(index);
1389128277c9SStephan Aßmus 		else
1390128277c9SStephan Aßmus 			temp.Add(index);
1391128277c9SStephan Aßmus 	}
1392128277c9SStephan Aßmus 	if (temp != *fSelection) {
1393128277c9SStephan Aßmus 		*fSelection = temp;
1394128277c9SStephan Aßmus 		_UpdateSelection();
1395128277c9SStephan Aßmus 	}
1396128277c9SStephan Aßmus }
1397128277c9SStephan Aßmus 
1398128277c9SStephan Aßmus // _Select
1399128277c9SStephan Aßmus void
1400128277c9SStephan Aßmus PathManipulator::_Select(int32 index, bool extend)
1401128277c9SStephan Aßmus {
1402128277c9SStephan Aßmus 	if (!extend)
1403128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1404128277c9SStephan Aßmus 	if (fSelection->Contains(index))
1405128277c9SStephan Aßmus 		fSelection->Remove(index);
1406128277c9SStephan Aßmus 	else
1407128277c9SStephan Aßmus 		fSelection->Add(index);
1408128277c9SStephan Aßmus 	// TODO: this can lead to unnecessary invalidation (maybe need to investigate)
1409128277c9SStephan Aßmus 	_UpdateSelection();
1410128277c9SStephan Aßmus }
1411128277c9SStephan Aßmus 
1412128277c9SStephan Aßmus // _Select
1413128277c9SStephan Aßmus void
1414128277c9SStephan Aßmus PathManipulator::_Select(const int32* indices, int32 count, bool extend)
1415128277c9SStephan Aßmus {
1416128277c9SStephan Aßmus 	if (extend) {
1417128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1418128277c9SStephan Aßmus 			if (!fSelection->Contains(indices[i]))
1419128277c9SStephan Aßmus 				fSelection->Add(indices[i]);
1420128277c9SStephan Aßmus 		}
1421128277c9SStephan Aßmus 	} else {
1422128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1423128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1424128277c9SStephan Aßmus 			fSelection->Add(indices[i]);
1425128277c9SStephan Aßmus 		}
1426128277c9SStephan Aßmus 	}
1427128277c9SStephan Aßmus 	_UpdateSelection();
1428128277c9SStephan Aßmus }
1429128277c9SStephan Aßmus 
1430128277c9SStephan Aßmus // _Deselect
1431128277c9SStephan Aßmus void
1432128277c9SStephan Aßmus PathManipulator::_Deselect(int32 index)
1433128277c9SStephan Aßmus {
1434128277c9SStephan Aßmus 	if (fSelection->Contains(index)) {
1435128277c9SStephan Aßmus 		fSelection->Remove(index);
1436128277c9SStephan Aßmus 		_UpdateSelection();
1437128277c9SStephan Aßmus 	}
1438128277c9SStephan Aßmus }
1439128277c9SStephan Aßmus 
1440128277c9SStephan Aßmus // _ShiftSelection
1441128277c9SStephan Aßmus void
1442128277c9SStephan Aßmus PathManipulator::_ShiftSelection(int32 startIndex, int32 direction)
1443128277c9SStephan Aßmus {
1444128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1445128277c9SStephan Aßmus 	if (count > 0) {
1446128277c9SStephan Aßmus 		int32* selection = fSelection->Items();
1447128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1448128277c9SStephan Aßmus 			if (selection[i] >= startIndex) {
1449128277c9SStephan Aßmus 				selection[i] += direction;
1450128277c9SStephan Aßmus 			}
1451128277c9SStephan Aßmus 		}
1452128277c9SStephan Aßmus 	}
1453128277c9SStephan Aßmus 	_UpdateSelection();
1454128277c9SStephan Aßmus }
1455128277c9SStephan Aßmus 
1456128277c9SStephan Aßmus // _IsSelected
1457128277c9SStephan Aßmus bool
1458128277c9SStephan Aßmus PathManipulator::_IsSelected(int32 index) const
1459128277c9SStephan Aßmus {
1460128277c9SStephan Aßmus 	return fSelection->Contains(index);
1461128277c9SStephan Aßmus }
1462128277c9SStephan Aßmus 
1463128277c9SStephan Aßmus // #pragma mark -
1464128277c9SStephan Aßmus 
1465128277c9SStephan Aßmus // _InvalidateCanvas
1466128277c9SStephan Aßmus void
1467128277c9SStephan Aßmus PathManipulator::_InvalidateCanvas(BRect rect) const
1468128277c9SStephan Aßmus {
1469f67876a0SStephan Aßmus 	// convert from canvas to view space
1470f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&rect);
1471128277c9SStephan Aßmus 	fCanvasView->Invalidate(rect);
1472128277c9SStephan Aßmus }
1473128277c9SStephan Aßmus 
1474128277c9SStephan Aßmus // _InvalidateHighlightPoints
1475128277c9SStephan Aßmus void
1476128277c9SStephan Aßmus PathManipulator::_InvalidateHighlightPoints(int32 newIndex, uint32 newMode)
1477128277c9SStephan Aßmus {
1478128277c9SStephan Aßmus 	BRect oldRect = _ControlPointRect(fCurrentPathPoint, fMode);
1479128277c9SStephan Aßmus 	BRect newRect = _ControlPointRect(newIndex, newMode);
1480128277c9SStephan Aßmus 	if (oldRect.IsValid())
1481128277c9SStephan Aßmus 		_InvalidateCanvas(oldRect);
1482128277c9SStephan Aßmus 	if (newRect.IsValid())
1483128277c9SStephan Aßmus 		_InvalidateCanvas(newRect);
1484128277c9SStephan Aßmus }
1485128277c9SStephan Aßmus 
1486128277c9SStephan Aßmus // _UpdateSelection
1487128277c9SStephan Aßmus void
1488128277c9SStephan Aßmus PathManipulator::_UpdateSelection() const
1489128277c9SStephan Aßmus {
1490128277c9SStephan Aßmus 	_InvalidateCanvas(_ControlPointRect());
1491128277c9SStephan Aßmus 	if (BWindow* window = fCanvasView->Window()) {
1492128277c9SStephan Aßmus 		window->PostMessage(MSG_UPDATE_SHAPE_UI);
1493128277c9SStephan Aßmus 	}
1494128277c9SStephan Aßmus }
1495128277c9SStephan Aßmus 
1496128277c9SStephan Aßmus // _ControlPointRect
1497128277c9SStephan Aßmus BRect
1498128277c9SStephan Aßmus PathManipulator::_ControlPointRect() const
1499128277c9SStephan Aßmus {
1500128277c9SStephan Aßmus 	BRect r = fPath->ControlPointBounds();
1501128277c9SStephan Aßmus 	r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1502128277c9SStephan Aßmus 	return r;
1503128277c9SStephan Aßmus }
1504128277c9SStephan Aßmus 
1505128277c9SStephan Aßmus // _ControlPointRect
1506128277c9SStephan Aßmus BRect
1507128277c9SStephan Aßmus PathManipulator::_ControlPointRect(int32 index, uint32 mode) const
1508128277c9SStephan Aßmus {
1509128277c9SStephan Aßmus 	BRect rect(0.0, 0.0, -1.0, -1.0);
1510128277c9SStephan Aßmus 	if (index >= 0) {
1511128277c9SStephan Aßmus 		BPoint p, pIn, pOut;
1512128277c9SStephan Aßmus 		fPath->GetPointsAt(index, p, pIn, pOut);
1513128277c9SStephan Aßmus 		switch (mode) {
1514128277c9SStephan Aßmus 			case MOVE_POINT:
1515128277c9SStephan Aßmus 			case TOGGLE_SHARP:
1516128277c9SStephan Aßmus 			case REMOVE_POINT:
1517128277c9SStephan Aßmus 			case CLOSE_PATH:
1518128277c9SStephan Aßmus 				rect.Set(p.x, p.y, p.x, p.y);
1519128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1520128277c9SStephan Aßmus 				break;
1521128277c9SStephan Aßmus 			case MOVE_POINT_IN:
1522128277c9SStephan Aßmus 			case TOGGLE_SHARP_IN:
1523128277c9SStephan Aßmus 			case REMOVE_POINT_IN:
1524128277c9SStephan Aßmus 				rect.Set(pIn.x, pIn.y, pIn.x, pIn.y);
1525128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1526128277c9SStephan Aßmus 				break;
1527128277c9SStephan Aßmus 			case MOVE_POINT_OUT:
1528128277c9SStephan Aßmus 			case TOGGLE_SHARP_OUT:
1529128277c9SStephan Aßmus 			case REMOVE_POINT_OUT:
1530128277c9SStephan Aßmus 				rect.Set(pOut.x, pOut.y, pOut.x, pOut.y);
1531128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1532128277c9SStephan Aßmus 				break;
1533128277c9SStephan Aßmus 			case SELECT_POINTS:
1534128277c9SStephan Aßmus 				rect.Set(min4(p.x, pIn.x, pOut.x, pOut.x),
1535128277c9SStephan Aßmus 						 min4(p.y, pIn.y, pOut.y, pOut.y),
1536128277c9SStephan Aßmus 						 max4(p.x, pIn.x, pOut.x, pOut.x),
1537128277c9SStephan Aßmus 						 max4(p.y, pIn.y, pOut.y, pOut.y));
1538128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1539128277c9SStephan Aßmus 				break;
1540128277c9SStephan Aßmus 		}
1541128277c9SStephan Aßmus 	}
1542128277c9SStephan Aßmus 	return rect;
1543128277c9SStephan Aßmus }
1544128277c9SStephan Aßmus 
1545128277c9SStephan Aßmus // #pragma mark -
1546128277c9SStephan Aßmus 
1547128277c9SStephan Aßmus // _SetModeForMousePos
1548128277c9SStephan Aßmus void
1549128277c9SStephan Aßmus PathManipulator::_SetModeForMousePos(BPoint where)
1550128277c9SStephan Aßmus {
1551128277c9SStephan Aßmus 	uint32 mode = UNDEFINED;
1552128277c9SStephan Aßmus 	int32 index = -1;
1553128277c9SStephan Aßmus 
1554f67876a0SStephan Aßmus 	float zoomLevel = fCanvasView->ZoomLevel();
1555128277c9SStephan Aßmus 
1556128277c9SStephan Aßmus 	// see if we're close enough at a control point
1557128277c9SStephan Aßmus 	BPoint point;
1558128277c9SStephan Aßmus 	BPoint pointIn;
1559128277c9SStephan Aßmus 	BPoint pointOut;
1560128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut)
1561128277c9SStephan Aßmus 					  && mode == UNDEFINED; i++) {
1562128277c9SStephan Aßmus 
1563128277c9SStephan Aßmus 		float distM = point_point_distance(point, where) * zoomLevel;
1564128277c9SStephan Aßmus 		float distIn = point_point_distance(pointIn, where) * zoomLevel;
1565128277c9SStephan Aßmus 		float distOut = point_point_distance(pointOut, where) * zoomLevel;
1566128277c9SStephan Aßmus 
1567128277c9SStephan Aßmus 		if (distM < MOVE_THRESHOLD) {
1568128277c9SStephan Aßmus 			if (i == 0 && fClickToClose
1569128277c9SStephan Aßmus 				&& !fPath->IsClosed() && fPath->CountPoints() > 1) {
1570128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1571128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : CLOSE_PATH);
1572128277c9SStephan Aßmus 				index = i;
1573128277c9SStephan Aßmus 			} else {
1574128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1575128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : MOVE_POINT);
1576128277c9SStephan Aßmus 				index = i;
1577128277c9SStephan Aßmus 			}
1578128277c9SStephan Aßmus 		}
15790e1ba39fSStephan Aßmus 		if (distM - distIn > 0.00001
15800e1ba39fSStephan Aßmus 			&& distIn < MOVE_THRESHOLD) {
1581128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_IN :
1582128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_IN : MOVE_POINT_IN);
1583128277c9SStephan Aßmus 			index = i;
1584128277c9SStephan Aßmus 		}
15850e1ba39fSStephan Aßmus 		if (distIn - distOut > 0.00001
15860e1ba39fSStephan Aßmus 			&& distOut < distM && distOut < MOVE_THRESHOLD) {
1587128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_OUT :
1588128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_OUT : MOVE_POINT_OUT);
1589128277c9SStephan Aßmus 			index = i;
1590128277c9SStephan Aßmus 		}
1591128277c9SStephan Aßmus 	}
1592128277c9SStephan Aßmus 	// selection mode overrides any other mode,
1593128277c9SStephan Aßmus 	// but we need to check for it after we know
1594128277c9SStephan Aßmus 	// the index of the point under the mouse (code above)
1595128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1596128277c9SStephan Aßmus 	if (fShiftDown && pointCount > 0) {
1597128277c9SStephan Aßmus 		mode = SELECT_POINTS;
1598128277c9SStephan Aßmus 	}
1599128277c9SStephan Aßmus 
1600128277c9SStephan Aßmus 	// see if user wants to start new sub path
1601128277c9SStephan Aßmus 	if (fAltDown) {
1602128277c9SStephan Aßmus 		mode = NEW_PATH;
1603128277c9SStephan Aßmus 		index = -1;
1604128277c9SStephan Aßmus 	}
1605128277c9SStephan Aßmus 
1606128277c9SStephan Aßmus 	// see if we're close enough at a line
1607128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1608128277c9SStephan Aßmus 		float distance;
1609128277c9SStephan Aßmus 		if (fPath->GetDistance(where, &distance, &index)) {
1610128277c9SStephan Aßmus 			if (distance < (INSERT_DIST_THRESHOLD / zoomLevel)) {
1611128277c9SStephan Aßmus 				mode = INSERT_POINT;
1612128277c9SStephan Aßmus 			}
1613128277c9SStephan Aßmus 		} else {
1614128277c9SStephan Aßmus 			// restore index, since it was changed by call above
1615128277c9SStephan Aßmus 			index = fCurrentPathPoint;
1616128277c9SStephan Aßmus 		}
1617128277c9SStephan Aßmus 	}
1618128277c9SStephan Aßmus 
1619128277c9SStephan Aßmus 	// nope, still undefined mode, last fall back
1620128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1621128277c9SStephan Aßmus 		if (fFallBackMode == SELECT_POINTS) {
1622128277c9SStephan Aßmus 			if (fPath->IsClosed() && pointCount > 0) {
1623128277c9SStephan Aßmus 				mode = SELECT_POINTS;
1624128277c9SStephan Aßmus 				index = -1;
1625128277c9SStephan Aßmus 			} else {
1626128277c9SStephan Aßmus 				mode = ADD_POINT;
1627128277c9SStephan Aßmus 				index = pointCount - 1;
1628128277c9SStephan Aßmus 			}
1629128277c9SStephan Aßmus 		} else {
1630128277c9SStephan Aßmus 			// user had clicked "New Path" icon
1631128277c9SStephan Aßmus 			mode = fFallBackMode;
1632128277c9SStephan Aßmus 		}
1633128277c9SStephan Aßmus 	}
1634128277c9SStephan Aßmus 	// switch mode if necessary
1635128277c9SStephan Aßmus 	if (mode != fMode || index != fCurrentPathPoint) {
1636128277c9SStephan Aßmus 		// invalidate path display (to highlight the respective point)
1637128277c9SStephan Aßmus 		_InvalidateHighlightPoints(index, mode);
1638128277c9SStephan Aßmus 		_SetMode(mode);
1639128277c9SStephan Aßmus 		fCurrentPathPoint = index;
1640128277c9SStephan Aßmus 	}
1641128277c9SStephan Aßmus }
1642128277c9SStephan Aßmus 
1643128277c9SStephan Aßmus // #pragma mark -
1644128277c9SStephan Aßmus 
1645128277c9SStephan Aßmus // _Nudge
1646128277c9SStephan Aßmus void
1647128277c9SStephan Aßmus PathManipulator::_Nudge(BPoint direction)
1648128277c9SStephan Aßmus {
1649128277c9SStephan Aßmus 	bigtime_t now = system_time();
1650128277c9SStephan Aßmus 	if (now - fLastNudgeTime > 500000) {
16510e1ba39fSStephan Aßmus 		fCanvasView->Perform(_FinishNudging());
1652128277c9SStephan Aßmus 	}
1653128277c9SStephan Aßmus 	fLastNudgeTime = now;
1654128277c9SStephan Aßmus 	fNudgeOffset += direction;
1655128277c9SStephan Aßmus 
16560e1ba39fSStephan Aßmus 	if (fTransformBox) {
16570e1ba39fSStephan Aßmus 		fTransformBox->NudgeBy(direction);
16580e1ba39fSStephan Aßmus 		return;
16590e1ba39fSStephan Aßmus 	}
16600e1ba39fSStephan Aßmus 
16610e1ba39fSStephan Aßmus 	if (!fNudgeCommand) {
16620e1ba39fSStephan Aßmus 
16630e1ba39fSStephan Aßmus 		bool fromSelection = !fSelection->IsEmpty();
16640e1ba39fSStephan Aßmus 
16650e1ba39fSStephan Aßmus 		int32 count = fromSelection ? fSelection->CountItems()
16660e1ba39fSStephan Aßmus 									: fPath->CountPoints();
16670e1ba39fSStephan Aßmus 		int32 indices[count];
16680e1ba39fSStephan Aßmus 		control_point points[count];
16690e1ba39fSStephan Aßmus 
16700e1ba39fSStephan Aßmus 		// init indices and points
16710e1ba39fSStephan Aßmus 		for (int32 i = 0; i < count; i++) {
16720e1ba39fSStephan Aßmus 			indices[i] = fromSelection ? fSelection->IndexAt(i) : i;
16730e1ba39fSStephan Aßmus 			fPath->GetPointsAt(indices[i],
16740e1ba39fSStephan Aßmus 							   points[i].point,
16750e1ba39fSStephan Aßmus 							   points[i].point_in,
16760e1ba39fSStephan Aßmus 							   points[i].point_out,
16770e1ba39fSStephan Aßmus 							   &points[i].connected);
16780e1ba39fSStephan Aßmus 		}
16790e1ba39fSStephan Aßmus 
16800e1ba39fSStephan Aßmus 		fNudgeCommand = new NudgePointsCommand(fPath, indices, points, count);
16810e1ba39fSStephan Aßmus 
16820e1ba39fSStephan Aßmus 		fNudgeCommand->SetNewTranslation(fNudgeOffset);
16830e1ba39fSStephan Aßmus 		fNudgeCommand->Redo();
16840e1ba39fSStephan Aßmus 
16850e1ba39fSStephan Aßmus 	} else {
16860e1ba39fSStephan Aßmus 		fNudgeCommand->SetNewTranslation(fNudgeOffset);
16870e1ba39fSStephan Aßmus 		fNudgeCommand->Redo();
16880e1ba39fSStephan Aßmus 	}
1689128277c9SStephan Aßmus 
1690128277c9SStephan Aßmus 	if (!fMouseDown)
1691128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1692128277c9SStephan Aßmus }
1693128277c9SStephan Aßmus 
1694128277c9SStephan Aßmus // _FinishNudging
16950e1ba39fSStephan Aßmus Command*
1696128277c9SStephan Aßmus PathManipulator::_FinishNudging()
1697128277c9SStephan Aßmus {
1698128277c9SStephan Aßmus 	fNudgeOffset = BPoint(0.0, 0.0);
1699128277c9SStephan Aßmus 
17000e1ba39fSStephan Aßmus 	Command* command;
17010e1ba39fSStephan Aßmus 
17020e1ba39fSStephan Aßmus 	if (fTransformBox) {
17030e1ba39fSStephan Aßmus 		command = fTransformBox->FinishNudging();
17040e1ba39fSStephan Aßmus 	} else {
17050e1ba39fSStephan Aßmus 		command = fNudgeCommand;
17060e1ba39fSStephan Aßmus 		fNudgeCommand = NULL;
17070e1ba39fSStephan Aßmus 	}
17080e1ba39fSStephan Aßmus 
17090e1ba39fSStephan Aßmus 	return command;
1710128277c9SStephan Aßmus }
1711128277c9SStephan Aßmus 
1712128277c9SStephan Aßmus 
1713