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