xref: /haiku/src/apps/icon-o-matic/shape/PathManipulator.cpp (revision 128277c969aa806add78941cd2972754c37a1572)
1*128277c9SStephan Aßmus /*
2*128277c9SStephan Aßmus  * Copyright 2006, Haiku.
3*128277c9SStephan Aßmus  * Distributed under the terms of the MIT License.
4*128277c9SStephan Aßmus  *
5*128277c9SStephan Aßmus  * Authors:
6*128277c9SStephan Aßmus  *		Stephan Aßmus <superstippi@gmx.de>
7*128277c9SStephan Aßmus  */
8*128277c9SStephan Aßmus 
9*128277c9SStephan Aßmus #include "PathManipulator.h"
10*128277c9SStephan Aßmus 
11*128277c9SStephan Aßmus #include <float.h>
12*128277c9SStephan Aßmus #include <stdio.h>
13*128277c9SStephan Aßmus 
14*128277c9SStephan Aßmus #include <Cursor.h>
15*128277c9SStephan Aßmus #include <Message.h>
16*128277c9SStephan Aßmus #include <Window.h>
17*128277c9SStephan Aßmus 
18*128277c9SStephan Aßmus #include "cursors.h"
19*128277c9SStephan Aßmus #include "support.h"
20*128277c9SStephan Aßmus 
21*128277c9SStephan Aßmus #include "StateView.h"
22*128277c9SStephan Aßmus #include "VectorPath.h"
23*128277c9SStephan Aßmus 
24*128277c9SStephan Aßmus #include "AddPointCommand.h"
25*128277c9SStephan Aßmus #include "ChangePointCommand.h"
26*128277c9SStephan Aßmus //#include "CloseCommand.h"
27*128277c9SStephan Aßmus #include "InsertPointCommand.h"
28*128277c9SStephan Aßmus //#include "NewPathCommand.h"
29*128277c9SStephan Aßmus //#include "NudgePointsCommand.h"
30*128277c9SStephan Aßmus //#include "RemovePathCommand.h"
31*128277c9SStephan Aßmus #include "RemovePointsCommand.h"
32*128277c9SStephan Aßmus //#include "ReversePathCommand.h"
33*128277c9SStephan Aßmus //#include "SelectPathCommand.h"
34*128277c9SStephan Aßmus //#include "SelectPointsCommand.h"
35*128277c9SStephan Aßmus 
36*128277c9SStephan Aßmus #define POINT_EXTEND 3.0
37*128277c9SStephan Aßmus #define CONTROL_POINT_EXTEND 2.0
38*128277c9SStephan Aßmus #define INSERT_DIST_THRESHOLD 7.0
39*128277c9SStephan Aßmus #define MOVE_THRESHOLD 9.0
40*128277c9SStephan Aßmus 
41*128277c9SStephan Aßmus enum {
42*128277c9SStephan Aßmus 	UNDEFINED,
43*128277c9SStephan Aßmus 
44*128277c9SStephan Aßmus 	NEW_PATH,
45*128277c9SStephan Aßmus 
46*128277c9SStephan Aßmus 	ADD_POINT,
47*128277c9SStephan Aßmus 	INSERT_POINT,
48*128277c9SStephan Aßmus 	MOVE_POINT,
49*128277c9SStephan Aßmus 	MOVE_POINT_IN,
50*128277c9SStephan Aßmus 	MOVE_POINT_OUT,
51*128277c9SStephan Aßmus 	CLOSE_PATH,
52*128277c9SStephan Aßmus 
53*128277c9SStephan Aßmus 	TOGGLE_SHARP,
54*128277c9SStephan Aßmus 	TOGGLE_SHARP_IN,
55*128277c9SStephan Aßmus 	TOGGLE_SHARP_OUT,
56*128277c9SStephan Aßmus 
57*128277c9SStephan Aßmus 	REMOVE_POINT,
58*128277c9SStephan Aßmus 	REMOVE_POINT_IN,
59*128277c9SStephan Aßmus 	REMOVE_POINT_OUT,
60*128277c9SStephan Aßmus 
61*128277c9SStephan Aßmus 	SELECT_POINTS,
62*128277c9SStephan Aßmus 	TRANSFORM_POINTS,
63*128277c9SStephan Aßmus 	TRANSLATE_POINTS,
64*128277c9SStephan Aßmus 
65*128277c9SStephan Aßmus 	SELECT_SUB_PATH,
66*128277c9SStephan Aßmus };
67*128277c9SStephan Aßmus 
68*128277c9SStephan Aßmus inline const char*
69*128277c9SStephan Aßmus string_for_mode(uint32 mode)
70*128277c9SStephan Aßmus {
71*128277c9SStephan Aßmus 	switch (mode) {
72*128277c9SStephan Aßmus 		case UNDEFINED:
73*128277c9SStephan Aßmus 			return "UNDEFINED";
74*128277c9SStephan Aßmus 		case NEW_PATH:
75*128277c9SStephan Aßmus 			return "NEW_PATH";
76*128277c9SStephan Aßmus 		case ADD_POINT:
77*128277c9SStephan Aßmus 			return "ADD_POINT";
78*128277c9SStephan Aßmus 		case INSERT_POINT:
79*128277c9SStephan Aßmus 			return "INSERT_POINT";
80*128277c9SStephan Aßmus 		case MOVE_POINT:
81*128277c9SStephan Aßmus 			return "MOVE_POINT";
82*128277c9SStephan Aßmus 		case MOVE_POINT_IN:
83*128277c9SStephan Aßmus 			return "MOVE_POINT_IN";
84*128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
85*128277c9SStephan Aßmus 			return "MOVE_POINT_OUT";
86*128277c9SStephan Aßmus 		case CLOSE_PATH:
87*128277c9SStephan Aßmus 			return "CLOSE_PATH";
88*128277c9SStephan Aßmus 		case TOGGLE_SHARP:
89*128277c9SStephan Aßmus 			return "TOGGLE_SHARP";
90*128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
91*128277c9SStephan Aßmus 			return "TOGGLE_SHARP_IN";
92*128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
93*128277c9SStephan Aßmus 			return "TOGGLE_SHARP_OUT";
94*128277c9SStephan Aßmus 		case REMOVE_POINT:
95*128277c9SStephan Aßmus 			return "REMOVE_POINT";
96*128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
97*128277c9SStephan Aßmus 			return "REMOVE_POINT_IN";
98*128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
99*128277c9SStephan Aßmus 			return "REMOVE_POINT_OUT";
100*128277c9SStephan Aßmus 		case SELECT_POINTS:
101*128277c9SStephan Aßmus 			return "SELECT_POINTS";
102*128277c9SStephan Aßmus 		case TRANSFORM_POINTS:
103*128277c9SStephan Aßmus 			return "TRANSFORM_POINTS";
104*128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
105*128277c9SStephan Aßmus 			return "TRANSLATE_POINTS";
106*128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
107*128277c9SStephan Aßmus 			return "SELECT_SUB_PATH";
108*128277c9SStephan Aßmus 	}
109*128277c9SStephan Aßmus 	return "<unknown mode>";
110*128277c9SStephan Aßmus }
111*128277c9SStephan Aßmus 
112*128277c9SStephan Aßmus class Selection : protected BList
113*128277c9SStephan Aßmus {
114*128277c9SStephan Aßmus public:
115*128277c9SStephan Aßmus 	inline Selection(int32 count = 20)
116*128277c9SStephan Aßmus 		: BList(count) {}
117*128277c9SStephan Aßmus 	inline ~Selection() {}
118*128277c9SStephan Aßmus 
119*128277c9SStephan Aßmus 	inline void Add(int32 value)
120*128277c9SStephan Aßmus 		{
121*128277c9SStephan Aßmus 			if (value >= 0) {
122*128277c9SStephan Aßmus 				// keep the list sorted
123*128277c9SStephan Aßmus 				int32 count = CountItems();
124*128277c9SStephan Aßmus 				int32 index = 0;
125*128277c9SStephan Aßmus 				for (; index < count; index++) {
126*128277c9SStephan Aßmus 					if (IndexAt(index) > value) {
127*128277c9SStephan Aßmus 						break;
128*128277c9SStephan Aßmus 					}
129*128277c9SStephan Aßmus 				}
130*128277c9SStephan Aßmus 				BList::AddItem((void*)value, index);
131*128277c9SStephan Aßmus 			}
132*128277c9SStephan Aßmus 		}
133*128277c9SStephan Aßmus 
134*128277c9SStephan Aßmus 	inline bool Remove(int32 value)
135*128277c9SStephan Aßmus 		{ return BList::RemoveItem((void*)value); }
136*128277c9SStephan Aßmus 
137*128277c9SStephan Aßmus 	inline bool Contains(int32 value) const
138*128277c9SStephan Aßmus 		{ return BList::HasItem((void*)value); }
139*128277c9SStephan Aßmus 
140*128277c9SStephan Aßmus 	inline bool IsEmpty() const
141*128277c9SStephan Aßmus 		{ return BList::IsEmpty(); }
142*128277c9SStephan Aßmus 
143*128277c9SStephan Aßmus 	inline int32 IndexAt(int32 index) const
144*128277c9SStephan Aßmus 		{ return (int32)BList::ItemAt(index); }
145*128277c9SStephan Aßmus 
146*128277c9SStephan Aßmus 	inline void MakeEmpty()
147*128277c9SStephan Aßmus 		{ BList::MakeEmpty(); }
148*128277c9SStephan Aßmus 
149*128277c9SStephan Aßmus 	inline int32* Items() const
150*128277c9SStephan Aßmus 		{ return (int32*)BList::Items(); }
151*128277c9SStephan Aßmus 
152*128277c9SStephan Aßmus 	inline const int32 CountItems() const
153*128277c9SStephan Aßmus 		{ return BList::CountItems(); }
154*128277c9SStephan Aßmus 
155*128277c9SStephan Aßmus 	inline Selection& operator =(const Selection& other)
156*128277c9SStephan Aßmus 		{
157*128277c9SStephan Aßmus 			MakeEmpty();
158*128277c9SStephan Aßmus 			int32 count = other.CountItems();
159*128277c9SStephan Aßmus 			int32* items = other.Items();
160*128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++) {
161*128277c9SStephan Aßmus 				Add(items[i]);
162*128277c9SStephan Aßmus 			}
163*128277c9SStephan Aßmus 			return *this;
164*128277c9SStephan Aßmus 		}
165*128277c9SStephan Aßmus 
166*128277c9SStephan Aßmus 	inline bool operator ==(const Selection& other)
167*128277c9SStephan Aßmus 		{
168*128277c9SStephan Aßmus 			if (other.CountItems() == CountItems()) {
169*128277c9SStephan Aßmus 				int32* items = Items();
170*128277c9SStephan Aßmus 				int32* otherItems = other.Items();
171*128277c9SStephan Aßmus 				for (int32 i = 0; i < CountItems(); i++) {
172*128277c9SStephan Aßmus 					if (items[i] != otherItems[i])
173*128277c9SStephan Aßmus 						return false;
174*128277c9SStephan Aßmus 					items++;
175*128277c9SStephan Aßmus 					otherItems++;
176*128277c9SStephan Aßmus 				}
177*128277c9SStephan Aßmus 				return true;
178*128277c9SStephan Aßmus 			} else
179*128277c9SStephan Aßmus 				return false;
180*128277c9SStephan Aßmus 		}
181*128277c9SStephan Aßmus 
182*128277c9SStephan Aßmus 	inline bool operator !=(const Selection& other)
183*128277c9SStephan Aßmus 	{
184*128277c9SStephan Aßmus 		return !(*this == other);
185*128277c9SStephan Aßmus 	}
186*128277c9SStephan Aßmus };
187*128277c9SStephan Aßmus 
188*128277c9SStephan Aßmus 
189*128277c9SStephan Aßmus // constructor
190*128277c9SStephan Aßmus PathManipulator::PathManipulator(VectorPath* path)
191*128277c9SStephan Aßmus 	: Manipulator(path),
192*128277c9SStephan Aßmus 	  fCanvasView(NULL),
193*128277c9SStephan Aßmus 
194*128277c9SStephan Aßmus 	  fCommandDown(false),
195*128277c9SStephan Aßmus 	  fOptionDown(false),
196*128277c9SStephan Aßmus 	  fShiftDown(false),
197*128277c9SStephan Aßmus 	  fAltDown(false),
198*128277c9SStephan Aßmus 
199*128277c9SStephan Aßmus 	  fClickToClose(false),
200*128277c9SStephan Aßmus 
201*128277c9SStephan Aßmus 	  fMode(NEW_PATH),
202*128277c9SStephan Aßmus 	  fFallBackMode(SELECT_POINTS),
203*128277c9SStephan Aßmus 
204*128277c9SStephan Aßmus 	  fMouseDown(false),
205*128277c9SStephan Aßmus 
206*128277c9SStephan Aßmus 	  fPath(path),
207*128277c9SStephan Aßmus 	  fCurrentPathPoint(-1),
208*128277c9SStephan Aßmus 
209*128277c9SStephan Aßmus 	  fChangePointCommand(NULL),
210*128277c9SStephan Aßmus 	  fInsertPointCommand(NULL),
211*128277c9SStephan Aßmus 	  fAddPointCommand(NULL),
212*128277c9SStephan Aßmus 
213*128277c9SStephan Aßmus 	  fSelection(new Selection()),
214*128277c9SStephan Aßmus 	  fOldSelection(new Selection()),
215*128277c9SStephan Aßmus 
216*128277c9SStephan Aßmus 	  fNudgeOffset(0.0, 0.0),
217*128277c9SStephan Aßmus 	  fLastNudgeTime(system_time())//,
218*128277c9SStephan Aßmus //	  fNudgeCommand(NULL)
219*128277c9SStephan Aßmus {
220*128277c9SStephan Aßmus }
221*128277c9SStephan Aßmus 
222*128277c9SStephan Aßmus // destructor
223*128277c9SStephan Aßmus PathManipulator::~PathManipulator()
224*128277c9SStephan Aßmus {
225*128277c9SStephan Aßmus 	delete fChangePointCommand;
226*128277c9SStephan Aßmus 	delete fInsertPointCommand;
227*128277c9SStephan Aßmus 	delete fAddPointCommand;
228*128277c9SStephan Aßmus 
229*128277c9SStephan Aßmus 	delete fSelection;
230*128277c9SStephan Aßmus 	delete fOldSelection;
231*128277c9SStephan Aßmus 
232*128277c9SStephan Aßmus //	delete fNudgeCommand;
233*128277c9SStephan Aßmus }
234*128277c9SStephan Aßmus 
235*128277c9SStephan Aßmus 
236*128277c9SStephan Aßmus // #pragma mark -
237*128277c9SStephan Aßmus 
238*128277c9SStephan Aßmus class StrokePathIterator : public VectorPath::Iterator {
239*128277c9SStephan Aßmus  public:
240*128277c9SStephan Aßmus 					StrokePathIterator(BView* drawingView)
241*128277c9SStephan Aßmus 						: fDrawingView(drawingView)
242*128277c9SStephan Aßmus 					{
243*128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
244*128277c9SStephan Aßmus 						fDrawingView->SetDrawingMode(B_OP_OVER);
245*128277c9SStephan Aßmus 					}
246*128277c9SStephan Aßmus 	virtual			~StrokePathIterator()
247*128277c9SStephan Aßmus 					{}
248*128277c9SStephan Aßmus 
249*128277c9SStephan Aßmus 	virtual	void	MoveTo(BPoint point)
250*128277c9SStephan Aßmus 					{
251*128277c9SStephan Aßmus 						fBlack = true;
252*128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
253*128277c9SStephan Aßmus 
254*128277c9SStephan Aßmus //						fCanvasView->ConvertFromCanvas(point);
255*128277c9SStephan Aßmus 						fDrawingView->MovePenTo(point);
256*128277c9SStephan Aßmus 					}
257*128277c9SStephan Aßmus 	virtual	void	LineTo(BPoint point)
258*128277c9SStephan Aßmus 					{
259*128277c9SStephan Aßmus 						if (fBlack)
260*128277c9SStephan Aßmus 							fDrawingView->SetHighColor(255, 255, 255, 255);
261*128277c9SStephan Aßmus 						else
262*128277c9SStephan Aßmus 							fDrawingView->SetHighColor(0, 0, 0, 255);
263*128277c9SStephan Aßmus 						fBlack = !fBlack;
264*128277c9SStephan Aßmus 
265*128277c9SStephan Aßmus //						fCanvasView->ConvertFromCanvas(point);
266*128277c9SStephan Aßmus 						fDrawingView->StrokeLine(point);
267*128277c9SStephan Aßmus 					}
268*128277c9SStephan Aßmus 
269*128277c9SStephan Aßmus  private:
270*128277c9SStephan Aßmus 	BView*			fDrawingView;
271*128277c9SStephan Aßmus 	bool			fBlack;
272*128277c9SStephan Aßmus };
273*128277c9SStephan Aßmus 
274*128277c9SStephan Aßmus // Draw
275*128277c9SStephan Aßmus void
276*128277c9SStephan Aßmus PathManipulator::Draw(BView* into, BRect updateRect)
277*128277c9SStephan Aßmus {
278*128277c9SStephan Aßmus 	// draw the Bezier curve, but only if editing
279*128277c9SStephan Aßmus 	// if not "editing", the path is actually on top all other modifiers
280*128277c9SStephan Aßmus 	// TODO: make this customizable in the GUI
281*128277c9SStephan Aßmus 	StrokePathIterator iterator(into);
282*128277c9SStephan Aßmus 	fPath->Iterate(&iterator, 1.0/*fCanvasView->ZoomLevel()*/);
283*128277c9SStephan Aßmus 
284*128277c9SStephan Aßmus 	into->SetLowColor(0, 0, 0, 255);
285*128277c9SStephan Aßmus 	BPoint point;
286*128277c9SStephan Aßmus 	BPoint pointIn;
287*128277c9SStephan Aßmus 	BPoint pointOut;
288*128277c9SStephan Aßmus 	rgb_color focusColor = (rgb_color){ 255, 0, 0, 255 };
289*128277c9SStephan Aßmus 	rgb_color highlightColor = (rgb_color){ 60, 60, 255, 255 };
290*128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut); i++) {
291*128277c9SStephan Aßmus 		bool highlight = fCurrentPathPoint == i;
292*128277c9SStephan Aßmus 		bool selected = fSelection->Contains(i);
293*128277c9SStephan Aßmus 		rgb_color normal = selected ? focusColor : (rgb_color){ 0, 0, 0, 255 };
294*128277c9SStephan Aßmus 		into->SetLowColor(normal);
295*128277c9SStephan Aßmus 		into->SetHighColor(255, 255, 255, 255);
296*128277c9SStephan Aßmus 		// convert to view coordinate space
297*128277c9SStephan Aßmus //		fCanvasView->ConvertFromCanvas(point);
298*128277c9SStephan Aßmus //		fCanvasView->ConvertFromCanvas(pointIn);
299*128277c9SStephan Aßmus //		fCanvasView->ConvertFromCanvas(pointOut);
300*128277c9SStephan Aßmus 		// connect the points belonging to one control point
301*128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_INVERT);
302*128277c9SStephan Aßmus 		into->StrokeLine(point, pointIn);
303*128277c9SStephan Aßmus 		into->StrokeLine(point, pointOut);
304*128277c9SStephan Aßmus 		// draw main control point
305*128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT ||
306*128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP ||
307*128277c9SStephan Aßmus 						  fMode == REMOVE_POINT ||
308*128277c9SStephan Aßmus 						  fMode == SELECT_POINTS ||
309*128277c9SStephan Aßmus 						  fMode == CLOSE_PATH)) {
310*128277c9SStephan Aßmus 
311*128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
312*128277c9SStephan Aßmus 		}
313*128277c9SStephan Aßmus 
314*128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_COPY);
315*128277c9SStephan Aßmus 		BRect r(point, point);
316*128277c9SStephan Aßmus 		r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
317*128277c9SStephan Aßmus 		into->StrokeRect(r, B_SOLID_LOW);
318*128277c9SStephan Aßmus 		r.InsetBy(1.0, 1.0);
319*128277c9SStephan Aßmus 		into->FillRect(r, B_SOLID_HIGH);
320*128277c9SStephan Aßmus 		// draw in control point
321*128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_IN ||
322*128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_IN ||
323*128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_IN ||
324*128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
325*128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
326*128277c9SStephan Aßmus 		else
327*128277c9SStephan Aßmus 			into->SetLowColor(normal);
328*128277c9SStephan Aßmus 		if (selected) {
329*128277c9SStephan Aßmus 			into->SetHighColor(220, 220, 220, 255);
330*128277c9SStephan Aßmus 		} else {
331*128277c9SStephan Aßmus 			into->SetHighColor(170, 170, 170, 255);
332*128277c9SStephan Aßmus 		}
333*128277c9SStephan Aßmus 		if (pointIn != point) {
334*128277c9SStephan Aßmus 			r.Set(pointIn.x - CONTROL_POINT_EXTEND, pointIn.y - CONTROL_POINT_EXTEND,
335*128277c9SStephan Aßmus 				  pointIn.x + CONTROL_POINT_EXTEND, pointIn.y + CONTROL_POINT_EXTEND);
336*128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
337*128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
338*128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
339*128277c9SStephan Aßmus 		}
340*128277c9SStephan Aßmus 		// draw out control point
341*128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_OUT ||
342*128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_OUT ||
343*128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_OUT ||
344*128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
345*128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
346*128277c9SStephan Aßmus 		else
347*128277c9SStephan Aßmus 			into->SetLowColor(normal);
348*128277c9SStephan Aßmus 		if (pointOut != point) {
349*128277c9SStephan Aßmus 			r.Set(pointOut.x - CONTROL_POINT_EXTEND, pointOut.y - CONTROL_POINT_EXTEND,
350*128277c9SStephan Aßmus 				  pointOut.x + CONTROL_POINT_EXTEND, pointOut.y + CONTROL_POINT_EXTEND);
351*128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
352*128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
353*128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
354*128277c9SStephan Aßmus 		}
355*128277c9SStephan Aßmus 	}
356*128277c9SStephan Aßmus }
357*128277c9SStephan Aßmus 
358*128277c9SStephan Aßmus // #pragma mark -
359*128277c9SStephan Aßmus 
360*128277c9SStephan Aßmus // MouseDown
361*128277c9SStephan Aßmus bool
362*128277c9SStephan Aßmus PathManipulator::MouseDown(BPoint where)
363*128277c9SStephan Aßmus {
364*128277c9SStephan Aßmus 	fMouseDown = true;
365*128277c9SStephan Aßmus 
366*128277c9SStephan Aßmus 	if (fMode == MOVE_POINT &&
367*128277c9SStephan Aßmus 		fSelection->CountItems() > 1 &&
368*128277c9SStephan Aßmus 		fSelection->Contains(fCurrentPathPoint)) {
369*128277c9SStephan Aßmus 		fMode = TRANSLATE_POINTS;
370*128277c9SStephan Aßmus 	}
371*128277c9SStephan Aßmus 
372*128277c9SStephan Aßmus 	// TODO: translate from BView to canvas space
373*128277c9SStephan Aßmus 	BPoint canvasWhere = where;
374*128277c9SStephan Aßmus 
375*128277c9SStephan Aßmus 	// maybe we're changing some point, so we construct the
376*128277c9SStephan Aßmus 	// "ChangePointCommand" here so that the point is remembered
377*128277c9SStephan Aßmus 	// in its current state
378*128277c9SStephan Aßmus 	delete fChangePointCommand;
379*128277c9SStephan Aßmus 	fChangePointCommand = NULL;
380*128277c9SStephan Aßmus 	switch (fMode) {
381*128277c9SStephan Aßmus 		case TOGGLE_SHARP:
382*128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
383*128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
384*128277c9SStephan Aßmus 		case MOVE_POINT:
385*128277c9SStephan Aßmus 		case MOVE_POINT_IN:
386*128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
387*128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
388*128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
389*128277c9SStephan Aßmus 			fChangePointCommand = new ChangePointCommand(fPath,
390*128277c9SStephan Aßmus 														 fCurrentPathPoint,
391*128277c9SStephan Aßmus 														 fSelection->Items(),
392*128277c9SStephan Aßmus 														 fSelection->CountItems());
393*128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
394*128277c9SStephan Aßmus 			break;
395*128277c9SStephan Aßmus 	}
396*128277c9SStephan Aßmus 
397*128277c9SStephan Aßmus 	// at this point we init doing something
398*128277c9SStephan Aßmus 	switch (fMode) {
399*128277c9SStephan Aßmus 		case ADD_POINT:
400*128277c9SStephan Aßmus 			_AddPoint(canvasWhere);
401*128277c9SStephan Aßmus 			break;
402*128277c9SStephan Aßmus 		case INSERT_POINT:
403*128277c9SStephan Aßmus 			_InsertPoint(canvasWhere, fCurrentPathPoint);
404*128277c9SStephan Aßmus 			break;
405*128277c9SStephan Aßmus 
406*128277c9SStephan Aßmus 		case TOGGLE_SHARP:
407*128277c9SStephan Aßmus 			_SetSharp(fCurrentPathPoint);
408*128277c9SStephan Aßmus 			// continue by dragging out the _connected_ in/out points
409*128277c9SStephan Aßmus 			break;
410*128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
411*128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
412*128277c9SStephan Aßmus 			// continue by moving the "in" point
413*128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_IN);
414*128277c9SStephan Aßmus 			break;
415*128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
416*128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
417*128277c9SStephan Aßmus 			// continue by moving the "out" point
418*128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
419*128277c9SStephan Aßmus 			break;
420*128277c9SStephan Aßmus 
421*128277c9SStephan Aßmus 		case MOVE_POINT:
422*128277c9SStephan Aßmus 		case MOVE_POINT_IN:
423*128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
424*128277c9SStephan Aßmus 			// the right thing happens since "fCurrentPathPoint"
425*128277c9SStephan Aßmus 			// points to the correct index
426*128277c9SStephan Aßmus 			break;
427*128277c9SStephan Aßmus 
428*128277c9SStephan Aßmus 		case CLOSE_PATH:
429*128277c9SStephan Aßmus //			SetClosed(true, true);
430*128277c9SStephan Aßmus 			break;
431*128277c9SStephan Aßmus 
432*128277c9SStephan Aßmus 		case REMOVE_POINT:
433*128277c9SStephan Aßmus 			if (fPath->CountPoints() == 1) {
434*128277c9SStephan Aßmus //				fCanvasView->Perform(new RemovePathCommand(this, fPath));
435*128277c9SStephan Aßmus 			} else {
436*128277c9SStephan Aßmus 				fCanvasView->Perform(new RemovePointsCommand(fPath,
437*128277c9SStephan Aßmus 															 fCurrentPathPoint,
438*128277c9SStephan Aßmus 															 fSelection->Items(),
439*128277c9SStephan Aßmus 															 fSelection->CountItems()));
440*128277c9SStephan Aßmus 				_RemovePoint(fCurrentPathPoint);
441*128277c9SStephan Aßmus 			}
442*128277c9SStephan Aßmus 			break;
443*128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
444*128277c9SStephan Aßmus 			_RemovePointIn(fCurrentPathPoint);
445*128277c9SStephan Aßmus 			break;
446*128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
447*128277c9SStephan Aßmus 			_RemovePointOut(fCurrentPathPoint);
448*128277c9SStephan Aßmus 			break;
449*128277c9SStephan Aßmus 
450*128277c9SStephan Aßmus 		case SELECT_POINTS:
451*128277c9SStephan Aßmus 			if (!fShiftDown) {
452*128277c9SStephan Aßmus 				fSelection->MakeEmpty();
453*128277c9SStephan Aßmus 				_UpdateSelection();
454*128277c9SStephan Aßmus 			}
455*128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
456*128277c9SStephan Aßmus 			if (fCurrentPathPoint >= 0) {
457*128277c9SStephan Aßmus 				_Select(fCurrentPathPoint, fShiftDown);
458*128277c9SStephan Aßmus 			}
459*128277c9SStephan Aßmus 			fCanvasView->BeginRectTracking(BRect(where, where),
460*128277c9SStephan Aßmus 										   B_TRACK_RECT_CORNER);
461*128277c9SStephan Aßmus 			break;
462*128277c9SStephan Aßmus 	}
463*128277c9SStephan Aßmus 
464*128277c9SStephan Aßmus 	fTrackingStart = canvasWhere;
465*128277c9SStephan Aßmus 	// remember the subpixel position
466*128277c9SStephan Aßmus 	// so that MouseMoved() will work even before
467*128277c9SStephan Aßmus 	// the integer position becomes different
468*128277c9SStephan Aßmus //	fCanvasView->ConvertToCanvas(where);
469*128277c9SStephan Aßmus 	fLastCanvasPos = where;
470*128277c9SStephan Aßmus 
471*128277c9SStephan Aßmus 	// the reason to exclude the select mode
472*128277c9SStephan Aßmus 	// is that the BView rect tracking does not
473*128277c9SStephan Aßmus 	// scroll the rect starting point along with us
474*128277c9SStephan Aßmus 	// (since we're doing no real scrolling)
475*128277c9SStephan Aßmus //	if (fMode != SELECT_POINTS)
476*128277c9SStephan Aßmus //		fCanvasView->SetAutoScrolling(true);
477*128277c9SStephan Aßmus 
478*128277c9SStephan Aßmus 	UpdateCursor();
479*128277c9SStephan Aßmus 
480*128277c9SStephan Aßmus 	return true;
481*128277c9SStephan Aßmus }
482*128277c9SStephan Aßmus 
483*128277c9SStephan Aßmus // MouseMoved
484*128277c9SStephan Aßmus void
485*128277c9SStephan Aßmus PathManipulator::MouseMoved(BPoint where)
486*128277c9SStephan Aßmus {
487*128277c9SStephan Aßmus 	// TODO: translate from BView to canvas space
488*128277c9SStephan Aßmus 	BPoint canvasWhere = where;
489*128277c9SStephan Aßmus 
490*128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
491*128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
492*128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
493*128277c9SStephan Aßmus 	if (fLastCanvasPos == canvasWhere)
494*128277c9SStephan Aßmus 		return;
495*128277c9SStephan Aßmus 
496*128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
497*128277c9SStephan Aßmus 
498*128277c9SStephan Aßmus 	if (fMode == CLOSE_PATH) {
499*128277c9SStephan Aßmus 		// continue by moving the point
500*128277c9SStephan Aßmus 		_SetMode(MOVE_POINT);
501*128277c9SStephan Aßmus 		delete fChangePointCommand;
502*128277c9SStephan Aßmus 		fChangePointCommand = new ChangePointCommand(fPath,
503*128277c9SStephan Aßmus 													 fCurrentPathPoint,
504*128277c9SStephan Aßmus 													 fSelection->Items(),
505*128277c9SStephan Aßmus 													 fSelection->CountItems());
506*128277c9SStephan Aßmus 	}
507*128277c9SStephan Aßmus 
508*128277c9SStephan Aßmus //	if (!fPrecise) {
509*128277c9SStephan Aßmus //		float offset = fmod(fOutlineWidth, 2.0) / 2.0;
510*128277c9SStephan Aßmus //		canvasWhere.point += BPoint(offset, offset);
511*128277c9SStephan Aßmus //	}
512*128277c9SStephan Aßmus 
513*128277c9SStephan Aßmus 	switch (fMode) {
514*128277c9SStephan Aßmus 		case ADD_POINT:
515*128277c9SStephan Aßmus 		case INSERT_POINT:
516*128277c9SStephan Aßmus 		case TOGGLE_SHARP:
517*128277c9SStephan Aßmus 			// drag the "out" control point, mirror the "in" control point
518*128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere, true);
519*128277c9SStephan Aßmus 			break;
520*128277c9SStephan Aßmus 		case MOVE_POINT:
521*128277c9SStephan Aßmus 			// drag all three control points at once
522*128277c9SStephan Aßmus 			fPath->SetPoint(fCurrentPathPoint, canvasWhere);
523*128277c9SStephan Aßmus 			break;
524*128277c9SStephan Aßmus 		case MOVE_POINT_IN:
525*128277c9SStephan Aßmus 			// drag in control point
526*128277c9SStephan Aßmus 			fPath->SetPointIn(fCurrentPathPoint, canvasWhere);
527*128277c9SStephan Aßmus 			break;
528*128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
529*128277c9SStephan Aßmus 			// drag out control point
530*128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere);
531*128277c9SStephan Aßmus 			break;
532*128277c9SStephan Aßmus 
533*128277c9SStephan Aßmus 		case SELECT_POINTS: {
534*128277c9SStephan Aßmus 			// change the selection
535*128277c9SStephan Aßmus 			BRect r;
536*128277c9SStephan Aßmus 			r.left = min_c(fTrackingStart.x, canvasWhere.x);
537*128277c9SStephan Aßmus 			r.top = min_c(fTrackingStart.y, canvasWhere.y);
538*128277c9SStephan Aßmus 			r.right = max_c(fTrackingStart.x, canvasWhere.x);
539*128277c9SStephan Aßmus 			r.bottom = max_c(fTrackingStart.y, canvasWhere.y);
540*128277c9SStephan Aßmus 			_Select(r);
541*128277c9SStephan Aßmus 			break;
542*128277c9SStephan Aßmus 		}
543*128277c9SStephan Aßmus 
544*128277c9SStephan Aßmus 		case TRANSLATE_POINTS: {
545*128277c9SStephan Aßmus 			BPoint offset = canvasWhere - fTrackingStart;
546*128277c9SStephan Aßmus 			_Nudge(offset);
547*128277c9SStephan Aßmus 			fTrackingStart = canvasWhere;
548*128277c9SStephan Aßmus 			break;
549*128277c9SStephan Aßmus 		}
550*128277c9SStephan Aßmus 	}
551*128277c9SStephan Aßmus }
552*128277c9SStephan Aßmus 
553*128277c9SStephan Aßmus // MouseUp
554*128277c9SStephan Aßmus Command*
555*128277c9SStephan Aßmus PathManipulator::MouseUp()
556*128277c9SStephan Aßmus {
557*128277c9SStephan Aßmus 	// prevent carrying out actions more than once by only
558*128277c9SStephan Aßmus 	// doing it if "fMouseDown" is true at the point of
559*128277c9SStephan Aßmus 	// entering this function
560*128277c9SStephan Aßmus 	if (!fMouseDown)
561*128277c9SStephan Aßmus 		return NULL;
562*128277c9SStephan Aßmus 	fMouseDown = false;
563*128277c9SStephan Aßmus 
564*128277c9SStephan Aßmus 	Command* command = NULL;
565*128277c9SStephan Aßmus 
566*128277c9SStephan Aßmus 	switch (fMode) {
567*128277c9SStephan Aßmus 
568*128277c9SStephan Aßmus 		case ADD_POINT:
569*128277c9SStephan Aßmus 			command = fAddPointCommand;
570*128277c9SStephan Aßmus 			fAddPointCommand = NULL;
571*128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
572*128277c9SStephan Aßmus 			break;
573*128277c9SStephan Aßmus 
574*128277c9SStephan Aßmus 		case INSERT_POINT:
575*128277c9SStephan Aßmus 			command = fInsertPointCommand;
576*128277c9SStephan Aßmus 			fInsertPointCommand = NULL;
577*128277c9SStephan Aßmus 			break;
578*128277c9SStephan Aßmus 
579*128277c9SStephan Aßmus 		case SELECT_POINTS:
580*128277c9SStephan Aßmus 			if (*fSelection != *fOldSelection) {
581*128277c9SStephan Aßmus //				command = new SelectPointsCommand(this, fPath,
582*128277c9SStephan Aßmus //												  fOldSelection->Items(),
583*128277c9SStephan Aßmus //												  fOldSelection->CountItems(),
584*128277c9SStephan Aßmus //												  fSelection->Items(),
585*128277c9SStephan Aßmus //												  fSelection->CountItems()));
586*128277c9SStephan Aßmus 			}
587*128277c9SStephan Aßmus 			fCanvasView->EndRectTracking();
588*128277c9SStephan Aßmus 			break;
589*128277c9SStephan Aßmus 
590*128277c9SStephan Aßmus 		case TOGGLE_SHARP:
591*128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
592*128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
593*128277c9SStephan Aßmus 		case MOVE_POINT:
594*128277c9SStephan Aßmus 		case MOVE_POINT_IN:
595*128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
596*128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
597*128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
598*128277c9SStephan Aßmus 			command = fChangePointCommand;
599*128277c9SStephan Aßmus 			fChangePointCommand = NULL;
600*128277c9SStephan Aßmus 			break;
601*128277c9SStephan Aßmus 
602*128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
603*128277c9SStephan Aßmus //			if (!fNudgeCommand) {
604*128277c9SStephan Aßmus 				// select just the point that was clicked
605*128277c9SStephan Aßmus 				*fOldSelection = *fSelection;
606*128277c9SStephan Aßmus 				if (fCurrentPathPoint >= 0) {
607*128277c9SStephan Aßmus 					_Select(fCurrentPathPoint, fShiftDown);
608*128277c9SStephan Aßmus 				}
609*128277c9SStephan Aßmus 				if (*fSelection != *fOldSelection) {
610*128277c9SStephan Aßmus //					command = new SelectPointsCommand(this, fPath,
611*128277c9SStephan Aßmus //													  fOldSelection->Items(),
612*128277c9SStephan Aßmus //													  fOldSelection->CountItems(),
613*128277c9SStephan Aßmus //													  fSelection->Items(),
614*128277c9SStephan Aßmus //													  fSelection->CountItems()));
615*128277c9SStephan Aßmus 				}
616*128277c9SStephan Aßmus //			} else {
617*128277c9SStephan Aßmus //				_FinishNudging();
618*128277c9SStephan Aßmus //			}
619*128277c9SStephan Aßmus 			break;
620*128277c9SStephan Aßmus 	}
621*128277c9SStephan Aßmus 
622*128277c9SStephan Aßmus 	return command;
623*128277c9SStephan Aßmus }
624*128277c9SStephan Aßmus 
625*128277c9SStephan Aßmus // MouseOver
626*128277c9SStephan Aßmus bool
627*128277c9SStephan Aßmus PathManipulator::MouseOver(BPoint where)
628*128277c9SStephan Aßmus {
629*128277c9SStephan Aßmus 	// TODO: translate from BView to canvas space
630*128277c9SStephan Aßmus 	BPoint canvasWhere = where;
631*128277c9SStephan Aßmus 
632*128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
633*128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
634*128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
635*128277c9SStephan Aßmus 	if (fMouseDown && fLastCanvasPos == canvasWhere)
636*128277c9SStephan Aßmus 		return false;
637*128277c9SStephan Aßmus 
638*128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
639*128277c9SStephan Aßmus 
640*128277c9SStephan Aßmus 	// hit testing
641*128277c9SStephan Aßmus 	// (use a subpixel mouse pos)
642*128277c9SStephan Aßmus //		fCanvasView->ConvertToCanvas(where);
643*128277c9SStephan Aßmus 	_SetModeForMousePos(where);
644*128277c9SStephan Aßmus 
645*128277c9SStephan Aßmus 	// TODO: always true?
646*128277c9SStephan Aßmus 	return true;
647*128277c9SStephan Aßmus }
648*128277c9SStephan Aßmus 
649*128277c9SStephan Aßmus // DoubleClicked
650*128277c9SStephan Aßmus bool
651*128277c9SStephan Aßmus PathManipulator::DoubleClicked(BPoint where)
652*128277c9SStephan Aßmus {
653*128277c9SStephan Aßmus 	return false;
654*128277c9SStephan Aßmus }
655*128277c9SStephan Aßmus 
656*128277c9SStephan Aßmus // Bounds
657*128277c9SStephan Aßmus BRect
658*128277c9SStephan Aßmus PathManipulator::Bounds()
659*128277c9SStephan Aßmus {
660*128277c9SStephan Aßmus 	return _ControlPointRect();
661*128277c9SStephan Aßmus }
662*128277c9SStephan Aßmus 
663*128277c9SStephan Aßmus // TrackingBounds
664*128277c9SStephan Aßmus BRect
665*128277c9SStephan Aßmus PathManipulator::TrackingBounds(BView* withinView)
666*128277c9SStephan Aßmus {
667*128277c9SStephan Aßmus 	return withinView->Bounds();
668*128277c9SStephan Aßmus }
669*128277c9SStephan Aßmus 
670*128277c9SStephan Aßmus // #pragma mark -
671*128277c9SStephan Aßmus 
672*128277c9SStephan Aßmus enum {
673*128277c9SStephan Aßmus 	MSG_SHAPE_TRANSFORM			= 'strn',
674*128277c9SStephan Aßmus 	MSG_SHAPE_REMOVE_POINTS		= 'srmp',
675*128277c9SStephan Aßmus 	MSG_UPDATE_SHAPE_UI			= 'udsi',
676*128277c9SStephan Aßmus };
677*128277c9SStephan Aßmus 
678*128277c9SStephan Aßmus // MessageReceived
679*128277c9SStephan Aßmus bool
680*128277c9SStephan Aßmus PathManipulator::MessageReceived(BMessage* message, Command** _command)
681*128277c9SStephan Aßmus {
682*128277c9SStephan Aßmus 	bool result = true;
683*128277c9SStephan Aßmus 	switch (message->what) {
684*128277c9SStephan Aßmus 		case MSG_SHAPE_TRANSFORM:
685*128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
686*128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
687*128277c9SStephan Aßmus 			break;
688*128277c9SStephan Aßmus 		case MSG_SHAPE_REMOVE_POINTS:
689*128277c9SStephan Aßmus 			*_command = _Delete();
690*128277c9SStephan Aßmus 			break;
691*128277c9SStephan Aßmus 		case B_SELECT_ALL: {
692*128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
693*128277c9SStephan Aßmus 			fSelection->MakeEmpty();
694*128277c9SStephan Aßmus 			int32 count = fPath->CountPoints();
695*128277c9SStephan Aßmus 			for (int32 i = 0; i < count; i++)
696*128277c9SStephan Aßmus 				fSelection->Add(i);
697*128277c9SStephan Aßmus 			if (*fOldSelection != *fSelection) {
698*128277c9SStephan Aßmus //				*_command = new SelectPointsCommand(this, fPath,
699*128277c9SStephan Aßmus //												   fOldSelection->Items(),
700*128277c9SStephan Aßmus //												   fOldSelection->CountItems(),
701*128277c9SStephan Aßmus //												   fSelection->Items(),
702*128277c9SStephan Aßmus //												   fSelection->CountItems()));
703*128277c9SStephan Aßmus 				count = fSelection->CountItems();
704*128277c9SStephan Aßmus 				int32 indices[count];
705*128277c9SStephan Aßmus 				memcpy(indices, fSelection->Items(), count * sizeof(int32));
706*128277c9SStephan Aßmus 				_Select(indices, count);
707*128277c9SStephan Aßmus 			}
708*128277c9SStephan Aßmus 			break;
709*128277c9SStephan Aßmus 		}
710*128277c9SStephan Aßmus 		default:
711*128277c9SStephan Aßmus 			result = false;
712*128277c9SStephan Aßmus 			break;
713*128277c9SStephan Aßmus 	}
714*128277c9SStephan Aßmus 	return result;
715*128277c9SStephan Aßmus }
716*128277c9SStephan Aßmus 
717*128277c9SStephan Aßmus 
718*128277c9SStephan Aßmus // ModifiersChanged
719*128277c9SStephan Aßmus void
720*128277c9SStephan Aßmus PathManipulator::ModifiersChanged(uint32 modifiers)
721*128277c9SStephan Aßmus {
722*128277c9SStephan Aßmus 	fCommandDown = modifiers & B_COMMAND_KEY;
723*128277c9SStephan Aßmus 	fOptionDown = modifiers & B_CONTROL_KEY;
724*128277c9SStephan Aßmus 	fShiftDown = modifiers & B_SHIFT_KEY;
725*128277c9SStephan Aßmus 	fAltDown = modifiers & B_OPTION_KEY;
726*128277c9SStephan Aßmus 
727*128277c9SStephan Aßmus 	// reevaluate mode
728*128277c9SStephan Aßmus 	if (!fMouseDown)
729*128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
730*128277c9SStephan Aßmus }
731*128277c9SStephan Aßmus 
732*128277c9SStephan Aßmus // HandleKeyDown
733*128277c9SStephan Aßmus bool
734*128277c9SStephan Aßmus PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
735*128277c9SStephan Aßmus {
736*128277c9SStephan Aßmus 	bool result = true;
737*128277c9SStephan Aßmus 
738*128277c9SStephan Aßmus 	float nudgeDist = 1.0;
739*128277c9SStephan Aßmus //	if (modifiers & B_SHIFT_KEY)
740*128277c9SStephan Aßmus //		nudgeDist /= fCanvasView->ZoomLevel();
741*128277c9SStephan Aßmus 
742*128277c9SStephan Aßmus 	switch (key) {
743*128277c9SStephan Aßmus 		// commit
744*128277c9SStephan Aßmus 		case B_RETURN:
745*128277c9SStephan Aßmus //			_Perform();
746*128277c9SStephan Aßmus 			break;
747*128277c9SStephan Aßmus 		// cancel
748*128277c9SStephan Aßmus 		case B_ESCAPE:
749*128277c9SStephan Aßmus 			if (fFallBackMode == NEW_PATH) {
750*128277c9SStephan Aßmus 				fFallBackMode = SELECT_POINTS;
751*128277c9SStephan Aßmus 				_SetModeForMousePos(fLastCanvasPos);
752*128277c9SStephan Aßmus 			} else
753*128277c9SStephan Aßmus //				_Cancel();
754*128277c9SStephan Aßmus 			break;
755*128277c9SStephan Aßmus 		case 't':
756*128277c9SStephan Aßmus 		case 'T':
757*128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
758*128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
759*128277c9SStephan Aßmus 			else
760*128277c9SStephan Aßmus 				result = false;
761*128277c9SStephan Aßmus 			break;
762*128277c9SStephan Aßmus 		// nudging
763*128277c9SStephan Aßmus 		case B_UP_ARROW:
764*128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, -nudgeDist));
765*128277c9SStephan Aßmus 			break;
766*128277c9SStephan Aßmus 		case B_DOWN_ARROW:
767*128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, nudgeDist));
768*128277c9SStephan Aßmus 			break;
769*128277c9SStephan Aßmus 		case B_LEFT_ARROW:
770*128277c9SStephan Aßmus 			_Nudge(BPoint(-nudgeDist, 0.0));
771*128277c9SStephan Aßmus 			break;
772*128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
773*128277c9SStephan Aßmus 			_Nudge(BPoint(nudgeDist, 0.0));
774*128277c9SStephan Aßmus 			break;
775*128277c9SStephan Aßmus 
776*128277c9SStephan Aßmus 		case B_DELETE:
777*128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
778*128277c9SStephan Aßmus 				*_command = _Delete();
779*128277c9SStephan Aßmus 			else
780*128277c9SStephan Aßmus 				result = false;
781*128277c9SStephan Aßmus 			break;
782*128277c9SStephan Aßmus 
783*128277c9SStephan Aßmus 		default:
784*128277c9SStephan Aßmus 			result = false;
785*128277c9SStephan Aßmus 	}
786*128277c9SStephan Aßmus 	return result;
787*128277c9SStephan Aßmus }
788*128277c9SStephan Aßmus 
789*128277c9SStephan Aßmus // HandleKeyUp
790*128277c9SStephan Aßmus bool
791*128277c9SStephan Aßmus PathManipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
792*128277c9SStephan Aßmus {
793*128277c9SStephan Aßmus 	bool handled = true;
794*128277c9SStephan Aßmus 	switch (key) {
795*128277c9SStephan Aßmus 		// nudging
796*128277c9SStephan Aßmus 		case B_UP_ARROW:
797*128277c9SStephan Aßmus 		case B_DOWN_ARROW:
798*128277c9SStephan Aßmus 		case B_LEFT_ARROW:
799*128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
800*128277c9SStephan Aßmus 			_FinishNudging();
801*128277c9SStephan Aßmus 			break;
802*128277c9SStephan Aßmus 		default:
803*128277c9SStephan Aßmus 			handled = false;
804*128277c9SStephan Aßmus 			break;
805*128277c9SStephan Aßmus 	}
806*128277c9SStephan Aßmus 	return handled;
807*128277c9SStephan Aßmus }
808*128277c9SStephan Aßmus 
809*128277c9SStephan Aßmus // UpdateCursor
810*128277c9SStephan Aßmus void
811*128277c9SStephan Aßmus PathManipulator::UpdateCursor()
812*128277c9SStephan Aßmus {
813*128277c9SStephan Aßmus 	const uchar* cursorData;
814*128277c9SStephan Aßmus 	switch (fMode) {
815*128277c9SStephan Aßmus 		case ADD_POINT:
816*128277c9SStephan Aßmus 			cursorData = kPathAddCursor;
817*128277c9SStephan Aßmus 			break;
818*128277c9SStephan Aßmus 		case INSERT_POINT:
819*128277c9SStephan Aßmus 			cursorData = kPathInsertCursor;
820*128277c9SStephan Aßmus 			break;
821*128277c9SStephan Aßmus 		case MOVE_POINT:
822*128277c9SStephan Aßmus 		case MOVE_POINT_IN:
823*128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
824*128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
825*128277c9SStephan Aßmus 			cursorData = kPathMoveCursor;
826*128277c9SStephan Aßmus 			break;
827*128277c9SStephan Aßmus 		case CLOSE_PATH:
828*128277c9SStephan Aßmus 			cursorData = kPathCloseCursor;
829*128277c9SStephan Aßmus 			break;
830*128277c9SStephan Aßmus 		case TOGGLE_SHARP:
831*128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
832*128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
833*128277c9SStephan Aßmus 			cursorData = kPathSharpCursor;
834*128277c9SStephan Aßmus 			break;
835*128277c9SStephan Aßmus 		case REMOVE_POINT:
836*128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
837*128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
838*128277c9SStephan Aßmus 			cursorData = kPathRemoveCursor;
839*128277c9SStephan Aßmus 			break;
840*128277c9SStephan Aßmus 		case SELECT_POINTS:
841*128277c9SStephan Aßmus 			cursorData = kPathSelectCursor;
842*128277c9SStephan Aßmus 			break;
843*128277c9SStephan Aßmus 
844*128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
845*128277c9SStephan Aßmus 			cursorData = B_HAND_CURSOR;
846*128277c9SStephan Aßmus 			break;
847*128277c9SStephan Aßmus 
848*128277c9SStephan Aßmus 		case UNDEFINED:
849*128277c9SStephan Aßmus 		default:
850*128277c9SStephan Aßmus 			cursorData = kStopCursor;
851*128277c9SStephan Aßmus 			break;
852*128277c9SStephan Aßmus 	}
853*128277c9SStephan Aßmus 	BCursor cursor(cursorData);
854*128277c9SStephan Aßmus 	fCanvasView->SetViewCursor(&cursor, true);
855*128277c9SStephan Aßmus 	fCanvasView->Sync();
856*128277c9SStephan Aßmus }
857*128277c9SStephan Aßmus 
858*128277c9SStephan Aßmus // AttachedToView
859*128277c9SStephan Aßmus void
860*128277c9SStephan Aßmus PathManipulator::AttachedToView(BView* view)
861*128277c9SStephan Aßmus {
862*128277c9SStephan Aßmus 	fCanvasView = dynamic_cast<StateView*>(view);
863*128277c9SStephan Aßmus }
864*128277c9SStephan Aßmus 
865*128277c9SStephan Aßmus // DetachedFromView
866*128277c9SStephan Aßmus void
867*128277c9SStephan Aßmus PathManipulator::DetachedFromView(BView* view)
868*128277c9SStephan Aßmus {
869*128277c9SStephan Aßmus 	fCanvasView = NULL;
870*128277c9SStephan Aßmus }
871*128277c9SStephan Aßmus 
872*128277c9SStephan Aßmus // #pragma mark -
873*128277c9SStephan Aßmus 
874*128277c9SStephan Aßmus // ObjectChanged
875*128277c9SStephan Aßmus void
876*128277c9SStephan Aßmus PathManipulator::ObjectChanged(const Observable* object)
877*128277c9SStephan Aßmus {
878*128277c9SStephan Aßmus 	// TODO: refine VectorPath listener interface and
879*128277c9SStephan Aßmus 	// implement more efficiently
880*128277c9SStephan Aßmus 	BRect currentBounds = _ControlPointRect();
881*128277c9SStephan Aßmus 	_InvalidateCanvas(currentBounds | fPreviousBounds);
882*128277c9SStephan Aßmus 	fPreviousBounds = currentBounds;
883*128277c9SStephan Aßmus 
884*128277c9SStephan Aßmus 	// reevaluate mode
885*128277c9SStephan Aßmus 	if (!fMouseDown)
886*128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
887*128277c9SStephan Aßmus }
888*128277c9SStephan Aßmus 
889*128277c9SStephan Aßmus // #pragma mark -
890*128277c9SStephan Aßmus 
891*128277c9SStephan Aßmus // ControlFlags
892*128277c9SStephan Aßmus uint32
893*128277c9SStephan Aßmus PathManipulator::ControlFlags() const
894*128277c9SStephan Aßmus {
895*128277c9SStephan Aßmus 	uint32 flags = 0;
896*128277c9SStephan Aßmus 
897*128277c9SStephan Aßmus //	flags |= SHAPE_UI_FLAGS_CAN_REVERSE_PATH;
898*128277c9SStephan Aßmus //
899*128277c9SStephan Aßmus //	if (!fSelection->IsEmpty())
900*128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_HAS_SELECTION;
901*128277c9SStephan Aßmus //	if (fPath->CountPoints() > 1)
902*128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_CAN_CLOSE_PATH;
903*128277c9SStephan Aßmus //	if (fPath->IsClosed())
904*128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_PATH_IS_CLOSED;
905*128277c9SStephan Aßmus 
906*128277c9SStephan Aßmus 	return flags;
907*128277c9SStephan Aßmus }
908*128277c9SStephan Aßmus 
909*128277c9SStephan Aßmus // ReversePath
910*128277c9SStephan Aßmus void
911*128277c9SStephan Aßmus PathManipulator::ReversePath()
912*128277c9SStephan Aßmus {
913*128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
914*128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
915*128277c9SStephan Aßmus 	if (count > 0) {
916*128277c9SStephan Aßmus 		Selection temp;
917*128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
918*128277c9SStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
919*128277c9SStephan Aßmus 		}
920*128277c9SStephan Aßmus 		*fSelection = temp;
921*128277c9SStephan Aßmus 	}
922*128277c9SStephan Aßmus 	fPath->Reverse();
923*128277c9SStephan Aßmus }
924*128277c9SStephan Aßmus 
925*128277c9SStephan Aßmus // #pragma mark -
926*128277c9SStephan Aßmus 
927*128277c9SStephan Aßmus // _SetMode
928*128277c9SStephan Aßmus void
929*128277c9SStephan Aßmus PathManipulator::_SetMode(uint32 mode)
930*128277c9SStephan Aßmus {
931*128277c9SStephan Aßmus 	if (fMode != mode) {
932*128277c9SStephan Aßmus //printf("switching mode: %s -> %s\n", string_for_mode(fMode), string_for_mode(mode));
933*128277c9SStephan Aßmus 		fMode = mode;
934*128277c9SStephan Aßmus 
935*128277c9SStephan Aßmus 		if (BWindow* window = fCanvasView->Window()) {
936*128277c9SStephan Aßmus 			window->PostMessage(MSG_UPDATE_SHAPE_UI);
937*128277c9SStephan Aßmus 		}
938*128277c9SStephan Aßmus 		UpdateCursor();
939*128277c9SStephan Aßmus 	}
940*128277c9SStephan Aßmus }
941*128277c9SStephan Aßmus 
942*128277c9SStephan Aßmus // _AddPoint
943*128277c9SStephan Aßmus void
944*128277c9SStephan Aßmus PathManipulator::_AddPoint(BPoint where)
945*128277c9SStephan Aßmus {
946*128277c9SStephan Aßmus 	if (fPath->AddPoint(where)) {
947*128277c9SStephan Aßmus 		fCurrentPathPoint = fPath->CountPoints() - 1;
948*128277c9SStephan Aßmus 
949*128277c9SStephan Aßmus 		delete fAddPointCommand;
950*128277c9SStephan Aßmus 		fAddPointCommand = new AddPointCommand(fPath, fCurrentPathPoint,
951*128277c9SStephan Aßmus 											   fSelection->Items(),
952*128277c9SStephan Aßmus 											   fSelection->CountItems());
953*128277c9SStephan Aßmus 
954*128277c9SStephan Aßmus 		_Select(fCurrentPathPoint, fShiftDown);
955*128277c9SStephan Aßmus 	}
956*128277c9SStephan Aßmus }
957*128277c9SStephan Aßmus 
958*128277c9SStephan Aßmus // scale_point
959*128277c9SStephan Aßmus BPoint
960*128277c9SStephan Aßmus scale_point(BPoint a, BPoint b, float scale)
961*128277c9SStephan Aßmus {
962*128277c9SStephan Aßmus 	return BPoint(a.x + (b.x - a.x) * scale,
963*128277c9SStephan Aßmus 				  a.y + (b.y - a.y) * scale);
964*128277c9SStephan Aßmus }
965*128277c9SStephan Aßmus 
966*128277c9SStephan Aßmus // _InsertPoint
967*128277c9SStephan Aßmus void
968*128277c9SStephan Aßmus PathManipulator::_InsertPoint(BPoint where, int32 index)
969*128277c9SStephan Aßmus {
970*128277c9SStephan Aßmus 	double scale;
971*128277c9SStephan Aßmus 
972*128277c9SStephan Aßmus 	BPoint point;
973*128277c9SStephan Aßmus 	BPoint pointIn;
974*128277c9SStephan Aßmus 	BPoint pointOut;
975*128277c9SStephan Aßmus 
976*128277c9SStephan Aßmus 	BPoint previous;
977*128277c9SStephan Aßmus 	BPoint previousOut;
978*128277c9SStephan Aßmus 	BPoint next;
979*128277c9SStephan Aßmus 	BPoint nextIn;
980*128277c9SStephan Aßmus 
981*128277c9SStephan Aßmus 	if (fPath->FindBezierScale(index - 1, where, &scale)
982*128277c9SStephan Aßmus 		&& scale >= 0.0 && scale <= 1.0
983*128277c9SStephan Aßmus 		&& fPath->GetPoint(index - 1, scale, point)) {
984*128277c9SStephan Aßmus 
985*128277c9SStephan Aßmus 		fPath->GetPointAt(index - 1, previous);
986*128277c9SStephan Aßmus 		fPath->GetPointOutAt(index - 1, previousOut);
987*128277c9SStephan Aßmus 		fPath->GetPointAt(index, next);
988*128277c9SStephan Aßmus 		fPath->GetPointInAt(index, nextIn);
989*128277c9SStephan Aßmus 
990*128277c9SStephan Aßmus 		where = scale_point(previousOut, nextIn, scale);
991*128277c9SStephan Aßmus 
992*128277c9SStephan Aßmus 		previousOut = scale_point(previous, previousOut, scale);
993*128277c9SStephan Aßmus 		nextIn = scale_point(next, nextIn, 1 - scale);
994*128277c9SStephan Aßmus 		pointIn = scale_point(previousOut, where, scale);
995*128277c9SStephan Aßmus 		pointOut = scale_point(nextIn, where, 1 - scale);
996*128277c9SStephan Aßmus 
997*128277c9SStephan Aßmus 		if (fPath->AddPoint(point, index)) {
998*128277c9SStephan Aßmus 
999*128277c9SStephan Aßmus 			fPath->SetPointIn(index, pointIn);
1000*128277c9SStephan Aßmus 			fPath->SetPointOut(index, pointOut);
1001*128277c9SStephan Aßmus 
1002*128277c9SStephan Aßmus 			delete fInsertPointCommand;
1003*128277c9SStephan Aßmus 			fInsertPointCommand = new InsertPointCommand(fPath, index,
1004*128277c9SStephan Aßmus 														 fSelection->Items(),
1005*128277c9SStephan Aßmus 														 fSelection->CountItems());
1006*128277c9SStephan Aßmus 
1007*128277c9SStephan Aßmus 			fPath->SetPointOut(index - 1, previousOut);
1008*128277c9SStephan Aßmus 			fPath->SetPointIn(index + 1, nextIn);
1009*128277c9SStephan Aßmus 
1010*128277c9SStephan Aßmus 			fCurrentPathPoint = index;
1011*128277c9SStephan Aßmus 			_ShiftSelection(fCurrentPathPoint, 1);
1012*128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
1013*128277c9SStephan Aßmus 		}
1014*128277c9SStephan Aßmus 	}
1015*128277c9SStephan Aßmus }
1016*128277c9SStephan Aßmus 
1017*128277c9SStephan Aßmus // _SetInOutConnected
1018*128277c9SStephan Aßmus void
1019*128277c9SStephan Aßmus PathManipulator::_SetInOutConnected(int32 index, bool connected)
1020*128277c9SStephan Aßmus {
1021*128277c9SStephan Aßmus 	fPath->SetInOutConnected(index, connected);
1022*128277c9SStephan Aßmus }
1023*128277c9SStephan Aßmus 
1024*128277c9SStephan Aßmus // _SetSharp
1025*128277c9SStephan Aßmus void
1026*128277c9SStephan Aßmus PathManipulator::_SetSharp(int32 index)
1027*128277c9SStephan Aßmus {
1028*128277c9SStephan Aßmus 	BPoint p;
1029*128277c9SStephan Aßmus 	fPath->GetPointAt(index, p);
1030*128277c9SStephan Aßmus 	fPath->SetPoint(index, p, p, p, true);
1031*128277c9SStephan Aßmus }
1032*128277c9SStephan Aßmus 
1033*128277c9SStephan Aßmus // _RemoveSelection
1034*128277c9SStephan Aßmus void
1035*128277c9SStephan Aßmus PathManipulator::_RemoveSelection()
1036*128277c9SStephan Aßmus {
1037*128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1038*128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
1039*128277c9SStephan Aßmus 		if (!fPath->RemovePoint(fSelection->IndexAt(i) - i))
1040*128277c9SStephan Aßmus 			break;
1041*128277c9SStephan Aßmus 	}
1042*128277c9SStephan Aßmus 
1043*128277c9SStephan Aßmus //	SetClosed(fPath->IsClosed() && fPath->CountPoints() > 1);
1044*128277c9SStephan Aßmus 
1045*128277c9SStephan Aßmus 	fSelection->MakeEmpty();
1046*128277c9SStephan Aßmus }
1047*128277c9SStephan Aßmus 
1048*128277c9SStephan Aßmus 
1049*128277c9SStephan Aßmus // _RemovePoint
1050*128277c9SStephan Aßmus void
1051*128277c9SStephan Aßmus PathManipulator::_RemovePoint(int32 index)
1052*128277c9SStephan Aßmus {
1053*128277c9SStephan Aßmus 	if (fPath->RemovePoint(index)) {
1054*128277c9SStephan Aßmus 		_Deselect(index);
1055*128277c9SStephan Aßmus 		_ShiftSelection(index + 1, -1);
1056*128277c9SStephan Aßmus 	}
1057*128277c9SStephan Aßmus }
1058*128277c9SStephan Aßmus 
1059*128277c9SStephan Aßmus // _RemovePointIn
1060*128277c9SStephan Aßmus void
1061*128277c9SStephan Aßmus PathManipulator::_RemovePointIn(int32 index)
1062*128277c9SStephan Aßmus {
1063*128277c9SStephan Aßmus 	BPoint p;
1064*128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1065*128277c9SStephan Aßmus 		fPath->SetPointIn(index, p);
1066*128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1067*128277c9SStephan Aßmus 	}
1068*128277c9SStephan Aßmus }
1069*128277c9SStephan Aßmus 
1070*128277c9SStephan Aßmus // _RemovePointOut
1071*128277c9SStephan Aßmus void
1072*128277c9SStephan Aßmus PathManipulator::_RemovePointOut(int32 index)
1073*128277c9SStephan Aßmus {
1074*128277c9SStephan Aßmus 	BPoint p;
1075*128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1076*128277c9SStephan Aßmus 		fPath->SetPointOut(index, p);
1077*128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1078*128277c9SStephan Aßmus 	}
1079*128277c9SStephan Aßmus }
1080*128277c9SStephan Aßmus 
1081*128277c9SStephan Aßmus // _Delete
1082*128277c9SStephan Aßmus Command*
1083*128277c9SStephan Aßmus PathManipulator::_Delete()
1084*128277c9SStephan Aßmus {
1085*128277c9SStephan Aßmus 	Command* command = NULL;
1086*128277c9SStephan Aßmus 	if (!fMouseDown) {
1087*128277c9SStephan Aßmus 		if (fSelection->CountItems() == fPath->CountPoints()) {
1088*128277c9SStephan Aßmus //			command = new RemovePathCommand(fPath);
1089*128277c9SStephan Aßmus 		} else {
1090*128277c9SStephan Aßmus 			command = new RemovePointsCommand(fPath,
1091*128277c9SStephan Aßmus 											  fSelection->Items(),
1092*128277c9SStephan Aßmus 											  fSelection->CountItems());
1093*128277c9SStephan Aßmus 			_RemoveSelection();
1094*128277c9SStephan Aßmus 		}
1095*128277c9SStephan Aßmus 
1096*128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1097*128277c9SStephan Aßmus 	}
1098*128277c9SStephan Aßmus 
1099*128277c9SStephan Aßmus 	return command;
1100*128277c9SStephan Aßmus }
1101*128277c9SStephan Aßmus 
1102*128277c9SStephan Aßmus // #pragma mark -
1103*128277c9SStephan Aßmus 
1104*128277c9SStephan Aßmus // _Select
1105*128277c9SStephan Aßmus void
1106*128277c9SStephan Aßmus PathManipulator::_Select(BRect r)
1107*128277c9SStephan Aßmus {
1108*128277c9SStephan Aßmus 	BPoint p;
1109*128277c9SStephan Aßmus 	int32 count = fPath->CountPoints();
1110*128277c9SStephan Aßmus 	Selection temp;
1111*128277c9SStephan Aßmus 	for (int32 i = 0; i < count && fPath->GetPointAt(i, p); i++) {
1112*128277c9SStephan Aßmus 		if (r.Contains(p)) {
1113*128277c9SStephan Aßmus 			temp.Add(i);
1114*128277c9SStephan Aßmus 		}
1115*128277c9SStephan Aßmus 	}
1116*128277c9SStephan Aßmus 	// merge old and new selection
1117*128277c9SStephan Aßmus 	count = fOldSelection->CountItems();
1118*128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
1119*128277c9SStephan Aßmus 		int32 index = fOldSelection->IndexAt(i);
1120*128277c9SStephan Aßmus 		if (temp.Contains(index))
1121*128277c9SStephan Aßmus 			temp.Remove(index);
1122*128277c9SStephan Aßmus 		else
1123*128277c9SStephan Aßmus 			temp.Add(index);
1124*128277c9SStephan Aßmus 	}
1125*128277c9SStephan Aßmus 	if (temp != *fSelection) {
1126*128277c9SStephan Aßmus 		*fSelection = temp;
1127*128277c9SStephan Aßmus 		_UpdateSelection();
1128*128277c9SStephan Aßmus 	}
1129*128277c9SStephan Aßmus }
1130*128277c9SStephan Aßmus 
1131*128277c9SStephan Aßmus // _Select
1132*128277c9SStephan Aßmus void
1133*128277c9SStephan Aßmus PathManipulator::_Select(int32 index, bool extend)
1134*128277c9SStephan Aßmus {
1135*128277c9SStephan Aßmus 	if (!extend)
1136*128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1137*128277c9SStephan Aßmus 	if (fSelection->Contains(index))
1138*128277c9SStephan Aßmus 		fSelection->Remove(index);
1139*128277c9SStephan Aßmus 	else
1140*128277c9SStephan Aßmus 		fSelection->Add(index);
1141*128277c9SStephan Aßmus 	// TODO: this can lead to unnecessary invalidation (maybe need to investigate)
1142*128277c9SStephan Aßmus 	_UpdateSelection();
1143*128277c9SStephan Aßmus }
1144*128277c9SStephan Aßmus 
1145*128277c9SStephan Aßmus // _Select
1146*128277c9SStephan Aßmus void
1147*128277c9SStephan Aßmus PathManipulator::_Select(const int32* indices, int32 count, bool extend)
1148*128277c9SStephan Aßmus {
1149*128277c9SStephan Aßmus 	if (extend) {
1150*128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1151*128277c9SStephan Aßmus 			if (!fSelection->Contains(indices[i]))
1152*128277c9SStephan Aßmus 				fSelection->Add(indices[i]);
1153*128277c9SStephan Aßmus 		}
1154*128277c9SStephan Aßmus 	} else {
1155*128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1156*128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1157*128277c9SStephan Aßmus 			fSelection->Add(indices[i]);
1158*128277c9SStephan Aßmus 		}
1159*128277c9SStephan Aßmus 	}
1160*128277c9SStephan Aßmus 	_UpdateSelection();
1161*128277c9SStephan Aßmus }
1162*128277c9SStephan Aßmus 
1163*128277c9SStephan Aßmus // _Deselect
1164*128277c9SStephan Aßmus void
1165*128277c9SStephan Aßmus PathManipulator::_Deselect(int32 index)
1166*128277c9SStephan Aßmus {
1167*128277c9SStephan Aßmus 	if (fSelection->Contains(index)) {
1168*128277c9SStephan Aßmus 		fSelection->Remove(index);
1169*128277c9SStephan Aßmus 		_UpdateSelection();
1170*128277c9SStephan Aßmus 	}
1171*128277c9SStephan Aßmus }
1172*128277c9SStephan Aßmus 
1173*128277c9SStephan Aßmus // _ShiftSelection
1174*128277c9SStephan Aßmus void
1175*128277c9SStephan Aßmus PathManipulator::_ShiftSelection(int32 startIndex, int32 direction)
1176*128277c9SStephan Aßmus {
1177*128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1178*128277c9SStephan Aßmus 	if (count > 0) {
1179*128277c9SStephan Aßmus 		int32* selection = fSelection->Items();
1180*128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1181*128277c9SStephan Aßmus 			if (selection[i] >= startIndex) {
1182*128277c9SStephan Aßmus 				selection[i] += direction;
1183*128277c9SStephan Aßmus 			}
1184*128277c9SStephan Aßmus 		}
1185*128277c9SStephan Aßmus 	}
1186*128277c9SStephan Aßmus 	_UpdateSelection();
1187*128277c9SStephan Aßmus }
1188*128277c9SStephan Aßmus 
1189*128277c9SStephan Aßmus // _IsSelected
1190*128277c9SStephan Aßmus bool
1191*128277c9SStephan Aßmus PathManipulator::_IsSelected(int32 index) const
1192*128277c9SStephan Aßmus {
1193*128277c9SStephan Aßmus 	return fSelection->Contains(index);
1194*128277c9SStephan Aßmus }
1195*128277c9SStephan Aßmus 
1196*128277c9SStephan Aßmus // #pragma mark -
1197*128277c9SStephan Aßmus 
1198*128277c9SStephan Aßmus // _InvalidateCanvas
1199*128277c9SStephan Aßmus void
1200*128277c9SStephan Aßmus PathManipulator::_InvalidateCanvas(BRect rect) const
1201*128277c9SStephan Aßmus {
1202*128277c9SStephan Aßmus 	// TODO: convert from canvas to view space
1203*128277c9SStephan Aßmus 	fCanvasView->Invalidate(rect);
1204*128277c9SStephan Aßmus }
1205*128277c9SStephan Aßmus 
1206*128277c9SStephan Aßmus // _InvalidateHighlightPoints
1207*128277c9SStephan Aßmus void
1208*128277c9SStephan Aßmus PathManipulator::_InvalidateHighlightPoints(int32 newIndex, uint32 newMode)
1209*128277c9SStephan Aßmus {
1210*128277c9SStephan Aßmus 	BRect oldRect = _ControlPointRect(fCurrentPathPoint, fMode);
1211*128277c9SStephan Aßmus 	BRect newRect = _ControlPointRect(newIndex, newMode);
1212*128277c9SStephan Aßmus 	if (oldRect.IsValid())
1213*128277c9SStephan Aßmus 		_InvalidateCanvas(oldRect);
1214*128277c9SStephan Aßmus 	if (newRect.IsValid())
1215*128277c9SStephan Aßmus 		_InvalidateCanvas(newRect);
1216*128277c9SStephan Aßmus }
1217*128277c9SStephan Aßmus 
1218*128277c9SStephan Aßmus // _UpdateSelection
1219*128277c9SStephan Aßmus void
1220*128277c9SStephan Aßmus PathManipulator::_UpdateSelection() const
1221*128277c9SStephan Aßmus {
1222*128277c9SStephan Aßmus 	_InvalidateCanvas(_ControlPointRect());
1223*128277c9SStephan Aßmus 	if (BWindow* window = fCanvasView->Window()) {
1224*128277c9SStephan Aßmus 		window->PostMessage(MSG_UPDATE_SHAPE_UI);
1225*128277c9SStephan Aßmus 	}
1226*128277c9SStephan Aßmus }
1227*128277c9SStephan Aßmus 
1228*128277c9SStephan Aßmus // _ControlPointRect
1229*128277c9SStephan Aßmus BRect
1230*128277c9SStephan Aßmus PathManipulator::_ControlPointRect() const
1231*128277c9SStephan Aßmus {
1232*128277c9SStephan Aßmus 	BRect r = fPath->ControlPointBounds();
1233*128277c9SStephan Aßmus 	r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1234*128277c9SStephan Aßmus 	return r;
1235*128277c9SStephan Aßmus }
1236*128277c9SStephan Aßmus 
1237*128277c9SStephan Aßmus // _ControlPointRect
1238*128277c9SStephan Aßmus BRect
1239*128277c9SStephan Aßmus PathManipulator::_ControlPointRect(int32 index, uint32 mode) const
1240*128277c9SStephan Aßmus {
1241*128277c9SStephan Aßmus 	BRect rect(0.0, 0.0, -1.0, -1.0);
1242*128277c9SStephan Aßmus 	if (index >= 0) {
1243*128277c9SStephan Aßmus 		BPoint p, pIn, pOut;
1244*128277c9SStephan Aßmus 		fPath->GetPointsAt(index, p, pIn, pOut);
1245*128277c9SStephan Aßmus 		switch (mode) {
1246*128277c9SStephan Aßmus 			case MOVE_POINT:
1247*128277c9SStephan Aßmus 			case TOGGLE_SHARP:
1248*128277c9SStephan Aßmus 			case REMOVE_POINT:
1249*128277c9SStephan Aßmus 			case CLOSE_PATH:
1250*128277c9SStephan Aßmus 				rect.Set(p.x, p.y, p.x, p.y);
1251*128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1252*128277c9SStephan Aßmus 				break;
1253*128277c9SStephan Aßmus 			case MOVE_POINT_IN:
1254*128277c9SStephan Aßmus 			case TOGGLE_SHARP_IN:
1255*128277c9SStephan Aßmus 			case REMOVE_POINT_IN:
1256*128277c9SStephan Aßmus 				rect.Set(pIn.x, pIn.y, pIn.x, pIn.y);
1257*128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1258*128277c9SStephan Aßmus 				break;
1259*128277c9SStephan Aßmus 			case MOVE_POINT_OUT:
1260*128277c9SStephan Aßmus 			case TOGGLE_SHARP_OUT:
1261*128277c9SStephan Aßmus 			case REMOVE_POINT_OUT:
1262*128277c9SStephan Aßmus 				rect.Set(pOut.x, pOut.y, pOut.x, pOut.y);
1263*128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1264*128277c9SStephan Aßmus 				break;
1265*128277c9SStephan Aßmus 			case SELECT_POINTS:
1266*128277c9SStephan Aßmus 				rect.Set(min4(p.x, pIn.x, pOut.x, pOut.x),
1267*128277c9SStephan Aßmus 						 min4(p.y, pIn.y, pOut.y, pOut.y),
1268*128277c9SStephan Aßmus 						 max4(p.x, pIn.x, pOut.x, pOut.x),
1269*128277c9SStephan Aßmus 						 max4(p.y, pIn.y, pOut.y, pOut.y));
1270*128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1271*128277c9SStephan Aßmus 				break;
1272*128277c9SStephan Aßmus 		}
1273*128277c9SStephan Aßmus 	}
1274*128277c9SStephan Aßmus 	return rect;
1275*128277c9SStephan Aßmus }
1276*128277c9SStephan Aßmus 
1277*128277c9SStephan Aßmus // #pragma mark -
1278*128277c9SStephan Aßmus 
1279*128277c9SStephan Aßmus // _SetModeForMousePos
1280*128277c9SStephan Aßmus void
1281*128277c9SStephan Aßmus PathManipulator::_SetModeForMousePos(BPoint where)
1282*128277c9SStephan Aßmus {
1283*128277c9SStephan Aßmus 	uint32 mode = UNDEFINED;
1284*128277c9SStephan Aßmus 	int32 index = -1;
1285*128277c9SStephan Aßmus 
1286*128277c9SStephan Aßmus 	float zoomLevel = 1.0;//fCanvasView->ZoomLevel();
1287*128277c9SStephan Aßmus 
1288*128277c9SStephan Aßmus 	// see if we're close enough at a control point
1289*128277c9SStephan Aßmus 	BPoint point;
1290*128277c9SStephan Aßmus 	BPoint pointIn;
1291*128277c9SStephan Aßmus 	BPoint pointOut;
1292*128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut)
1293*128277c9SStephan Aßmus 					  && mode == UNDEFINED; i++) {
1294*128277c9SStephan Aßmus 
1295*128277c9SStephan Aßmus 		float distM = point_point_distance(point, where) * zoomLevel;
1296*128277c9SStephan Aßmus 		float distIn = point_point_distance(pointIn, where) * zoomLevel;
1297*128277c9SStephan Aßmus 		float distOut = point_point_distance(pointOut, where) * zoomLevel;
1298*128277c9SStephan Aßmus 
1299*128277c9SStephan Aßmus 		if (distM < MOVE_THRESHOLD) {
1300*128277c9SStephan Aßmus 			if (i == 0 && fClickToClose
1301*128277c9SStephan Aßmus 				&& !fPath->IsClosed() && fPath->CountPoints() > 1) {
1302*128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1303*128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : CLOSE_PATH);
1304*128277c9SStephan Aßmus 				index = i;
1305*128277c9SStephan Aßmus 			} else {
1306*128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1307*128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : MOVE_POINT);
1308*128277c9SStephan Aßmus 				index = i;
1309*128277c9SStephan Aßmus 			}
1310*128277c9SStephan Aßmus 		}
1311*128277c9SStephan Aßmus 		if (distIn < distM && distIn < MOVE_THRESHOLD) {
1312*128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_IN :
1313*128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_IN : MOVE_POINT_IN);
1314*128277c9SStephan Aßmus 			index = i;
1315*128277c9SStephan Aßmus 		}
1316*128277c9SStephan Aßmus 		if (distOut < distIn && distOut < distM && distOut < MOVE_THRESHOLD) {
1317*128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_OUT :
1318*128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_OUT : MOVE_POINT_OUT);
1319*128277c9SStephan Aßmus 			index = i;
1320*128277c9SStephan Aßmus 		}
1321*128277c9SStephan Aßmus 	}
1322*128277c9SStephan Aßmus 	// selection mode overrides any other mode,
1323*128277c9SStephan Aßmus 	// but we need to check for it after we know
1324*128277c9SStephan Aßmus 	// the index of the point under the mouse (code above)
1325*128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1326*128277c9SStephan Aßmus 	if (fShiftDown && pointCount > 0) {
1327*128277c9SStephan Aßmus 		mode = SELECT_POINTS;
1328*128277c9SStephan Aßmus 	}
1329*128277c9SStephan Aßmus 
1330*128277c9SStephan Aßmus 	// see if user wants to start new sub path
1331*128277c9SStephan Aßmus 	if (fAltDown) {
1332*128277c9SStephan Aßmus 		mode = NEW_PATH;
1333*128277c9SStephan Aßmus 		index = -1;
1334*128277c9SStephan Aßmus 	}
1335*128277c9SStephan Aßmus 
1336*128277c9SStephan Aßmus 	// see if we're close enough at a line
1337*128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1338*128277c9SStephan Aßmus 		float distance;
1339*128277c9SStephan Aßmus 		if (fPath->GetDistance(where, &distance, &index)) {
1340*128277c9SStephan Aßmus 			if (distance < (INSERT_DIST_THRESHOLD / zoomLevel)) {
1341*128277c9SStephan Aßmus 				mode = INSERT_POINT;
1342*128277c9SStephan Aßmus 			}
1343*128277c9SStephan Aßmus 		} else {
1344*128277c9SStephan Aßmus 			// restore index, since it was changed by call above
1345*128277c9SStephan Aßmus 			index = fCurrentPathPoint;
1346*128277c9SStephan Aßmus 		}
1347*128277c9SStephan Aßmus 	}
1348*128277c9SStephan Aßmus 
1349*128277c9SStephan Aßmus 	// nope, still undefined mode, last fall back
1350*128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1351*128277c9SStephan Aßmus 		if (fFallBackMode == SELECT_POINTS) {
1352*128277c9SStephan Aßmus 			if (fPath->IsClosed() && pointCount > 0) {
1353*128277c9SStephan Aßmus 				mode = SELECT_POINTS;
1354*128277c9SStephan Aßmus 				index = -1;
1355*128277c9SStephan Aßmus 			} else {
1356*128277c9SStephan Aßmus 				mode = ADD_POINT;
1357*128277c9SStephan Aßmus 				index = pointCount - 1;
1358*128277c9SStephan Aßmus 			}
1359*128277c9SStephan Aßmus 		} else {
1360*128277c9SStephan Aßmus 			// user had clicked "New Path" icon
1361*128277c9SStephan Aßmus 			mode = fFallBackMode;
1362*128277c9SStephan Aßmus 		}
1363*128277c9SStephan Aßmus 	}
1364*128277c9SStephan Aßmus 	// switch mode if necessary
1365*128277c9SStephan Aßmus 	if (mode != fMode || index != fCurrentPathPoint) {
1366*128277c9SStephan Aßmus 		// invalidate path display (to highlight the respective point)
1367*128277c9SStephan Aßmus 		_InvalidateHighlightPoints(index, mode);
1368*128277c9SStephan Aßmus 		_SetMode(mode);
1369*128277c9SStephan Aßmus 		fCurrentPathPoint = index;
1370*128277c9SStephan Aßmus 	}
1371*128277c9SStephan Aßmus }
1372*128277c9SStephan Aßmus 
1373*128277c9SStephan Aßmus // #pragma mark -
1374*128277c9SStephan Aßmus 
1375*128277c9SStephan Aßmus // _Nudge
1376*128277c9SStephan Aßmus void
1377*128277c9SStephan Aßmus PathManipulator::_Nudge(BPoint direction)
1378*128277c9SStephan Aßmus {
1379*128277c9SStephan Aßmus 	bigtime_t now = system_time();
1380*128277c9SStephan Aßmus 	if (now - fLastNudgeTime > 500000) {
1381*128277c9SStephan Aßmus 		_FinishNudging();
1382*128277c9SStephan Aßmus 	}
1383*128277c9SStephan Aßmus 	fLastNudgeTime = now;
1384*128277c9SStephan Aßmus 	fNudgeOffset += direction;
1385*128277c9SStephan Aßmus 
1386*128277c9SStephan Aßmus //	if (!fNudgeCommand) {
1387*128277c9SStephan Aßmus //
1388*128277c9SStephan Aßmus //		bool fromSelection = !fSelection->IsEmpty();
1389*128277c9SStephan Aßmus //
1390*128277c9SStephan Aßmus //		int32 count = fromSelection ? fSelection->CountItems()
1391*128277c9SStephan Aßmus //									: fPath->CountPoints();
1392*128277c9SStephan Aßmus //		int32* indices = new int32[count];
1393*128277c9SStephan Aßmus //		control_point* points = new control_point[count];
1394*128277c9SStephan Aßmus //
1395*128277c9SStephan Aßmus //		// init indices and points
1396*128277c9SStephan Aßmus //		for (int32 i = 0; i < count; i++) {
1397*128277c9SStephan Aßmus //			indices[i] = fromSelection ? fSelection->IndexAt(i) : i;
1398*128277c9SStephan Aßmus //			fPath->GetPointsAt(indices[i],
1399*128277c9SStephan Aßmus //							   points[i].point,
1400*128277c9SStephan Aßmus //							   points[i].point_in,
1401*128277c9SStephan Aßmus //							   points[i].point_out,
1402*128277c9SStephan Aßmus //							   &points[i].connected);
1403*128277c9SStephan Aßmus //		}
1404*128277c9SStephan Aßmus //
1405*128277c9SStephan Aßmus //			fNudgeCommand = new NudgePointsCommand(this, fPath,
1406*128277c9SStephan Aßmus //												 indices, points, count);
1407*128277c9SStephan Aßmus //
1408*128277c9SStephan Aßmus //			fNudgeCommand->SetNewTranslation(fNudgeOffset);
1409*128277c9SStephan Aßmus //			fNudgeCommand->Redo(fCanvasView);
1410*128277c9SStephan Aßmus //
1411*128277c9SStephan Aßmus //		delete[] indices;
1412*128277c9SStephan Aßmus //		delete[] points;
1413*128277c9SStephan Aßmus //
1414*128277c9SStephan Aßmus //	} else {
1415*128277c9SStephan Aßmus //		fNudgeCommand->SetNewTranslation(fNudgeOffset);
1416*128277c9SStephan Aßmus //		fNudgeCommand->Redo(fCanvasView);
1417*128277c9SStephan Aßmus //	}
1418*128277c9SStephan Aßmus 
1419*128277c9SStephan Aßmus 	if (!fMouseDown)
1420*128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1421*128277c9SStephan Aßmus }
1422*128277c9SStephan Aßmus 
1423*128277c9SStephan Aßmus // _FinishNudging
1424*128277c9SStephan Aßmus void
1425*128277c9SStephan Aßmus PathManipulator::_FinishNudging()
1426*128277c9SStephan Aßmus {
1427*128277c9SStephan Aßmus 	fNudgeOffset = BPoint(0.0, 0.0);
1428*128277c9SStephan Aßmus 
1429*128277c9SStephan Aßmus //	if (fNudgeCommand) {
1430*128277c9SStephan Aßmus //		fCanvasView->Perform(fNudgeCommand);
1431*128277c9SStephan Aßmus //		fNudgeCommand = NULL;
1432*128277c9SStephan Aßmus //	}
1433*128277c9SStephan Aßmus }
1434*128277c9SStephan Aßmus 
1435*128277c9SStephan Aßmus 
1436