xref: /haiku/src/apps/icon-o-matic/shape/PathManipulator.cpp (revision 0a5625377a42de66e78b9dba91bf5c3777ce2ce8)
1128277c9SStephan Aßmus /*
2a9cee0f1SStephan Aßmus  * Copyright 2006-2009, Stephan Aßmus <superstippi@gmx.de>.
3*0a562537SZardshard  * Copyright 2023, Haiku, Inc.
4a9cee0f1SStephan Aßmus  * All rights reserved. Distributed under the terms of the MIT License.
5*0a562537SZardshard  *
6*0a562537SZardshard  * Authors:
7*0a562537SZardshard  *             Zardshard
8128277c9SStephan Aßmus  */
9128277c9SStephan Aßmus 
10128277c9SStephan Aßmus #include "PathManipulator.h"
11128277c9SStephan Aßmus 
12*0a562537SZardshard #include <algorithm>
13128277c9SStephan Aßmus #include <float.h>
14128277c9SStephan Aßmus #include <stdio.h>
15*0a562537SZardshard #include <vector>
16128277c9SStephan Aßmus 
17518852fcSAdrien Destugues #include <Catalog.h>
18128277c9SStephan Aßmus #include <Cursor.h>
19518852fcSAdrien Destugues #include <Locale.h>
20128277c9SStephan Aßmus #include <Message.h>
21f4bd80a2SStephan Aßmus #include <MenuItem.h>
22f4bd80a2SStephan Aßmus #include <PopUpMenu.h>
23f4c2f7ebSJonathan Schleifer #include <StackOrHeapArray.h>
24128277c9SStephan Aßmus #include <Window.h>
25128277c9SStephan Aßmus 
26128277c9SStephan Aßmus #include "cursors.h"
27128277c9SStephan Aßmus #include "support.h"
28128277c9SStephan Aßmus 
29f67876a0SStephan Aßmus #include "CanvasView.h"
30128277c9SStephan Aßmus 
31128277c9SStephan Aßmus #include "AddPointCommand.h"
32128277c9SStephan Aßmus #include "ChangePointCommand.h"
33128277c9SStephan Aßmus //#include "CloseCommand.h"
34128277c9SStephan Aßmus #include "InsertPointCommand.h"
357c4b3726SStephan Aßmus #include "FlipPointsCommand.h"
36128277c9SStephan Aßmus //#include "NewPathCommand.h"
370e1ba39fSStephan Aßmus #include "NudgePointsCommand.h"
38128277c9SStephan Aßmus //#include "RemovePathCommand.h"
39128277c9SStephan Aßmus #include "RemovePointsCommand.h"
40128277c9SStephan Aßmus //#include "ReversePathCommand.h"
41128277c9SStephan Aßmus //#include "SelectPathCommand.h"
42128277c9SStephan Aßmus //#include "SelectPointsCommand.h"
43f4bd80a2SStephan Aßmus #include "SplitPointsCommand.h"
440e1ba39fSStephan Aßmus #include "TransformPointsBox.h"
45128277c9SStephan Aßmus 
46518852fcSAdrien Destugues 
47546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT
48546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "Icon-O-Matic-PathManipulator"
49128277c9SStephan Aßmus #define POINT_EXTEND 3.0
50128277c9SStephan Aßmus #define CONTROL_POINT_EXTEND 2.0
51128277c9SStephan Aßmus #define INSERT_DIST_THRESHOLD 7.0
52128277c9SStephan Aßmus #define MOVE_THRESHOLD 9.0
53128277c9SStephan Aßmus 
54518852fcSAdrien Destugues 
55128277c9SStephan Aßmus enum {
56128277c9SStephan Aßmus 	UNDEFINED,
57128277c9SStephan Aßmus 
58128277c9SStephan Aßmus 	NEW_PATH,
59128277c9SStephan Aßmus 
60128277c9SStephan Aßmus 	ADD_POINT,
61128277c9SStephan Aßmus 	INSERT_POINT,
62128277c9SStephan Aßmus 	MOVE_POINT,
63128277c9SStephan Aßmus 	MOVE_POINT_IN,
64128277c9SStephan Aßmus 	MOVE_POINT_OUT,
65128277c9SStephan Aßmus 	CLOSE_PATH,
66128277c9SStephan Aßmus 
67128277c9SStephan Aßmus 	TOGGLE_SHARP,
68128277c9SStephan Aßmus 	TOGGLE_SHARP_IN,
69128277c9SStephan Aßmus 	TOGGLE_SHARP_OUT,
70128277c9SStephan Aßmus 
71128277c9SStephan Aßmus 	REMOVE_POINT,
72128277c9SStephan Aßmus 	REMOVE_POINT_IN,
73128277c9SStephan Aßmus 	REMOVE_POINT_OUT,
74128277c9SStephan Aßmus 
75128277c9SStephan Aßmus 	SELECT_POINTS,
76128277c9SStephan Aßmus 	TRANSFORM_POINTS,
77128277c9SStephan Aßmus 	TRANSLATE_POINTS,
78128277c9SStephan Aßmus 
79128277c9SStephan Aßmus 	SELECT_SUB_PATH,
80128277c9SStephan Aßmus };
81128277c9SStephan Aßmus 
82f4bd80a2SStephan Aßmus enum {
83f4bd80a2SStephan Aßmus 	MSG_TRANSFORM				= 'strn',
84f4bd80a2SStephan Aßmus 	MSG_REMOVE_POINTS			= 'srmp',
85f4bd80a2SStephan Aßmus 	MSG_UPDATE_SHAPE_UI			= 'udsi',
86f4bd80a2SStephan Aßmus 
87f4bd80a2SStephan Aßmus 	MSG_SPLIT_POINTS			= 'splt',
887c4b3726SStephan Aßmus 	MSG_FLIP_POINTS				= 'flip',
89f4bd80a2SStephan Aßmus };
90f4bd80a2SStephan Aßmus 
91128277c9SStephan Aßmus inline const char*
string_for_mode(uint32 mode)92128277c9SStephan Aßmus string_for_mode(uint32 mode)
93128277c9SStephan Aßmus {
94128277c9SStephan Aßmus 	switch (mode) {
95128277c9SStephan Aßmus 		case UNDEFINED:
96128277c9SStephan Aßmus 			return "UNDEFINED";
97128277c9SStephan Aßmus 		case NEW_PATH:
98128277c9SStephan Aßmus 			return "NEW_PATH";
99128277c9SStephan Aßmus 		case ADD_POINT:
100128277c9SStephan Aßmus 			return "ADD_POINT";
101128277c9SStephan Aßmus 		case INSERT_POINT:
102128277c9SStephan Aßmus 			return "INSERT_POINT";
103128277c9SStephan Aßmus 		case MOVE_POINT:
104128277c9SStephan Aßmus 			return "MOVE_POINT";
105128277c9SStephan Aßmus 		case MOVE_POINT_IN:
106128277c9SStephan Aßmus 			return "MOVE_POINT_IN";
107128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
108128277c9SStephan Aßmus 			return "MOVE_POINT_OUT";
109128277c9SStephan Aßmus 		case CLOSE_PATH:
110128277c9SStephan Aßmus 			return "CLOSE_PATH";
111128277c9SStephan Aßmus 		case TOGGLE_SHARP:
112128277c9SStephan Aßmus 			return "TOGGLE_SHARP";
113128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
114128277c9SStephan Aßmus 			return "TOGGLE_SHARP_IN";
115128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
116128277c9SStephan Aßmus 			return "TOGGLE_SHARP_OUT";
117128277c9SStephan Aßmus 		case REMOVE_POINT:
118128277c9SStephan Aßmus 			return "REMOVE_POINT";
119128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
120128277c9SStephan Aßmus 			return "REMOVE_POINT_IN";
121128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
122128277c9SStephan Aßmus 			return "REMOVE_POINT_OUT";
123128277c9SStephan Aßmus 		case SELECT_POINTS:
124128277c9SStephan Aßmus 			return "SELECT_POINTS";
125128277c9SStephan Aßmus 		case TRANSFORM_POINTS:
126128277c9SStephan Aßmus 			return "TRANSFORM_POINTS";
127128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
128128277c9SStephan Aßmus 			return "TRANSLATE_POINTS";
129128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
130128277c9SStephan Aßmus 			return "SELECT_SUB_PATH";
131128277c9SStephan Aßmus 	}
132128277c9SStephan Aßmus 	return "<unknown mode>";
133128277c9SStephan Aßmus }
134128277c9SStephan Aßmus 
135*0a562537SZardshard // NOTE: this class extends std::vector<int32> since neither BList or
136*0a562537SZardshard // BObjectList would suffice. The backing array of BList and BObjectList is a
137*0a562537SZardshard // void* array. The Items function should return an int32 array. This is a
138*0a562537SZardshard // problem since sizeof(void*) is not necessarily equal to sizeof(int32).
139*0a562537SZardshard class PathManipulator::Selection : protected std::vector<int32>
140128277c9SStephan Aßmus {
141128277c9SStephan Aßmus public:
Selection(int32 count=20)142128277c9SStephan Aßmus 	inline Selection(int32 count = 20)
143*0a562537SZardshard 		: _inherited() { reserve(count); }
~Selection()144128277c9SStephan Aßmus 	inline ~Selection() {}
145128277c9SStephan Aßmus 
Add(int32 value)146128277c9SStephan Aßmus 	inline void Add(int32 value)
147128277c9SStephan Aßmus 	{
148128277c9SStephan Aßmus 		if (value >= 0) {
149128277c9SStephan Aßmus 			// keep the list sorted
150*0a562537SZardshard 			insert(std::upper_bound(begin(), end(), value), value);
151128277c9SStephan Aßmus 		}
152128277c9SStephan Aßmus 	}
153128277c9SStephan Aßmus 
Remove(int32 value)154128277c9SStephan Aßmus 	inline bool Remove(int32 value)
155*0a562537SZardshard 	{
156*0a562537SZardshard 		if (!Contains(value))
157*0a562537SZardshard 			return false;
158*0a562537SZardshard 		erase(std::lower_bound(begin(), end(), value));
159*0a562537SZardshard 		return true;
160*0a562537SZardshard 	}
161128277c9SStephan Aßmus 
Contains(int32 value) const162128277c9SStephan Aßmus 	inline bool Contains(int32 value) const
163*0a562537SZardshard 		{ return std::binary_search(begin(), end(), value); }
164128277c9SStephan Aßmus 
IsEmpty() const165128277c9SStephan Aßmus 	inline bool IsEmpty() const
166*0a562537SZardshard 		{ return size() == 0; }
167128277c9SStephan Aßmus 
IndexAt(int32 index) const168128277c9SStephan Aßmus 	inline int32 IndexAt(int32 index) const
169*0a562537SZardshard 		{ return at(index); }
170128277c9SStephan Aßmus 
MakeEmpty()171128277c9SStephan Aßmus 	inline void MakeEmpty()
172*0a562537SZardshard 		{ clear(); }
173128277c9SStephan Aßmus 
Items() const174*0a562537SZardshard 	inline const int32* Items() const
175*0a562537SZardshard 		{ return &(*this)[0]; }
176128277c9SStephan Aßmus 
CountItems() const177128277c9SStephan Aßmus 	inline const int32 CountItems() const
178*0a562537SZardshard 		{ return size(); }
179128277c9SStephan Aßmus 
operator =(const Selection & other)180128277c9SStephan Aßmus 	inline Selection& operator =(const Selection& other)
181128277c9SStephan Aßmus 	{
182*0a562537SZardshard 		_inherited::operator=(other);
183128277c9SStephan Aßmus 		return *this;
184128277c9SStephan Aßmus 	}
185128277c9SStephan Aßmus 
operator ==(const Selection & other)186128277c9SStephan Aßmus 	inline bool operator ==(const Selection& other)
187*0a562537SZardshard 		{ return (_inherited)*this == (_inherited)other; }
188128277c9SStephan Aßmus 
operator !=(const Selection & other)189128277c9SStephan Aßmus 	inline bool operator !=(const Selection& other)
190*0a562537SZardshard 		{ return (_inherited)*this != (_inherited)other; }
191*0a562537SZardshard 
192*0a562537SZardshard private:
193*0a562537SZardshard 	typedef std::vector<int32> _inherited;
194128277c9SStephan Aßmus };
195128277c9SStephan Aßmus 
196128277c9SStephan Aßmus 
PathManipulator(VectorPath * path)197128277c9SStephan Aßmus PathManipulator::PathManipulator(VectorPath* path)
1980e1ba39fSStephan Aßmus 	: Manipulator(NULL),
199128277c9SStephan Aßmus 	  fCanvasView(NULL),
200128277c9SStephan Aßmus 
201128277c9SStephan Aßmus 	  fCommandDown(false),
202128277c9SStephan Aßmus 	  fOptionDown(false),
203128277c9SStephan Aßmus 	  fShiftDown(false),
204128277c9SStephan Aßmus 	  fAltDown(false),
205128277c9SStephan Aßmus 
206128277c9SStephan Aßmus 	  fClickToClose(false),
207128277c9SStephan Aßmus 
208128277c9SStephan Aßmus 	  fMode(NEW_PATH),
209128277c9SStephan Aßmus 	  fFallBackMode(SELECT_POINTS),
210128277c9SStephan Aßmus 
211128277c9SStephan Aßmus 	  fMouseDown(false),
212128277c9SStephan Aßmus 
213128277c9SStephan Aßmus 	  fPath(path),
214128277c9SStephan Aßmus 	  fCurrentPathPoint(-1),
215128277c9SStephan Aßmus 
216128277c9SStephan Aßmus 	  fChangePointCommand(NULL),
217128277c9SStephan Aßmus 	  fInsertPointCommand(NULL),
218128277c9SStephan Aßmus 	  fAddPointCommand(NULL),
219128277c9SStephan Aßmus 
220128277c9SStephan Aßmus 	  fSelection(new Selection()),
221128277c9SStephan Aßmus 	  fOldSelection(new Selection()),
2220e1ba39fSStephan Aßmus 	  fTransformBox(NULL),
223128277c9SStephan Aßmus 
224128277c9SStephan Aßmus 	  fNudgeOffset(0.0, 0.0),
2250e1ba39fSStephan Aßmus 	  fLastNudgeTime(system_time()),
2260e1ba39fSStephan Aßmus 	  fNudgeCommand(NULL)
227128277c9SStephan Aßmus {
2280cbb6c11SStephan Aßmus 	fPath->AcquireReference();
22905fd3818SStephan Aßmus 	fPath->AddListener(this);
2300e1ba39fSStephan Aßmus 	fPath->AddObserver(this);
231128277c9SStephan Aßmus }
232128277c9SStephan Aßmus 
233*0a562537SZardshard 
~PathManipulator()234128277c9SStephan Aßmus PathManipulator::~PathManipulator()
235128277c9SStephan Aßmus {
236128277c9SStephan Aßmus 	delete fChangePointCommand;
237128277c9SStephan Aßmus 	delete fInsertPointCommand;
238128277c9SStephan Aßmus 	delete fAddPointCommand;
239128277c9SStephan Aßmus 
240128277c9SStephan Aßmus 	delete fSelection;
241128277c9SStephan Aßmus 	delete fOldSelection;
2420e1ba39fSStephan Aßmus 	delete fTransformBox;
243128277c9SStephan Aßmus 
2440e1ba39fSStephan Aßmus 	delete fNudgeCommand;
2450e1ba39fSStephan Aßmus 
2460e1ba39fSStephan Aßmus 	fPath->RemoveObserver(this);
2470e1ba39fSStephan Aßmus 	fPath->RemoveListener(this);
2480cbb6c11SStephan Aßmus 	fPath->ReleaseReference();
249128277c9SStephan Aßmus }
250128277c9SStephan Aßmus 
251128277c9SStephan Aßmus 
252128277c9SStephan Aßmus // #pragma mark -
253128277c9SStephan Aßmus 
254*0a562537SZardshard 
255128277c9SStephan Aßmus class StrokePathIterator : public VectorPath::Iterator {
256128277c9SStephan Aßmus  public:
StrokePathIterator(CanvasView * canvasView,BView * drawingView)257f67876a0SStephan Aßmus 					StrokePathIterator(CanvasView* canvasView,
258f67876a0SStephan Aßmus 									   BView* drawingView)
259f67876a0SStephan Aßmus 						: fCanvasView(canvasView),
260f67876a0SStephan Aßmus 						  fDrawingView(drawingView)
261128277c9SStephan Aßmus 					{
262128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
263128277c9SStephan Aßmus 						fDrawingView->SetDrawingMode(B_OP_OVER);
264128277c9SStephan Aßmus 					}
~StrokePathIterator()265128277c9SStephan Aßmus 	virtual			~StrokePathIterator()
266128277c9SStephan Aßmus 					{}
267128277c9SStephan Aßmus 
MoveTo(BPoint point)268128277c9SStephan Aßmus 	virtual	void	MoveTo(BPoint point)
269128277c9SStephan Aßmus 					{
270128277c9SStephan Aßmus 						fBlack = true;
271f67876a0SStephan Aßmus 						fSkip = false;
272128277c9SStephan Aßmus 						fDrawingView->SetHighColor(0, 0, 0, 255);
273128277c9SStephan Aßmus 
274f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
275128277c9SStephan Aßmus 						fDrawingView->MovePenTo(point);
276128277c9SStephan Aßmus 					}
LineTo(BPoint point)277128277c9SStephan Aßmus 	virtual	void	LineTo(BPoint point)
278128277c9SStephan Aßmus 					{
279f67876a0SStephan Aßmus 						fCanvasView->ConvertFromCanvas(&point);
280f67876a0SStephan Aßmus 						if (!fSkip) {
281128277c9SStephan Aßmus 							if (fBlack)
282128277c9SStephan Aßmus 								fDrawingView->SetHighColor(255, 255, 255, 255);
283128277c9SStephan Aßmus 							else
284128277c9SStephan Aßmus 								fDrawingView->SetHighColor(0, 0, 0, 255);
285128277c9SStephan Aßmus 							fBlack = !fBlack;
286128277c9SStephan Aßmus 
287128277c9SStephan Aßmus 							fDrawingView->StrokeLine(point);
288f67876a0SStephan Aßmus 						} else {
289f67876a0SStephan Aßmus 							fDrawingView->MovePenTo(point);
290f67876a0SStephan Aßmus 						}
291f67876a0SStephan Aßmus 						fSkip = !fSkip;
292128277c9SStephan Aßmus 					}
293128277c9SStephan Aßmus 
294128277c9SStephan Aßmus  private:
295f67876a0SStephan Aßmus 	CanvasView*		fCanvasView;
296128277c9SStephan Aßmus 	BView*			fDrawingView;
297128277c9SStephan Aßmus 	bool			fBlack;
298f67876a0SStephan Aßmus 	bool			fSkip;
299128277c9SStephan Aßmus };
300128277c9SStephan Aßmus 
301*0a562537SZardshard 
302128277c9SStephan Aßmus void
Draw(BView * into,BRect updateRect)303128277c9SStephan Aßmus PathManipulator::Draw(BView* into, BRect updateRect)
304128277c9SStephan Aßmus {
30564dd737cSStephan Aßmus 	// draw the Bezier curve, but only if not "editing",
30664dd737cSStephan Aßmus 	// the path is actually on top all other modifiers
307128277c9SStephan Aßmus 	// TODO: make this customizable in the GUI
30864dd737cSStephan Aßmus 
30964dd737cSStephan Aßmus 	#if __HAIKU__
31064dd737cSStephan Aßmus 	uint32 flags = into->Flags();
31164dd737cSStephan Aßmus 	into->SetFlags(flags | B_SUBPIXEL_PRECISE);
31264dd737cSStephan Aßmus 	#endif // __HAIKU__
31364dd737cSStephan Aßmus 
314f67876a0SStephan Aßmus 	StrokePathIterator iterator(fCanvasView, into);
315f67876a0SStephan Aßmus 	fPath->Iterate(&iterator, fCanvasView->ZoomLevel());
316128277c9SStephan Aßmus 
31764dd737cSStephan Aßmus 	#if __HAIKU__
31864dd737cSStephan Aßmus 	into->SetFlags(flags);
31964dd737cSStephan Aßmus 	#endif // __HAIKU__
32064dd737cSStephan Aßmus 
321128277c9SStephan Aßmus 	into->SetLowColor(0, 0, 0, 255);
322128277c9SStephan Aßmus 	BPoint point;
323128277c9SStephan Aßmus 	BPoint pointIn;
324128277c9SStephan Aßmus 	BPoint pointOut;
325128277c9SStephan Aßmus 	rgb_color focusColor = (rgb_color){ 255, 0, 0, 255 };
326128277c9SStephan Aßmus 	rgb_color highlightColor = (rgb_color){ 60, 60, 255, 255 };
327128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut); i++) {
328128277c9SStephan Aßmus 		bool highlight = fCurrentPathPoint == i;
329128277c9SStephan Aßmus 		bool selected = fSelection->Contains(i);
330128277c9SStephan Aßmus 		rgb_color normal = selected ? focusColor : (rgb_color){ 0, 0, 0, 255 };
331128277c9SStephan Aßmus 		into->SetLowColor(normal);
332128277c9SStephan Aßmus 		into->SetHighColor(255, 255, 255, 255);
333128277c9SStephan Aßmus 		// convert to view coordinate space
334f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&point);
335f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointIn);
336f67876a0SStephan Aßmus 		fCanvasView->ConvertFromCanvas(&pointOut);
337128277c9SStephan Aßmus 		// connect the points belonging to one control point
338128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_INVERT);
339128277c9SStephan Aßmus 		into->StrokeLine(point, pointIn);
340128277c9SStephan Aßmus 		into->StrokeLine(point, pointOut);
341128277c9SStephan Aßmus 		// draw main control point
342128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT ||
343128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP ||
344128277c9SStephan Aßmus 						  fMode == REMOVE_POINT ||
345128277c9SStephan Aßmus 						  fMode == SELECT_POINTS ||
346128277c9SStephan Aßmus 						  fMode == CLOSE_PATH)) {
347128277c9SStephan Aßmus 
348128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
349128277c9SStephan Aßmus 		}
350128277c9SStephan Aßmus 
351128277c9SStephan Aßmus 		into->SetDrawingMode(B_OP_COPY);
352128277c9SStephan Aßmus 		BRect r(point, point);
353128277c9SStephan Aßmus 		r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
354128277c9SStephan Aßmus 		into->StrokeRect(r, B_SOLID_LOW);
355128277c9SStephan Aßmus 		r.InsetBy(1.0, 1.0);
356128277c9SStephan Aßmus 		into->FillRect(r, B_SOLID_HIGH);
357128277c9SStephan Aßmus 		// draw in control point
358128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_IN ||
359128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_IN ||
360128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_IN ||
361128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
362128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
363128277c9SStephan Aßmus 		else
364128277c9SStephan Aßmus 			into->SetLowColor(normal);
365128277c9SStephan Aßmus 		if (selected) {
366128277c9SStephan Aßmus 			into->SetHighColor(220, 220, 220, 255);
367128277c9SStephan Aßmus 		} else {
368128277c9SStephan Aßmus 			into->SetHighColor(170, 170, 170, 255);
369128277c9SStephan Aßmus 		}
370128277c9SStephan Aßmus 		if (pointIn != point) {
371128277c9SStephan Aßmus 			r.Set(pointIn.x - CONTROL_POINT_EXTEND, pointIn.y - CONTROL_POINT_EXTEND,
372128277c9SStephan Aßmus 				  pointIn.x + CONTROL_POINT_EXTEND, pointIn.y + CONTROL_POINT_EXTEND);
373128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
374128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
375128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
376128277c9SStephan Aßmus 		}
377128277c9SStephan Aßmus 		// draw out control point
378128277c9SStephan Aßmus 		if (highlight && (fMode == MOVE_POINT_OUT ||
379128277c9SStephan Aßmus 						  fMode == TOGGLE_SHARP_OUT ||
380128277c9SStephan Aßmus 						  fMode == REMOVE_POINT_OUT ||
381128277c9SStephan Aßmus 						  fMode == SELECT_POINTS))
382128277c9SStephan Aßmus 			into->SetLowColor(highlightColor);
383128277c9SStephan Aßmus 		else
384128277c9SStephan Aßmus 			into->SetLowColor(normal);
385128277c9SStephan Aßmus 		if (pointOut != point) {
386128277c9SStephan Aßmus 			r.Set(pointOut.x - CONTROL_POINT_EXTEND, pointOut.y - CONTROL_POINT_EXTEND,
387128277c9SStephan Aßmus 				  pointOut.x + CONTROL_POINT_EXTEND, pointOut.y + CONTROL_POINT_EXTEND);
388128277c9SStephan Aßmus 			into->StrokeRect(r, B_SOLID_LOW);
389128277c9SStephan Aßmus 			r.InsetBy(1.0, 1.0);
390128277c9SStephan Aßmus 			into->FillRect(r, B_SOLID_HIGH);
391128277c9SStephan Aßmus 		}
392128277c9SStephan Aßmus 	}
3930e1ba39fSStephan Aßmus 
3940e1ba39fSStephan Aßmus 	if (fTransformBox) {
3950e1ba39fSStephan Aßmus 		fTransformBox->Draw(into, updateRect);
3960e1ba39fSStephan Aßmus 	}
397128277c9SStephan Aßmus }
398128277c9SStephan Aßmus 
399*0a562537SZardshard 
400128277c9SStephan Aßmus // #pragma mark -
401128277c9SStephan Aßmus 
402*0a562537SZardshard 
403128277c9SStephan Aßmus bool
MouseDown(BPoint where)404128277c9SStephan Aßmus PathManipulator::MouseDown(BPoint where)
405128277c9SStephan Aßmus {
406128277c9SStephan Aßmus 	fMouseDown = true;
407128277c9SStephan Aßmus 
4080e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
4090e1ba39fSStephan Aßmus 		if (fTransformBox) {
4100e1ba39fSStephan Aßmus 			fTransformBox->MouseDown(where);
4110e1ba39fSStephan Aßmus 
4120e1ba39fSStephan Aßmus //			if (!fTransformBox->IsRotating())
4130e1ba39fSStephan Aßmus //				fCanvasView->SetAutoScrolling(true);
4140e1ba39fSStephan Aßmus 		}
4150e1ba39fSStephan Aßmus 		return true;
4160e1ba39fSStephan Aßmus 	}
4170e1ba39fSStephan Aßmus 
418128277c9SStephan Aßmus 	if (fMode == MOVE_POINT &&
419128277c9SStephan Aßmus 		fSelection->CountItems() > 1 &&
420128277c9SStephan Aßmus 		fSelection->Contains(fCurrentPathPoint)) {
421128277c9SStephan Aßmus 		fMode = TRANSLATE_POINTS;
422128277c9SStephan Aßmus 	}
423128277c9SStephan Aßmus 
4244fac07a0SStephan Aßmus 	// apply the canvas view mouse filter depending on current mode
4254fac07a0SStephan Aßmus 	if (fMode == ADD_POINT || fMode == TRANSLATE_POINTS)
4264fac07a0SStephan Aßmus 		fCanvasView->FilterMouse(&where);
4274fac07a0SStephan Aßmus 
428128277c9SStephan Aßmus 	BPoint canvasWhere = where;
429f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
430128277c9SStephan Aßmus 
431128277c9SStephan Aßmus 	// maybe we're changing some point, so we construct the
432128277c9SStephan Aßmus 	// "ChangePointCommand" here so that the point is remembered
433128277c9SStephan Aßmus 	// in its current state
4344fac07a0SStephan Aßmus 	// apply the canvas view mouse filter depending on current mode
435128277c9SStephan Aßmus 	delete fChangePointCommand;
436128277c9SStephan Aßmus 	fChangePointCommand = NULL;
437128277c9SStephan Aßmus 	switch (fMode) {
438128277c9SStephan Aßmus 		case TOGGLE_SHARP:
439128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
440128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
441128277c9SStephan Aßmus 		case MOVE_POINT:
442128277c9SStephan Aßmus 		case MOVE_POINT_IN:
443128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
444128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
445128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
446128277c9SStephan Aßmus 			fChangePointCommand = new ChangePointCommand(fPath,
447128277c9SStephan Aßmus 														 fCurrentPathPoint,
448128277c9SStephan Aßmus 														 fSelection->Items(),
449128277c9SStephan Aßmus 														 fSelection->CountItems());
450128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
451128277c9SStephan Aßmus 			break;
452128277c9SStephan Aßmus 	}
453128277c9SStephan Aßmus 
454128277c9SStephan Aßmus 	// at this point we init doing something
455128277c9SStephan Aßmus 	switch (fMode) {
456128277c9SStephan Aßmus 		case ADD_POINT:
457128277c9SStephan Aßmus 			_AddPoint(canvasWhere);
458128277c9SStephan Aßmus 			break;
459128277c9SStephan Aßmus 		case INSERT_POINT:
460128277c9SStephan Aßmus 			_InsertPoint(canvasWhere, fCurrentPathPoint);
461128277c9SStephan Aßmus 			break;
462128277c9SStephan Aßmus 
463128277c9SStephan Aßmus 		case TOGGLE_SHARP:
464128277c9SStephan Aßmus 			_SetSharp(fCurrentPathPoint);
465128277c9SStephan Aßmus 			// continue by dragging out the _connected_ in/out points
466128277c9SStephan Aßmus 			break;
467128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
468128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
469128277c9SStephan Aßmus 			// continue by moving the "in" point
470128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_IN);
471128277c9SStephan Aßmus 			break;
472128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
473128277c9SStephan Aßmus 			_SetInOutConnected(fCurrentPathPoint, false);
474128277c9SStephan Aßmus 			// continue by moving the "out" point
475128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
476128277c9SStephan Aßmus 			break;
477128277c9SStephan Aßmus 
478128277c9SStephan Aßmus 		case MOVE_POINT:
479128277c9SStephan Aßmus 		case MOVE_POINT_IN:
480128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
481128277c9SStephan Aßmus 			// the right thing happens since "fCurrentPathPoint"
482128277c9SStephan Aßmus 			// points to the correct index
483128277c9SStephan Aßmus 			break;
484128277c9SStephan Aßmus 
485128277c9SStephan Aßmus 		case CLOSE_PATH:
486128277c9SStephan Aßmus //			SetClosed(true, true);
487128277c9SStephan Aßmus 			break;
488128277c9SStephan Aßmus 
489128277c9SStephan Aßmus 		case REMOVE_POINT:
490128277c9SStephan Aßmus 			if (fPath->CountPoints() == 1) {
491128277c9SStephan Aßmus //				fCanvasView->Perform(new RemovePathCommand(this, fPath));
492128277c9SStephan Aßmus 			} else {
493128277c9SStephan Aßmus 				fCanvasView->Perform(new RemovePointsCommand(fPath,
494128277c9SStephan Aßmus 															 fCurrentPathPoint,
495128277c9SStephan Aßmus 															 fSelection->Items(),
496128277c9SStephan Aßmus 															 fSelection->CountItems()));
497128277c9SStephan Aßmus 				_RemovePoint(fCurrentPathPoint);
498128277c9SStephan Aßmus 			}
499128277c9SStephan Aßmus 			break;
500128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
501128277c9SStephan Aßmus 			_RemovePointIn(fCurrentPathPoint);
502128277c9SStephan Aßmus 			break;
503128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
504128277c9SStephan Aßmus 			_RemovePointOut(fCurrentPathPoint);
505128277c9SStephan Aßmus 			break;
506128277c9SStephan Aßmus 
507f4bd80a2SStephan Aßmus 		case SELECT_POINTS: {
508f4bd80a2SStephan Aßmus 			// TODO: this works so that you can deselect all points
509f4bd80a2SStephan Aßmus 			// when clicking outside the path even if pressing shift
510f4bd80a2SStephan Aßmus 			// in case the path is open... a better way would be
511f4bd80a2SStephan Aßmus 			// to deselect all on mouse up, if the mouse has not moved
512f4bd80a2SStephan Aßmus 			bool appendSelection;
513f4bd80a2SStephan Aßmus 			if (fPath->IsClosed())
514f4bd80a2SStephan Aßmus 				appendSelection = fShiftDown;
515f4bd80a2SStephan Aßmus 			else
516f4bd80a2SStephan Aßmus 				appendSelection = fShiftDown && fCurrentPathPoint >= 0;
517f4bd80a2SStephan Aßmus 
518f4bd80a2SStephan Aßmus 			if (!appendSelection) {
519128277c9SStephan Aßmus 				fSelection->MakeEmpty();
520128277c9SStephan Aßmus 				_UpdateSelection();
521128277c9SStephan Aßmus 			}
522128277c9SStephan Aßmus 			*fOldSelection = *fSelection;
523128277c9SStephan Aßmus 			if (fCurrentPathPoint >= 0) {
524f4bd80a2SStephan Aßmus 				_Select(fCurrentPathPoint, appendSelection);
525128277c9SStephan Aßmus 			}
526128277c9SStephan Aßmus 			fCanvasView->BeginRectTracking(BRect(where, where),
527128277c9SStephan Aßmus 				B_TRACK_RECT_CORNER);
528128277c9SStephan Aßmus 			break;
529128277c9SStephan Aßmus 		}
530f4bd80a2SStephan Aßmus 	}
531128277c9SStephan Aßmus 
532128277c9SStephan Aßmus 	fTrackingStart = canvasWhere;
533128277c9SStephan Aßmus 	// remember the subpixel position
534128277c9SStephan Aßmus 	// so that MouseMoved() will work even before
535128277c9SStephan Aßmus 	// the integer position becomes different
536128277c9SStephan Aßmus 	fLastCanvasPos = where;
537f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&fLastCanvasPos);
538128277c9SStephan Aßmus 
539128277c9SStephan Aßmus 	// the reason to exclude the select mode
540128277c9SStephan Aßmus 	// is that the BView rect tracking does not
541128277c9SStephan Aßmus 	// scroll the rect starting point along with us
542128277c9SStephan Aßmus 	// (since we're doing no real scrolling)
543128277c9SStephan Aßmus //	if (fMode != SELECT_POINTS)
544128277c9SStephan Aßmus //		fCanvasView->SetAutoScrolling(true);
545128277c9SStephan Aßmus 
546128277c9SStephan Aßmus 	UpdateCursor();
547128277c9SStephan Aßmus 
548128277c9SStephan Aßmus 	return true;
549128277c9SStephan Aßmus }
550128277c9SStephan Aßmus 
551*0a562537SZardshard 
552128277c9SStephan Aßmus void
MouseMoved(BPoint where)553128277c9SStephan Aßmus PathManipulator::MouseMoved(BPoint where)
554128277c9SStephan Aßmus {
5554fac07a0SStephan Aßmus 	fCanvasView->FilterMouse(&where);
5564fac07a0SStephan Aßmus 		// NOTE: only filter mouse coords in mouse moved, no other
5574fac07a0SStephan Aßmus 		// mouse function
558128277c9SStephan Aßmus 	BPoint canvasWhere = where;
559f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
560128277c9SStephan Aßmus 
561128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
562128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
563128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
564128277c9SStephan Aßmus 	if (fLastCanvasPos == canvasWhere)
565128277c9SStephan Aßmus 		return;
566128277c9SStephan Aßmus 
567128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
568128277c9SStephan Aßmus 
5690e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
5700e1ba39fSStephan Aßmus 		if (fTransformBox) {
5710e1ba39fSStephan Aßmus 			fTransformBox->MouseMoved(where);
5720e1ba39fSStephan Aßmus 		}
5730e1ba39fSStephan Aßmus 		return;
5740e1ba39fSStephan Aßmus 	}
5750e1ba39fSStephan Aßmus 
576128277c9SStephan Aßmus 	if (fMode == CLOSE_PATH) {
577128277c9SStephan Aßmus 		// continue by moving the point
578128277c9SStephan Aßmus 		_SetMode(MOVE_POINT);
579128277c9SStephan Aßmus 		delete fChangePointCommand;
580128277c9SStephan Aßmus 		fChangePointCommand = new ChangePointCommand(fPath,
581128277c9SStephan Aßmus 													 fCurrentPathPoint,
582128277c9SStephan Aßmus 													 fSelection->Items(),
583128277c9SStephan Aßmus 													 fSelection->CountItems());
584128277c9SStephan Aßmus 	}
585128277c9SStephan Aßmus 
586128277c9SStephan Aßmus //	if (!fPrecise) {
587128277c9SStephan Aßmus //		float offset = fmod(fOutlineWidth, 2.0) / 2.0;
588128277c9SStephan Aßmus //		canvasWhere.point += BPoint(offset, offset);
589128277c9SStephan Aßmus //	}
590128277c9SStephan Aßmus 
591128277c9SStephan Aßmus 	switch (fMode) {
592128277c9SStephan Aßmus 		case ADD_POINT:
593128277c9SStephan Aßmus 		case INSERT_POINT:
594128277c9SStephan Aßmus 		case TOGGLE_SHARP:
595128277c9SStephan Aßmus 			// drag the "out" control point, mirror the "in" control point
596128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere, true);
597128277c9SStephan Aßmus 			break;
598128277c9SStephan Aßmus 		case MOVE_POINT:
599128277c9SStephan Aßmus 			// drag all three control points at once
600128277c9SStephan Aßmus 			fPath->SetPoint(fCurrentPathPoint, canvasWhere);
601128277c9SStephan Aßmus 			break;
602128277c9SStephan Aßmus 		case MOVE_POINT_IN:
603128277c9SStephan Aßmus 			// drag in control point
604128277c9SStephan Aßmus 			fPath->SetPointIn(fCurrentPathPoint, canvasWhere);
605128277c9SStephan Aßmus 			break;
606128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
607128277c9SStephan Aßmus 			// drag out control point
608128277c9SStephan Aßmus 			fPath->SetPointOut(fCurrentPathPoint, canvasWhere);
609128277c9SStephan Aßmus 			break;
610128277c9SStephan Aßmus 
611128277c9SStephan Aßmus 		case SELECT_POINTS: {
612128277c9SStephan Aßmus 			// change the selection
613128277c9SStephan Aßmus 			BRect r;
614128277c9SStephan Aßmus 			r.left = min_c(fTrackingStart.x, canvasWhere.x);
615128277c9SStephan Aßmus 			r.top = min_c(fTrackingStart.y, canvasWhere.y);
616128277c9SStephan Aßmus 			r.right = max_c(fTrackingStart.x, canvasWhere.x);
617128277c9SStephan Aßmus 			r.bottom = max_c(fTrackingStart.y, canvasWhere.y);
618128277c9SStephan Aßmus 			_Select(r);
619128277c9SStephan Aßmus 			break;
620128277c9SStephan Aßmus 		}
621128277c9SStephan Aßmus 
622128277c9SStephan Aßmus 		case TRANSLATE_POINTS: {
623128277c9SStephan Aßmus 			BPoint offset = canvasWhere - fTrackingStart;
624128277c9SStephan Aßmus 			_Nudge(offset);
625128277c9SStephan Aßmus 			fTrackingStart = canvasWhere;
626128277c9SStephan Aßmus 			break;
627128277c9SStephan Aßmus 		}
628128277c9SStephan Aßmus 	}
629128277c9SStephan Aßmus }
630128277c9SStephan Aßmus 
631*0a562537SZardshard 
632128277c9SStephan Aßmus Command*
MouseUp()633128277c9SStephan Aßmus PathManipulator::MouseUp()
634128277c9SStephan Aßmus {
635128277c9SStephan Aßmus 	// prevent carrying out actions more than once by only
636128277c9SStephan Aßmus 	// doing it if "fMouseDown" is true at the point of
637128277c9SStephan Aßmus 	// entering this function
638128277c9SStephan Aßmus 	if (!fMouseDown)
639128277c9SStephan Aßmus 		return NULL;
640128277c9SStephan Aßmus 	fMouseDown = false;
641128277c9SStephan Aßmus 
6420e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
6430e1ba39fSStephan Aßmus 		if (fTransformBox) {
6440e1ba39fSStephan Aßmus 			return fTransformBox->MouseUp();
6450e1ba39fSStephan Aßmus 		}
6460e1ba39fSStephan Aßmus 		return NULL;
6470e1ba39fSStephan Aßmus 	}
6480e1ba39fSStephan Aßmus 
649128277c9SStephan Aßmus 	Command* command = NULL;
650128277c9SStephan Aßmus 
651128277c9SStephan Aßmus 	switch (fMode) {
652128277c9SStephan Aßmus 
653128277c9SStephan Aßmus 		case ADD_POINT:
654128277c9SStephan Aßmus 			command = fAddPointCommand;
655128277c9SStephan Aßmus 			fAddPointCommand = NULL;
656128277c9SStephan Aßmus 			_SetMode(MOVE_POINT_OUT);
657128277c9SStephan Aßmus 			break;
658128277c9SStephan Aßmus 
659128277c9SStephan Aßmus 		case INSERT_POINT:
660128277c9SStephan Aßmus 			command = fInsertPointCommand;
661128277c9SStephan Aßmus 			fInsertPointCommand = NULL;
662128277c9SStephan Aßmus 			break;
663128277c9SStephan Aßmus 
664128277c9SStephan Aßmus 		case SELECT_POINTS:
665128277c9SStephan Aßmus 			if (*fSelection != *fOldSelection) {
666128277c9SStephan Aßmus //				command = new SelectPointsCommand(this, fPath,
667128277c9SStephan Aßmus //												  fOldSelection->Items(),
668128277c9SStephan Aßmus //												  fOldSelection->CountItems(),
669128277c9SStephan Aßmus //												  fSelection->Items(),
670128277c9SStephan Aßmus //												  fSelection->CountItems()));
671128277c9SStephan Aßmus 			}
672128277c9SStephan Aßmus 			fCanvasView->EndRectTracking();
673128277c9SStephan Aßmus 			break;
674128277c9SStephan Aßmus 
675128277c9SStephan Aßmus 		case TOGGLE_SHARP:
676128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
677128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
678128277c9SStephan Aßmus 		case MOVE_POINT:
679128277c9SStephan Aßmus 		case MOVE_POINT_IN:
680128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
681128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
682128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
683128277c9SStephan Aßmus 			command = fChangePointCommand;
684128277c9SStephan Aßmus 			fChangePointCommand = NULL;
685128277c9SStephan Aßmus 			break;
686128277c9SStephan Aßmus 
687128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
6880e1ba39fSStephan Aßmus 			if (!fNudgeCommand) {
689128277c9SStephan Aßmus 				// select just the point that was clicked
690128277c9SStephan Aßmus 				*fOldSelection = *fSelection;
691128277c9SStephan Aßmus 				if (fCurrentPathPoint >= 0) {
692128277c9SStephan Aßmus 					_Select(fCurrentPathPoint, fShiftDown);
693128277c9SStephan Aßmus 				}
694128277c9SStephan Aßmus 				if (*fSelection != *fOldSelection) {
695128277c9SStephan Aßmus //					command = new SelectPointsCommand(this, fPath,
696128277c9SStephan Aßmus //													  fOldSelection->Items(),
697128277c9SStephan Aßmus //													  fOldSelection->CountItems(),
698128277c9SStephan Aßmus //													  fSelection->Items(),
699128277c9SStephan Aßmus //													  fSelection->CountItems()));
700128277c9SStephan Aßmus 				}
7010e1ba39fSStephan Aßmus 			} else {
7020e1ba39fSStephan Aßmus 				command = _FinishNudging();
7030e1ba39fSStephan Aßmus 			}
704128277c9SStephan Aßmus 			break;
705128277c9SStephan Aßmus 	}
706128277c9SStephan Aßmus 
707128277c9SStephan Aßmus 	return command;
708128277c9SStephan Aßmus }
709128277c9SStephan Aßmus 
710*0a562537SZardshard 
711128277c9SStephan Aßmus bool
MouseOver(BPoint where)712128277c9SStephan Aßmus PathManipulator::MouseOver(BPoint where)
713128277c9SStephan Aßmus {
7140e1ba39fSStephan Aßmus 	if (fMode == TRANSFORM_POINTS) {
7150e1ba39fSStephan Aßmus 		if (fTransformBox) {
7160e1ba39fSStephan Aßmus 			return fTransformBox->MouseOver(where);
7170e1ba39fSStephan Aßmus 		}
7180e1ba39fSStephan Aßmus 		return false;
7190e1ba39fSStephan Aßmus 	}
7200e1ba39fSStephan Aßmus 
721128277c9SStephan Aßmus 	BPoint canvasWhere = where;
722f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&canvasWhere);
723128277c9SStephan Aßmus 
724128277c9SStephan Aßmus 	// since the tablet is generating mouse moved messages
725128277c9SStephan Aßmus 	// even if only the pressure changes (and not the actual mouse position)
726128277c9SStephan Aßmus 	// we insert this additional check to prevent too much calculation
727128277c9SStephan Aßmus 	if (fMouseDown && fLastCanvasPos == canvasWhere)
728128277c9SStephan Aßmus 		return false;
729128277c9SStephan Aßmus 
730128277c9SStephan Aßmus 	fLastCanvasPos = canvasWhere;
731128277c9SStephan Aßmus 
732128277c9SStephan Aßmus 	// hit testing
733128277c9SStephan Aßmus 	// (use a subpixel mouse pos)
734f67876a0SStephan Aßmus 	fCanvasView->ConvertToCanvas(&where);
735128277c9SStephan Aßmus 	_SetModeForMousePos(where);
736128277c9SStephan Aßmus 
737128277c9SStephan Aßmus 	// TODO: always true?
738128277c9SStephan Aßmus 	return true;
739128277c9SStephan Aßmus }
740128277c9SStephan Aßmus 
741*0a562537SZardshard 
742128277c9SStephan Aßmus bool
DoubleClicked(BPoint where)743128277c9SStephan Aßmus PathManipulator::DoubleClicked(BPoint where)
744128277c9SStephan Aßmus {
745128277c9SStephan Aßmus 	return false;
746128277c9SStephan Aßmus }
747128277c9SStephan Aßmus 
748*0a562537SZardshard 
749f4bd80a2SStephan Aßmus bool
ShowContextMenu(BPoint where)750f4bd80a2SStephan Aßmus PathManipulator::ShowContextMenu(BPoint where)
751f4bd80a2SStephan Aßmus {
752a9cee0f1SStephan Aßmus 	// Change the selection to the current point if it isn't currently
753a9cee0f1SStephan Aßmus 	// selected. This could will only be chosen if the user right-clicked
754a9cee0f1SStephan Aßmus 	// a path point directly.
755a9cee0f1SStephan Aßmus 	if (fCurrentPathPoint >= 0 && !fSelection->Contains(fCurrentPathPoint)) {
756a9cee0f1SStephan Aßmus 		fSelection->MakeEmpty();
757a9cee0f1SStephan Aßmus 		_UpdateSelection();
758a9cee0f1SStephan Aßmus 		*fOldSelection = *fSelection;
759a9cee0f1SStephan Aßmus 		_Select(fCurrentPathPoint, false);
760a9cee0f1SStephan Aßmus 	}
761a9cee0f1SStephan Aßmus 
762f4bd80a2SStephan Aßmus 	BPopUpMenu* menu = new BPopUpMenu("context menu", false, false);
763f4bd80a2SStephan Aßmus 	BMessage* message;
764f4bd80a2SStephan Aßmus 	BMenuItem* item;
765f4bd80a2SStephan Aßmus 
766f4bd80a2SStephan Aßmus 	bool hasSelection = fSelection->CountItems() > 0;
767f4bd80a2SStephan Aßmus 
768a9cee0f1SStephan Aßmus 	if (fCurrentPathPoint < 0) {
769f4bd80a2SStephan Aßmus 		message = new BMessage(B_SELECT_ALL);
770a4e4beafSHumdinger 		item = new BMenuItem(B_TRANSLATE("Select all"), message, 'A');
771f4bd80a2SStephan Aßmus 		menu->AddItem(item);
772f4bd80a2SStephan Aßmus 
773f4bd80a2SStephan Aßmus 		menu->AddSeparatorItem();
774a9cee0f1SStephan Aßmus 	}
775f4bd80a2SStephan Aßmus 
776f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_TRANSFORM);
777518852fcSAdrien Destugues 	item = new BMenuItem(B_TRANSLATE("Transform"), message, 'T');
778f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
779f4bd80a2SStephan Aßmus 	menu->AddItem(item);
780f4bd80a2SStephan Aßmus 
781f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_SPLIT_POINTS);
782518852fcSAdrien Destugues 	item = new BMenuItem(B_TRANSLATE("Split"), message);
783f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
784f4bd80a2SStephan Aßmus 	menu->AddItem(item);
785f4bd80a2SStephan Aßmus 
7867c4b3726SStephan Aßmus 	message = new BMessage(MSG_FLIP_POINTS);
787518852fcSAdrien Destugues 	item = new BMenuItem(B_TRANSLATE("Flip"), message);
7887c4b3726SStephan Aßmus 	item->SetEnabled(hasSelection);
7897c4b3726SStephan Aßmus 	menu->AddItem(item);
7907c4b3726SStephan Aßmus 
791f4bd80a2SStephan Aßmus 	message = new BMessage(MSG_REMOVE_POINTS);
792518852fcSAdrien Destugues 	item = new BMenuItem(B_TRANSLATE("Remove"), message);
793f4bd80a2SStephan Aßmus 	item->SetEnabled(hasSelection);
794f4bd80a2SStephan Aßmus 	menu->AddItem(item);
795f4bd80a2SStephan Aßmus 
796f4bd80a2SStephan Aßmus 	// go
797f4bd80a2SStephan Aßmus 	menu->SetTargetForItems(fCanvasView);
798f4bd80a2SStephan Aßmus 	menu->SetAsyncAutoDestruct(true);
799f4bd80a2SStephan Aßmus 	menu->SetFont(be_plain_font);
800f4bd80a2SStephan Aßmus 	where = fCanvasView->ConvertToScreen(where);
801f4bd80a2SStephan Aßmus 	BRect mouseRect(where, where);
802f4bd80a2SStephan Aßmus 	mouseRect.InsetBy(-10.0, -10.0);
803f4bd80a2SStephan Aßmus 	where += BPoint(5.0, 5.0);
804f4bd80a2SStephan Aßmus 	menu->Go(where, true, false, mouseRect, true);
805f4bd80a2SStephan Aßmus 
806f4bd80a2SStephan Aßmus 	return true;
807f4bd80a2SStephan Aßmus }
808f4bd80a2SStephan Aßmus 
809*0a562537SZardshard 
810f4bd80a2SStephan Aßmus // #pragma mark -
811f4bd80a2SStephan Aßmus 
812*0a562537SZardshard 
813128277c9SStephan Aßmus BRect
Bounds()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 
821*0a562537SZardshard 
822128277c9SStephan Aßmus BRect
TrackingBounds(BView * withinView)823128277c9SStephan Aßmus PathManipulator::TrackingBounds(BView* withinView)
824128277c9SStephan Aßmus {
825128277c9SStephan Aßmus 	return withinView->Bounds();
826128277c9SStephan Aßmus }
827128277c9SStephan Aßmus 
828*0a562537SZardshard 
829128277c9SStephan Aßmus // #pragma mark -
830128277c9SStephan Aßmus 
831*0a562537SZardshard 
832128277c9SStephan Aßmus bool
MessageReceived(BMessage * message,Command ** _command)833128277c9SStephan Aßmus PathManipulator::MessageReceived(BMessage* message, Command** _command)
834128277c9SStephan Aßmus {
835128277c9SStephan Aßmus 	bool result = true;
836128277c9SStephan Aßmus 	switch (message->what) {
837f4bd80a2SStephan Aßmus 		case MSG_TRANSFORM:
838128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
839128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
840128277c9SStephan Aßmus 			break;
841f4bd80a2SStephan Aßmus 		case MSG_REMOVE_POINTS:
842128277c9SStephan Aßmus 			*_command = _Delete();
843128277c9SStephan Aßmus 			break;
844f4bd80a2SStephan Aßmus 		case MSG_SPLIT_POINTS:
845f4bd80a2SStephan Aßmus 			*_command = new SplitPointsCommand(fPath,
846f4bd80a2SStephan Aßmus 											   fSelection->Items(),
847f4bd80a2SStephan Aßmus 											   fSelection->CountItems());
848f4bd80a2SStephan Aßmus 			break;
8497c4b3726SStephan Aßmus 		case MSG_FLIP_POINTS:
8507c4b3726SStephan Aßmus 			*_command = new FlipPointsCommand(fPath,
8517c4b3726SStephan Aßmus 											  fSelection->Items(),
8527c4b3726SStephan Aßmus 											  fSelection->CountItems());
8537c4b3726SStephan Aßmus 			break;
854128277c9SStephan Aßmus 		case B_SELECT_ALL: {
855128277c9SStephan Aßmus 			int32 count = fPath->CountPoints();
856128277c9SStephan Aßmus 			int32 indices[count];
857*0a562537SZardshard 
858*0a562537SZardshard 			for (int32 i = 0; i < count; i++)
859*0a562537SZardshard 				indices[i] = i;
860*0a562537SZardshard 
861128277c9SStephan Aßmus 			_Select(indices, count);
862128277c9SStephan Aßmus 			break;
863128277c9SStephan Aßmus 		}
864128277c9SStephan Aßmus 		default:
865128277c9SStephan Aßmus 			result = false;
866128277c9SStephan Aßmus 			break;
867128277c9SStephan Aßmus 	}
868128277c9SStephan Aßmus 	return result;
869128277c9SStephan Aßmus }
870128277c9SStephan Aßmus 
871128277c9SStephan Aßmus 
872128277c9SStephan Aßmus void
ModifiersChanged(uint32 modifiers)873128277c9SStephan Aßmus PathManipulator::ModifiersChanged(uint32 modifiers)
874128277c9SStephan Aßmus {
875128277c9SStephan Aßmus 	fCommandDown = modifiers & B_COMMAND_KEY;
876128277c9SStephan Aßmus 	fOptionDown = modifiers & B_CONTROL_KEY;
877128277c9SStephan Aßmus 	fShiftDown = modifiers & B_SHIFT_KEY;
878128277c9SStephan Aßmus 	fAltDown = modifiers & B_OPTION_KEY;
879128277c9SStephan Aßmus 
8800e1ba39fSStephan Aßmus 	if (fTransformBox) {
8810e1ba39fSStephan Aßmus 		fTransformBox->ModifiersChanged(modifiers);
8820e1ba39fSStephan Aßmus 		return;
8830e1ba39fSStephan Aßmus 	}
884128277c9SStephan Aßmus 	// reevaluate mode
885128277c9SStephan Aßmus 	if (!fMouseDown)
886128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
887128277c9SStephan Aßmus }
888128277c9SStephan Aßmus 
889*0a562537SZardshard 
890128277c9SStephan Aßmus bool
HandleKeyDown(uint32 key,uint32 modifiers,Command ** _command)891128277c9SStephan Aßmus PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
892128277c9SStephan Aßmus {
893128277c9SStephan Aßmus 	bool result = true;
894128277c9SStephan Aßmus 
895128277c9SStephan Aßmus 	float nudgeDist = 1.0;
896f67876a0SStephan Aßmus 	if (modifiers & B_SHIFT_KEY)
897f67876a0SStephan Aßmus 		nudgeDist /= fCanvasView->ZoomLevel();
898128277c9SStephan Aßmus 
899128277c9SStephan Aßmus 	switch (key) {
900128277c9SStephan Aßmus 		// commit
901128277c9SStephan Aßmus 		case B_RETURN:
9020e1ba39fSStephan Aßmus 			if (fTransformBox) {
9034fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9044fac07a0SStephan Aßmus 			}// else
905128277c9SStephan Aßmus //				_Perform();
906128277c9SStephan Aßmus 			break;
907128277c9SStephan Aßmus 		// cancel
908128277c9SStephan Aßmus 		case B_ESCAPE:
9090e1ba39fSStephan Aßmus 			if (fTransformBox) {
9100e1ba39fSStephan Aßmus 				fTransformBox->Cancel();
9114fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9120e1ba39fSStephan Aßmus 			} else if (fFallBackMode == NEW_PATH) {
913128277c9SStephan Aßmus 				fFallBackMode = SELECT_POINTS;
9144fac07a0SStephan Aßmus 				_SetTransformBox(NULL);
9154fac07a0SStephan Aßmus 			}// else
916128277c9SStephan Aßmus //				_Cancel();
917128277c9SStephan Aßmus 			break;
918128277c9SStephan Aßmus 		case 't':
919128277c9SStephan Aßmus 		case 'T':
920128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
921128277c9SStephan Aßmus 				_SetMode(TRANSFORM_POINTS);
922128277c9SStephan Aßmus 			else
923128277c9SStephan Aßmus 				result = false;
924128277c9SStephan Aßmus 			break;
925128277c9SStephan Aßmus 		// nudging
926128277c9SStephan Aßmus 		case B_UP_ARROW:
927128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, -nudgeDist));
928128277c9SStephan Aßmus 			break;
929128277c9SStephan Aßmus 		case B_DOWN_ARROW:
930128277c9SStephan Aßmus 			_Nudge(BPoint(0.0, nudgeDist));
931128277c9SStephan Aßmus 			break;
932128277c9SStephan Aßmus 		case B_LEFT_ARROW:
933128277c9SStephan Aßmus 			_Nudge(BPoint(-nudgeDist, 0.0));
934128277c9SStephan Aßmus 			break;
935128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
936128277c9SStephan Aßmus 			_Nudge(BPoint(nudgeDist, 0.0));
937128277c9SStephan Aßmus 			break;
938128277c9SStephan Aßmus 
939128277c9SStephan Aßmus 		case B_DELETE:
940128277c9SStephan Aßmus 			if (!fSelection->IsEmpty())
941128277c9SStephan Aßmus 				*_command = _Delete();
942128277c9SStephan Aßmus 			else
943128277c9SStephan Aßmus 				result = false;
944128277c9SStephan Aßmus 			break;
945128277c9SStephan Aßmus 
946128277c9SStephan Aßmus 		default:
947128277c9SStephan Aßmus 			result = false;
948128277c9SStephan Aßmus 	}
949128277c9SStephan Aßmus 	return result;
950128277c9SStephan Aßmus }
951128277c9SStephan Aßmus 
952*0a562537SZardshard 
953128277c9SStephan Aßmus bool
HandleKeyUp(uint32 key,uint32 modifiers,Command ** _command)954128277c9SStephan Aßmus PathManipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
955128277c9SStephan Aßmus {
956128277c9SStephan Aßmus 	bool handled = true;
957128277c9SStephan Aßmus 	switch (key) {
958128277c9SStephan Aßmus 		// nudging
959128277c9SStephan Aßmus 		case B_UP_ARROW:
960128277c9SStephan Aßmus 		case B_DOWN_ARROW:
961128277c9SStephan Aßmus 		case B_LEFT_ARROW:
962128277c9SStephan Aßmus 		case B_RIGHT_ARROW:
9630e1ba39fSStephan Aßmus 			*_command = _FinishNudging();
964128277c9SStephan Aßmus 			break;
965128277c9SStephan Aßmus 		default:
966128277c9SStephan Aßmus 			handled = false;
967128277c9SStephan Aßmus 			break;
968128277c9SStephan Aßmus 	}
969128277c9SStephan Aßmus 	return handled;
970128277c9SStephan Aßmus }
971128277c9SStephan Aßmus 
972*0a562537SZardshard 
9737c4b3726SStephan Aßmus bool
UpdateCursor()974128277c9SStephan Aßmus PathManipulator::UpdateCursor()
975128277c9SStephan Aßmus {
9767c4b3726SStephan Aßmus 	if (fTransformBox)
9777c4b3726SStephan Aßmus 		return fTransformBox->UpdateCursor();
9787c4b3726SStephan Aßmus 
979128277c9SStephan Aßmus 	const uchar* cursorData;
980128277c9SStephan Aßmus 	switch (fMode) {
981128277c9SStephan Aßmus 		case ADD_POINT:
982128277c9SStephan Aßmus 			cursorData = kPathAddCursor;
983128277c9SStephan Aßmus 			break;
984128277c9SStephan Aßmus 		case INSERT_POINT:
985128277c9SStephan Aßmus 			cursorData = kPathInsertCursor;
986128277c9SStephan Aßmus 			break;
987128277c9SStephan Aßmus 		case MOVE_POINT:
988128277c9SStephan Aßmus 		case MOVE_POINT_IN:
989128277c9SStephan Aßmus 		case MOVE_POINT_OUT:
990128277c9SStephan Aßmus 		case TRANSLATE_POINTS:
991128277c9SStephan Aßmus 			cursorData = kPathMoveCursor;
992128277c9SStephan Aßmus 			break;
993128277c9SStephan Aßmus 		case CLOSE_PATH:
994128277c9SStephan Aßmus 			cursorData = kPathCloseCursor;
995128277c9SStephan Aßmus 			break;
996128277c9SStephan Aßmus 		case TOGGLE_SHARP:
997128277c9SStephan Aßmus 		case TOGGLE_SHARP_IN:
998128277c9SStephan Aßmus 		case TOGGLE_SHARP_OUT:
999128277c9SStephan Aßmus 			cursorData = kPathSharpCursor;
1000128277c9SStephan Aßmus 			break;
1001128277c9SStephan Aßmus 		case REMOVE_POINT:
1002128277c9SStephan Aßmus 		case REMOVE_POINT_IN:
1003128277c9SStephan Aßmus 		case REMOVE_POINT_OUT:
1004128277c9SStephan Aßmus 			cursorData = kPathRemoveCursor;
1005128277c9SStephan Aßmus 			break;
1006128277c9SStephan Aßmus 		case SELECT_POINTS:
1007128277c9SStephan Aßmus 			cursorData = kPathSelectCursor;
1008128277c9SStephan Aßmus 			break;
1009128277c9SStephan Aßmus 
1010128277c9SStephan Aßmus 		case SELECT_SUB_PATH:
1011128277c9SStephan Aßmus 			cursorData = B_HAND_CURSOR;
1012128277c9SStephan Aßmus 			break;
1013128277c9SStephan Aßmus 
1014128277c9SStephan Aßmus 		case UNDEFINED:
1015128277c9SStephan Aßmus 		default:
1016128277c9SStephan Aßmus 			cursorData = kStopCursor;
1017128277c9SStephan Aßmus 			break;
1018128277c9SStephan Aßmus 	}
1019128277c9SStephan Aßmus 	BCursor cursor(cursorData);
1020128277c9SStephan Aßmus 	fCanvasView->SetViewCursor(&cursor, true);
1021128277c9SStephan Aßmus 	fCanvasView->Sync();
10227c4b3726SStephan Aßmus 
10237c4b3726SStephan Aßmus 	return true;
1024128277c9SStephan Aßmus }
1025128277c9SStephan Aßmus 
1026*0a562537SZardshard 
1027128277c9SStephan Aßmus void
AttachedToView(BView * view)1028128277c9SStephan Aßmus PathManipulator::AttachedToView(BView* view)
1029128277c9SStephan Aßmus {
1030f67876a0SStephan Aßmus 	fCanvasView = dynamic_cast<CanvasView*>(view);
1031128277c9SStephan Aßmus }
1032128277c9SStephan Aßmus 
1033*0a562537SZardshard 
1034128277c9SStephan Aßmus void
DetachedFromView(BView * view)1035128277c9SStephan Aßmus PathManipulator::DetachedFromView(BView* view)
1036128277c9SStephan Aßmus {
1037128277c9SStephan Aßmus 	fCanvasView = NULL;
1038128277c9SStephan Aßmus }
1039128277c9SStephan Aßmus 
1040*0a562537SZardshard 
1041128277c9SStephan Aßmus // #pragma mark -
1042128277c9SStephan Aßmus 
1043*0a562537SZardshard 
1044128277c9SStephan Aßmus void
ObjectChanged(const Observable * object)1045128277c9SStephan Aßmus PathManipulator::ObjectChanged(const Observable* object)
1046128277c9SStephan Aßmus {
1047128277c9SStephan Aßmus 	// TODO: refine VectorPath listener interface and
1048128277c9SStephan Aßmus 	// implement more efficiently
1049128277c9SStephan Aßmus 	BRect currentBounds = _ControlPointRect();
1050128277c9SStephan Aßmus 	_InvalidateCanvas(currentBounds | fPreviousBounds);
1051128277c9SStephan Aßmus 	fPreviousBounds = currentBounds;
1052128277c9SStephan Aßmus 
1053128277c9SStephan Aßmus 	// reevaluate mode
10540e1ba39fSStephan Aßmus 	if (!fMouseDown && !fTransformBox)
1055128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1056128277c9SStephan Aßmus }
1057128277c9SStephan Aßmus 
1058*0a562537SZardshard 
1059128277c9SStephan Aßmus // #pragma mark -
1060128277c9SStephan Aßmus 
1061*0a562537SZardshard 
106205fd3818SStephan Aßmus void
PointAdded(int32 index)106305fd3818SStephan Aßmus PathManipulator::PointAdded(int32 index)
106405fd3818SStephan Aßmus {
106505fd3818SStephan Aßmus 	ObjectChanged(fPath);
106605fd3818SStephan Aßmus }
106705fd3818SStephan Aßmus 
1068*0a562537SZardshard 
106905fd3818SStephan Aßmus void
PointRemoved(int32 index)107005fd3818SStephan Aßmus PathManipulator::PointRemoved(int32 index)
107105fd3818SStephan Aßmus {
10720e1ba39fSStephan Aßmus 	fSelection->Remove(index);
107305fd3818SStephan Aßmus 	ObjectChanged(fPath);
107405fd3818SStephan Aßmus }
107505fd3818SStephan Aßmus 
1076*0a562537SZardshard 
107705fd3818SStephan Aßmus void
PointChanged(int32 index)107805fd3818SStephan Aßmus PathManipulator::PointChanged(int32 index)
107905fd3818SStephan Aßmus {
108005fd3818SStephan Aßmus 	ObjectChanged(fPath);
108105fd3818SStephan Aßmus }
108205fd3818SStephan Aßmus 
1083*0a562537SZardshard 
108405fd3818SStephan Aßmus void
PathChanged()108505fd3818SStephan Aßmus PathManipulator::PathChanged()
108605fd3818SStephan Aßmus {
108705fd3818SStephan Aßmus 	ObjectChanged(fPath);
108805fd3818SStephan Aßmus }
108905fd3818SStephan Aßmus 
1090*0a562537SZardshard 
109105fd3818SStephan Aßmus void
PathClosedChanged()109205fd3818SStephan Aßmus PathManipulator::PathClosedChanged()
109305fd3818SStephan Aßmus {
109405fd3818SStephan Aßmus 	ObjectChanged(fPath);
109505fd3818SStephan Aßmus }
109605fd3818SStephan Aßmus 
1097*0a562537SZardshard 
109805fd3818SStephan Aßmus void
PathReversed()109905fd3818SStephan Aßmus PathManipulator::PathReversed()
110005fd3818SStephan Aßmus {
11010e1ba39fSStephan Aßmus 	// reverse selection along with path
11020e1ba39fSStephan Aßmus 	int32 count = fSelection->CountItems();
11030e1ba39fSStephan Aßmus 	int32 pointCount = fPath->CountPoints();
11040e1ba39fSStephan Aßmus 	if (count > 0) {
11050e1ba39fSStephan Aßmus 		Selection temp;
11060e1ba39fSStephan Aßmus 		for (int32 i = 0; i < count; i++) {
11070e1ba39fSStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
11080e1ba39fSStephan Aßmus 		}
11090e1ba39fSStephan Aßmus 		*fSelection = temp;
11100e1ba39fSStephan Aßmus 	}
11110e1ba39fSStephan Aßmus 
111205fd3818SStephan Aßmus 	ObjectChanged(fPath);
111305fd3818SStephan Aßmus }
111405fd3818SStephan Aßmus 
1115*0a562537SZardshard 
111605fd3818SStephan Aßmus // #pragma mark -
111705fd3818SStephan Aßmus 
1118*0a562537SZardshard 
1119128277c9SStephan Aßmus uint32
ControlFlags() const1120128277c9SStephan Aßmus PathManipulator::ControlFlags() const
1121128277c9SStephan Aßmus {
1122128277c9SStephan Aßmus 	uint32 flags = 0;
1123128277c9SStephan Aßmus 
1124128277c9SStephan Aßmus //	flags |= SHAPE_UI_FLAGS_CAN_REVERSE_PATH;
1125128277c9SStephan Aßmus //
1126128277c9SStephan Aßmus //	if (!fSelection->IsEmpty())
1127128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_HAS_SELECTION;
1128128277c9SStephan Aßmus //	if (fPath->CountPoints() > 1)
1129128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_CAN_CLOSE_PATH;
1130128277c9SStephan Aßmus //	if (fPath->IsClosed())
1131128277c9SStephan Aßmus //		flags |= SHAPE_UI_FLAGS_PATH_IS_CLOSED;
11320e1ba39fSStephan Aßmus //	if (fTransformBox)
11330e1ba39fSStephan Aßmus //		flags |= SHAPE_UI_FLAGS_IS_TRANSFORMING;
1134128277c9SStephan Aßmus 
1135128277c9SStephan Aßmus 	return flags;
1136128277c9SStephan Aßmus }
1137128277c9SStephan Aßmus 
1138*0a562537SZardshard 
1139128277c9SStephan Aßmus void
ReversePath()1140128277c9SStephan Aßmus PathManipulator::ReversePath()
1141128277c9SStephan Aßmus {
1142128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1143128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1144128277c9SStephan Aßmus 	if (count > 0) {
1145128277c9SStephan Aßmus 		Selection temp;
1146128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1147128277c9SStephan Aßmus 			temp.Add((pointCount - 1) - fSelection->IndexAt(i));
1148128277c9SStephan Aßmus 		}
1149128277c9SStephan Aßmus 		*fSelection = temp;
1150128277c9SStephan Aßmus 	}
1151128277c9SStephan Aßmus 	fPath->Reverse();
1152128277c9SStephan Aßmus }
1153128277c9SStephan Aßmus 
1154*0a562537SZardshard 
1155128277c9SStephan Aßmus // #pragma mark -
1156128277c9SStephan Aßmus 
1157*0a562537SZardshard 
1158128277c9SStephan Aßmus void
_SetMode(uint32 mode)1159128277c9SStephan Aßmus PathManipulator::_SetMode(uint32 mode)
1160128277c9SStephan Aßmus {
1161128277c9SStephan Aßmus 	if (fMode != mode) {
1162128277c9SStephan Aßmus //printf("switching mode: %s -> %s\n", string_for_mode(fMode), string_for_mode(mode));
1163128277c9SStephan Aßmus 		fMode = mode;
1164128277c9SStephan Aßmus 
11650e1ba39fSStephan Aßmus 		if (fMode == TRANSFORM_POINTS) {
11660e1ba39fSStephan Aßmus 			_SetTransformBox(new TransformPointsBox(fCanvasView,
11670e1ba39fSStephan Aßmus 													this,
11680e1ba39fSStephan Aßmus 													fPath,
11690e1ba39fSStephan Aßmus 													fSelection->Items(),
11700e1ba39fSStephan Aßmus 													fSelection->CountItems()));
11710e1ba39fSStephan Aßmus //			fCanvasView->Perform(new EnterTransformPointsCommand(this,
11720e1ba39fSStephan Aßmus //														  fSelection->Items(),
11730e1ba39fSStephan Aßmus //														  fSelection->CountItems()));
11740e1ba39fSStephan Aßmus 		} else {
11750e1ba39fSStephan Aßmus 			if (fTransformBox)
11760e1ba39fSStephan Aßmus 				_SetTransformBox(NULL);
11770e1ba39fSStephan Aßmus 		}
11780e1ba39fSStephan Aßmus 
1179128277c9SStephan Aßmus 		if (BWindow* window = fCanvasView->Window()) {
1180128277c9SStephan Aßmus 			window->PostMessage(MSG_UPDATE_SHAPE_UI);
1181128277c9SStephan Aßmus 		}
1182128277c9SStephan Aßmus 		UpdateCursor();
1183128277c9SStephan Aßmus 	}
1184128277c9SStephan Aßmus }
1185128277c9SStephan Aßmus 
11860e1ba39fSStephan Aßmus 
11870e1ba39fSStephan Aßmus void
_SetTransformBox(TransformPointsBox * transformBox)11880e1ba39fSStephan Aßmus PathManipulator::_SetTransformBox(TransformPointsBox* transformBox)
11890e1ba39fSStephan Aßmus {
11900e1ba39fSStephan Aßmus 	if (fTransformBox == transformBox)
11910e1ba39fSStephan Aßmus 		return;
11920e1ba39fSStephan Aßmus 
11930e1ba39fSStephan Aßmus 	BRect dirty(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN);
11940e1ba39fSStephan Aßmus 	if (fTransformBox) {
11950e1ba39fSStephan Aßmus 		// get rid of transform box display
11960e1ba39fSStephan Aßmus 		dirty = fTransformBox->Bounds();
11970e1ba39fSStephan Aßmus 		delete fTransformBox;
11980e1ba39fSStephan Aßmus 	}
11990e1ba39fSStephan Aßmus 
12000e1ba39fSStephan Aßmus 	fTransformBox = transformBox;
12010e1ba39fSStephan Aßmus 
12024fac07a0SStephan Aßmus 	// TODO: this is weird, fMode should only be set in _SetMode, not
12034fac07a0SStephan Aßmus 	// here as well, also this method could be called this way
12044fac07a0SStephan Aßmus 	// _SetModeForMousePos -> _SetMode -> _SetTransformBox
12054fac07a0SStephan Aßmus 	// and then below it does _SetModeForMousePos again...
12060e1ba39fSStephan Aßmus 	if (fTransformBox) {
12070e1ba39fSStephan Aßmus 		fTransformBox->MouseMoved(fLastCanvasPos);
12080e1ba39fSStephan Aßmus 		if (fMode != TRANSFORM_POINTS) {
12090e1ba39fSStephan Aßmus 			fMode = TRANSFORM_POINTS;
12100e1ba39fSStephan Aßmus 		}
12110e1ba39fSStephan Aßmus 		dirty = dirty | fTransformBox->Bounds();
12120e1ba39fSStephan Aßmus 	} else {
12130e1ba39fSStephan Aßmus 		if (fMode == TRANSFORM_POINTS) {
12140e1ba39fSStephan Aßmus 			_SetModeForMousePos(fLastCanvasPos);
12150e1ba39fSStephan Aßmus 		}
12160e1ba39fSStephan Aßmus 	}
12170e1ba39fSStephan Aßmus 
12180e1ba39fSStephan Aßmus 	if (dirty.IsValid()) {
12190e1ba39fSStephan Aßmus 		dirty.InsetBy(-8, -8);
12200e1ba39fSStephan Aßmus 		fCanvasView->Invalidate(dirty);
12210e1ba39fSStephan Aßmus 	}
12220e1ba39fSStephan Aßmus }
12230e1ba39fSStephan Aßmus 
1224*0a562537SZardshard 
1225128277c9SStephan Aßmus void
_AddPoint(BPoint where)1226128277c9SStephan Aßmus PathManipulator::_AddPoint(BPoint where)
1227128277c9SStephan Aßmus {
1228128277c9SStephan Aßmus 	if (fPath->AddPoint(where)) {
1229128277c9SStephan Aßmus 		fCurrentPathPoint = fPath->CountPoints() - 1;
1230128277c9SStephan Aßmus 
1231128277c9SStephan Aßmus 		delete fAddPointCommand;
1232128277c9SStephan Aßmus 		fAddPointCommand = new AddPointCommand(fPath, fCurrentPathPoint,
1233128277c9SStephan Aßmus 											   fSelection->Items(),
1234128277c9SStephan Aßmus 											   fSelection->CountItems());
1235128277c9SStephan Aßmus 
1236128277c9SStephan Aßmus 		_Select(fCurrentPathPoint, fShiftDown);
1237128277c9SStephan Aßmus 	}
1238128277c9SStephan Aßmus }
1239128277c9SStephan Aßmus 
1240*0a562537SZardshard 
1241128277c9SStephan Aßmus BPoint
scale_point(BPoint a,BPoint b,float scale)1242128277c9SStephan Aßmus scale_point(BPoint a, BPoint b, float scale)
1243128277c9SStephan Aßmus {
1244128277c9SStephan Aßmus 	return BPoint(a.x + (b.x - a.x) * scale,
1245128277c9SStephan Aßmus 				  a.y + (b.y - a.y) * scale);
1246128277c9SStephan Aßmus }
1247128277c9SStephan Aßmus 
1248*0a562537SZardshard 
1249128277c9SStephan Aßmus void
_InsertPoint(BPoint where,int32 index)1250128277c9SStephan Aßmus PathManipulator::_InsertPoint(BPoint where, int32 index)
1251128277c9SStephan Aßmus {
1252128277c9SStephan Aßmus 	double scale;
1253128277c9SStephan Aßmus 
1254128277c9SStephan Aßmus 	BPoint point;
1255128277c9SStephan Aßmus 	BPoint pointIn;
1256128277c9SStephan Aßmus 	BPoint pointOut;
1257128277c9SStephan Aßmus 
1258128277c9SStephan Aßmus 	BPoint previous;
1259128277c9SStephan Aßmus 	BPoint previousOut;
1260128277c9SStephan Aßmus 	BPoint next;
1261128277c9SStephan Aßmus 	BPoint nextIn;
1262128277c9SStephan Aßmus 
1263128277c9SStephan Aßmus 	if (fPath->FindBezierScale(index - 1, where, &scale)
1264128277c9SStephan Aßmus 		&& scale >= 0.0 && scale <= 1.0
1265128277c9SStephan Aßmus 		&& fPath->GetPoint(index - 1, scale, point)) {
1266128277c9SStephan Aßmus 
1267128277c9SStephan Aßmus 		fPath->GetPointAt(index - 1, previous);
1268128277c9SStephan Aßmus 		fPath->GetPointOutAt(index - 1, previousOut);
1269128277c9SStephan Aßmus 		fPath->GetPointAt(index, next);
1270128277c9SStephan Aßmus 		fPath->GetPointInAt(index, nextIn);
1271128277c9SStephan Aßmus 
1272128277c9SStephan Aßmus 		where = scale_point(previousOut, nextIn, scale);
1273128277c9SStephan Aßmus 
1274128277c9SStephan Aßmus 		previousOut = scale_point(previous, previousOut, scale);
1275128277c9SStephan Aßmus 		nextIn = scale_point(next, nextIn, 1 - scale);
1276128277c9SStephan Aßmus 		pointIn = scale_point(previousOut, where, scale);
1277128277c9SStephan Aßmus 		pointOut = scale_point(nextIn, where, 1 - scale);
1278128277c9SStephan Aßmus 
1279128277c9SStephan Aßmus 		if (fPath->AddPoint(point, index)) {
1280128277c9SStephan Aßmus 
1281128277c9SStephan Aßmus 			fPath->SetPointIn(index, pointIn);
1282128277c9SStephan Aßmus 			fPath->SetPointOut(index, pointOut);
1283128277c9SStephan Aßmus 
1284128277c9SStephan Aßmus 			delete fInsertPointCommand;
1285128277c9SStephan Aßmus 			fInsertPointCommand = new InsertPointCommand(fPath, index,
1286128277c9SStephan Aßmus 														 fSelection->Items(),
1287128277c9SStephan Aßmus 														 fSelection->CountItems());
1288128277c9SStephan Aßmus 
1289128277c9SStephan Aßmus 			fPath->SetPointOut(index - 1, previousOut);
1290128277c9SStephan Aßmus 			fPath->SetPointIn(index + 1, nextIn);
1291128277c9SStephan Aßmus 
1292128277c9SStephan Aßmus 			fCurrentPathPoint = index;
1293128277c9SStephan Aßmus 			_ShiftSelection(fCurrentPathPoint, 1);
1294128277c9SStephan Aßmus 			_Select(fCurrentPathPoint, fShiftDown);
1295128277c9SStephan Aßmus 		}
1296128277c9SStephan Aßmus 	}
1297128277c9SStephan Aßmus }
1298128277c9SStephan Aßmus 
1299*0a562537SZardshard 
1300128277c9SStephan Aßmus void
_SetInOutConnected(int32 index,bool connected)1301128277c9SStephan Aßmus PathManipulator::_SetInOutConnected(int32 index, bool connected)
1302128277c9SStephan Aßmus {
1303128277c9SStephan Aßmus 	fPath->SetInOutConnected(index, connected);
1304128277c9SStephan Aßmus }
1305128277c9SStephan Aßmus 
1306*0a562537SZardshard 
1307128277c9SStephan Aßmus void
_SetSharp(int32 index)1308128277c9SStephan Aßmus PathManipulator::_SetSharp(int32 index)
1309128277c9SStephan Aßmus {
1310128277c9SStephan Aßmus 	BPoint p;
1311128277c9SStephan Aßmus 	fPath->GetPointAt(index, p);
1312128277c9SStephan Aßmus 	fPath->SetPoint(index, p, p, p, true);
1313128277c9SStephan Aßmus }
1314128277c9SStephan Aßmus 
1315*0a562537SZardshard 
1316128277c9SStephan Aßmus void
_RemoveSelection()1317128277c9SStephan Aßmus PathManipulator::_RemoveSelection()
1318128277c9SStephan Aßmus {
13190e1ba39fSStephan Aßmus 	// NOTE: copy selection since removing points will
13200e1ba39fSStephan Aßmus 	// trigger notifications, and that will influence the
13210e1ba39fSStephan Aßmus 	// selection
13220e1ba39fSStephan Aßmus 	Selection selection = *fSelection;
13230e1ba39fSStephan Aßmus 	int32 count = selection.CountItems();
1324128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
13250e1ba39fSStephan Aßmus 		if (!fPath->RemovePoint(selection.IndexAt(i) - i))
1326128277c9SStephan Aßmus 			break;
1327128277c9SStephan Aßmus 	}
1328128277c9SStephan Aßmus 
13290e1ba39fSStephan Aßmus 	fPath->SetClosed(fPath->IsClosed() && fPath->CountPoints() > 1);
1330128277c9SStephan Aßmus 
1331128277c9SStephan Aßmus 	fSelection->MakeEmpty();
1332128277c9SStephan Aßmus }
1333128277c9SStephan Aßmus 
1334128277c9SStephan Aßmus 
1335128277c9SStephan Aßmus void
_RemovePoint(int32 index)1336128277c9SStephan Aßmus PathManipulator::_RemovePoint(int32 index)
1337128277c9SStephan Aßmus {
1338128277c9SStephan Aßmus 	if (fPath->RemovePoint(index)) {
1339128277c9SStephan Aßmus 		_Deselect(index);
1340128277c9SStephan Aßmus 		_ShiftSelection(index + 1, -1);
1341128277c9SStephan Aßmus 	}
1342128277c9SStephan Aßmus }
1343128277c9SStephan Aßmus 
1344*0a562537SZardshard 
1345128277c9SStephan Aßmus void
_RemovePointIn(int32 index)1346128277c9SStephan Aßmus PathManipulator::_RemovePointIn(int32 index)
1347128277c9SStephan Aßmus {
1348128277c9SStephan Aßmus 	BPoint p;
1349128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1350128277c9SStephan Aßmus 		fPath->SetPointIn(index, p);
1351128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1352128277c9SStephan Aßmus 	}
1353128277c9SStephan Aßmus }
1354128277c9SStephan Aßmus 
1355*0a562537SZardshard 
1356128277c9SStephan Aßmus void
_RemovePointOut(int32 index)1357128277c9SStephan Aßmus PathManipulator::_RemovePointOut(int32 index)
1358128277c9SStephan Aßmus {
1359128277c9SStephan Aßmus 	BPoint p;
1360128277c9SStephan Aßmus 	if (fPath->GetPointAt(index, p)) {
1361128277c9SStephan Aßmus 		fPath->SetPointOut(index, p);
1362128277c9SStephan Aßmus 		fPath->SetInOutConnected(index, false);
1363128277c9SStephan Aßmus 	}
1364128277c9SStephan Aßmus }
1365128277c9SStephan Aßmus 
1366*0a562537SZardshard 
1367128277c9SStephan Aßmus Command*
_Delete()1368128277c9SStephan Aßmus PathManipulator::_Delete()
1369128277c9SStephan Aßmus {
1370128277c9SStephan Aßmus 	Command* command = NULL;
1371128277c9SStephan Aßmus 	if (!fMouseDown) {
13720e1ba39fSStephan Aßmus 		// make sure we apply an on-going transformation before we proceed
13730e1ba39fSStephan Aßmus 		if (fTransformBox) {
13740e1ba39fSStephan Aßmus 			_SetTransformBox(NULL);
13750e1ba39fSStephan Aßmus 		}
13760e1ba39fSStephan Aßmus 
1377128277c9SStephan Aßmus 		if (fSelection->CountItems() == fPath->CountPoints()) {
1378128277c9SStephan Aßmus //			command = new RemovePathCommand(fPath);
1379128277c9SStephan Aßmus 		} else {
1380128277c9SStephan Aßmus 			command = new RemovePointsCommand(fPath,
1381128277c9SStephan Aßmus 											  fSelection->Items(),
1382128277c9SStephan Aßmus 											  fSelection->CountItems());
1383128277c9SStephan Aßmus 			_RemoveSelection();
1384128277c9SStephan Aßmus 		}
1385128277c9SStephan Aßmus 
1386128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1387128277c9SStephan Aßmus 	}
1388128277c9SStephan Aßmus 
1389128277c9SStephan Aßmus 	return command;
1390128277c9SStephan Aßmus }
1391128277c9SStephan Aßmus 
1392*0a562537SZardshard 
1393128277c9SStephan Aßmus // #pragma mark -
1394128277c9SStephan Aßmus 
1395*0a562537SZardshard 
1396128277c9SStephan Aßmus void
_Select(BRect r)1397128277c9SStephan Aßmus PathManipulator::_Select(BRect r)
1398128277c9SStephan Aßmus {
1399128277c9SStephan Aßmus 	BPoint p;
1400f4bd80a2SStephan Aßmus 	BPoint pIn;
1401f4bd80a2SStephan Aßmus 	BPoint pOut;
1402128277c9SStephan Aßmus 	int32 count = fPath->CountPoints();
1403128277c9SStephan Aßmus 	Selection temp;
1404f4bd80a2SStephan Aßmus 	for (int32 i = 0; i < count && fPath->GetPointsAt(i, p, pIn, pOut); i++) {
1405f4bd80a2SStephan Aßmus 		if (r.Contains(p) || r.Contains(pIn) || r.Contains(pOut)) {
1406128277c9SStephan Aßmus 			temp.Add(i);
1407128277c9SStephan Aßmus 		}
1408128277c9SStephan Aßmus 	}
1409128277c9SStephan Aßmus 	// merge old and new selection
1410128277c9SStephan Aßmus 	count = fOldSelection->CountItems();
1411128277c9SStephan Aßmus 	for (int32 i = 0; i < count; i++) {
1412128277c9SStephan Aßmus 		int32 index = fOldSelection->IndexAt(i);
1413128277c9SStephan Aßmus 		if (temp.Contains(index))
1414128277c9SStephan Aßmus 			temp.Remove(index);
1415128277c9SStephan Aßmus 		else
1416128277c9SStephan Aßmus 			temp.Add(index);
1417128277c9SStephan Aßmus 	}
1418128277c9SStephan Aßmus 	if (temp != *fSelection) {
1419128277c9SStephan Aßmus 		*fSelection = temp;
1420128277c9SStephan Aßmus 		_UpdateSelection();
1421128277c9SStephan Aßmus 	}
1422128277c9SStephan Aßmus }
1423128277c9SStephan Aßmus 
1424*0a562537SZardshard 
1425128277c9SStephan Aßmus void
_Select(int32 index,bool extend)1426128277c9SStephan Aßmus PathManipulator::_Select(int32 index, bool extend)
1427128277c9SStephan Aßmus {
1428128277c9SStephan Aßmus 	if (!extend)
1429128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1430128277c9SStephan Aßmus 	if (fSelection->Contains(index))
1431128277c9SStephan Aßmus 		fSelection->Remove(index);
1432128277c9SStephan Aßmus 	else
1433128277c9SStephan Aßmus 		fSelection->Add(index);
1434128277c9SStephan Aßmus 	// TODO: this can lead to unnecessary invalidation (maybe need to investigate)
1435128277c9SStephan Aßmus 	_UpdateSelection();
1436128277c9SStephan Aßmus }
1437128277c9SStephan Aßmus 
1438*0a562537SZardshard 
1439128277c9SStephan Aßmus void
_Select(const int32 * indices,int32 count,bool extend)1440128277c9SStephan Aßmus PathManipulator::_Select(const int32* indices, int32 count, bool extend)
1441128277c9SStephan Aßmus {
1442128277c9SStephan Aßmus 	if (extend) {
1443128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1444128277c9SStephan Aßmus 			if (!fSelection->Contains(indices[i]))
1445128277c9SStephan Aßmus 				fSelection->Add(indices[i]);
1446128277c9SStephan Aßmus 		}
1447128277c9SStephan Aßmus 	} else {
1448128277c9SStephan Aßmus 		fSelection->MakeEmpty();
1449128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1450128277c9SStephan Aßmus 			fSelection->Add(indices[i]);
1451128277c9SStephan Aßmus 		}
1452128277c9SStephan Aßmus 	}
1453128277c9SStephan Aßmus 	_UpdateSelection();
1454128277c9SStephan Aßmus }
1455128277c9SStephan Aßmus 
1456*0a562537SZardshard 
1457128277c9SStephan Aßmus void
_Deselect(int32 index)1458128277c9SStephan Aßmus PathManipulator::_Deselect(int32 index)
1459128277c9SStephan Aßmus {
1460128277c9SStephan Aßmus 	if (fSelection->Contains(index)) {
1461128277c9SStephan Aßmus 		fSelection->Remove(index);
1462128277c9SStephan Aßmus 		_UpdateSelection();
1463128277c9SStephan Aßmus 	}
1464128277c9SStephan Aßmus }
1465128277c9SStephan Aßmus 
1466*0a562537SZardshard 
1467128277c9SStephan Aßmus void
_ShiftSelection(int32 startIndex,int32 direction)1468128277c9SStephan Aßmus PathManipulator::_ShiftSelection(int32 startIndex, int32 direction)
1469128277c9SStephan Aßmus {
1470128277c9SStephan Aßmus 	int32 count = fSelection->CountItems();
1471128277c9SStephan Aßmus 	if (count > 0) {
1472128277c9SStephan Aßmus 		for (int32 i = 0; i < count; i++) {
1473*0a562537SZardshard 			int32 index = fSelection->IndexAt(i);
1474*0a562537SZardshard 			if (index >= startIndex) {
1475*0a562537SZardshard 				fSelection->Remove(index);
1476*0a562537SZardshard 				fSelection->Add(index + direction);
1477128277c9SStephan Aßmus 			}
1478128277c9SStephan Aßmus 		}
1479128277c9SStephan Aßmus 	}
1480128277c9SStephan Aßmus 	_UpdateSelection();
1481128277c9SStephan Aßmus }
1482128277c9SStephan Aßmus 
1483*0a562537SZardshard 
1484128277c9SStephan Aßmus bool
_IsSelected(int32 index) const1485128277c9SStephan Aßmus PathManipulator::_IsSelected(int32 index) const
1486128277c9SStephan Aßmus {
1487128277c9SStephan Aßmus 	return fSelection->Contains(index);
1488128277c9SStephan Aßmus }
1489128277c9SStephan Aßmus 
1490*0a562537SZardshard 
1491128277c9SStephan Aßmus // #pragma mark -
1492128277c9SStephan Aßmus 
1493*0a562537SZardshard 
1494128277c9SStephan Aßmus void
_InvalidateCanvas(BRect rect) const1495128277c9SStephan Aßmus PathManipulator::_InvalidateCanvas(BRect rect) const
1496128277c9SStephan Aßmus {
1497f67876a0SStephan Aßmus 	// convert from canvas to view space
1498f67876a0SStephan Aßmus 	fCanvasView->ConvertFromCanvas(&rect);
1499128277c9SStephan Aßmus 	fCanvasView->Invalidate(rect);
1500128277c9SStephan Aßmus }
1501128277c9SStephan Aßmus 
1502*0a562537SZardshard 
1503128277c9SStephan Aßmus void
_InvalidateHighlightPoints(int32 newIndex,uint32 newMode)1504128277c9SStephan Aßmus PathManipulator::_InvalidateHighlightPoints(int32 newIndex, uint32 newMode)
1505128277c9SStephan Aßmus {
1506128277c9SStephan Aßmus 	BRect oldRect = _ControlPointRect(fCurrentPathPoint, fMode);
1507128277c9SStephan Aßmus 	BRect newRect = _ControlPointRect(newIndex, newMode);
1508128277c9SStephan Aßmus 	if (oldRect.IsValid())
1509128277c9SStephan Aßmus 		_InvalidateCanvas(oldRect);
1510128277c9SStephan Aßmus 	if (newRect.IsValid())
1511128277c9SStephan Aßmus 		_InvalidateCanvas(newRect);
1512128277c9SStephan Aßmus }
1513128277c9SStephan Aßmus 
1514*0a562537SZardshard 
1515128277c9SStephan Aßmus void
_UpdateSelection() const1516128277c9SStephan Aßmus PathManipulator::_UpdateSelection() const
1517128277c9SStephan Aßmus {
1518128277c9SStephan Aßmus 	_InvalidateCanvas(_ControlPointRect());
1519128277c9SStephan Aßmus 	if (BWindow* window = fCanvasView->Window()) {
1520128277c9SStephan Aßmus 		window->PostMessage(MSG_UPDATE_SHAPE_UI);
1521128277c9SStephan Aßmus 	}
1522128277c9SStephan Aßmus }
1523128277c9SStephan Aßmus 
1524*0a562537SZardshard 
1525128277c9SStephan Aßmus BRect
_ControlPointRect() const1526128277c9SStephan Aßmus PathManipulator::_ControlPointRect() const
1527128277c9SStephan Aßmus {
1528128277c9SStephan Aßmus 	BRect r = fPath->ControlPointBounds();
1529128277c9SStephan Aßmus 	r.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1530128277c9SStephan Aßmus 	return r;
1531128277c9SStephan Aßmus }
1532128277c9SStephan Aßmus 
1533*0a562537SZardshard 
1534128277c9SStephan Aßmus BRect
_ControlPointRect(int32 index,uint32 mode) const1535128277c9SStephan Aßmus PathManipulator::_ControlPointRect(int32 index, uint32 mode) const
1536128277c9SStephan Aßmus {
1537128277c9SStephan Aßmus 	BRect rect(0.0, 0.0, -1.0, -1.0);
1538128277c9SStephan Aßmus 	if (index >= 0) {
1539128277c9SStephan Aßmus 		BPoint p, pIn, pOut;
1540128277c9SStephan Aßmus 		fPath->GetPointsAt(index, p, pIn, pOut);
1541128277c9SStephan Aßmus 		switch (mode) {
1542128277c9SStephan Aßmus 			case MOVE_POINT:
1543128277c9SStephan Aßmus 			case TOGGLE_SHARP:
1544128277c9SStephan Aßmus 			case REMOVE_POINT:
1545128277c9SStephan Aßmus 			case CLOSE_PATH:
1546128277c9SStephan Aßmus 				rect.Set(p.x, p.y, p.x, p.y);
1547128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1548128277c9SStephan Aßmus 				break;
1549128277c9SStephan Aßmus 			case MOVE_POINT_IN:
1550128277c9SStephan Aßmus 			case TOGGLE_SHARP_IN:
1551128277c9SStephan Aßmus 			case REMOVE_POINT_IN:
1552128277c9SStephan Aßmus 				rect.Set(pIn.x, pIn.y, pIn.x, pIn.y);
1553128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1554128277c9SStephan Aßmus 				break;
1555128277c9SStephan Aßmus 			case MOVE_POINT_OUT:
1556128277c9SStephan Aßmus 			case TOGGLE_SHARP_OUT:
1557128277c9SStephan Aßmus 			case REMOVE_POINT_OUT:
1558128277c9SStephan Aßmus 				rect.Set(pOut.x, pOut.y, pOut.x, pOut.y);
1559128277c9SStephan Aßmus 				rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND);
1560128277c9SStephan Aßmus 				break;
1561128277c9SStephan Aßmus 			case SELECT_POINTS:
1562128277c9SStephan Aßmus 				rect.Set(min4(p.x, pIn.x, pOut.x, pOut.x),
1563128277c9SStephan Aßmus 						 min4(p.y, pIn.y, pOut.y, pOut.y),
1564128277c9SStephan Aßmus 						 max4(p.x, pIn.x, pOut.x, pOut.x),
1565128277c9SStephan Aßmus 						 max4(p.y, pIn.y, pOut.y, pOut.y));
1566128277c9SStephan Aßmus 				rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND);
1567128277c9SStephan Aßmus 				break;
1568128277c9SStephan Aßmus 		}
1569128277c9SStephan Aßmus 	}
1570128277c9SStephan Aßmus 	return rect;
1571128277c9SStephan Aßmus }
1572128277c9SStephan Aßmus 
1573*0a562537SZardshard 
1574128277c9SStephan Aßmus // #pragma mark -
1575128277c9SStephan Aßmus 
1576*0a562537SZardshard 
1577128277c9SStephan Aßmus void
_SetModeForMousePos(BPoint where)1578128277c9SStephan Aßmus PathManipulator::_SetModeForMousePos(BPoint where)
1579128277c9SStephan Aßmus {
1580128277c9SStephan Aßmus 	uint32 mode = UNDEFINED;
1581128277c9SStephan Aßmus 	int32 index = -1;
1582128277c9SStephan Aßmus 
1583f67876a0SStephan Aßmus 	float zoomLevel = fCanvasView->ZoomLevel();
1584128277c9SStephan Aßmus 
1585128277c9SStephan Aßmus 	// see if we're close enough at a control point
1586128277c9SStephan Aßmus 	BPoint point;
1587128277c9SStephan Aßmus 	BPoint pointIn;
1588128277c9SStephan Aßmus 	BPoint pointOut;
1589128277c9SStephan Aßmus 	for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut)
1590128277c9SStephan Aßmus 					  && mode == UNDEFINED; i++) {
1591128277c9SStephan Aßmus 
1592128277c9SStephan Aßmus 		float distM = point_point_distance(point, where) * zoomLevel;
1593128277c9SStephan Aßmus 		float distIn = point_point_distance(pointIn, where) * zoomLevel;
1594128277c9SStephan Aßmus 		float distOut = point_point_distance(pointOut, where) * zoomLevel;
1595128277c9SStephan Aßmus 
1596128277c9SStephan Aßmus 		if (distM < MOVE_THRESHOLD) {
1597128277c9SStephan Aßmus 			if (i == 0 && fClickToClose
1598128277c9SStephan Aßmus 				&& !fPath->IsClosed() && fPath->CountPoints() > 1) {
1599128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1600128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : CLOSE_PATH);
1601128277c9SStephan Aßmus 				index = i;
1602128277c9SStephan Aßmus 			} else {
1603128277c9SStephan Aßmus 				mode = fCommandDown ? TOGGLE_SHARP :
1604128277c9SStephan Aßmus 							(fOptionDown ? REMOVE_POINT : MOVE_POINT);
1605128277c9SStephan Aßmus 				index = i;
1606128277c9SStephan Aßmus 			}
1607128277c9SStephan Aßmus 		}
16080e1ba39fSStephan Aßmus 		if (distM - distIn > 0.00001
16090e1ba39fSStephan Aßmus 			&& distIn < MOVE_THRESHOLD) {
1610128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_IN :
1611128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_IN : MOVE_POINT_IN);
1612128277c9SStephan Aßmus 			index = i;
1613128277c9SStephan Aßmus 		}
16140e1ba39fSStephan Aßmus 		if (distIn - distOut > 0.00001
16150e1ba39fSStephan Aßmus 			&& distOut < distM && distOut < MOVE_THRESHOLD) {
1616128277c9SStephan Aßmus 			mode = fCommandDown ? TOGGLE_SHARP_OUT :
1617128277c9SStephan Aßmus 						(fOptionDown ? REMOVE_POINT_OUT : MOVE_POINT_OUT);
1618128277c9SStephan Aßmus 			index = i;
1619128277c9SStephan Aßmus 		}
1620128277c9SStephan Aßmus 	}
1621128277c9SStephan Aßmus 	// selection mode overrides any other mode,
1622128277c9SStephan Aßmus 	// but we need to check for it after we know
1623128277c9SStephan Aßmus 	// the index of the point under the mouse (code above)
1624128277c9SStephan Aßmus 	int32 pointCount = fPath->CountPoints();
1625128277c9SStephan Aßmus 	if (fShiftDown && pointCount > 0) {
1626128277c9SStephan Aßmus 		mode = SELECT_POINTS;
1627128277c9SStephan Aßmus 	}
1628128277c9SStephan Aßmus 
1629128277c9SStephan Aßmus 	// see if user wants to start new sub path
1630128277c9SStephan Aßmus 	if (fAltDown) {
1631128277c9SStephan Aßmus 		mode = NEW_PATH;
1632128277c9SStephan Aßmus 		index = -1;
1633128277c9SStephan Aßmus 	}
1634128277c9SStephan Aßmus 
1635128277c9SStephan Aßmus 	// see if we're close enough at a line
1636128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1637128277c9SStephan Aßmus 		float distance;
1638128277c9SStephan Aßmus 		if (fPath->GetDistance(where, &distance, &index)) {
1639128277c9SStephan Aßmus 			if (distance < (INSERT_DIST_THRESHOLD / zoomLevel)) {
1640128277c9SStephan Aßmus 				mode = INSERT_POINT;
1641128277c9SStephan Aßmus 			}
1642128277c9SStephan Aßmus 		} else {
1643128277c9SStephan Aßmus 			// restore index, since it was changed by call above
1644128277c9SStephan Aßmus 			index = fCurrentPathPoint;
1645128277c9SStephan Aßmus 		}
1646128277c9SStephan Aßmus 	}
1647128277c9SStephan Aßmus 
1648128277c9SStephan Aßmus 	// nope, still undefined mode, last fall back
1649128277c9SStephan Aßmus 	if (mode == UNDEFINED) {
1650128277c9SStephan Aßmus 		if (fFallBackMode == SELECT_POINTS) {
1651128277c9SStephan Aßmus 			if (fPath->IsClosed() && pointCount > 0) {
1652128277c9SStephan Aßmus 				mode = SELECT_POINTS;
1653128277c9SStephan Aßmus 				index = -1;
1654128277c9SStephan Aßmus 			} else {
1655128277c9SStephan Aßmus 				mode = ADD_POINT;
1656128277c9SStephan Aßmus 				index = pointCount - 1;
1657128277c9SStephan Aßmus 			}
1658128277c9SStephan Aßmus 		} else {
1659128277c9SStephan Aßmus 			// user had clicked "New Path" icon
1660128277c9SStephan Aßmus 			mode = fFallBackMode;
1661128277c9SStephan Aßmus 		}
1662128277c9SStephan Aßmus 	}
1663128277c9SStephan Aßmus 	// switch mode if necessary
1664128277c9SStephan Aßmus 	if (mode != fMode || index != fCurrentPathPoint) {
1665128277c9SStephan Aßmus 		// invalidate path display (to highlight the respective point)
1666128277c9SStephan Aßmus 		_InvalidateHighlightPoints(index, mode);
1667128277c9SStephan Aßmus 		_SetMode(mode);
1668128277c9SStephan Aßmus 		fCurrentPathPoint = index;
1669128277c9SStephan Aßmus 	}
1670128277c9SStephan Aßmus }
1671128277c9SStephan Aßmus 
1672*0a562537SZardshard 
1673128277c9SStephan Aßmus // #pragma mark -
1674128277c9SStephan Aßmus 
1675*0a562537SZardshard 
1676128277c9SStephan Aßmus void
_Nudge(BPoint direction)1677128277c9SStephan Aßmus PathManipulator::_Nudge(BPoint direction)
1678128277c9SStephan Aßmus {
1679128277c9SStephan Aßmus 	bigtime_t now = system_time();
1680128277c9SStephan Aßmus 	if (now - fLastNudgeTime > 500000) {
16810e1ba39fSStephan Aßmus 		fCanvasView->Perform(_FinishNudging());
1682128277c9SStephan Aßmus 	}
1683128277c9SStephan Aßmus 	fLastNudgeTime = now;
1684128277c9SStephan Aßmus 	fNudgeOffset += direction;
1685128277c9SStephan Aßmus 
16860e1ba39fSStephan Aßmus 	if (fTransformBox) {
16870e1ba39fSStephan Aßmus 		fTransformBox->NudgeBy(direction);
16880e1ba39fSStephan Aßmus 		return;
16890e1ba39fSStephan Aßmus 	}
16900e1ba39fSStephan Aßmus 
16910e1ba39fSStephan Aßmus 	if (!fNudgeCommand) {
16920e1ba39fSStephan Aßmus 
16930e1ba39fSStephan Aßmus 		bool fromSelection = !fSelection->IsEmpty();
16940e1ba39fSStephan Aßmus 
16950e1ba39fSStephan Aßmus 		int32 count = fromSelection ? fSelection->CountItems()
16960e1ba39fSStephan Aßmus 									: fPath->CountPoints();
16970e1ba39fSStephan Aßmus 		int32 indices[count];
1698f4c2f7ebSJonathan Schleifer 		BStackOrHeapArray<control_point, 64> points(count);
16990e1ba39fSStephan Aßmus 
17000e1ba39fSStephan Aßmus 		// init indices and points
17010e1ba39fSStephan Aßmus 		for (int32 i = 0; i < count; i++) {
17020e1ba39fSStephan Aßmus 			indices[i] = fromSelection ? fSelection->IndexAt(i) : i;
17030e1ba39fSStephan Aßmus 			fPath->GetPointsAt(indices[i],
17040e1ba39fSStephan Aßmus 							   points[i].point,
17050e1ba39fSStephan Aßmus 							   points[i].point_in,
17060e1ba39fSStephan Aßmus 							   points[i].point_out,
17070e1ba39fSStephan Aßmus 							   &points[i].connected);
17080e1ba39fSStephan Aßmus 		}
17090e1ba39fSStephan Aßmus 
17100e1ba39fSStephan Aßmus 		fNudgeCommand = new NudgePointsCommand(fPath, indices, points, count);
17110e1ba39fSStephan Aßmus 
17120e1ba39fSStephan Aßmus 		fNudgeCommand->SetNewTranslation(fNudgeOffset);
17130e1ba39fSStephan Aßmus 		fNudgeCommand->Redo();
17140e1ba39fSStephan Aßmus 
17150e1ba39fSStephan Aßmus 	} else {
17160e1ba39fSStephan Aßmus 		fNudgeCommand->SetNewTranslation(fNudgeOffset);
17170e1ba39fSStephan Aßmus 		fNudgeCommand->Redo();
17180e1ba39fSStephan Aßmus 	}
1719128277c9SStephan Aßmus 
1720128277c9SStephan Aßmus 	if (!fMouseDown)
1721128277c9SStephan Aßmus 		_SetModeForMousePos(fLastCanvasPos);
1722128277c9SStephan Aßmus }
1723128277c9SStephan Aßmus 
1724*0a562537SZardshard 
17250e1ba39fSStephan Aßmus Command*
_FinishNudging()1726128277c9SStephan Aßmus PathManipulator::_FinishNudging()
1727128277c9SStephan Aßmus {
1728128277c9SStephan Aßmus 	fNudgeOffset = BPoint(0.0, 0.0);
1729128277c9SStephan Aßmus 
17300e1ba39fSStephan Aßmus 	Command* command;
17310e1ba39fSStephan Aßmus 
17320e1ba39fSStephan Aßmus 	if (fTransformBox) {
17330e1ba39fSStephan Aßmus 		command = fTransformBox->FinishNudging();
17340e1ba39fSStephan Aßmus 	} else {
17350e1ba39fSStephan Aßmus 		command = fNudgeCommand;
17360e1ba39fSStephan Aßmus 		fNudgeCommand = NULL;
17370e1ba39fSStephan Aßmus 	}
17380e1ba39fSStephan Aßmus 
17390e1ba39fSStephan Aßmus 	return command;
1740128277c9SStephan Aßmus }
1741