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