xref: /haiku/src/apps/icon-o-matic/shape/PathManipulator.cpp (revision a9cee0f1c4f5e3031132cc6f79bcaffdb9a72e08)
1128277c9SStephan Aßmus /*
2*a9cee0f1SStephan Aßmus  * Copyright 2006-2009, Stephan Aßmus <superstippi@gmx.de>.
3*a9cee0f1SStephan Aßmus  * All rights reserved. Distributed under the terms of the MIT License.
4128277c9SStephan Aßmus  */
5128277c9SStephan Aßmus 
6128277c9SStephan Aßmus #include "PathManipulator.h"
7128277c9SStephan Aßmus 
8128277c9SStephan Aßmus #include <float.h>
9128277c9SStephan Aßmus #include <stdio.h>
10128277c9SStephan Aßmus 
11128277c9SStephan Aßmus #include <Cursor.h>
12128277c9SStephan Aßmus #include <Message.h>
13f4bd80a2SStephan Aßmus #include <MenuItem.h>
14f4bd80a2SStephan Aßmus #include <PopUpMenu.h>
15128277c9SStephan Aßmus #include <Window.h>
16128277c9SStephan Aßmus 
17128277c9SStephan Aßmus #include "cursors.h"
18128277c9SStephan Aßmus #include "support.h"
19128277c9SStephan Aßmus 
20f67876a0SStephan Aßmus #include "CanvasView.h"
21128277c9SStephan Aßmus 
22128277c9SStephan Aßmus #include "AddPointCommand.h"
23128277c9SStephan Aßmus #include "ChangePointCommand.h"
24128277c9SStephan Aßmus //#include "CloseCommand.h"
25128277c9SStephan Aßmus #include "InsertPointCommand.h"
267c4b3726SStephan Aßmus #include "FlipPointsCommand.h"
27128277c9SStephan Aßmus //#include "NewPathCommand.h"
280e1ba39fSStephan Aßmus #include "NudgePointsCommand.h"
29128277c9SStephan Aßmus //#include "RemovePathCommand.h"
30128277c9SStephan Aßmus #include "RemovePointsCommand.h"
31128277c9SStephan Aßmus //#include "ReversePathCommand.h"
32128277c9SStephan Aßmus //#include "SelectPathCommand.h"
33128277c9SStephan Aßmus //#include "SelectPointsCommand.h"
34f4bd80a2SStephan Aßmus #include "SplitPointsCommand.h"
350e1ba39fSStephan Aßmus #include "TransformPointsBox.h"
36128277c9SStephan Aßmus 
37128277c9SStephan Aßmus #define POINT_EXTEND 3.0
38128277c9SStephan Aßmus #define CONTROL_POINT_EXTEND 2.0
39128277c9SStephan Aßmus #define INSERT_DIST_THRESHOLD 7.0
40128277c9SStephan Aßmus #define MOVE_THRESHOLD 9.0
41128277c9SStephan Aßmus 
42128277c9SStephan Aßmus enum {
43128277c9SStephan Aßmus 	UNDEFINED,
44128277c9SStephan Aßmus 
45128277c9SStephan Aßmus 	NEW_PATH,
46128277c9SStephan Aßmus 
47128277c9SStephan Aßmus 	ADD_POINT,
48128277c9SStephan Aßmus 	INSERT_POINT,
49128277c9SStephan Aßmus 	MOVE_POINT,
50128277c9SStephan Aßmus 	MOVE_POINT_IN,
51128277c9SStephan Aßmus 	MOVE_POINT_OUT,
52128277c9SStephan Aßmus 	CLOSE_PATH,
53128277c9SStephan Aßmus 
54128277c9SStephan Aßmus 	TOGGLE_SHARP,
55128277c9SStephan Aßmus 	TOGGLE_SHARP_IN,
56128277c9SStephan Aßmus 	TOGGLE_SHARP_OUT,
57128277c9SStephan Aßmus 
58128277c9SStephan Aßmus 	REMOVE_POINT,
59128277c9SStephan Aßmus 	REMOVE_POINT_IN,
60128277c9SStephan Aßmus 	REMOVE_POINT_OUT,
61128277c9SStephan Aßmus 
62128277c9SStephan Aßmus 	SELECT_POINTS,
63128277c9SStephan Aßmus 	TRANSFORM_POINTS,
64128277c9SStephan Aßmus 	TRANSLATE_POINTS,
65128277c9SStephan Aßmus 
66128277c9SStephan Aßmus 	SELECT_SUB_PATH,
67128277c9SStephan Aßmus };
68128277c9SStephan Aßmus 
69f4bd80a2SStephan Aßmus enum {
70f4bd80a2SStephan Aßmus 	MSG_TRANSFORM				= 'strn',
71f4bd80a2SStephan Aßmus 	MSG_REMOVE_POINTS			= 'srmp',
72f4bd80a2SStephan Aßmus 	MSG_UPDATE_SHAPE_UI			= 'udsi',
73f4bd80a2SStephan Aßmus 
74f4bd80a2SStephan Aßmus 	MSG_SPLIT_POINTS			= 'splt',
757c4b3726SStephan Aßmus 	MSG_FLIP_POINTS				= 'flip',
76f4bd80a2SStephan Aßmus };
77f4bd80a2SStephan Aßmus 
78128277c9SStephan Aßmus inline const char*
79128277c9SStephan Aßmus string_for_mode(uint32 mode)
80128277c9SStephan Aßmus {
81128277c9SStephan Aßmus 	switch (mode) {
82128277c9SStephan Aßmus 		case UNDEFINED:
83128277c9SStephan Aßmus 			return "UNDEFINED";
84128277c9SStephan Aßmus 		case NEW_PATH:
85128277c9SStephan Aßmus 			return "NEW_PATH";
86128277c9SStephan Aßmus 		case ADD_POINT:
87128277c9SStephan Aßmus 			return "ADD_POINT";
88128277c9SStephan Aßmus 		case INSERT_POINT:
89128277c9SStephan Aßmus 			return "INSERT_POINT";
90128277c9SStephan Aßmus 		case MOVE_POINT:
91128277c9SStephan Aßmus 			return "MOVE_POINT";
92128277c9SStephan Aßmus 		case MOVE_POINT_IN:
93128277c9SStephan Aßmus 			return "MOVE_POINT_IN";
94128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
95128277c9SStephan Aßmus 			return "MOVE_POINT_OUT";
96128277c9SStephan Aßmus 		case CLOSE_PATH:
97128277c9SStephan Aßmus 			return "CLOSE_PATH";
98128277c9SStephan Aßmus 		case TOGGLE_SHARP:
99128277c9SStephan Aßmus 			return "TOGGLE_SHARP";
100128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
101128277c9SStephan Aßmus 			return "TOGGLE_SHARP_IN";
102128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
103128277c9SStephan Aßmus 			return "TOGGLE_SHARP_OUT";
104128277c9SStephan Aßmus 		case REMOVE_POINT:
105128277c9SStephan Aßmus 			return "REMOVE_POINT";
106128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
107128277c9SStephan Aßmus 			return "REMOVE_POINT_IN";
108128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
109128277c9SStephan Aßmus 			return "REMOVE_POINT_OUT";
110128277c9SStephan Aßmus 		case SELECT_POINTS:
111128277c9SStephan Aßmus 			return "SELECT_POINTS";
112128277c9SStephan Aßmus 		case TRANSFORM_POINTS:
113128277c9SStephan Aßmus 			return "TRANSFORM_POINTS";
114128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
115128277c9SStephan Aßmus 			return "TRANSLATE_POINTS";
116128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
117128277c9SStephan Aßmus 			return "SELECT_SUB_PATH";
118128277c9SStephan Aßmus 	}
119128277c9SStephan Aßmus 	return "<unknown mode>";
120128277c9SStephan Aßmus }
121128277c9SStephan Aßmus 
122ce181bb0SStephan Aßmus class PathManipulator::Selection : protected BList
123128277c9SStephan Aßmus {
124128277c9SStephan Aßmus public:
125128277c9SStephan Aßmus 	inline Selection(int32 count = 20)
126128277c9SStephan Aßmus 		: BList(count) {}
127128277c9SStephan Aßmus 	inline ~Selection() {}
128128277c9SStephan Aßmus 
129128277c9SStephan Aßmus 	inline void Add(int32 value)
130128277c9SStephan Aßmus 		{
131128277c9SStephan Aßmus 			if (value >= 0) {
132128277c9SStephan Aßmus 				// keep the list sorted
133128277c9SStephan Aßmus 				int32 count = CountItems();
134128277c9SStephan Aßmus 				int32 index = 0;
135128277c9SStephan Aßmus 				for (; index < count; index++) {
136128277c9SStephan Aßmus 					if (IndexAt(index) > value) {
137128277c9SStephan Aßmus 						break;
138128277c9SStephan Aßmus 					}
139128277c9SStephan Aßmus 				}
140128277c9SStephan Aßmus 				BList::AddItem((void*)value, index);
141128277c9SStephan Aßmus 			}
142128277c9SStephan Aßmus 		}
143128277c9SStephan Aßmus 
144128277c9SStephan Aßmus 	inline bool Remove(int32 value)
145128277c9SStephan Aßmus 		{ return BList::RemoveItem((void*)value); }
146128277c9SStephan Aßmus 
147128277c9SStephan Aßmus 	inline bool Contains(int32 value) const
148128277c9SStephan Aßmus 		{ return BList::HasItem((void*)value); }
149128277c9SStephan Aßmus 
150128277c9SStephan Aßmus 	inline bool IsEmpty() const
151128277c9SStephan Aßmus 		{ return BList::IsEmpty(); }
152128277c9SStephan Aßmus 
153128277c9SStephan Aßmus 	inline int32 IndexAt(int32 index) const
154128277c9SStephan Aßmus 		{ return (int32)BList::ItemAt(index); }
155128277c9SStephan Aßmus 
156128277c9SStephan Aßmus 	inline void MakeEmpty()
157128277c9SStephan Aßmus 		{ BList::MakeEmpty(); }
158128277c9SStephan Aßmus 
159128277c9SStephan Aßmus 	inline int32* Items() const
160128277c9SStephan Aßmus 		{ return (int32*)BList::Items(); }
161128277c9SStephan Aßmus 
162128277c9SStephan Aßmus 	inline const int32 CountItems() const
163128277c9SStephan Aßmus 		{ return BList::CountItems(); }
164128277c9SStephan Aßmus 
165128277c9SStephan Aßmus 	inline Selection& operator =(const Selection& other)
166128277c9SStephan Aßmus 		{
167128277c9SStephan Aßmus 			MakeEmpty();
168128277c9SStephan Aßmus 			int32 count = other.CountItems();
169128277c9SStephan Aßmus 			int32* items = other.Items();
170128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++) {
171128277c9SStephan Aßmus 				Add(items[i]);
172128277c9SStephan Aßmus 			}
173128277c9SStephan Aßmus 			return *this;
174128277c9SStephan Aßmus 		}
175128277c9SStephan Aßmus 
176128277c9SStephan Aßmus 	inline bool operator ==(const Selection& other)
177128277c9SStephan Aßmus 		{
178128277c9SStephan Aßmus 			if (other.CountItems() == CountItems()) {
179128277c9SStephan Aßmus 				int32* items = Items();
180128277c9SStephan Aßmus 				int32* otherItems = other.Items();
181128277c9SStephan Aßmus 				for (int32 i = 0; i < CountItems(); i++) {
182128277c9SStephan Aßmus 					if (items[i] != otherItems[i])
183128277c9SStephan Aßmus 						return false;
184128277c9SStephan Aßmus 					items++;
185128277c9SStephan Aßmus 					otherItems++;
186128277c9SStephan Aßmus 				}
187128277c9SStephan Aßmus 				return true;
188128277c9SStephan Aßmus 			} else
189128277c9SStephan Aßmus 				return false;
190128277c9SStephan Aßmus 		}
191128277c9SStephan Aßmus 
192128277c9SStephan Aßmus 	inline bool operator !=(const Selection& other)
193128277c9SStephan Aßmus 	{
194128277c9SStephan Aßmus 		return !(*this == other);
195128277c9SStephan Aßmus 	}
196128277c9SStephan Aßmus };
197128277c9SStephan Aßmus 
198128277c9SStephan Aßmus 
199128277c9SStephan Aßmus // constructor
200128277c9SStephan Aßmus PathManipulator::PathManipulator(VectorPath* path)
2010e1ba39fSStephan Aßmus 	: Manipulator(NULL),
202128277c9SStephan Aßmus 	  fCanvasView(NULL),
203128277c9SStephan Aßmus 
204128277c9SStephan Aßmus 	  fCommandDown(false),
205128277c9SStephan Aßmus 	  fOptionDown(false),
206128277c9SStephan Aßmus 	  fShiftDown(false),
207128277c9SStephan Aßmus 	  fAltDown(false),
208128277c9SStephan Aßmus 
209128277c9SStephan Aßmus 	  fClickToClose(false),
210128277c9SStephan Aßmus 
211128277c9SStephan Aßmus 	  fMode(NEW_PATH),
212128277c9SStephan Aßmus 	  fFallBackMode(SELECT_POINTS),
213128277c9SStephan Aßmus 
214128277c9SStephan Aßmus 	  fMouseDown(false),
215128277c9SStephan Aßmus 
216128277c9SStephan Aßmus 	  fPath(path),
217128277c9SStephan Aßmus 	  fCurrentPathPoint(-1),
218128277c9SStephan Aßmus 
219128277c9SStephan Aßmus 	  fChangePointCommand(NULL),
220128277c9SStephan Aßmus 	  fInsertPointCommand(NULL),
221128277c9SStephan Aßmus 	  fAddPointCommand(NULL),
222128277c9SStephan Aßmus 
223128277c9SStephan Aßmus 	  fSelection(new Selection()),
224128277c9SStephan Aßmus 	  fOldSelection(new Selection()),
2250e1ba39fSStephan Aßmus 	  fTransformBox(NULL),
226128277c9SStephan Aßmus 
227128277c9SStephan Aßmus 	  fNudgeOffset(0.0, 0.0),
2280e1ba39fSStephan Aßmus 	  fLastNudgeTime(system_time()),
2290e1ba39fSStephan Aßmus 	  fNudgeCommand(NULL)
230128277c9SStephan Aßmus {
2310e1ba39fSStephan Aßmus 	fPath->Acquire();
23205fd3818SStephan Aßmus 	fPath->AddListener(this);
2330e1ba39fSStephan Aßmus 	fPath->AddObserver(this);
234128277c9SStephan Aßmus }
235128277c9SStephan Aßmus 
236128277c9SStephan Aßmus // destructor
237128277c9SStephan Aßmus PathManipulator::~PathManipulator()
238128277c9SStephan Aßmus {
239128277c9SStephan Aßmus 	delete fChangePointCommand;
240128277c9SStephan Aßmus 	delete fInsertPointCommand;
241128277c9SStephan Aßmus 	delete fAddPointCommand;
242128277c9SStephan Aßmus 
243128277c9SStephan Aßmus 	delete fSelection;
244128277c9SStephan Aßmus 	delete fOldSelection;
2450e1ba39fSStephan Aßmus 	delete fTransformBox;
246128277c9SStephan Aßmus 
2470e1ba39fSStephan Aßmus 	delete fNudgeCommand;
2480e1ba39fSStephan Aßmus 
2490e1ba39fSStephan Aßmus 	fPath->RemoveObserver(this);
2500e1ba39fSStephan Aßmus 	fPath->RemoveListener(this);
2510e1ba39fSStephan Aßmus 	fPath->Release();
252128277c9SStephan Aßmus }
253128277c9SStephan Aßmus 
254128277c9SStephan Aßmus 
255128277c9SStephan Aßmus // #pragma mark -
256128277c9SStephan Aßmus 
257128277c9SStephan Aßmus class StrokePathIterator : public VectorPath::Iterator {
258128277c9SStephan Aßmus  public:
259f67876a0SStephan Aßmus 					StrokePathIterator(CanvasView* canvasView,
260f67876a0SStephan Aßmus 									   BView* drawingView)
261f67876a0SStephan Aßmus 						: fCanvasView(canvasView),
262f67876a0SStephan Aßmus 						  fDrawingView(drawingView)
263128277c9SStephan Aßmus 					{
264128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
265128277c9SStephan Aßmus 						fDrawingView->SetDrawingMode(B_OP_OVER);
266128277c9SStephan Aßmus 					}
267128277c9SStephan Aßmus 	virtual			~StrokePathIterator()
268128277c9SStephan Aßmus 					{}
269128277c9SStephan Aßmus 
270128277c9SStephan Aßmus 	virtual	void	MoveTo(BPoint point)
271128277c9SStephan Aßmus 					{
272128277c9SStephan Aßmus 						fBlack = true;
273f67876a0SStephan Aßmus 						fSkip = false;
274128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
275128277c9SStephan Aßmus 
276f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
277128277c9SStephan Aßmus 						fDrawingView->MovePenTo(point);
278128277c9SStephan Aßmus 					}
279128277c9SStephan Aßmus 	virtual	void	LineTo(BPoint point)
280128277c9SStephan Aßmus 					{
281f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
282f67876a0SStephan Aßmus 						if (!fSkip) {
283128277c9SStephan Aßmus 							if (fBlack)
284128277c9SStephan Aßmus 								fDrawingView->SetHighColor(255, 255, 255, 255);
285128277c9SStephan Aßmus 							else
286128277c9SStephan Aßmus 								fDrawingView->SetHighColor(0, 0, 0, 255);
287128277c9SStephan Aßmus 							fBlack = !fBlack;
288128277c9SStephan Aßmus 
289128277c9SStephan Aßmus 							fDrawingView->StrokeLine(point);
290f67876a0SStephan Aßmus 						} else {
291f67876a0SStephan Aßmus 							fDrawingView->MovePenTo(point);
292f67876a0SStephan Aßmus 						}
293f67876a0SStephan Aßmus 						fSkip = !fSkip;
294128277c9SStephan Aßmus 					}
295128277c9SStephan Aßmus 
296128277c9SStephan Aßmus  private:
297f67876a0SStephan Aßmus 	CanvasView*		fCanvasView;
298128277c9SStephan Aßmus 	BView*			fDrawingView;
299128277c9SStephan Aßmus 	bool			fBlack;
300f67876a0SStephan Aßmus 	bool			fSkip;
301128277c9SStephan Aßmus };
302128277c9SStephan Aßmus 
303128277c9SStephan Aßmus // Draw
304128277c9SStephan Aßmus void
305128277c9SStephan Aßmus PathManipulator::Draw(BView* into, BRect updateRect)
306128277c9SStephan Aßmus {
30764dd737cSStephan Aßmus 	// draw the Bezier curve, but only if not "editing",
30864dd737cSStephan Aßmus 	// the path is actually on top all other modifiers
309128277c9SStephan Aßmus 	// TODO: make this customizable in the GUI
31064dd737cSStephan Aßmus 
31164dd737cSStephan Aßmus 	#if __HAIKU__
31264dd737cSStephan Aßmus 	uint32 flags = into->Flags();
31364dd737cSStephan Aßmus 	into->SetFlags(flags | B_SUBPIXEL_PRECISE);
31464dd737cSStephan Aßmus 	#endif // __HAIKU__
31564dd737cSStephan Aßmus 
316f67876a0SStephan Aßmus 	StrokePathIterator iterator(fCanvasView, into);
317f67876a0SStephan Aßmus 	fPath->Iterate(&iterator, fCanvasView->ZoomLevel());
318128277c9SStephan Aßmus 
31964dd737cSStephan Aßmus 	#if __HAIKU__
32064dd737cSStephan Aßmus 	into->SetFlags(flags);
32164dd737cSStephan Aßmus 	#endif // __HAIKU__
32264dd737cSStephan Aßmus 
323128277c9SStephan Aßmus 	into->SetLowColor(0, 0, 0, 255);
324128277c9SStephan Aßmus 	BPoint point;
325128277c9SStephan Aßmus 	BPoint pointIn;
326128277c9SStephan Aßmus 	BPoint pointOut;
327128277c9SStephan Aßmus 	rgb_color focusColor = (rgb_color){ 255, 0, 0, 255 };
328128277c9SStephan Aßmus 	rgb_color highlightColor = (rgb_color){ 60, 60, 255, 255 };
329128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut); i++) {
330128277c9SStephan Aßmus 		bool highlight = fCurrentPathPoint == i;
331128277c9SStephan Aßmus 		bool selected = fSelection->Contains(i);
332128277c9SStephan Aßmus 		rgb_color normal = selected ? focusColor : (rgb_color){ 0, 0, 0, 255 };
333128277c9SStephan Aßmus 		into->SetLowColor(normal);
334128277c9SStephan Aßmus 		into->SetHighColor(255, 255, 255, 255);
335128277c9SStephan Aßmus 		// convert to view coordinate space
336f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&point);
337f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointIn);
338f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointOut);
339128277c9SStephan Aßmus 		// connect the points belonging to one control point
340128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_INVERT);
341128277c9SStephan Aßmus 		into->StrokeLine(point, pointIn);
342128277c9SStephan Aßmus 		into->StrokeLine(point, pointOut);
343128277c9SStephan Aßmus 		// draw main control point
344128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT ||
345128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP ||
346128277c9SStephan Aßmus 						  fMode == REMOVE_POINT ||
347128277c9SStephan Aßmus 						  fMode == SELECT_POINTS ||
348128277c9SStephan Aßmus 						  fMode == CLOSE_PATH)) {
349128277c9SStephan Aßmus 
350128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
351128277c9SStephan Aßmus 		}
352128277c9SStephan Aßmus 
353128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_COPY);
354128277c9SStephan Aßmus 		BRect r(point, point);
355128277c9SStephan Aßmus 		r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
356128277c9SStephan Aßmus 		into->StrokeRect(r, B_SOLID_LOW);
357128277c9SStephan Aßmus 		r.InsetBy(1.0, 1.0);
358128277c9SStephan Aßmus 		into->FillRect(r, B_SOLID_HIGH);
359128277c9SStephan Aßmus 		// draw in control point
360128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_IN ||
361128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_IN ||
362128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_IN ||
363128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
364128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
365128277c9SStephan Aßmus 		else
366128277c9SStephan Aßmus 			into->SetLowColor(normal);
367128277c9SStephan Aßmus 		if (selected) {
368128277c9SStephan Aßmus 			into->SetHighColor(220, 220, 220, 255);
369128277c9SStephan Aßmus 		} else {
370128277c9SStephan Aßmus 			into->SetHighColor(170, 170, 170, 255);
371128277c9SStephan Aßmus 		}
372128277c9SStephan Aßmus 		if (pointIn != point) {
373128277c9SStephan Aßmus 			r.Set(pointIn.x - CONTROL_POINT_EXTEND, pointIn.y - CONTROL_POINT_EXTEND,
374128277c9SStephan Aßmus 				  pointIn.x + CONTROL_POINT_EXTEND, pointIn.y + CONTROL_POINT_EXTEND);
375128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
376128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
377128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
378128277c9SStephan Aßmus 		}
379128277c9SStephan Aßmus 		// draw out control point
380128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_OUT ||
381128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_OUT ||
382128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_OUT ||
383128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
384128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
385128277c9SStephan Aßmus 		else
386128277c9SStephan Aßmus 			into->SetLowColor(normal);
387128277c9SStephan Aßmus 		if (pointOut != point) {
388128277c9SStephan Aßmus 			r.Set(pointOut.x - CONTROL_POINT_EXTEND, pointOut.y - CONTROL_POINT_EXTEND,
389128277c9SStephan Aßmus 				  pointOut.x + CONTROL_POINT_EXTEND, pointOut.y + CONTROL_POINT_EXTEND);
390128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
391128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
392128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
393128277c9SStephan Aßmus 		}
394128277c9SStephan Aßmus 	}
3950e1ba39fSStephan Aßmus 
3960e1ba39fSStephan Aßmus 	if (fTransformBox) {
3970e1ba39fSStephan Aßmus 		fTransformBox->Draw(into, updateRect);
3980e1ba39fSStephan Aßmus 	}
399128277c9SStephan Aßmus }
400128277c9SStephan Aßmus 
401128277c9SStephan Aßmus // #pragma mark -
402128277c9SStephan Aßmus 
403128277c9SStephan Aßmus // MouseDown
404128277c9SStephan Aßmus bool
405128277c9SStephan Aßmus PathManipulator::MouseDown(BPoint where)
406128277c9SStephan Aßmus {
407128277c9SStephan Aßmus 	fMouseDown = true;
408128277c9SStephan Aßmus 
4090e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
4100e1ba39fSStephan Aßmus 		if (fTransformBox) {
4110e1ba39fSStephan Aßmus 			fTransformBox->MouseDown(where);
4120e1ba39fSStephan Aßmus 
4130e1ba39fSStephan Aßmus //			if (!fTransformBox->IsRotating())
4140e1ba39fSStephan Aßmus //				fCanvasView->SetAutoScrolling(true);
4150e1ba39fSStephan Aßmus 		}
4160e1ba39fSStephan Aßmus 		return true;
4170e1ba39fSStephan Aßmus 	}
4180e1ba39fSStephan Aßmus 
419128277c9SStephan Aßmus 	if (fMode == MOVE_POINT &&
420128277c9SStephan Aßmus 		fSelection->CountItems() > 1 &&
421128277c9SStephan Aßmus 		fSelection->Contains(fCurrentPathPoint)) {
422128277c9SStephan Aßmus 		fMode = TRANSLATE_POINTS;
423128277c9SStephan Aßmus 	}
424128277c9SStephan Aßmus 
4254fac07a0SStephan Aßmus 	// apply the canvas view mouse filter depending on current mode
4264fac07a0SStephan Aßmus 	if (fMode == ADD_POINT || fMode == TRANSLATE_POINTS)
4274fac07a0SStephan Aßmus 		fCanvasView->FilterMouse(&where);
4284fac07a0SStephan Aßmus 
429128277c9SStephan Aßmus 	BPoint canvasWhere = where;
430f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
431128277c9SStephan Aßmus 
432128277c9SStephan Aßmus 	// maybe we're changing some point, so we construct the
433128277c9SStephan Aßmus 	// "ChangePointCommand" here so that the point is remembered
434128277c9SStephan Aßmus 	// in its current state
4354fac07a0SStephan Aßmus 	// apply the canvas view mouse filter depending on current mode
436128277c9SStephan Aßmus 	delete fChangePointCommand;
437128277c9SStephan Aßmus 	fChangePointCommand = NULL;
438128277c9SStephan Aßmus 	switch (fMode) {
439128277c9SStephan Aßmus 		case TOGGLE_SHARP:
440128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
441128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
442128277c9SStephan Aßmus 		case MOVE_POINT:
443128277c9SStephan Aßmus 		case MOVE_POINT_IN:
444128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
445128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
446128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
447128277c9SStephan Aßmus 			fChangePointCommand = new ChangePointCommand(fPath,
448128277c9SStephan Aßmus 														 fCurrentPathPoint,
449128277c9SStephan Aßmus 														 fSelection->Items(),
450128277c9SStephan Aßmus 														 fSelection->CountItems());
451128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
452128277c9SStephan Aßmus 			break;
453128277c9SStephan Aßmus 	}
454128277c9SStephan Aßmus 
455128277c9SStephan Aßmus 	// at this point we init doing something
456128277c9SStephan Aßmus 	switch (fMode) {
457128277c9SStephan Aßmus 		case ADD_POINT:
458128277c9SStephan Aßmus 			_AddPoint(canvasWhere);
459128277c9SStephan Aßmus 			break;
460128277c9SStephan Aßmus 		case INSERT_POINT:
461128277c9SStephan Aßmus 			_InsertPoint(canvasWhere, fCurrentPathPoint);
462128277c9SStephan Aßmus 			break;
463128277c9SStephan Aßmus 
464128277c9SStephan Aßmus 		case TOGGLE_SHARP:
465128277c9SStephan Aßmus 			_SetSharp(fCurrentPathPoint);
466128277c9SStephan Aßmus 			// continue by dragging out the _connected_ in/out points
467128277c9SStephan Aßmus 			break;
468128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
469128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
470128277c9SStephan Aßmus 			// continue by moving the "in" point
471128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_IN);
472128277c9SStephan Aßmus 			break;
473128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
474128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
475128277c9SStephan Aßmus 			// continue by moving the "out" point
476128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
477128277c9SStephan Aßmus 			break;
478128277c9SStephan Aßmus 
479128277c9SStephan Aßmus 		case MOVE_POINT:
480128277c9SStephan Aßmus 		case MOVE_POINT_IN:
481128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
482128277c9SStephan Aßmus 			// the right thing happens since "fCurrentPathPoint"
483128277c9SStephan Aßmus 			// points to the correct index
484128277c9SStephan Aßmus 			break;
485128277c9SStephan Aßmus 
486128277c9SStephan Aßmus 		case CLOSE_PATH:
487128277c9SStephan Aßmus //			SetClosed(true, true);
488128277c9SStephan Aßmus 			break;
489128277c9SStephan Aßmus 
490128277c9SStephan Aßmus 		case REMOVE_POINT:
491128277c9SStephan Aßmus 			if (fPath->CountPoints() == 1) {
492128277c9SStephan Aßmus //				fCanvasView->Perform(new RemovePathCommand(this, fPath));
493128277c9SStephan Aßmus 			} else {
494128277c9SStephan Aßmus 				fCanvasView->Perform(new RemovePointsCommand(fPath,
495128277c9SStephan Aßmus 															 fCurrentPathPoint,
496128277c9SStephan Aßmus 															 fSelection->Items(),
497128277c9SStephan Aßmus 															 fSelection->CountItems()));
498128277c9SStephan Aßmus 				_RemovePoint(fCurrentPathPoint);
499128277c9SStephan Aßmus 			}
500128277c9SStephan Aßmus 			break;
501128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
502128277c9SStephan Aßmus 			_RemovePointIn(fCurrentPathPoint);
503128277c9SStephan Aßmus 			break;
504128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
505128277c9SStephan Aßmus 			_RemovePointOut(fCurrentPathPoint);
506128277c9SStephan Aßmus 			break;
507128277c9SStephan Aßmus 
508f4bd80a2SStephan Aßmus 		case SELECT_POINTS: {
509f4bd80a2SStephan Aßmus 			// TODO: this works so that you can deselect all points
510f4bd80a2SStephan Aßmus 			// when clicking outside the path even if pressing shift
511f4bd80a2SStephan Aßmus 			// in case the path is open... a better way would be
512f4bd80a2SStephan Aßmus 			// to deselect all on mouse up, if the mouse has not moved
513f4bd80a2SStephan Aßmus 			bool appendSelection;
514f4bd80a2SStephan Aßmus 			if (fPath->IsClosed())
515f4bd80a2SStephan Aßmus 				appendSelection = fShiftDown;
516f4bd80a2SStephan Aßmus 			else
517f4bd80a2SStephan Aßmus 				appendSelection = fShiftDown && fCurrentPathPoint >= 0;
518f4bd80a2SStephan Aßmus 
519f4bd80a2SStephan Aßmus 			if (!appendSelection) {
520128277c9SStephan Aßmus 				fSelection->MakeEmpty();
521128277c9SStephan Aßmus 				_UpdateSelection();
522128277c9SStephan Aßmus 			}
523128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
524128277c9SStephan Aßmus 			if (fCurrentPathPoint >= 0) {
525f4bd80a2SStephan Aßmus 				_Select(fCurrentPathPoint, appendSelection);
526128277c9SStephan Aßmus 			}
527128277c9SStephan Aßmus 			fCanvasView->BeginRectTracking(BRect(where, where),
528128277c9SStephan Aßmus 				B_TRACK_RECT_CORNER);
529128277c9SStephan Aßmus 			break;
530128277c9SStephan Aßmus 		}
531f4bd80a2SStephan Aßmus 	}
532128277c9SStephan Aßmus 
533128277c9SStephan Aßmus 	fTrackingStart = canvasWhere;
534128277c9SStephan Aßmus 	// remember the subpixel position
535128277c9SStephan Aßmus 	// so that MouseMoved() will work even before
536128277c9SStephan Aßmus 	// the integer position becomes different
537128277c9SStephan Aßmus 	fLastCanvasPos = where;
538f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&fLastCanvasPos);
539128277c9SStephan Aßmus 
540128277c9SStephan Aßmus 	// the reason to exclude the select mode
541128277c9SStephan Aßmus 	// is that the BView rect tracking does not
542128277c9SStephan Aßmus 	// scroll the rect starting point along with us
543128277c9SStephan Aßmus 	// (since we're doing no real scrolling)
544128277c9SStephan Aßmus //	if (fMode != SELECT_POINTS)
545128277c9SStephan Aßmus //		fCanvasView->SetAutoScrolling(true);
546128277c9SStephan Aßmus 
547128277c9SStephan Aßmus 	UpdateCursor();
548128277c9SStephan Aßmus 
549128277c9SStephan Aßmus 	return true;
550128277c9SStephan Aßmus }
551128277c9SStephan Aßmus 
552128277c9SStephan Aßmus // MouseMoved
553128277c9SStephan Aßmus void
554128277c9SStephan Aßmus PathManipulator::MouseMoved(BPoint where)
555128277c9SStephan Aßmus {
5564fac07a0SStephan Aßmus 	fCanvasView->FilterMouse(&where);
5574fac07a0SStephan Aßmus 		// NOTE: only filter mouse coords in mouse moved, no other
5584fac07a0SStephan Aßmus 		// mouse function
559128277c9SStephan Aßmus 	BPoint canvasWhere = where;
560f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
561128277c9SStephan Aßmus 
562128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
563128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
564128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
565128277c9SStephan Aßmus 	if (fLastCanvasPos == canvasWhere)
566128277c9SStephan Aßmus 		return;
567128277c9SStephan Aßmus 
568128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
569128277c9SStephan Aßmus 
5700e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
5710e1ba39fSStephan Aßmus 		if (fTransformBox) {
5720e1ba39fSStephan Aßmus 			fTransformBox->MouseMoved(where);
5730e1ba39fSStephan Aßmus 		}
5740e1ba39fSStephan Aßmus 		return;
5750e1ba39fSStephan Aßmus 	}
5760e1ba39fSStephan Aßmus 
577128277c9SStephan Aßmus 	if (fMode == CLOSE_PATH) {
578128277c9SStephan Aßmus 		// continue by moving the point
579128277c9SStephan Aßmus 		_SetMode(MOVE_POINT);
580128277c9SStephan Aßmus 		delete fChangePointCommand;
581128277c9SStephan Aßmus 		fChangePointCommand = new ChangePointCommand(fPath,
582128277c9SStephan Aßmus 													 fCurrentPathPoint,
583128277c9SStephan Aßmus 													 fSelection->Items(),
584128277c9SStephan Aßmus 													 fSelection->CountItems());
585128277c9SStephan Aßmus 	}
586128277c9SStephan Aßmus 
587128277c9SStephan Aßmus //	if (!fPrecise) {
588128277c9SStephan Aßmus //		float offset = fmod(fOutlineWidth, 2.0) / 2.0;
589128277c9SStephan Aßmus //		canvasWhere.point += BPoint(offset, offset);
590128277c9SStephan Aßmus //	}
591128277c9SStephan Aßmus 
592128277c9SStephan Aßmus 	switch (fMode) {
593128277c9SStephan Aßmus 		case ADD_POINT:
594128277c9SStephan Aßmus 		case INSERT_POINT:
595128277c9SStephan Aßmus 		case TOGGLE_SHARP:
596128277c9SStephan Aßmus 			// drag the "out" control point, mirror the "in" control point
597128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere, true);
598128277c9SStephan Aßmus 			break;
599128277c9SStephan Aßmus 		case MOVE_POINT:
600128277c9SStephan Aßmus 			// drag all three control points at once
601128277c9SStephan Aßmus 			fPath->SetPoint(fCurrentPathPoint, canvasWhere);
602128277c9SStephan Aßmus 			break;
603128277c9SStephan Aßmus 		case MOVE_POINT_IN:
604128277c9SStephan Aßmus 			// drag in control point
605128277c9SStephan Aßmus 			fPath->SetPointIn(fCurrentPathPoint, canvasWhere);
606128277c9SStephan Aßmus 			break;
607128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
608128277c9SStephan Aßmus 			// drag out control point
609128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere);
610128277c9SStephan Aßmus 			break;
611128277c9SStephan Aßmus 
612128277c9SStephan Aßmus 		case SELECT_POINTS: {
613128277c9SStephan Aßmus 			// change the selection
614128277c9SStephan Aßmus 			BRect r;
615128277c9SStephan Aßmus 			r.left = min_c(fTrackingStart.x, canvasWhere.x);
616128277c9SStephan Aßmus 			r.top = min_c(fTrackingStart.y, canvasWhere.y);
617128277c9SStephan Aßmus 			r.right = max_c(fTrackingStart.x, canvasWhere.x);
618128277c9SStephan Aßmus 			r.bottom = max_c(fTrackingStart.y, canvasWhere.y);
619128277c9SStephan Aßmus 			_Select(r);
620128277c9SStephan Aßmus 			break;
621128277c9SStephan Aßmus 		}
622128277c9SStephan Aßmus 
623128277c9SStephan Aßmus 		case TRANSLATE_POINTS: {
624128277c9SStephan Aßmus 			BPoint offset = canvasWhere - fTrackingStart;
625128277c9SStephan Aßmus 			_Nudge(offset);
626128277c9SStephan Aßmus 			fTrackingStart = canvasWhere;
627128277c9SStephan Aßmus 			break;
628128277c9SStephan Aßmus 		}
629128277c9SStephan Aßmus 	}
630128277c9SStephan Aßmus }
631128277c9SStephan Aßmus 
632128277c9SStephan Aßmus // MouseUp
633128277c9SStephan Aßmus Command*
634128277c9SStephan Aßmus PathManipulator::MouseUp()
635128277c9SStephan Aßmus {
636128277c9SStephan Aßmus 	// prevent carrying out actions more than once by only
637128277c9SStephan Aßmus 	// doing it if "fMouseDown" is true at the point of
638128277c9SStephan Aßmus 	// entering this function
639128277c9SStephan Aßmus 	if (!fMouseDown)
640128277c9SStephan Aßmus 		return NULL;
641128277c9SStephan Aßmus 	fMouseDown = false;
642128277c9SStephan Aßmus 
6430e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
6440e1ba39fSStephan Aßmus 		if (fTransformBox) {
6450e1ba39fSStephan Aßmus 			return fTransformBox->MouseUp();
6460e1ba39fSStephan Aßmus 		}
6470e1ba39fSStephan Aßmus 		return NULL;
6480e1ba39fSStephan Aßmus 	}
6490e1ba39fSStephan Aßmus 
650128277c9SStephan Aßmus 	Command* command = NULL;
651128277c9SStephan Aßmus 
652128277c9SStephan Aßmus 	switch (fMode) {
653128277c9SStephan Aßmus 
654128277c9SStephan Aßmus 		case ADD_POINT:
655128277c9SStephan Aßmus 			command = fAddPointCommand;
656128277c9SStephan Aßmus 			fAddPointCommand = NULL;
657128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
658128277c9SStephan Aßmus 			break;
659128277c9SStephan Aßmus 
660128277c9SStephan Aßmus 		case INSERT_POINT:
661128277c9SStephan Aßmus 			command = fInsertPointCommand;
662128277c9SStephan Aßmus 			fInsertPointCommand = NULL;
663128277c9SStephan Aßmus 			break;
664128277c9SStephan Aßmus 
665128277c9SStephan Aßmus 		case SELECT_POINTS:
666128277c9SStephan Aßmus 			if (*fSelection != *fOldSelection) {
667128277c9SStephan Aßmus //				command = new SelectPointsCommand(this, fPath,
668128277c9SStephan Aßmus //												  fOldSelection->Items(),
669128277c9SStephan Aßmus //												  fOldSelection->CountItems(),
670128277c9SStephan Aßmus //												  fSelection->Items(),
671128277c9SStephan Aßmus //												  fSelection->CountItems()));
672128277c9SStephan Aßmus 			}
673128277c9SStephan Aßmus 			fCanvasView->EndRectTracking();
674128277c9SStephan Aßmus 			break;
675128277c9SStephan Aßmus 
676128277c9SStephan Aßmus 		case TOGGLE_SHARP:
677128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
678128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
679128277c9SStephan Aßmus 		case MOVE_POINT:
680128277c9SStephan Aßmus 		case MOVE_POINT_IN:
681128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
682128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
683128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
684128277c9SStephan Aßmus 			command = fChangePointCommand;
685128277c9SStephan Aßmus 			fChangePointCommand = NULL;
686128277c9SStephan Aßmus 			break;
687128277c9SStephan Aßmus 
688128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
6890e1ba39fSStephan Aßmus 			if (!fNudgeCommand) {
690128277c9SStephan Aßmus 				// select just the point that was clicked
691128277c9SStephan Aßmus 				*fOldSelection = *fSelection;
692128277c9SStephan Aßmus 				if (fCurrentPathPoint >= 0) {
693128277c9SStephan Aßmus 					_Select(fCurrentPathPoint, fShiftDown);
694128277c9SStephan Aßmus 				}
695128277c9SStephan Aßmus 				if (*fSelection != *fOldSelection) {
696128277c9SStephan Aßmus //					command = new SelectPointsCommand(this, fPath,
697128277c9SStephan Aßmus //													  fOldSelection->Items(),
698128277c9SStephan Aßmus //													  fOldSelection->CountItems(),
699128277c9SStephan Aßmus //													  fSelection->Items(),
700128277c9SStephan Aßmus //													  fSelection->CountItems()));
701128277c9SStephan Aßmus 				}
7020e1ba39fSStephan Aßmus 			} else {
7030e1ba39fSStephan Aßmus 				command = _FinishNudging();
7040e1ba39fSStephan Aßmus 			}
705128277c9SStephan Aßmus 			break;
706128277c9SStephan Aßmus 	}
707128277c9SStephan Aßmus 
708128277c9SStephan Aßmus 	return command;
709128277c9SStephan Aßmus }
710128277c9SStephan Aßmus 
711128277c9SStephan Aßmus // MouseOver
712128277c9SStephan Aßmus bool
713128277c9SStephan Aßmus PathManipulator::MouseOver(BPoint where)
714128277c9SStephan Aßmus {
7150e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
7160e1ba39fSStephan Aßmus 		if (fTransformBox) {
7170e1ba39fSStephan Aßmus 			return fTransformBox->MouseOver(where);
7180e1ba39fSStephan Aßmus 		}
7190e1ba39fSStephan Aßmus 		return false;
7200e1ba39fSStephan Aßmus 	}
7210e1ba39fSStephan Aßmus 
722128277c9SStephan Aßmus 	BPoint canvasWhere = where;
723f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
724128277c9SStephan Aßmus 
725128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
726128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
727128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
728128277c9SStephan Aßmus 	if (fMouseDown && fLastCanvasPos == canvasWhere)
729128277c9SStephan Aßmus 		return false;
730128277c9SStephan Aßmus 
731128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
732128277c9SStephan Aßmus 
733128277c9SStephan Aßmus 	// hit testing
734128277c9SStephan Aßmus 	// (use a subpixel mouse pos)
735f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&where);
736128277c9SStephan Aßmus 	_SetModeForMousePos(where);
737128277c9SStephan Aßmus 
738128277c9SStephan Aßmus 	// TODO: always true?
739128277c9SStephan Aßmus 	return true;
740128277c9SStephan Aßmus }
741128277c9SStephan Aßmus 
742128277c9SStephan Aßmus // DoubleClicked
743128277c9SStephan Aßmus bool
744128277c9SStephan Aßmus PathManipulator::DoubleClicked(BPoint where)
745128277c9SStephan Aßmus {
746128277c9SStephan Aßmus 	return false;
747128277c9SStephan Aßmus }
748128277c9SStephan Aßmus 
749f4bd80a2SStephan Aßmus // ShowContextMenu
750f4bd80a2SStephan Aßmus bool
751f4bd80a2SStephan Aßmus PathManipulator::ShowContextMenu(BPoint where)
752f4bd80a2SStephan Aßmus {
753*a9cee0f1SStephan Aßmus 	// Change the selection to the current point if it isn't currently
754*a9cee0f1SStephan Aßmus 	// selected. This could will only be chosen if the user right-clicked
755*a9cee0f1SStephan Aßmus 	// a path point directly.
756*a9cee0f1SStephan Aßmus 	if (fCurrentPathPoint >= 0 && !fSelection->Contains(fCurrentPathPoint)) {
757*a9cee0f1SStephan Aßmus 		fSelection->MakeEmpty();
758*a9cee0f1SStephan Aßmus 		_UpdateSelection();
759*a9cee0f1SStephan Aßmus 		*fOldSelection = *fSelection;
760*a9cee0f1SStephan Aßmus 		_Select(fCurrentPathPoint, false);
761*a9cee0f1SStephan Aßmus 	}
762*a9cee0f1SStephan Aßmus 
763f4bd80a2SStephan Aßmus 	BPopUpMenu* menu = new BPopUpMenu("context menu", false, false);
764f4bd80a2SStephan Aßmus 	BMessage* message;
765f4bd80a2SStephan Aßmus 	BMenuItem* item;
766f4bd80a2SStephan Aßmus 
767f4bd80a2SStephan Aßmus 	bool hasSelection = fSelection->CountItems() > 0;
768f4bd80a2SStephan Aßmus 
769*a9cee0f1SStephan Aßmus 	if (fCurrentPathPoint < 0) {
770f4bd80a2SStephan Aßmus 		message = new BMessage(B_SELECT_ALL);
771f4bd80a2SStephan Aßmus 		item = new BMenuItem("Select All", message, 'A');
772f4bd80a2SStephan Aßmus 		menu->AddItem(item);
773f4bd80a2SStephan Aßmus 
774f4bd80a2SStephan Aßmus 		menu->AddSeparatorItem();
775*a9cee0f1SStephan Aßmus 	}
776f4bd80a2SStephan Aßmus 
777f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_TRANSFORM);
778*a9cee0f1SStephan Aßmus 	item = new BMenuItem("Transform", message, 'T');
779f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
780f4bd80a2SStephan Aßmus 	menu->AddItem(item);
781f4bd80a2SStephan Aßmus 
782f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_SPLIT_POINTS);
783f4bd80a2SStephan Aßmus 	item = new BMenuItem("Split", message);
784f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
785f4bd80a2SStephan Aßmus 	menu->AddItem(item);
786f4bd80a2SStephan Aßmus 
7877c4b3726SStephan Aßmus 	message = new BMessage(MSG_FLIP_POINTS);
7887c4b3726SStephan Aßmus 	item = new BMenuItem("Flip", message);
7897c4b3726SStephan Aßmus 	item->SetEnabled(hasSelection);
7907c4b3726SStephan Aßmus 	menu->AddItem(item);
7917c4b3726SStephan Aßmus 
792f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_REMOVE_POINTS);
793*a9cee0f1SStephan Aßmus 	item = new BMenuItem("Remove", message);
794f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
795f4bd80a2SStephan Aßmus 	menu->AddItem(item);
796f4bd80a2SStephan Aßmus 
797f4bd80a2SStephan Aßmus 	// go
798f4bd80a2SStephan Aßmus 	menu->SetTargetForItems(fCanvasView);
799f4bd80a2SStephan Aßmus 	menu->SetAsyncAutoDestruct(true);
800f4bd80a2SStephan Aßmus 	menu->SetFont(be_plain_font);
801f4bd80a2SStephan Aßmus 	where = fCanvasView->ConvertToScreen(where);
802f4bd80a2SStephan Aßmus 	BRect mouseRect(where, where);
803f4bd80a2SStephan Aßmus 	mouseRect.InsetBy(-10.0, -10.0);
804f4bd80a2SStephan Aßmus 	where += BPoint(5.0, 5.0);
805f4bd80a2SStephan Aßmus 	menu->Go(where, true, false, mouseRect, true);
806f4bd80a2SStephan Aßmus 
807f4bd80a2SStephan Aßmus 	return true;
808f4bd80a2SStephan Aßmus }
809f4bd80a2SStephan Aßmus 
810f4bd80a2SStephan Aßmus // #pragma mark -
811f4bd80a2SStephan Aßmus 
812128277c9SStephan Aßmus // Bounds
813128277c9SStephan Aßmus BRect
814128277c9SStephan Aßmus PathManipulator::Bounds()
815128277c9SStephan Aßmus {
816f67876a0SStephan Aßmus 	BRect r = _ControlPointRect();
817f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&r);
818f67876a0SStephan Aßmus 	return r;
819128277c9SStephan Aßmus }
820128277c9SStephan Aßmus 
821128277c9SStephan Aßmus // TrackingBounds
822128277c9SStephan Aßmus BRect
823128277c9SStephan Aßmus PathManipulator::TrackingBounds(BView* withinView)
824128277c9SStephan Aßmus {
825128277c9SStephan Aßmus 	return withinView->Bounds();
826128277c9SStephan Aßmus }
827128277c9SStephan Aßmus 
828128277c9SStephan Aßmus // #pragma mark -
829128277c9SStephan Aßmus 
830128277c9SStephan Aßmus // MessageReceived
831128277c9SStephan Aßmus bool
832128277c9SStephan Aßmus PathManipulator::MessageReceived(BMessage* message, Command** _command)
833128277c9SStephan Aßmus {
834128277c9SStephan Aßmus 	bool result = true;
835128277c9SStephan Aßmus 	switch (message->what) {
836f4bd80a2SStephan Aßmus 		case MSG_TRANSFORM:
837128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
838128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
839128277c9SStephan Aßmus 			break;
840f4bd80a2SStephan Aßmus 		case MSG_REMOVE_POINTS:
841128277c9SStephan Aßmus 			*_command = _Delete();
842128277c9SStephan Aßmus 			break;
843f4bd80a2SStephan Aßmus 		case MSG_SPLIT_POINTS:
844f4bd80a2SStephan Aßmus 			*_command = new SplitPointsCommand(fPath,
845f4bd80a2SStephan Aßmus 											   fSelection->Items(),
846f4bd80a2SStephan Aßmus 											   fSelection->CountItems());
847f4bd80a2SStephan Aßmus 			break;
8487c4b3726SStephan Aßmus 		case MSG_FLIP_POINTS:
8497c4b3726SStephan Aßmus 			*_command = new FlipPointsCommand(fPath,
8507c4b3726SStephan Aßmus 											  fSelection->Items(),
8517c4b3726SStephan Aßmus 											  fSelection->CountItems());
8527c4b3726SStephan Aßmus 			break;
853128277c9SStephan Aßmus 		case B_SELECT_ALL: {
854128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
855128277c9SStephan Aßmus 			fSelection->MakeEmpty();
856128277c9SStephan Aßmus 			int32 count = fPath->CountPoints();
857128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++)
858128277c9SStephan Aßmus 				fSelection->Add(i);
859128277c9SStephan Aßmus 			if (*fOldSelection != *fSelection) {
860128277c9SStephan Aßmus //				*_command = new SelectPointsCommand(this, fPath,
861128277c9SStephan Aßmus //												   fOldSelection->Items(),
862128277c9SStephan Aßmus //												   fOldSelection->CountItems(),
863128277c9SStephan Aßmus //												   fSelection->Items(),
864128277c9SStephan Aßmus //												   fSelection->CountItems()));
865128277c9SStephan Aßmus 				count = fSelection->CountItems();
866128277c9SStephan Aßmus 				int32 indices[count];
867128277c9SStephan Aßmus 				memcpy(indices, fSelection->Items(), count * sizeof(int32));
868128277c9SStephan Aßmus 				_Select(indices, count);
869128277c9SStephan Aßmus 			}
870128277c9SStephan Aßmus 			break;
871128277c9SStephan Aßmus 		}
872128277c9SStephan Aßmus 		default:
873128277c9SStephan Aßmus 			result = false;
874128277c9SStephan Aßmus 			break;
875128277c9SStephan Aßmus 	}
876128277c9SStephan Aßmus 	return result;
877128277c9SStephan Aßmus }
878128277c9SStephan Aßmus 
879128277c9SStephan Aßmus 
880128277c9SStephan Aßmus // ModifiersChanged
881128277c9SStephan Aßmus void
882128277c9SStephan Aßmus PathManipulator::ModifiersChanged(uint32 modifiers)
883128277c9SStephan Aßmus {
884128277c9SStephan Aßmus 	fCommandDown = modifiers & B_COMMAND_KEY;
885128277c9SStephan Aßmus 	fOptionDown = modifiers & B_CONTROL_KEY;
886128277c9SStephan Aßmus 	fShiftDown = modifiers & B_SHIFT_KEY;
887128277c9SStephan Aßmus 	fAltDown = modifiers & B_OPTION_KEY;
888128277c9SStephan Aßmus 
8890e1ba39fSStephan Aßmus 	if (fTransformBox) {
8900e1ba39fSStephan Aßmus 		fTransformBox->ModifiersChanged(modifiers);
8910e1ba39fSStephan Aßmus 		return;
8920e1ba39fSStephan Aßmus 	}
893128277c9SStephan Aßmus 	// reevaluate mode
894128277c9SStephan Aßmus 	if (!fMouseDown)
895128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
896128277c9SStephan Aßmus }
897128277c9SStephan Aßmus 
898128277c9SStephan Aßmus // HandleKeyDown
899128277c9SStephan Aßmus bool
900128277c9SStephan Aßmus PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
901128277c9SStephan Aßmus {
902128277c9SStephan Aßmus 	bool result = true;
903128277c9SStephan Aßmus 
904128277c9SStephan Aßmus 	float nudgeDist = 1.0;
905f67876a0SStephan Aßmus 	if (modifiers & B_SHIFT_KEY)
906f67876a0SStephan Aßmus 		nudgeDist /= fCanvasView->ZoomLevel();
907128277c9SStephan Aßmus 
908128277c9SStephan Aßmus 	switch (key) {
909128277c9SStephan Aßmus 		// commit
910128277c9SStephan Aßmus 		case B_RETURN:
9110e1ba39fSStephan Aßmus 			if (fTransformBox) {
9124fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9134fac07a0SStephan Aßmus 			}// else
914128277c9SStephan Aßmus //				_Perform();
915128277c9SStephan Aßmus 			break;
916128277c9SStephan Aßmus 		// cancel
917128277c9SStephan Aßmus 		case B_ESCAPE:
9180e1ba39fSStephan Aßmus 			if (fTransformBox) {
9190e1ba39fSStephan Aßmus 				fTransformBox->Cancel();
9204fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9210e1ba39fSStephan Aßmus 			} else if (fFallBackMode == NEW_PATH) {
922128277c9SStephan Aßmus 				fFallBackMode = SELECT_POINTS;
9234fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9244fac07a0SStephan Aßmus 			}// else
925128277c9SStephan Aßmus //				_Cancel();
926128277c9SStephan Aßmus 			break;
927128277c9SStephan Aßmus 		case 't':
928128277c9SStephan Aßmus 		case 'T':
929128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
930128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
931128277c9SStephan Aßmus 			else
932128277c9SStephan Aßmus 				result = false;
933128277c9SStephan Aßmus 			break;
934128277c9SStephan Aßmus 		// nudging
935128277c9SStephan Aßmus 		case B_UP_ARROW:
936128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, -nudgeDist));
937128277c9SStephan Aßmus 			break;
938128277c9SStephan Aßmus 		case B_DOWN_ARROW:
939128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, nudgeDist));
940128277c9SStephan Aßmus 			break;
941128277c9SStephan Aßmus 		case B_LEFT_ARROW:
942128277c9SStephan Aßmus 			_Nudge(BPoint(-nudgeDist, 0.0));
943128277c9SStephan Aßmus 			break;
944128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
945128277c9SStephan Aßmus 			_Nudge(BPoint(nudgeDist, 0.0));
946128277c9SStephan Aßmus 			break;
947128277c9SStephan Aßmus 
948128277c9SStephan Aßmus 		case B_DELETE:
949128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
950128277c9SStephan Aßmus 				*_command = _Delete();
951128277c9SStephan Aßmus 			else
952128277c9SStephan Aßmus 				result = false;
953128277c9SStephan Aßmus 			break;
954128277c9SStephan Aßmus 
955128277c9SStephan Aßmus 		default:
956128277c9SStephan Aßmus 			result = false;
957128277c9SStephan Aßmus 	}
958128277c9SStephan Aßmus 	return result;
959128277c9SStephan Aßmus }
960128277c9SStephan Aßmus 
961128277c9SStephan Aßmus // HandleKeyUp
962128277c9SStephan Aßmus bool
963128277c9SStephan Aßmus PathManipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
964128277c9SStephan Aßmus {
965128277c9SStephan Aßmus 	bool handled = true;
966128277c9SStephan Aßmus 	switch (key) {
967128277c9SStephan Aßmus 		// nudging
968128277c9SStephan Aßmus 		case B_UP_ARROW:
969128277c9SStephan Aßmus 		case B_DOWN_ARROW:
970128277c9SStephan Aßmus 		case B_LEFT_ARROW:
971128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
9720e1ba39fSStephan Aßmus 			*_command = _FinishNudging();
973128277c9SStephan Aßmus 			break;
974128277c9SStephan Aßmus 		default:
975128277c9SStephan Aßmus 			handled = false;
976128277c9SStephan Aßmus 			break;
977128277c9SStephan Aßmus 	}
978128277c9SStephan Aßmus 	return handled;
979128277c9SStephan Aßmus }
980128277c9SStephan Aßmus 
981128277c9SStephan Aßmus // UpdateCursor
9827c4b3726SStephan Aßmus bool
983128277c9SStephan Aßmus PathManipulator::UpdateCursor()
984128277c9SStephan Aßmus {
9857c4b3726SStephan Aßmus 	if (fTransformBox)
9867c4b3726SStephan Aßmus 		return fTransformBox->UpdateCursor();
9877c4b3726SStephan Aßmus 
988128277c9SStephan Aßmus 	const uchar* cursorData;
989128277c9SStephan Aßmus 	switch (fMode) {
990128277c9SStephan Aßmus 		case ADD_POINT:
991128277c9SStephan Aßmus 			cursorData = kPathAddCursor;
992128277c9SStephan Aßmus 			break;
993128277c9SStephan Aßmus 		case INSERT_POINT:
994128277c9SStephan Aßmus 			cursorData = kPathInsertCursor;
995128277c9SStephan Aßmus 			break;
996128277c9SStephan Aßmus 		case MOVE_POINT:
997128277c9SStephan Aßmus 		case MOVE_POINT_IN:
998128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
999128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
1000128277c9SStephan Aßmus 			cursorData = kPathMoveCursor;
1001128277c9SStephan Aßmus 			break;
1002128277c9SStephan Aßmus 		case CLOSE_PATH:
1003128277c9SStephan Aßmus 			cursorData = kPathCloseCursor;
1004128277c9SStephan Aßmus 			break;
1005128277c9SStephan Aßmus 		case TOGGLE_SHARP:
1006128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
1007128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
1008128277c9SStephan Aßmus 			cursorData = kPathSharpCursor;
1009128277c9SStephan Aßmus 			break;
1010128277c9SStephan Aßmus 		case REMOVE_POINT:
1011128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
1012128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
1013128277c9SStephan Aßmus 			cursorData = kPathRemoveCursor;
1014128277c9SStephan Aßmus 			break;
1015128277c9SStephan Aßmus 		case SELECT_POINTS:
1016128277c9SStephan Aßmus 			cursorData = kPathSelectCursor;
1017128277c9SStephan Aßmus 			break;
1018128277c9SStephan Aßmus 
1019128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
1020128277c9SStephan Aßmus 			cursorData = B_HAND_CURSOR;
1021128277c9SStephan Aßmus 			break;
1022128277c9SStephan Aßmus 
1023128277c9SStephan Aßmus 		case UNDEFINED:
1024128277c9SStephan Aßmus 		default:
1025128277c9SStephan Aßmus 			cursorData = kStopCursor;
1026128277c9SStephan Aßmus 			break;
1027128277c9SStephan Aßmus 	}
1028128277c9SStephan Aßmus 	BCursor cursor(cursorData);
1029128277c9SStephan Aßmus 	fCanvasView->SetViewCursor(&cursor, true);
1030128277c9SStephan Aßmus 	fCanvasView->Sync();
10317c4b3726SStephan Aßmus 
10327c4b3726SStephan Aßmus 	return true;
1033128277c9SStephan Aßmus }
1034128277c9SStephan Aßmus 
1035128277c9SStephan Aßmus // AttachedToView
1036128277c9SStephan Aßmus void
1037128277c9SStephan Aßmus PathManipulator::AttachedToView(BView* view)
1038128277c9SStephan Aßmus {
1039f67876a0SStephan Aßmus 	fCanvasView = dynamic_cast<CanvasView*>(view);
1040128277c9SStephan Aßmus }
1041128277c9SStephan Aßmus 
1042128277c9SStephan Aßmus // DetachedFromView
1043128277c9SStephan Aßmus void
1044128277c9SStephan Aßmus PathManipulator::DetachedFromView(BView* view)
1045128277c9SStephan Aßmus {
1046128277c9SStephan Aßmus 	fCanvasView = NULL;
1047128277c9SStephan Aßmus }
1048128277c9SStephan Aßmus 
1049128277c9SStephan Aßmus // #pragma mark -
1050128277c9SStephan Aßmus 
1051128277c9SStephan Aßmus // ObjectChanged
1052128277c9SStephan Aßmus void
1053128277c9SStephan Aßmus PathManipulator::ObjectChanged(const Observable* object)
1054128277c9SStephan Aßmus {
1055128277c9SStephan Aßmus 	// TODO: refine VectorPath listener interface and
1056128277c9SStephan Aßmus 	// implement more efficiently
1057128277c9SStephan Aßmus 	BRect currentBounds = _ControlPointRect();
1058128277c9SStephan Aßmus 	_InvalidateCanvas(currentBounds | fPreviousBounds);
1059128277c9SStephan Aßmus 	fPreviousBounds = currentBounds;
1060128277c9SStephan Aßmus 
1061128277c9SStephan Aßmus 	// reevaluate mode
10620e1ba39fSStephan Aßmus 	if (!fMouseDown && !fTransformBox)
1063128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1064128277c9SStephan Aßmus }
1065128277c9SStephan Aßmus 
1066128277c9SStephan Aßmus // #pragma mark -
1067128277c9SStephan Aßmus 
106805fd3818SStephan Aßmus // PointAdded
106905fd3818SStephan Aßmus void
107005fd3818SStephan Aßmus PathManipulator::PointAdded(int32 index)
107105fd3818SStephan Aßmus {
107205fd3818SStephan Aßmus 	ObjectChanged(fPath);
107305fd3818SStephan Aßmus }
107405fd3818SStephan Aßmus 
107505fd3818SStephan Aßmus // PointRemoved
107605fd3818SStephan Aßmus void
107705fd3818SStephan Aßmus PathManipulator::PointRemoved(int32 index)
107805fd3818SStephan Aßmus {
10790e1ba39fSStephan Aßmus 	fSelection->Remove(index);
108005fd3818SStephan Aßmus 	ObjectChanged(fPath);
108105fd3818SStephan Aßmus }
108205fd3818SStephan Aßmus 
108305fd3818SStephan Aßmus // PointChanged
108405fd3818SStephan Aßmus void
108505fd3818SStephan Aßmus PathManipulator::PointChanged(int32 index)
108605fd3818SStephan Aßmus {
108705fd3818SStephan Aßmus 	ObjectChanged(fPath);
108805fd3818SStephan Aßmus }
108905fd3818SStephan Aßmus 
109005fd3818SStephan Aßmus // PathChanged
109105fd3818SStephan Aßmus void
109205fd3818SStephan Aßmus PathManipulator::PathChanged()
109305fd3818SStephan Aßmus {
109405fd3818SStephan Aßmus 	ObjectChanged(fPath);
109505fd3818SStephan Aßmus }
109605fd3818SStephan Aßmus 
109705fd3818SStephan Aßmus // PathClosedChanged
109805fd3818SStephan Aßmus void
109905fd3818SStephan Aßmus PathManipulator::PathClosedChanged()
110005fd3818SStephan Aßmus {
110105fd3818SStephan Aßmus 	ObjectChanged(fPath);
110205fd3818SStephan Aßmus }
110305fd3818SStephan Aßmus 
110405fd3818SStephan Aßmus // PathReversed
110505fd3818SStephan Aßmus void
110605fd3818SStephan Aßmus PathManipulator::PathReversed()
110705fd3818SStephan Aßmus {
11080e1ba39fSStephan Aßmus 	// reverse selection along with path
11090e1ba39fSStephan Aßmus 	int32 count = fSelection->CountItems();
11100e1ba39fSStephan Aßmus 	int32 pointCount = fPath->CountPoints();
11110e1ba39fSStephan Aßmus 	if (count > 0) {
11120e1ba39fSStephan Aßmus 		Selection temp;
11130e1ba39fSStephan Aßmus 		for (int32 i = 0; i < count; i++) {
11140e1ba39fSStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
11150e1ba39fSStephan Aßmus 		}
11160e1ba39fSStephan Aßmus 		*fSelection = temp;
11170e1ba39fSStephan Aßmus 	}
11180e1ba39fSStephan Aßmus 
111905fd3818SStephan Aßmus 	ObjectChanged(fPath);
112005fd3818SStephan Aßmus }
112105fd3818SStephan Aßmus 
112205fd3818SStephan Aßmus // #pragma mark -
112305fd3818SStephan Aßmus 
1124128277c9SStephan Aßmus // ControlFlags
1125128277c9SStephan Aßmus uint32
1126128277c9SStephan Aßmus PathManipulator::ControlFlags() const
1127128277c9SStephan Aßmus {
1128128277c9SStephan Aßmus 	uint32 flags = 0;
1129128277c9SStephan Aßmus 
1130128277c9SStephan Aßmus //	flags |= SHAPE_UI_FLAGS_CAN_REVERSE_PATH;
1131128277c9SStephan Aßmus //
1132128277c9SStephan Aßmus //	if (!fSelection->IsEmpty())
1133128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_HAS_SELECTION;
1134128277c9SStephan Aßmus //	if (fPath->CountPoints() > 1)
1135128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_CAN_CLOSE_PATH;
1136128277c9SStephan Aßmus //	if (fPath->IsClosed())
1137128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_PATH_IS_CLOSED;
11380e1ba39fSStephan Aßmus //	if (fTransformBox)
11390e1ba39fSStephan Aßmus //		flags |= SHAPE_UI_FLAGS_IS_TRANSFORMING;
1140128277c9SStephan Aßmus 
1141128277c9SStephan Aßmus 	return flags;
1142128277c9SStephan Aßmus }
1143128277c9SStephan Aßmus 
1144128277c9SStephan Aßmus // ReversePath
1145128277c9SStephan Aßmus void
1146128277c9SStephan Aßmus PathManipulator::ReversePath()
1147128277c9SStephan Aßmus {
1148128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1149128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1150128277c9SStephan Aßmus 	if (count > 0) {
1151128277c9SStephan Aßmus 		Selection temp;
1152128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1153128277c9SStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
1154128277c9SStephan Aßmus 		}
1155128277c9SStephan Aßmus 		*fSelection = temp;
1156128277c9SStephan Aßmus 	}
1157128277c9SStephan Aßmus 	fPath->Reverse();
1158128277c9SStephan Aßmus }
1159128277c9SStephan Aßmus 
1160128277c9SStephan Aßmus // #pragma mark -
1161128277c9SStephan Aßmus 
1162128277c9SStephan Aßmus // _SetMode
1163128277c9SStephan Aßmus void
1164128277c9SStephan Aßmus PathManipulator::_SetMode(uint32 mode)
1165128277c9SStephan Aßmus {
1166128277c9SStephan Aßmus 	if (fMode != mode) {
1167128277c9SStephan Aßmus //printf("switching mode: %s -> %s\n", string_for_mode(fMode), string_for_mode(mode));
1168128277c9SStephan Aßmus 		fMode = mode;
1169128277c9SStephan Aßmus 
11700e1ba39fSStephan Aßmus 		if (fMode == TRANSFORM_POINTS) {
11710e1ba39fSStephan Aßmus 			_SetTransformBox(new TransformPointsBox(fCanvasView,
11720e1ba39fSStephan Aßmus 													this,
11730e1ba39fSStephan Aßmus 													fPath,
11740e1ba39fSStephan Aßmus 													fSelection->Items(),
11750e1ba39fSStephan Aßmus 													fSelection->CountItems()));
11760e1ba39fSStephan Aßmus //			fCanvasView->Perform(new EnterTransformPointsCommand(this,
11770e1ba39fSStephan Aßmus //														  fSelection->Items(),
11780e1ba39fSStephan Aßmus //														  fSelection->CountItems()));
11790e1ba39fSStephan Aßmus 		} else {
11800e1ba39fSStephan Aßmus 			if (fTransformBox)
11810e1ba39fSStephan Aßmus 				_SetTransformBox(NULL);
11820e1ba39fSStephan Aßmus 		}
11830e1ba39fSStephan Aßmus 
1184128277c9SStephan Aßmus 		if (BWindow* window = fCanvasView->Window()) {
1185128277c9SStephan Aßmus 			window->PostMessage(MSG_UPDATE_SHAPE_UI);
1186128277c9SStephan Aßmus 		}
1187128277c9SStephan Aßmus 		UpdateCursor();
1188128277c9SStephan Aßmus 	}
1189128277c9SStephan Aßmus }
1190128277c9SStephan Aßmus 
11910e1ba39fSStephan Aßmus 
11920e1ba39fSStephan Aßmus // _SetTransformBox
11930e1ba39fSStephan Aßmus void
11940e1ba39fSStephan Aßmus PathManipulator::_SetTransformBox(TransformPointsBox* transformBox)
11950e1ba39fSStephan Aßmus {
11960e1ba39fSStephan Aßmus 	if (fTransformBox == transformBox)
11970e1ba39fSStephan Aßmus 		return;
11980e1ba39fSStephan Aßmus 
11990e1ba39fSStephan Aßmus 	BRect dirty(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN);
12000e1ba39fSStephan Aßmus 	if (fTransformBox) {
12010e1ba39fSStephan Aßmus 		// get rid of transform box display
12020e1ba39fSStephan Aßmus 		dirty = fTransformBox->Bounds();
12030e1ba39fSStephan Aßmus 		delete fTransformBox;
12040e1ba39fSStephan Aßmus 	}
12050e1ba39fSStephan Aßmus 
12060e1ba39fSStephan Aßmus 	fTransformBox = transformBox;
12070e1ba39fSStephan Aßmus 
12084fac07a0SStephan Aßmus 	// TODO: this is weird, fMode should only be set in _SetMode, not
12094fac07a0SStephan Aßmus 	// here as well, also this method could be called this way
12104fac07a0SStephan Aßmus 	// _SetModeForMousePos -> _SetMode -> _SetTransformBox
12114fac07a0SStephan Aßmus 	// and then below it does _SetModeForMousePos again...
12120e1ba39fSStephan Aßmus 	if (fTransformBox) {
12130e1ba39fSStephan Aßmus 		fTransformBox->MouseMoved(fLastCanvasPos);
12140e1ba39fSStephan Aßmus 		if (fMode != TRANSFORM_POINTS) {
12150e1ba39fSStephan Aßmus 			fMode = TRANSFORM_POINTS;
12160e1ba39fSStephan Aßmus 		}
12170e1ba39fSStephan Aßmus 		dirty = dirty | fTransformBox->Bounds();
12180e1ba39fSStephan Aßmus 	} else {
12190e1ba39fSStephan Aßmus 		if (fMode == TRANSFORM_POINTS) {
12200e1ba39fSStephan Aßmus 			_SetModeForMousePos(fLastCanvasPos);
12210e1ba39fSStephan Aßmus 		}
12220e1ba39fSStephan Aßmus 	}
12230e1ba39fSStephan Aßmus 
12240e1ba39fSStephan Aßmus 	if (dirty.IsValid()) {
12250e1ba39fSStephan Aßmus 		dirty.InsetBy(-8, -8);
12260e1ba39fSStephan Aßmus 		fCanvasView->Invalidate(dirty);
12270e1ba39fSStephan Aßmus 	}
12280e1ba39fSStephan Aßmus }
12290e1ba39fSStephan Aßmus 
1230128277c9SStephan Aßmus // _AddPoint
1231128277c9SStephan Aßmus void
1232128277c9SStephan Aßmus PathManipulator::_AddPoint(BPoint where)
1233128277c9SStephan Aßmus {
1234128277c9SStephan Aßmus 	if (fPath->AddPoint(where)) {
1235128277c9SStephan Aßmus 		fCurrentPathPoint = fPath->CountPoints() - 1;
1236128277c9SStephan Aßmus 
1237128277c9SStephan Aßmus 		delete fAddPointCommand;
1238128277c9SStephan Aßmus 		fAddPointCommand = new AddPointCommand(fPath, fCurrentPathPoint,
1239128277c9SStephan Aßmus 											   fSelection->Items(),
1240128277c9SStephan Aßmus 											   fSelection->CountItems());
1241128277c9SStephan Aßmus 
1242128277c9SStephan Aßmus 		_Select(fCurrentPathPoint, fShiftDown);
1243128277c9SStephan Aßmus 	}
1244128277c9SStephan Aßmus }
1245128277c9SStephan Aßmus 
1246128277c9SStephan Aßmus // scale_point
1247128277c9SStephan Aßmus BPoint
1248128277c9SStephan Aßmus scale_point(BPoint a, BPoint b, float scale)
1249128277c9SStephan Aßmus {
1250128277c9SStephan Aßmus 	return BPoint(a.x + (b.x - a.x) * scale,
1251128277c9SStephan Aßmus 				  a.y + (b.y - a.y) * scale);
1252128277c9SStephan Aßmus }
1253128277c9SStephan Aßmus 
1254128277c9SStephan Aßmus // _InsertPoint
1255128277c9SStephan Aßmus void
1256128277c9SStephan Aßmus PathManipulator::_InsertPoint(BPoint where, int32 index)
1257128277c9SStephan Aßmus {
1258128277c9SStephan Aßmus 	double scale;
1259128277c9SStephan Aßmus 
1260128277c9SStephan Aßmus 	BPoint point;
1261128277c9SStephan Aßmus 	BPoint pointIn;
1262128277c9SStephan Aßmus 	BPoint pointOut;
1263128277c9SStephan Aßmus 
1264128277c9SStephan Aßmus 	BPoint previous;
1265128277c9SStephan Aßmus 	BPoint previousOut;
1266128277c9SStephan Aßmus 	BPoint next;
1267128277c9SStephan Aßmus 	BPoint nextIn;
1268128277c9SStephan Aßmus 
1269128277c9SStephan Aßmus 	if (fPath->FindBezierScale(index - 1, where, &scale)
1270128277c9SStephan Aßmus 		&& scale >= 0.0 && scale <= 1.0
1271128277c9SStephan Aßmus 		&& fPath->GetPoint(index - 1, scale, point)) {
1272128277c9SStephan Aßmus 
1273128277c9SStephan Aßmus 		fPath->GetPointAt(index - 1, previous);
1274128277c9SStephan Aßmus 		fPath->GetPointOutAt(index - 1, previousOut);
1275128277c9SStephan Aßmus 		fPath->GetPointAt(index, next);
1276128277c9SStephan Aßmus 		fPath->GetPointInAt(index, nextIn);
1277128277c9SStephan Aßmus 
1278128277c9SStephan Aßmus 		where = scale_point(previousOut, nextIn, scale);
1279128277c9SStephan Aßmus 
1280128277c9SStephan Aßmus 		previousOut = scale_point(previous, previousOut, scale);
1281128277c9SStephan Aßmus 		nextIn = scale_point(next, nextIn, 1 - scale);
1282128277c9SStephan Aßmus 		pointIn = scale_point(previousOut, where, scale);
1283128277c9SStephan Aßmus 		pointOut = scale_point(nextIn, where, 1 - scale);
1284128277c9SStephan Aßmus 
1285128277c9SStephan Aßmus 		if (fPath->AddPoint(point, index)) {
1286128277c9SStephan Aßmus 
1287128277c9SStephan Aßmus 			fPath->SetPointIn(index, pointIn);
1288128277c9SStephan Aßmus 			fPath->SetPointOut(index, pointOut);
1289128277c9SStephan Aßmus 
1290128277c9SStephan Aßmus 			delete fInsertPointCommand;
1291128277c9SStephan Aßmus 			fInsertPointCommand = new InsertPointCommand(fPath, index,
1292128277c9SStephan Aßmus 														 fSelection->Items(),
1293128277c9SStephan Aßmus 														 fSelection->CountItems());
1294128277c9SStephan Aßmus 
1295128277c9SStephan Aßmus 			fPath->SetPointOut(index - 1, previousOut);
1296128277c9SStephan Aßmus 			fPath->SetPointIn(index + 1, nextIn);
1297128277c9SStephan Aßmus 
1298128277c9SStephan Aßmus 			fCurrentPathPoint = index;
1299128277c9SStephan Aßmus 			_ShiftSelection(fCurrentPathPoint, 1);
1300128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
1301128277c9SStephan Aßmus 		}
1302128277c9SStephan Aßmus 	}
1303128277c9SStephan Aßmus }
1304128277c9SStephan Aßmus 
1305128277c9SStephan Aßmus // _SetInOutConnected
1306128277c9SStephan Aßmus void
1307128277c9SStephan Aßmus PathManipulator::_SetInOutConnected(int32 index, bool connected)
1308128277c9SStephan Aßmus {
1309128277c9SStephan Aßmus 	fPath->SetInOutConnected(index, connected);
1310128277c9SStephan Aßmus }
1311128277c9SStephan Aßmus 
1312128277c9SStephan Aßmus // _SetSharp
1313128277c9SStephan Aßmus void
1314128277c9SStephan Aßmus PathManipulator::_SetSharp(int32 index)
1315128277c9SStephan Aßmus {
1316128277c9SStephan Aßmus 	BPoint p;
1317128277c9SStephan Aßmus 	fPath->GetPointAt(index, p);
1318128277c9SStephan Aßmus 	fPath->SetPoint(index, p, p, p, true);
1319128277c9SStephan Aßmus }
1320128277c9SStephan Aßmus 
1321128277c9SStephan Aßmus // _RemoveSelection
1322128277c9SStephan Aßmus void
1323128277c9SStephan Aßmus PathManipulator::_RemoveSelection()
1324128277c9SStephan Aßmus {
13250e1ba39fSStephan Aßmus 	// NOTE: copy selection since removing points will
13260e1ba39fSStephan Aßmus 	// trigger notifications, and that will influence the
13270e1ba39fSStephan Aßmus 	// selection
13280e1ba39fSStephan Aßmus 	Selection selection = *fSelection;
13290e1ba39fSStephan Aßmus 	int32 count = selection.CountItems();
1330128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
13310e1ba39fSStephan Aßmus 		if (!fPath->RemovePoint(selection.IndexAt(i) - i))
1332128277c9SStephan Aßmus 			break;
1333128277c9SStephan Aßmus 	}
1334128277c9SStephan Aßmus 
13350e1ba39fSStephan Aßmus 	fPath->SetClosed(fPath->IsClosed() && fPath->CountPoints() > 1);
1336128277c9SStephan Aßmus 
1337128277c9SStephan Aßmus 	fSelection->MakeEmpty();
1338128277c9SStephan Aßmus }
1339128277c9SStephan Aßmus 
1340128277c9SStephan Aßmus 
1341128277c9SStephan Aßmus // _RemovePoint
1342128277c9SStephan Aßmus void
1343128277c9SStephan Aßmus PathManipulator::_RemovePoint(int32 index)
1344128277c9SStephan Aßmus {
1345128277c9SStephan Aßmus 	if (fPath->RemovePoint(index)) {
1346128277c9SStephan Aßmus 		_Deselect(index);
1347128277c9SStephan Aßmus 		_ShiftSelection(index + 1, -1);
1348128277c9SStephan Aßmus 	}
1349128277c9SStephan Aßmus }
1350128277c9SStephan Aßmus 
1351128277c9SStephan Aßmus // _RemovePointIn
1352128277c9SStephan Aßmus void
1353128277c9SStephan Aßmus PathManipulator::_RemovePointIn(int32 index)
1354128277c9SStephan Aßmus {
1355128277c9SStephan Aßmus 	BPoint p;
1356128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1357128277c9SStephan Aßmus 		fPath->SetPointIn(index, p);
1358128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1359128277c9SStephan Aßmus 	}
1360128277c9SStephan Aßmus }
1361128277c9SStephan Aßmus 
1362128277c9SStephan Aßmus // _RemovePointOut
1363128277c9SStephan Aßmus void
1364128277c9SStephan Aßmus PathManipulator::_RemovePointOut(int32 index)
1365128277c9SStephan Aßmus {
1366128277c9SStephan Aßmus 	BPoint p;
1367128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1368128277c9SStephan Aßmus 		fPath->SetPointOut(index, p);
1369128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1370128277c9SStephan Aßmus 	}
1371128277c9SStephan Aßmus }
1372128277c9SStephan Aßmus 
1373128277c9SStephan Aßmus // _Delete
1374128277c9SStephan Aßmus Command*
1375128277c9SStephan Aßmus PathManipulator::_Delete()
1376128277c9SStephan Aßmus {
1377128277c9SStephan Aßmus 	Command* command = NULL;
1378128277c9SStephan Aßmus 	if (!fMouseDown) {
13790e1ba39fSStephan Aßmus 		// make sure we apply an on-going transformation before we proceed
13800e1ba39fSStephan Aßmus 		if (fTransformBox) {
13810e1ba39fSStephan Aßmus 			_SetTransformBox(NULL);
13820e1ba39fSStephan Aßmus 		}
13830e1ba39fSStephan Aßmus 
1384128277c9SStephan Aßmus 		if (fSelection->CountItems() == fPath->CountPoints()) {
1385128277c9SStephan Aßmus //			command = new RemovePathCommand(fPath);
1386128277c9SStephan Aßmus 		} else {
1387128277c9SStephan Aßmus 			command = new RemovePointsCommand(fPath,
1388128277c9SStephan Aßmus 											  fSelection->Items(),
1389128277c9SStephan Aßmus 											  fSelection->CountItems());
1390128277c9SStephan Aßmus 			_RemoveSelection();
1391128277c9SStephan Aßmus 		}
1392128277c9SStephan Aßmus 
1393128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1394128277c9SStephan Aßmus 	}
1395128277c9SStephan Aßmus 
1396128277c9SStephan Aßmus 	return command;
1397128277c9SStephan Aßmus }
1398128277c9SStephan Aßmus 
1399128277c9SStephan Aßmus // #pragma mark -
1400128277c9SStephan Aßmus 
1401128277c9SStephan Aßmus // _Select
1402128277c9SStephan Aßmus void
1403128277c9SStephan Aßmus PathManipulator::_Select(BRect r)
1404128277c9SStephan Aßmus {
1405128277c9SStephan Aßmus 	BPoint p;
1406f4bd80a2SStephan Aßmus 	BPoint pIn;
1407f4bd80a2SStephan Aßmus 	BPoint pOut;
1408128277c9SStephan Aßmus 	int32 count = fPath->CountPoints();
1409128277c9SStephan Aßmus 	Selection temp;
1410f4bd80a2SStephan Aßmus 	for (int32 i = 0; i < count && fPath->GetPointsAt(i, p, pIn, pOut); i++) {
1411f4bd80a2SStephan Aßmus 		if (r.Contains(p) || r.Contains(pIn) || r.Contains(pOut)) {
1412128277c9SStephan Aßmus 			temp.Add(i);
1413128277c9SStephan Aßmus 		}
1414128277c9SStephan Aßmus 	}
1415128277c9SStephan Aßmus 	// merge old and new selection
1416128277c9SStephan Aßmus 	count = fOldSelection->CountItems();
1417128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
1418128277c9SStephan Aßmus 		int32 index = fOldSelection->IndexAt(i);
1419128277c9SStephan Aßmus 		if (temp.Contains(index))
1420128277c9SStephan Aßmus 			temp.Remove(index);
1421128277c9SStephan Aßmus 		else
1422128277c9SStephan Aßmus 			temp.Add(index);
1423128277c9SStephan Aßmus 	}
1424128277c9SStephan Aßmus 	if (temp != *fSelection) {
1425128277c9SStephan Aßmus 		*fSelection = temp;
1426128277c9SStephan Aßmus 		_UpdateSelection();
1427128277c9SStephan Aßmus 	}
1428128277c9SStephan Aßmus }
1429128277c9SStephan Aßmus 
1430128277c9SStephan Aßmus // _Select
1431128277c9SStephan Aßmus void
1432128277c9SStephan Aßmus PathManipulator::_Select(int32 index, bool extend)
1433128277c9SStephan Aßmus {
1434128277c9SStephan Aßmus 	if (!extend)
1435128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1436128277c9SStephan Aßmus 	if (fSelection->Contains(index))
1437128277c9SStephan Aßmus 		fSelection->Remove(index);
1438128277c9SStephan Aßmus 	else
1439128277c9SStephan Aßmus 		fSelection->Add(index);
1440128277c9SStephan Aßmus 	// TODO: this can lead to unnecessary invalidation (maybe need to investigate)
1441128277c9SStephan Aßmus 	_UpdateSelection();
1442128277c9SStephan Aßmus }
1443128277c9SStephan Aßmus 
1444128277c9SStephan Aßmus // _Select
1445128277c9SStephan Aßmus void
1446128277c9SStephan Aßmus PathManipulator::_Select(const int32* indices, int32 count, bool extend)
1447128277c9SStephan Aßmus {
1448128277c9SStephan Aßmus 	if (extend) {
1449128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1450128277c9SStephan Aßmus 			if (!fSelection->Contains(indices[i]))
1451128277c9SStephan Aßmus 				fSelection->Add(indices[i]);
1452128277c9SStephan Aßmus 		}
1453128277c9SStephan Aßmus 	} else {
1454128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1455128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1456128277c9SStephan Aßmus 			fSelection->Add(indices[i]);
1457128277c9SStephan Aßmus 		}
1458128277c9SStephan Aßmus 	}
1459128277c9SStephan Aßmus 	_UpdateSelection();
1460128277c9SStephan Aßmus }
1461128277c9SStephan Aßmus 
1462128277c9SStephan Aßmus // _Deselect
1463128277c9SStephan Aßmus void
1464128277c9SStephan Aßmus PathManipulator::_Deselect(int32 index)
1465128277c9SStephan Aßmus {
1466128277c9SStephan Aßmus 	if (fSelection->Contains(index)) {
1467128277c9SStephan Aßmus 		fSelection->Remove(index);
1468128277c9SStephan Aßmus 		_UpdateSelection();
1469128277c9SStephan Aßmus 	}
1470128277c9SStephan Aßmus }
1471128277c9SStephan Aßmus 
1472128277c9SStephan Aßmus // _ShiftSelection
1473128277c9SStephan Aßmus void
1474128277c9SStephan Aßmus PathManipulator::_ShiftSelection(int32 startIndex, int32 direction)
1475128277c9SStephan Aßmus {
1476128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1477128277c9SStephan Aßmus 	if (count > 0) {
1478128277c9SStephan Aßmus 		int32* selection = fSelection->Items();
1479128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1480128277c9SStephan Aßmus 			if (selection[i] >= startIndex) {
1481128277c9SStephan Aßmus 				selection[i] += direction;
1482128277c9SStephan Aßmus 			}
1483128277c9SStephan Aßmus 		}
1484128277c9SStephan Aßmus 	}
1485128277c9SStephan Aßmus 	_UpdateSelection();
1486128277c9SStephan Aßmus }
1487128277c9SStephan Aßmus 
1488128277c9SStephan Aßmus // _IsSelected
1489128277c9SStephan Aßmus bool
1490128277c9SStephan Aßmus PathManipulator::_IsSelected(int32 index) const
1491128277c9SStephan Aßmus {
1492128277c9SStephan Aßmus 	return fSelection->Contains(index);
1493128277c9SStephan Aßmus }
1494128277c9SStephan Aßmus 
1495128277c9SStephan Aßmus // #pragma mark -
1496128277c9SStephan Aßmus 
1497128277c9SStephan Aßmus // _InvalidateCanvas
1498128277c9SStephan Aßmus void
1499128277c9SStephan Aßmus PathManipulator::_InvalidateCanvas(BRect rect) const
1500128277c9SStephan Aßmus {
1501f67876a0SStephan Aßmus 	// convert from canvas to view space
1502f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&rect);
1503128277c9SStephan Aßmus 	fCanvasView->Invalidate(rect);
1504128277c9SStephan Aßmus }
1505128277c9SStephan Aßmus 
1506128277c9SStephan Aßmus // _InvalidateHighlightPoints
1507128277c9SStephan Aßmus void
1508128277c9SStephan Aßmus PathManipulator::_InvalidateHighlightPoints(int32 newIndex, uint32 newMode)
1509128277c9SStephan Aßmus {
1510128277c9SStephan Aßmus 	BRect oldRect = _ControlPointRect(fCurrentPathPoint, fMode);
1511128277c9SStephan Aßmus 	BRect newRect = _ControlPointRect(newIndex, newMode);
1512128277c9SStephan Aßmus 	if (oldRect.IsValid())
1513128277c9SStephan Aßmus 		_InvalidateCanvas(oldRect);
1514128277c9SStephan Aßmus 	if (newRect.IsValid())
1515128277c9SStephan Aßmus 		_InvalidateCanvas(newRect);
1516128277c9SStephan Aßmus }
1517128277c9SStephan Aßmus 
1518128277c9SStephan Aßmus // _UpdateSelection
1519128277c9SStephan Aßmus void
1520128277c9SStephan Aßmus PathManipulator::_UpdateSelection() const
1521128277c9SStephan Aßmus {
1522128277c9SStephan Aßmus 	_InvalidateCanvas(_ControlPointRect());
1523128277c9SStephan Aßmus 	if (BWindow* window = fCanvasView->Window()) {
1524128277c9SStephan Aßmus 		window->PostMessage(MSG_UPDATE_SHAPE_UI);
1525128277c9SStephan Aßmus 	}
1526128277c9SStephan Aßmus }
1527128277c9SStephan Aßmus 
1528128277c9SStephan Aßmus // _ControlPointRect
1529128277c9SStephan Aßmus BRect
1530128277c9SStephan Aßmus PathManipulator::_ControlPointRect() const
1531128277c9SStephan Aßmus {
1532128277c9SStephan Aßmus 	BRect r = fPath->ControlPointBounds();
1533128277c9SStephan Aßmus 	r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1534128277c9SStephan Aßmus 	return r;
1535128277c9SStephan Aßmus }
1536128277c9SStephan Aßmus 
1537128277c9SStephan Aßmus // _ControlPointRect
1538128277c9SStephan Aßmus BRect
1539128277c9SStephan Aßmus PathManipulator::_ControlPointRect(int32 index, uint32 mode) const
1540128277c9SStephan Aßmus {
1541128277c9SStephan Aßmus 	BRect rect(0.0, 0.0, -1.0, -1.0);
1542128277c9SStephan Aßmus 	if (index >= 0) {
1543128277c9SStephan Aßmus 		BPoint p, pIn, pOut;
1544128277c9SStephan Aßmus 		fPath->GetPointsAt(index, p, pIn, pOut);
1545128277c9SStephan Aßmus 		switch (mode) {
1546128277c9SStephan Aßmus 			case MOVE_POINT:
1547128277c9SStephan Aßmus 			case TOGGLE_SHARP:
1548128277c9SStephan Aßmus 			case REMOVE_POINT:
1549128277c9SStephan Aßmus 			case CLOSE_PATH:
1550128277c9SStephan Aßmus 				rect.Set(p.x, p.y, p.x, p.y);
1551128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1552128277c9SStephan Aßmus 				break;
1553128277c9SStephan Aßmus 			case MOVE_POINT_IN:
1554128277c9SStephan Aßmus 			case TOGGLE_SHARP_IN:
1555128277c9SStephan Aßmus 			case REMOVE_POINT_IN:
1556128277c9SStephan Aßmus 				rect.Set(pIn.x, pIn.y, pIn.x, pIn.y);
1557128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1558128277c9SStephan Aßmus 				break;
1559128277c9SStephan Aßmus 			case MOVE_POINT_OUT:
1560128277c9SStephan Aßmus 			case TOGGLE_SHARP_OUT:
1561128277c9SStephan Aßmus 			case REMOVE_POINT_OUT:
1562128277c9SStephan Aßmus 				rect.Set(pOut.x, pOut.y, pOut.x, pOut.y);
1563128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1564128277c9SStephan Aßmus 				break;
1565128277c9SStephan Aßmus 			case SELECT_POINTS:
1566128277c9SStephan Aßmus 				rect.Set(min4(p.x, pIn.x, pOut.x, pOut.x),
1567128277c9SStephan Aßmus 						 min4(p.y, pIn.y, pOut.y, pOut.y),
1568128277c9SStephan Aßmus 						 max4(p.x, pIn.x, pOut.x, pOut.x),
1569128277c9SStephan Aßmus 						 max4(p.y, pIn.y, pOut.y, pOut.y));
1570128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1571128277c9SStephan Aßmus 				break;
1572128277c9SStephan Aßmus 		}
1573128277c9SStephan Aßmus 	}
1574128277c9SStephan Aßmus 	return rect;
1575128277c9SStephan Aßmus }
1576128277c9SStephan Aßmus 
1577128277c9SStephan Aßmus // #pragma mark -
1578128277c9SStephan Aßmus 
1579128277c9SStephan Aßmus // _SetModeForMousePos
1580128277c9SStephan Aßmus void
1581128277c9SStephan Aßmus PathManipulator::_SetModeForMousePos(BPoint where)
1582128277c9SStephan Aßmus {
1583128277c9SStephan Aßmus 	uint32 mode = UNDEFINED;
1584128277c9SStephan Aßmus 	int32 index = -1;
1585128277c9SStephan Aßmus 
1586f67876a0SStephan Aßmus 	float zoomLevel = fCanvasView->ZoomLevel();
1587128277c9SStephan Aßmus 
1588128277c9SStephan Aßmus 	// see if we're close enough at a control point
1589128277c9SStephan Aßmus 	BPoint point;
1590128277c9SStephan Aßmus 	BPoint pointIn;
1591128277c9SStephan Aßmus 	BPoint pointOut;
1592128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut)
1593128277c9SStephan Aßmus 					  && mode == UNDEFINED; i++) {
1594128277c9SStephan Aßmus 
1595128277c9SStephan Aßmus 		float distM = point_point_distance(point, where) * zoomLevel;
1596128277c9SStephan Aßmus 		float distIn = point_point_distance(pointIn, where) * zoomLevel;
1597128277c9SStephan Aßmus 		float distOut = point_point_distance(pointOut, where) * zoomLevel;
1598128277c9SStephan Aßmus 
1599128277c9SStephan Aßmus 		if (distM < MOVE_THRESHOLD) {
1600128277c9SStephan Aßmus 			if (i == 0 && fClickToClose
1601128277c9SStephan Aßmus 				&& !fPath->IsClosed() && fPath->CountPoints() > 1) {
1602128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1603128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : CLOSE_PATH);
1604128277c9SStephan Aßmus 				index = i;
1605128277c9SStephan Aßmus 			} else {
1606128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1607128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : MOVE_POINT);
1608128277c9SStephan Aßmus 				index = i;
1609128277c9SStephan Aßmus 			}
1610128277c9SStephan Aßmus 		}
16110e1ba39fSStephan Aßmus 		if (distM - distIn > 0.00001
16120e1ba39fSStephan Aßmus 			&& distIn < MOVE_THRESHOLD) {
1613128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_IN :
1614128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_IN : MOVE_POINT_IN);
1615128277c9SStephan Aßmus 			index = i;
1616128277c9SStephan Aßmus 		}
16170e1ba39fSStephan Aßmus 		if (distIn - distOut > 0.00001
16180e1ba39fSStephan Aßmus 			&& distOut < distM && distOut < MOVE_THRESHOLD) {
1619128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_OUT :
1620128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_OUT : MOVE_POINT_OUT);
1621128277c9SStephan Aßmus 			index = i;
1622128277c9SStephan Aßmus 		}
1623128277c9SStephan Aßmus 	}
1624128277c9SStephan Aßmus 	// selection mode overrides any other mode,
1625128277c9SStephan Aßmus 	// but we need to check for it after we know
1626128277c9SStephan Aßmus 	// the index of the point under the mouse (code above)
1627128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1628128277c9SStephan Aßmus 	if (fShiftDown && pointCount > 0) {
1629128277c9SStephan Aßmus 		mode = SELECT_POINTS;
1630128277c9SStephan Aßmus 	}
1631128277c9SStephan Aßmus 
1632128277c9SStephan Aßmus 	// see if user wants to start new sub path
1633128277c9SStephan Aßmus 	if (fAltDown) {
1634128277c9SStephan Aßmus 		mode = NEW_PATH;
1635128277c9SStephan Aßmus 		index = -1;
1636128277c9SStephan Aßmus 	}
1637128277c9SStephan Aßmus 
1638128277c9SStephan Aßmus 	// see if we're close enough at a line
1639128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1640128277c9SStephan Aßmus 		float distance;
1641128277c9SStephan Aßmus 		if (fPath->GetDistance(where, &distance, &index)) {
1642128277c9SStephan Aßmus 			if (distance < (INSERT_DIST_THRESHOLD / zoomLevel)) {
1643128277c9SStephan Aßmus 				mode = INSERT_POINT;
1644128277c9SStephan Aßmus 			}
1645128277c9SStephan Aßmus 		} else {
1646128277c9SStephan Aßmus 			// restore index, since it was changed by call above
1647128277c9SStephan Aßmus 			index = fCurrentPathPoint;
1648128277c9SStephan Aßmus 		}
1649128277c9SStephan Aßmus 	}
1650128277c9SStephan Aßmus 
1651128277c9SStephan Aßmus 	// nope, still undefined mode, last fall back
1652128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1653128277c9SStephan Aßmus 		if (fFallBackMode == SELECT_POINTS) {
1654128277c9SStephan Aßmus 			if (fPath->IsClosed() && pointCount > 0) {
1655128277c9SStephan Aßmus 				mode = SELECT_POINTS;
1656128277c9SStephan Aßmus 				index = -1;
1657128277c9SStephan Aßmus 			} else {
1658128277c9SStephan Aßmus 				mode = ADD_POINT;
1659128277c9SStephan Aßmus 				index = pointCount - 1;
1660128277c9SStephan Aßmus 			}
1661128277c9SStephan Aßmus 		} else {
1662128277c9SStephan Aßmus 			// user had clicked "New Path" icon
1663128277c9SStephan Aßmus 			mode = fFallBackMode;
1664128277c9SStephan Aßmus 		}
1665128277c9SStephan Aßmus 	}
1666128277c9SStephan Aßmus 	// switch mode if necessary
1667128277c9SStephan Aßmus 	if (mode != fMode || index != fCurrentPathPoint) {
1668128277c9SStephan Aßmus 		// invalidate path display (to highlight the respective point)
1669128277c9SStephan Aßmus 		_InvalidateHighlightPoints(index, mode);
1670128277c9SStephan Aßmus 		_SetMode(mode);
1671128277c9SStephan Aßmus 		fCurrentPathPoint = index;
1672128277c9SStephan Aßmus 	}
1673128277c9SStephan Aßmus }
1674128277c9SStephan Aßmus 
1675128277c9SStephan Aßmus // #pragma mark -
1676128277c9SStephan Aßmus 
1677128277c9SStephan Aßmus // _Nudge
1678128277c9SStephan Aßmus void
1679128277c9SStephan Aßmus PathManipulator::_Nudge(BPoint direction)
1680128277c9SStephan Aßmus {
1681128277c9SStephan Aßmus 	bigtime_t now = system_time();
1682128277c9SStephan Aßmus 	if (now - fLastNudgeTime > 500000) {
16830e1ba39fSStephan Aßmus 		fCanvasView->Perform(_FinishNudging());
1684128277c9SStephan Aßmus 	}
1685128277c9SStephan Aßmus 	fLastNudgeTime = now;
1686128277c9SStephan Aßmus 	fNudgeOffset += direction;
1687128277c9SStephan Aßmus 
16880e1ba39fSStephan Aßmus 	if (fTransformBox) {
16890e1ba39fSStephan Aßmus 		fTransformBox->NudgeBy(direction);
16900e1ba39fSStephan Aßmus 		return;
16910e1ba39fSStephan Aßmus 	}
16920e1ba39fSStephan Aßmus 
16930e1ba39fSStephan Aßmus 	if (!fNudgeCommand) {
16940e1ba39fSStephan Aßmus 
16950e1ba39fSStephan Aßmus 		bool fromSelection = !fSelection->IsEmpty();
16960e1ba39fSStephan Aßmus 
16970e1ba39fSStephan Aßmus 		int32 count = fromSelection ? fSelection->CountItems()
16980e1ba39fSStephan Aßmus 									: fPath->CountPoints();
16990e1ba39fSStephan Aßmus 		int32 indices[count];
17000e1ba39fSStephan Aßmus 		control_point points[count];
17010e1ba39fSStephan Aßmus 
17020e1ba39fSStephan Aßmus 		// init indices and points
17030e1ba39fSStephan Aßmus 		for (int32 i = 0; i < count; i++) {
17040e1ba39fSStephan Aßmus 			indices[i] = fromSelection ? fSelection->IndexAt(i) : i;
17050e1ba39fSStephan Aßmus 			fPath->GetPointsAt(indices[i],
17060e1ba39fSStephan Aßmus 							   points[i].point,
17070e1ba39fSStephan Aßmus 							   points[i].point_in,
17080e1ba39fSStephan Aßmus 							   points[i].point_out,
17090e1ba39fSStephan Aßmus 							   &points[i].connected);
17100e1ba39fSStephan Aßmus 		}
17110e1ba39fSStephan Aßmus 
17120e1ba39fSStephan Aßmus 		fNudgeCommand = new NudgePointsCommand(fPath, indices, points, count);
17130e1ba39fSStephan Aßmus 
17140e1ba39fSStephan Aßmus 		fNudgeCommand->SetNewTranslation(fNudgeOffset);
17150e1ba39fSStephan Aßmus 		fNudgeCommand->Redo();
17160e1ba39fSStephan Aßmus 
17170e1ba39fSStephan Aßmus 	} else {
17180e1ba39fSStephan Aßmus 		fNudgeCommand->SetNewTranslation(fNudgeOffset);
17190e1ba39fSStephan Aßmus 		fNudgeCommand->Redo();
17200e1ba39fSStephan Aßmus 	}
1721128277c9SStephan Aßmus 
1722128277c9SStephan Aßmus 	if (!fMouseDown)
1723128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1724128277c9SStephan Aßmus }
1725128277c9SStephan Aßmus 
1726128277c9SStephan Aßmus // _FinishNudging
17270e1ba39fSStephan Aßmus Command*
1728128277c9SStephan Aßmus PathManipulator::_FinishNudging()
1729128277c9SStephan Aßmus {
1730128277c9SStephan Aßmus 	fNudgeOffset = BPoint(0.0, 0.0);
1731128277c9SStephan Aßmus 
17320e1ba39fSStephan Aßmus 	Command* command;
17330e1ba39fSStephan Aßmus 
17340e1ba39fSStephan Aßmus 	if (fTransformBox) {
17350e1ba39fSStephan Aßmus 		command = fTransformBox->FinishNudging();
17360e1ba39fSStephan Aßmus 	} else {
17370e1ba39fSStephan Aßmus 		command = fNudgeCommand;
17380e1ba39fSStephan Aßmus 		fNudgeCommand = NULL;
17390e1ba39fSStephan Aßmus 	}
17400e1ba39fSStephan Aßmus 
17410e1ba39fSStephan Aßmus 	return command;
1742128277c9SStephan Aßmus }
1743128277c9SStephan Aßmus 
1744128277c9SStephan Aßmus 
1745