1128277c9SStephan Aßmus /* 2128277c9SStephan Aßmus * Copyright 2006, Haiku. 3128277c9SStephan Aßmus * Distributed under the terms of the MIT License. 4128277c9SStephan Aßmus * 5128277c9SStephan Aßmus * Authors: 6128277c9SStephan Aßmus * Stephan Aßmus <superstippi@gmx.de> 7128277c9SStephan Aßmus */ 8128277c9SStephan Aßmus 9128277c9SStephan Aßmus #include "PathManipulator.h" 10128277c9SStephan Aßmus 11128277c9SStephan Aßmus #include <float.h> 12128277c9SStephan Aßmus #include <stdio.h> 13128277c9SStephan Aßmus 14128277c9SStephan Aßmus #include <Cursor.h> 15128277c9SStephan Aßmus #include <Message.h> 16128277c9SStephan Aßmus #include <Window.h> 17128277c9SStephan Aßmus 18128277c9SStephan Aßmus #include "cursors.h" 19128277c9SStephan Aßmus #include "support.h" 20128277c9SStephan Aßmus 21f67876a0SStephan Aßmus #include "CanvasView.h" 22128277c9SStephan Aßmus #include "VectorPath.h" 23128277c9SStephan Aßmus 24128277c9SStephan Aßmus #include "AddPointCommand.h" 25128277c9SStephan Aßmus #include "ChangePointCommand.h" 26128277c9SStephan Aßmus //#include "CloseCommand.h" 27128277c9SStephan Aßmus #include "InsertPointCommand.h" 28128277c9SStephan Aßmus //#include "NewPathCommand.h" 29128277c9SStephan Aßmus //#include "NudgePointsCommand.h" 30128277c9SStephan Aßmus //#include "RemovePathCommand.h" 31128277c9SStephan Aßmus #include "RemovePointsCommand.h" 32128277c9SStephan Aßmus //#include "ReversePathCommand.h" 33128277c9SStephan Aßmus //#include "SelectPathCommand.h" 34128277c9SStephan Aßmus //#include "SelectPointsCommand.h" 35128277c9SStephan Aßmus 36128277c9SStephan Aßmus #define POINT_EXTEND 3.0 37128277c9SStephan Aßmus #define CONTROL_POINT_EXTEND 2.0 38128277c9SStephan Aßmus #define INSERT_DIST_THRESHOLD 7.0 39128277c9SStephan Aßmus #define MOVE_THRESHOLD 9.0 40128277c9SStephan Aßmus 41128277c9SStephan Aßmus enum { 42128277c9SStephan Aßmus UNDEFINED, 43128277c9SStephan Aßmus 44128277c9SStephan Aßmus NEW_PATH, 45128277c9SStephan Aßmus 46128277c9SStephan Aßmus ADD_POINT, 47128277c9SStephan Aßmus INSERT_POINT, 48128277c9SStephan Aßmus MOVE_POINT, 49128277c9SStephan Aßmus MOVE_POINT_IN, 50128277c9SStephan Aßmus MOVE_POINT_OUT, 51128277c9SStephan Aßmus CLOSE_PATH, 52128277c9SStephan Aßmus 53128277c9SStephan Aßmus TOGGLE_SHARP, 54128277c9SStephan Aßmus TOGGLE_SHARP_IN, 55128277c9SStephan Aßmus TOGGLE_SHARP_OUT, 56128277c9SStephan Aßmus 57128277c9SStephan Aßmus REMOVE_POINT, 58128277c9SStephan Aßmus REMOVE_POINT_IN, 59128277c9SStephan Aßmus REMOVE_POINT_OUT, 60128277c9SStephan Aßmus 61128277c9SStephan Aßmus SELECT_POINTS, 62128277c9SStephan Aßmus TRANSFORM_POINTS, 63128277c9SStephan Aßmus TRANSLATE_POINTS, 64128277c9SStephan Aßmus 65128277c9SStephan Aßmus SELECT_SUB_PATH, 66128277c9SStephan Aßmus }; 67128277c9SStephan Aßmus 68128277c9SStephan Aßmus inline const char* 69128277c9SStephan Aßmus string_for_mode(uint32 mode) 70128277c9SStephan Aßmus { 71128277c9SStephan Aßmus switch (mode) { 72128277c9SStephan Aßmus case UNDEFINED: 73128277c9SStephan Aßmus return "UNDEFINED"; 74128277c9SStephan Aßmus case NEW_PATH: 75128277c9SStephan Aßmus return "NEW_PATH"; 76128277c9SStephan Aßmus case ADD_POINT: 77128277c9SStephan Aßmus return "ADD_POINT"; 78128277c9SStephan Aßmus case INSERT_POINT: 79128277c9SStephan Aßmus return "INSERT_POINT"; 80128277c9SStephan Aßmus case MOVE_POINT: 81128277c9SStephan Aßmus return "MOVE_POINT"; 82128277c9SStephan Aßmus case MOVE_POINT_IN: 83128277c9SStephan Aßmus return "MOVE_POINT_IN"; 84128277c9SStephan Aßmus case MOVE_POINT_OUT: 85128277c9SStephan Aßmus return "MOVE_POINT_OUT"; 86128277c9SStephan Aßmus case CLOSE_PATH: 87128277c9SStephan Aßmus return "CLOSE_PATH"; 88128277c9SStephan Aßmus case TOGGLE_SHARP: 89128277c9SStephan Aßmus return "TOGGLE_SHARP"; 90128277c9SStephan Aßmus case TOGGLE_SHARP_IN: 91128277c9SStephan Aßmus return "TOGGLE_SHARP_IN"; 92128277c9SStephan Aßmus case TOGGLE_SHARP_OUT: 93128277c9SStephan Aßmus return "TOGGLE_SHARP_OUT"; 94128277c9SStephan Aßmus case REMOVE_POINT: 95128277c9SStephan Aßmus return "REMOVE_POINT"; 96128277c9SStephan Aßmus case REMOVE_POINT_IN: 97128277c9SStephan Aßmus return "REMOVE_POINT_IN"; 98128277c9SStephan Aßmus case REMOVE_POINT_OUT: 99128277c9SStephan Aßmus return "REMOVE_POINT_OUT"; 100128277c9SStephan Aßmus case SELECT_POINTS: 101128277c9SStephan Aßmus return "SELECT_POINTS"; 102128277c9SStephan Aßmus case TRANSFORM_POINTS: 103128277c9SStephan Aßmus return "TRANSFORM_POINTS"; 104128277c9SStephan Aßmus case TRANSLATE_POINTS: 105128277c9SStephan Aßmus return "TRANSLATE_POINTS"; 106128277c9SStephan Aßmus case SELECT_SUB_PATH: 107128277c9SStephan Aßmus return "SELECT_SUB_PATH"; 108128277c9SStephan Aßmus } 109128277c9SStephan Aßmus return "<unknown mode>"; 110128277c9SStephan Aßmus } 111128277c9SStephan Aßmus 112*ce181bb0SStephan Aßmus class PathManipulator::Selection : protected BList 113128277c9SStephan Aßmus { 114128277c9SStephan Aßmus public: 115128277c9SStephan Aßmus inline Selection(int32 count = 20) 116128277c9SStephan Aßmus : BList(count) {} 117128277c9SStephan Aßmus inline ~Selection() {} 118128277c9SStephan Aßmus 119128277c9SStephan Aßmus inline void Add(int32 value) 120128277c9SStephan Aßmus { 121128277c9SStephan Aßmus if (value >= 0) { 122128277c9SStephan Aßmus // keep the list sorted 123128277c9SStephan Aßmus int32 count = CountItems(); 124128277c9SStephan Aßmus int32 index = 0; 125128277c9SStephan Aßmus for (; index < count; index++) { 126128277c9SStephan Aßmus if (IndexAt(index) > value) { 127128277c9SStephan Aßmus break; 128128277c9SStephan Aßmus } 129128277c9SStephan Aßmus } 130128277c9SStephan Aßmus BList::AddItem((void*)value, index); 131128277c9SStephan Aßmus } 132128277c9SStephan Aßmus } 133128277c9SStephan Aßmus 134128277c9SStephan Aßmus inline bool Remove(int32 value) 135128277c9SStephan Aßmus { return BList::RemoveItem((void*)value); } 136128277c9SStephan Aßmus 137128277c9SStephan Aßmus inline bool Contains(int32 value) const 138128277c9SStephan Aßmus { return BList::HasItem((void*)value); } 139128277c9SStephan Aßmus 140128277c9SStephan Aßmus inline bool IsEmpty() const 141128277c9SStephan Aßmus { return BList::IsEmpty(); } 142128277c9SStephan Aßmus 143128277c9SStephan Aßmus inline int32 IndexAt(int32 index) const 144128277c9SStephan Aßmus { return (int32)BList::ItemAt(index); } 145128277c9SStephan Aßmus 146128277c9SStephan Aßmus inline void MakeEmpty() 147128277c9SStephan Aßmus { BList::MakeEmpty(); } 148128277c9SStephan Aßmus 149128277c9SStephan Aßmus inline int32* Items() const 150128277c9SStephan Aßmus { return (int32*)BList::Items(); } 151128277c9SStephan Aßmus 152128277c9SStephan Aßmus inline const int32 CountItems() const 153128277c9SStephan Aßmus { return BList::CountItems(); } 154128277c9SStephan Aßmus 155128277c9SStephan Aßmus inline Selection& operator =(const Selection& other) 156128277c9SStephan Aßmus { 157128277c9SStephan Aßmus MakeEmpty(); 158128277c9SStephan Aßmus int32 count = other.CountItems(); 159128277c9SStephan Aßmus int32* items = other.Items(); 160128277c9SStephan Aßmus for (int32 i = 0; i < count; i++) { 161128277c9SStephan Aßmus Add(items[i]); 162128277c9SStephan Aßmus } 163128277c9SStephan Aßmus return *this; 164128277c9SStephan Aßmus } 165128277c9SStephan Aßmus 166128277c9SStephan Aßmus inline bool operator ==(const Selection& other) 167128277c9SStephan Aßmus { 168128277c9SStephan Aßmus if (other.CountItems() == CountItems()) { 169128277c9SStephan Aßmus int32* items = Items(); 170128277c9SStephan Aßmus int32* otherItems = other.Items(); 171128277c9SStephan Aßmus for (int32 i = 0; i < CountItems(); i++) { 172128277c9SStephan Aßmus if (items[i] != otherItems[i]) 173128277c9SStephan Aßmus return false; 174128277c9SStephan Aßmus items++; 175128277c9SStephan Aßmus otherItems++; 176128277c9SStephan Aßmus } 177128277c9SStephan Aßmus return true; 178128277c9SStephan Aßmus } else 179128277c9SStephan Aßmus return false; 180128277c9SStephan Aßmus } 181128277c9SStephan Aßmus 182128277c9SStephan Aßmus inline bool operator !=(const Selection& other) 183128277c9SStephan Aßmus { 184128277c9SStephan Aßmus return !(*this == other); 185128277c9SStephan Aßmus } 186128277c9SStephan Aßmus }; 187128277c9SStephan Aßmus 188128277c9SStephan Aßmus 189128277c9SStephan Aßmus // constructor 190128277c9SStephan Aßmus PathManipulator::PathManipulator(VectorPath* path) 191128277c9SStephan Aßmus : Manipulator(path), 192128277c9SStephan Aßmus fCanvasView(NULL), 193128277c9SStephan Aßmus 194128277c9SStephan Aßmus fCommandDown(false), 195128277c9SStephan Aßmus fOptionDown(false), 196128277c9SStephan Aßmus fShiftDown(false), 197128277c9SStephan Aßmus fAltDown(false), 198128277c9SStephan Aßmus 199128277c9SStephan Aßmus fClickToClose(false), 200128277c9SStephan Aßmus 201128277c9SStephan Aßmus fMode(NEW_PATH), 202128277c9SStephan Aßmus fFallBackMode(SELECT_POINTS), 203128277c9SStephan Aßmus 204128277c9SStephan Aßmus fMouseDown(false), 205128277c9SStephan Aßmus 206128277c9SStephan Aßmus fPath(path), 207128277c9SStephan Aßmus fCurrentPathPoint(-1), 208128277c9SStephan Aßmus 209128277c9SStephan Aßmus fChangePointCommand(NULL), 210128277c9SStephan Aßmus fInsertPointCommand(NULL), 211128277c9SStephan Aßmus fAddPointCommand(NULL), 212128277c9SStephan Aßmus 213128277c9SStephan Aßmus fSelection(new Selection()), 214128277c9SStephan Aßmus fOldSelection(new Selection()), 215128277c9SStephan Aßmus 216128277c9SStephan Aßmus fNudgeOffset(0.0, 0.0), 217128277c9SStephan Aßmus fLastNudgeTime(system_time())//, 218128277c9SStephan Aßmus // fNudgeCommand(NULL) 219128277c9SStephan Aßmus { 220128277c9SStephan Aßmus } 221128277c9SStephan Aßmus 222128277c9SStephan Aßmus // destructor 223128277c9SStephan Aßmus PathManipulator::~PathManipulator() 224128277c9SStephan Aßmus { 225128277c9SStephan Aßmus delete fChangePointCommand; 226128277c9SStephan Aßmus delete fInsertPointCommand; 227128277c9SStephan Aßmus delete fAddPointCommand; 228128277c9SStephan Aßmus 229128277c9SStephan Aßmus delete fSelection; 230128277c9SStephan Aßmus delete fOldSelection; 231128277c9SStephan Aßmus 232128277c9SStephan Aßmus // delete fNudgeCommand; 233128277c9SStephan Aßmus } 234128277c9SStephan Aßmus 235128277c9SStephan Aßmus 236128277c9SStephan Aßmus // #pragma mark - 237128277c9SStephan Aßmus 238128277c9SStephan Aßmus class StrokePathIterator : public VectorPath::Iterator { 239128277c9SStephan Aßmus public: 240f67876a0SStephan Aßmus StrokePathIterator(CanvasView* canvasView, 241f67876a0SStephan Aßmus BView* drawingView) 242f67876a0SStephan Aßmus : fCanvasView(canvasView), 243f67876a0SStephan Aßmus fDrawingView(drawingView) 244128277c9SStephan Aßmus { 245128277c9SStephan Aßmus fDrawingView->SetHighColor(0, 0, 0, 255); 246128277c9SStephan Aßmus fDrawingView->SetDrawingMode(B_OP_OVER); 247128277c9SStephan Aßmus } 248128277c9SStephan Aßmus virtual ~StrokePathIterator() 249128277c9SStephan Aßmus {} 250128277c9SStephan Aßmus 251128277c9SStephan Aßmus virtual void MoveTo(BPoint point) 252128277c9SStephan Aßmus { 253128277c9SStephan Aßmus fBlack = true; 254f67876a0SStephan Aßmus fSkip = false; 255128277c9SStephan Aßmus fDrawingView->SetHighColor(0, 0, 0, 255); 256128277c9SStephan Aßmus 257f67876a0SStephan Aßmus fCanvasView->ConvertFromCanvas(&point); 258128277c9SStephan Aßmus fDrawingView->MovePenTo(point); 259128277c9SStephan Aßmus } 260128277c9SStephan Aßmus virtual void LineTo(BPoint point) 261128277c9SStephan Aßmus { 262f67876a0SStephan Aßmus fCanvasView->ConvertFromCanvas(&point); 263f67876a0SStephan Aßmus if (!fSkip) { 264128277c9SStephan Aßmus if (fBlack) 265128277c9SStephan Aßmus fDrawingView->SetHighColor(255, 255, 255, 255); 266128277c9SStephan Aßmus else 267128277c9SStephan Aßmus fDrawingView->SetHighColor(0, 0, 0, 255); 268128277c9SStephan Aßmus fBlack = !fBlack; 269128277c9SStephan Aßmus 270128277c9SStephan Aßmus fDrawingView->StrokeLine(point); 271f67876a0SStephan Aßmus } else { 272f67876a0SStephan Aßmus fDrawingView->MovePenTo(point); 273f67876a0SStephan Aßmus } 274f67876a0SStephan Aßmus fSkip = !fSkip; 275128277c9SStephan Aßmus } 276128277c9SStephan Aßmus 277128277c9SStephan Aßmus private: 278f67876a0SStephan Aßmus CanvasView* fCanvasView; 279128277c9SStephan Aßmus BView* fDrawingView; 280128277c9SStephan Aßmus bool fBlack; 281f67876a0SStephan Aßmus bool fSkip; 282128277c9SStephan Aßmus }; 283128277c9SStephan Aßmus 284128277c9SStephan Aßmus // Draw 285128277c9SStephan Aßmus void 286128277c9SStephan Aßmus PathManipulator::Draw(BView* into, BRect updateRect) 287128277c9SStephan Aßmus { 288128277c9SStephan Aßmus // draw the Bezier curve, but only if editing 289128277c9SStephan Aßmus // if not "editing", the path is actually on top all other modifiers 290128277c9SStephan Aßmus // TODO: make this customizable in the GUI 291f67876a0SStephan Aßmus StrokePathIterator iterator(fCanvasView, into); 292f67876a0SStephan Aßmus fPath->Iterate(&iterator, fCanvasView->ZoomLevel()); 293128277c9SStephan Aßmus 294128277c9SStephan Aßmus into->SetLowColor(0, 0, 0, 255); 295128277c9SStephan Aßmus BPoint point; 296128277c9SStephan Aßmus BPoint pointIn; 297128277c9SStephan Aßmus BPoint pointOut; 298128277c9SStephan Aßmus rgb_color focusColor = (rgb_color){ 255, 0, 0, 255 }; 299128277c9SStephan Aßmus rgb_color highlightColor = (rgb_color){ 60, 60, 255, 255 }; 300128277c9SStephan Aßmus for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut); i++) { 301128277c9SStephan Aßmus bool highlight = fCurrentPathPoint == i; 302128277c9SStephan Aßmus bool selected = fSelection->Contains(i); 303128277c9SStephan Aßmus rgb_color normal = selected ? focusColor : (rgb_color){ 0, 0, 0, 255 }; 304128277c9SStephan Aßmus into->SetLowColor(normal); 305128277c9SStephan Aßmus into->SetHighColor(255, 255, 255, 255); 306128277c9SStephan Aßmus // convert to view coordinate space 307f67876a0SStephan Aßmus fCanvasView->ConvertFromCanvas(&point); 308f67876a0SStephan Aßmus fCanvasView->ConvertFromCanvas(&pointIn); 309f67876a0SStephan Aßmus fCanvasView->ConvertFromCanvas(&pointOut); 310128277c9SStephan Aßmus // connect the points belonging to one control point 311128277c9SStephan Aßmus into->SetDrawingMode(B_OP_INVERT); 312128277c9SStephan Aßmus into->StrokeLine(point, pointIn); 313128277c9SStephan Aßmus into->StrokeLine(point, pointOut); 314128277c9SStephan Aßmus // draw main control point 315128277c9SStephan Aßmus if (highlight && (fMode == MOVE_POINT || 316128277c9SStephan Aßmus fMode == TOGGLE_SHARP || 317128277c9SStephan Aßmus fMode == REMOVE_POINT || 318128277c9SStephan Aßmus fMode == SELECT_POINTS || 319128277c9SStephan Aßmus fMode == CLOSE_PATH)) { 320128277c9SStephan Aßmus 321128277c9SStephan Aßmus into->SetLowColor(highlightColor); 322128277c9SStephan Aßmus } 323128277c9SStephan Aßmus 324128277c9SStephan Aßmus into->SetDrawingMode(B_OP_COPY); 325128277c9SStephan Aßmus BRect r(point, point); 326128277c9SStephan Aßmus r.InsetBy(-POINT_EXTEND, -POINT_EXTEND); 327128277c9SStephan Aßmus into->StrokeRect(r, B_SOLID_LOW); 328128277c9SStephan Aßmus r.InsetBy(1.0, 1.0); 329128277c9SStephan Aßmus into->FillRect(r, B_SOLID_HIGH); 330128277c9SStephan Aßmus // draw in control point 331128277c9SStephan Aßmus if (highlight && (fMode == MOVE_POINT_IN || 332128277c9SStephan Aßmus fMode == TOGGLE_SHARP_IN || 333128277c9SStephan Aßmus fMode == REMOVE_POINT_IN || 334128277c9SStephan Aßmus fMode == SELECT_POINTS)) 335128277c9SStephan Aßmus into->SetLowColor(highlightColor); 336128277c9SStephan Aßmus else 337128277c9SStephan Aßmus into->SetLowColor(normal); 338128277c9SStephan Aßmus if (selected) { 339128277c9SStephan Aßmus into->SetHighColor(220, 220, 220, 255); 340128277c9SStephan Aßmus } else { 341128277c9SStephan Aßmus into->SetHighColor(170, 170, 170, 255); 342128277c9SStephan Aßmus } 343128277c9SStephan Aßmus if (pointIn != point) { 344128277c9SStephan Aßmus r.Set(pointIn.x - CONTROL_POINT_EXTEND, pointIn.y - CONTROL_POINT_EXTEND, 345128277c9SStephan Aßmus pointIn.x + CONTROL_POINT_EXTEND, pointIn.y + CONTROL_POINT_EXTEND); 346128277c9SStephan Aßmus into->StrokeRect(r, B_SOLID_LOW); 347128277c9SStephan Aßmus r.InsetBy(1.0, 1.0); 348128277c9SStephan Aßmus into->FillRect(r, B_SOLID_HIGH); 349128277c9SStephan Aßmus } 350128277c9SStephan Aßmus // draw out control point 351128277c9SStephan Aßmus if (highlight && (fMode == MOVE_POINT_OUT || 352128277c9SStephan Aßmus fMode == TOGGLE_SHARP_OUT || 353128277c9SStephan Aßmus fMode == REMOVE_POINT_OUT || 354128277c9SStephan Aßmus fMode == SELECT_POINTS)) 355128277c9SStephan Aßmus into->SetLowColor(highlightColor); 356128277c9SStephan Aßmus else 357128277c9SStephan Aßmus into->SetLowColor(normal); 358128277c9SStephan Aßmus if (pointOut != point) { 359128277c9SStephan Aßmus r.Set(pointOut.x - CONTROL_POINT_EXTEND, pointOut.y - CONTROL_POINT_EXTEND, 360128277c9SStephan Aßmus pointOut.x + CONTROL_POINT_EXTEND, pointOut.y + CONTROL_POINT_EXTEND); 361128277c9SStephan Aßmus into->StrokeRect(r, B_SOLID_LOW); 362128277c9SStephan Aßmus r.InsetBy(1.0, 1.0); 363128277c9SStephan Aßmus into->FillRect(r, B_SOLID_HIGH); 364128277c9SStephan Aßmus } 365128277c9SStephan Aßmus } 366128277c9SStephan Aßmus } 367128277c9SStephan Aßmus 368128277c9SStephan Aßmus // #pragma mark - 369128277c9SStephan Aßmus 370128277c9SStephan Aßmus // MouseDown 371128277c9SStephan Aßmus bool 372128277c9SStephan Aßmus PathManipulator::MouseDown(BPoint where) 373128277c9SStephan Aßmus { 374128277c9SStephan Aßmus fMouseDown = true; 375128277c9SStephan Aßmus 376128277c9SStephan Aßmus if (fMode == MOVE_POINT && 377128277c9SStephan Aßmus fSelection->CountItems() > 1 && 378128277c9SStephan Aßmus fSelection->Contains(fCurrentPathPoint)) { 379128277c9SStephan Aßmus fMode = TRANSLATE_POINTS; 380128277c9SStephan Aßmus } 381128277c9SStephan Aßmus 382128277c9SStephan Aßmus BPoint canvasWhere = where; 383f67876a0SStephan Aßmus fCanvasView->ConvertToCanvas(&canvasWhere); 384128277c9SStephan Aßmus 385128277c9SStephan Aßmus // maybe we're changing some point, so we construct the 386128277c9SStephan Aßmus // "ChangePointCommand" here so that the point is remembered 387128277c9SStephan Aßmus // in its current state 388128277c9SStephan Aßmus delete fChangePointCommand; 389128277c9SStephan Aßmus fChangePointCommand = NULL; 390128277c9SStephan Aßmus switch (fMode) { 391128277c9SStephan Aßmus case TOGGLE_SHARP: 392128277c9SStephan Aßmus case TOGGLE_SHARP_IN: 393128277c9SStephan Aßmus case TOGGLE_SHARP_OUT: 394128277c9SStephan Aßmus case MOVE_POINT: 395128277c9SStephan Aßmus case MOVE_POINT_IN: 396128277c9SStephan Aßmus case MOVE_POINT_OUT: 397128277c9SStephan Aßmus case REMOVE_POINT_IN: 398128277c9SStephan Aßmus case REMOVE_POINT_OUT: 399128277c9SStephan Aßmus fChangePointCommand = new ChangePointCommand(fPath, 400128277c9SStephan Aßmus fCurrentPathPoint, 401128277c9SStephan Aßmus fSelection->Items(), 402128277c9SStephan Aßmus fSelection->CountItems()); 403128277c9SStephan Aßmus _Select(fCurrentPathPoint, fShiftDown); 404128277c9SStephan Aßmus break; 405128277c9SStephan Aßmus } 406128277c9SStephan Aßmus 407128277c9SStephan Aßmus // at this point we init doing something 408128277c9SStephan Aßmus switch (fMode) { 409128277c9SStephan Aßmus case ADD_POINT: 410128277c9SStephan Aßmus _AddPoint(canvasWhere); 411128277c9SStephan Aßmus break; 412128277c9SStephan Aßmus case INSERT_POINT: 413128277c9SStephan Aßmus _InsertPoint(canvasWhere, fCurrentPathPoint); 414128277c9SStephan Aßmus break; 415128277c9SStephan Aßmus 416128277c9SStephan Aßmus case TOGGLE_SHARP: 417128277c9SStephan Aßmus _SetSharp(fCurrentPathPoint); 418128277c9SStephan Aßmus // continue by dragging out the _connected_ in/out points 419128277c9SStephan Aßmus break; 420128277c9SStephan Aßmus case TOGGLE_SHARP_IN: 421128277c9SStephan Aßmus _SetInOutConnected(fCurrentPathPoint, false); 422128277c9SStephan Aßmus // continue by moving the "in" point 423128277c9SStephan Aßmus _SetMode(MOVE_POINT_IN); 424128277c9SStephan Aßmus break; 425128277c9SStephan Aßmus case TOGGLE_SHARP_OUT: 426128277c9SStephan Aßmus _SetInOutConnected(fCurrentPathPoint, false); 427128277c9SStephan Aßmus // continue by moving the "out" point 428128277c9SStephan Aßmus _SetMode(MOVE_POINT_OUT); 429128277c9SStephan Aßmus break; 430128277c9SStephan Aßmus 431128277c9SStephan Aßmus case MOVE_POINT: 432128277c9SStephan Aßmus case MOVE_POINT_IN: 433128277c9SStephan Aßmus case MOVE_POINT_OUT: 434128277c9SStephan Aßmus // the right thing happens since "fCurrentPathPoint" 435128277c9SStephan Aßmus // points to the correct index 436128277c9SStephan Aßmus break; 437128277c9SStephan Aßmus 438128277c9SStephan Aßmus case CLOSE_PATH: 439128277c9SStephan Aßmus // SetClosed(true, true); 440128277c9SStephan Aßmus break; 441128277c9SStephan Aßmus 442128277c9SStephan Aßmus case REMOVE_POINT: 443128277c9SStephan Aßmus if (fPath->CountPoints() == 1) { 444128277c9SStephan Aßmus // fCanvasView->Perform(new RemovePathCommand(this, fPath)); 445128277c9SStephan Aßmus } else { 446128277c9SStephan Aßmus fCanvasView->Perform(new RemovePointsCommand(fPath, 447128277c9SStephan Aßmus fCurrentPathPoint, 448128277c9SStephan Aßmus fSelection->Items(), 449128277c9SStephan Aßmus fSelection->CountItems())); 450128277c9SStephan Aßmus _RemovePoint(fCurrentPathPoint); 451128277c9SStephan Aßmus } 452128277c9SStephan Aßmus break; 453128277c9SStephan Aßmus case REMOVE_POINT_IN: 454128277c9SStephan Aßmus _RemovePointIn(fCurrentPathPoint); 455128277c9SStephan Aßmus break; 456128277c9SStephan Aßmus case REMOVE_POINT_OUT: 457128277c9SStephan Aßmus _RemovePointOut(fCurrentPathPoint); 458128277c9SStephan Aßmus break; 459128277c9SStephan Aßmus 460128277c9SStephan Aßmus case SELECT_POINTS: 461128277c9SStephan Aßmus if (!fShiftDown) { 462128277c9SStephan Aßmus fSelection->MakeEmpty(); 463128277c9SStephan Aßmus _UpdateSelection(); 464128277c9SStephan Aßmus } 465128277c9SStephan Aßmus *fOldSelection = *fSelection; 466128277c9SStephan Aßmus if (fCurrentPathPoint >= 0) { 467128277c9SStephan Aßmus _Select(fCurrentPathPoint, fShiftDown); 468128277c9SStephan Aßmus } 469128277c9SStephan Aßmus fCanvasView->BeginRectTracking(BRect(where, where), 470128277c9SStephan Aßmus B_TRACK_RECT_CORNER); 471128277c9SStephan Aßmus break; 472128277c9SStephan Aßmus } 473128277c9SStephan Aßmus 474128277c9SStephan Aßmus fTrackingStart = canvasWhere; 475128277c9SStephan Aßmus // remember the subpixel position 476128277c9SStephan Aßmus // so that MouseMoved() will work even before 477128277c9SStephan Aßmus // the integer position becomes different 478128277c9SStephan Aßmus fLastCanvasPos = where; 479f67876a0SStephan Aßmus fCanvasView->ConvertToCanvas(&fLastCanvasPos); 480128277c9SStephan Aßmus 481128277c9SStephan Aßmus // the reason to exclude the select mode 482128277c9SStephan Aßmus // is that the BView rect tracking does not 483128277c9SStephan Aßmus // scroll the rect starting point along with us 484128277c9SStephan Aßmus // (since we're doing no real scrolling) 485128277c9SStephan Aßmus // if (fMode != SELECT_POINTS) 486128277c9SStephan Aßmus // fCanvasView->SetAutoScrolling(true); 487128277c9SStephan Aßmus 488128277c9SStephan Aßmus UpdateCursor(); 489128277c9SStephan Aßmus 490128277c9SStephan Aßmus return true; 491128277c9SStephan Aßmus } 492128277c9SStephan Aßmus 493128277c9SStephan Aßmus // MouseMoved 494128277c9SStephan Aßmus void 495128277c9SStephan Aßmus PathManipulator::MouseMoved(BPoint where) 496128277c9SStephan Aßmus { 497128277c9SStephan Aßmus BPoint canvasWhere = where; 498f67876a0SStephan Aßmus fCanvasView->ConvertToCanvas(&canvasWhere); 499128277c9SStephan Aßmus 500128277c9SStephan Aßmus // since the tablet is generating mouse moved messages 501128277c9SStephan Aßmus // even if only the pressure changes (and not the actual mouse position) 502128277c9SStephan Aßmus // we insert this additional check to prevent too much calculation 503128277c9SStephan Aßmus if (fLastCanvasPos == canvasWhere) 504128277c9SStephan Aßmus return; 505128277c9SStephan Aßmus 506128277c9SStephan Aßmus fLastCanvasPos = canvasWhere; 507128277c9SStephan Aßmus 508128277c9SStephan Aßmus if (fMode == CLOSE_PATH) { 509128277c9SStephan Aßmus // continue by moving the point 510128277c9SStephan Aßmus _SetMode(MOVE_POINT); 511128277c9SStephan Aßmus delete fChangePointCommand; 512128277c9SStephan Aßmus fChangePointCommand = new ChangePointCommand(fPath, 513128277c9SStephan Aßmus fCurrentPathPoint, 514128277c9SStephan Aßmus fSelection->Items(), 515128277c9SStephan Aßmus fSelection->CountItems()); 516128277c9SStephan Aßmus } 517128277c9SStephan Aßmus 518128277c9SStephan Aßmus // if (!fPrecise) { 519128277c9SStephan Aßmus // float offset = fmod(fOutlineWidth, 2.0) / 2.0; 520128277c9SStephan Aßmus // canvasWhere.point += BPoint(offset, offset); 521128277c9SStephan Aßmus // } 522128277c9SStephan Aßmus 523128277c9SStephan Aßmus switch (fMode) { 524128277c9SStephan Aßmus case ADD_POINT: 525128277c9SStephan Aßmus case INSERT_POINT: 526128277c9SStephan Aßmus case TOGGLE_SHARP: 527128277c9SStephan Aßmus // drag the "out" control point, mirror the "in" control point 528128277c9SStephan Aßmus fPath->SetPointOut(fCurrentPathPoint, canvasWhere, true); 529128277c9SStephan Aßmus break; 530128277c9SStephan Aßmus case MOVE_POINT: 531128277c9SStephan Aßmus // drag all three control points at once 532128277c9SStephan Aßmus fPath->SetPoint(fCurrentPathPoint, canvasWhere); 533128277c9SStephan Aßmus break; 534128277c9SStephan Aßmus case MOVE_POINT_IN: 535128277c9SStephan Aßmus // drag in control point 536128277c9SStephan Aßmus fPath->SetPointIn(fCurrentPathPoint, canvasWhere); 537128277c9SStephan Aßmus break; 538128277c9SStephan Aßmus case MOVE_POINT_OUT: 539128277c9SStephan Aßmus // drag out control point 540128277c9SStephan Aßmus fPath->SetPointOut(fCurrentPathPoint, canvasWhere); 541128277c9SStephan Aßmus break; 542128277c9SStephan Aßmus 543128277c9SStephan Aßmus case SELECT_POINTS: { 544128277c9SStephan Aßmus // change the selection 545128277c9SStephan Aßmus BRect r; 546128277c9SStephan Aßmus r.left = min_c(fTrackingStart.x, canvasWhere.x); 547128277c9SStephan Aßmus r.top = min_c(fTrackingStart.y, canvasWhere.y); 548128277c9SStephan Aßmus r.right = max_c(fTrackingStart.x, canvasWhere.x); 549128277c9SStephan Aßmus r.bottom = max_c(fTrackingStart.y, canvasWhere.y); 550128277c9SStephan Aßmus _Select(r); 551128277c9SStephan Aßmus break; 552128277c9SStephan Aßmus } 553128277c9SStephan Aßmus 554128277c9SStephan Aßmus case TRANSLATE_POINTS: { 555128277c9SStephan Aßmus BPoint offset = canvasWhere - fTrackingStart; 556128277c9SStephan Aßmus _Nudge(offset); 557128277c9SStephan Aßmus fTrackingStart = canvasWhere; 558128277c9SStephan Aßmus break; 559128277c9SStephan Aßmus } 560128277c9SStephan Aßmus } 561128277c9SStephan Aßmus } 562128277c9SStephan Aßmus 563128277c9SStephan Aßmus // MouseUp 564128277c9SStephan Aßmus Command* 565128277c9SStephan Aßmus PathManipulator::MouseUp() 566128277c9SStephan Aßmus { 567128277c9SStephan Aßmus // prevent carrying out actions more than once by only 568128277c9SStephan Aßmus // doing it if "fMouseDown" is true at the point of 569128277c9SStephan Aßmus // entering this function 570128277c9SStephan Aßmus if (!fMouseDown) 571128277c9SStephan Aßmus return NULL; 572128277c9SStephan Aßmus fMouseDown = false; 573128277c9SStephan Aßmus 574128277c9SStephan Aßmus Command* command = NULL; 575128277c9SStephan Aßmus 576128277c9SStephan Aßmus switch (fMode) { 577128277c9SStephan Aßmus 578128277c9SStephan Aßmus case ADD_POINT: 579128277c9SStephan Aßmus command = fAddPointCommand; 580128277c9SStephan Aßmus fAddPointCommand = NULL; 581128277c9SStephan Aßmus _SetMode(MOVE_POINT_OUT); 582128277c9SStephan Aßmus break; 583128277c9SStephan Aßmus 584128277c9SStephan Aßmus case INSERT_POINT: 585128277c9SStephan Aßmus command = fInsertPointCommand; 586128277c9SStephan Aßmus fInsertPointCommand = NULL; 587128277c9SStephan Aßmus break; 588128277c9SStephan Aßmus 589128277c9SStephan Aßmus case SELECT_POINTS: 590128277c9SStephan Aßmus if (*fSelection != *fOldSelection) { 591128277c9SStephan Aßmus // command = new SelectPointsCommand(this, fPath, 592128277c9SStephan Aßmus // fOldSelection->Items(), 593128277c9SStephan Aßmus // fOldSelection->CountItems(), 594128277c9SStephan Aßmus // fSelection->Items(), 595128277c9SStephan Aßmus // fSelection->CountItems())); 596128277c9SStephan Aßmus } 597128277c9SStephan Aßmus fCanvasView->EndRectTracking(); 598128277c9SStephan Aßmus break; 599128277c9SStephan Aßmus 600128277c9SStephan Aßmus case TOGGLE_SHARP: 601128277c9SStephan Aßmus case TOGGLE_SHARP_IN: 602128277c9SStephan Aßmus case TOGGLE_SHARP_OUT: 603128277c9SStephan Aßmus case MOVE_POINT: 604128277c9SStephan Aßmus case MOVE_POINT_IN: 605128277c9SStephan Aßmus case MOVE_POINT_OUT: 606128277c9SStephan Aßmus case REMOVE_POINT_IN: 607128277c9SStephan Aßmus case REMOVE_POINT_OUT: 608128277c9SStephan Aßmus command = fChangePointCommand; 609128277c9SStephan Aßmus fChangePointCommand = NULL; 610128277c9SStephan Aßmus break; 611128277c9SStephan Aßmus 612128277c9SStephan Aßmus case TRANSLATE_POINTS: 613128277c9SStephan Aßmus // if (!fNudgeCommand) { 614128277c9SStephan Aßmus // select just the point that was clicked 615128277c9SStephan Aßmus *fOldSelection = *fSelection; 616128277c9SStephan Aßmus if (fCurrentPathPoint >= 0) { 617128277c9SStephan Aßmus _Select(fCurrentPathPoint, fShiftDown); 618128277c9SStephan Aßmus } 619128277c9SStephan Aßmus if (*fSelection != *fOldSelection) { 620128277c9SStephan Aßmus // command = new SelectPointsCommand(this, fPath, 621128277c9SStephan Aßmus // fOldSelection->Items(), 622128277c9SStephan Aßmus // fOldSelection->CountItems(), 623128277c9SStephan Aßmus // fSelection->Items(), 624128277c9SStephan Aßmus // fSelection->CountItems())); 625128277c9SStephan Aßmus } 626128277c9SStephan Aßmus // } else { 627128277c9SStephan Aßmus // _FinishNudging(); 628128277c9SStephan Aßmus // } 629128277c9SStephan Aßmus break; 630128277c9SStephan Aßmus } 631128277c9SStephan Aßmus 632128277c9SStephan Aßmus return command; 633128277c9SStephan Aßmus } 634128277c9SStephan Aßmus 635128277c9SStephan Aßmus // MouseOver 636128277c9SStephan Aßmus bool 637128277c9SStephan Aßmus PathManipulator::MouseOver(BPoint where) 638128277c9SStephan Aßmus { 639128277c9SStephan Aßmus BPoint canvasWhere = where; 640f67876a0SStephan Aßmus fCanvasView->ConvertToCanvas(&canvasWhere); 641128277c9SStephan Aßmus 642128277c9SStephan Aßmus // since the tablet is generating mouse moved messages 643128277c9SStephan Aßmus // even if only the pressure changes (and not the actual mouse position) 644128277c9SStephan Aßmus // we insert this additional check to prevent too much calculation 645128277c9SStephan Aßmus if (fMouseDown && fLastCanvasPos == canvasWhere) 646128277c9SStephan Aßmus return false; 647128277c9SStephan Aßmus 648128277c9SStephan Aßmus fLastCanvasPos = canvasWhere; 649128277c9SStephan Aßmus 650128277c9SStephan Aßmus // hit testing 651128277c9SStephan Aßmus // (use a subpixel mouse pos) 652f67876a0SStephan Aßmus fCanvasView->ConvertToCanvas(&where); 653128277c9SStephan Aßmus _SetModeForMousePos(where); 654128277c9SStephan Aßmus 655128277c9SStephan Aßmus // TODO: always true? 656128277c9SStephan Aßmus return true; 657128277c9SStephan Aßmus } 658128277c9SStephan Aßmus 659128277c9SStephan Aßmus // DoubleClicked 660128277c9SStephan Aßmus bool 661128277c9SStephan Aßmus PathManipulator::DoubleClicked(BPoint where) 662128277c9SStephan Aßmus { 663128277c9SStephan Aßmus return false; 664128277c9SStephan Aßmus } 665128277c9SStephan Aßmus 666128277c9SStephan Aßmus // Bounds 667128277c9SStephan Aßmus BRect 668128277c9SStephan Aßmus PathManipulator::Bounds() 669128277c9SStephan Aßmus { 670f67876a0SStephan Aßmus BRect r = _ControlPointRect(); 671f67876a0SStephan Aßmus fCanvasView->ConvertFromCanvas(&r); 672f67876a0SStephan Aßmus return r; 673128277c9SStephan Aßmus } 674128277c9SStephan Aßmus 675128277c9SStephan Aßmus // TrackingBounds 676128277c9SStephan Aßmus BRect 677128277c9SStephan Aßmus PathManipulator::TrackingBounds(BView* withinView) 678128277c9SStephan Aßmus { 679128277c9SStephan Aßmus return withinView->Bounds(); 680128277c9SStephan Aßmus } 681128277c9SStephan Aßmus 682128277c9SStephan Aßmus // #pragma mark - 683128277c9SStephan Aßmus 684128277c9SStephan Aßmus enum { 685128277c9SStephan Aßmus MSG_SHAPE_TRANSFORM = 'strn', 686128277c9SStephan Aßmus MSG_SHAPE_REMOVE_POINTS = 'srmp', 687128277c9SStephan Aßmus MSG_UPDATE_SHAPE_UI = 'udsi', 688128277c9SStephan Aßmus }; 689128277c9SStephan Aßmus 690128277c9SStephan Aßmus // MessageReceived 691128277c9SStephan Aßmus bool 692128277c9SStephan Aßmus PathManipulator::MessageReceived(BMessage* message, Command** _command) 693128277c9SStephan Aßmus { 694128277c9SStephan Aßmus bool result = true; 695128277c9SStephan Aßmus switch (message->what) { 696128277c9SStephan Aßmus case MSG_SHAPE_TRANSFORM: 697128277c9SStephan Aßmus if (!fSelection->IsEmpty()) 698128277c9SStephan Aßmus _SetMode(TRANSFORM_POINTS); 699128277c9SStephan Aßmus break; 700128277c9SStephan Aßmus case MSG_SHAPE_REMOVE_POINTS: 701128277c9SStephan Aßmus *_command = _Delete(); 702128277c9SStephan Aßmus break; 703128277c9SStephan Aßmus case B_SELECT_ALL: { 704128277c9SStephan Aßmus *fOldSelection = *fSelection; 705128277c9SStephan Aßmus fSelection->MakeEmpty(); 706128277c9SStephan Aßmus int32 count = fPath->CountPoints(); 707128277c9SStephan Aßmus for (int32 i = 0; i < count; i++) 708128277c9SStephan Aßmus fSelection->Add(i); 709128277c9SStephan Aßmus if (*fOldSelection != *fSelection) { 710128277c9SStephan Aßmus // *_command = new SelectPointsCommand(this, fPath, 711128277c9SStephan Aßmus // fOldSelection->Items(), 712128277c9SStephan Aßmus // fOldSelection->CountItems(), 713128277c9SStephan Aßmus // fSelection->Items(), 714128277c9SStephan Aßmus // fSelection->CountItems())); 715128277c9SStephan Aßmus count = fSelection->CountItems(); 716128277c9SStephan Aßmus int32 indices[count]; 717128277c9SStephan Aßmus memcpy(indices, fSelection->Items(), count * sizeof(int32)); 718128277c9SStephan Aßmus _Select(indices, count); 719128277c9SStephan Aßmus } 720128277c9SStephan Aßmus break; 721128277c9SStephan Aßmus } 722128277c9SStephan Aßmus default: 723128277c9SStephan Aßmus result = false; 724128277c9SStephan Aßmus break; 725128277c9SStephan Aßmus } 726128277c9SStephan Aßmus return result; 727128277c9SStephan Aßmus } 728128277c9SStephan Aßmus 729128277c9SStephan Aßmus 730128277c9SStephan Aßmus // ModifiersChanged 731128277c9SStephan Aßmus void 732128277c9SStephan Aßmus PathManipulator::ModifiersChanged(uint32 modifiers) 733128277c9SStephan Aßmus { 734128277c9SStephan Aßmus fCommandDown = modifiers & B_COMMAND_KEY; 735128277c9SStephan Aßmus fOptionDown = modifiers & B_CONTROL_KEY; 736128277c9SStephan Aßmus fShiftDown = modifiers & B_SHIFT_KEY; 737128277c9SStephan Aßmus fAltDown = modifiers & B_OPTION_KEY; 738128277c9SStephan Aßmus 739128277c9SStephan Aßmus // reevaluate mode 740128277c9SStephan Aßmus if (!fMouseDown) 741128277c9SStephan Aßmus _SetModeForMousePos(fLastCanvasPos); 742128277c9SStephan Aßmus } 743128277c9SStephan Aßmus 744128277c9SStephan Aßmus // HandleKeyDown 745128277c9SStephan Aßmus bool 746128277c9SStephan Aßmus PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command) 747128277c9SStephan Aßmus { 748128277c9SStephan Aßmus bool result = true; 749128277c9SStephan Aßmus 750128277c9SStephan Aßmus float nudgeDist = 1.0; 751f67876a0SStephan Aßmus if (modifiers & B_SHIFT_KEY) 752f67876a0SStephan Aßmus nudgeDist /= fCanvasView->ZoomLevel(); 753128277c9SStephan Aßmus 754128277c9SStephan Aßmus switch (key) { 755128277c9SStephan Aßmus // commit 756128277c9SStephan Aßmus case B_RETURN: 757128277c9SStephan Aßmus // _Perform(); 758128277c9SStephan Aßmus break; 759128277c9SStephan Aßmus // cancel 760128277c9SStephan Aßmus case B_ESCAPE: 761128277c9SStephan Aßmus if (fFallBackMode == NEW_PATH) { 762128277c9SStephan Aßmus fFallBackMode = SELECT_POINTS; 763128277c9SStephan Aßmus _SetModeForMousePos(fLastCanvasPos); 764128277c9SStephan Aßmus } else 765128277c9SStephan Aßmus // _Cancel(); 766128277c9SStephan Aßmus break; 767128277c9SStephan Aßmus case 't': 768128277c9SStephan Aßmus case 'T': 769128277c9SStephan Aßmus if (!fSelection->IsEmpty()) 770128277c9SStephan Aßmus _SetMode(TRANSFORM_POINTS); 771128277c9SStephan Aßmus else 772128277c9SStephan Aßmus result = false; 773128277c9SStephan Aßmus break; 774128277c9SStephan Aßmus // nudging 775128277c9SStephan Aßmus case B_UP_ARROW: 776128277c9SStephan Aßmus _Nudge(BPoint(0.0, -nudgeDist)); 777128277c9SStephan Aßmus break; 778128277c9SStephan Aßmus case B_DOWN_ARROW: 779128277c9SStephan Aßmus _Nudge(BPoint(0.0, nudgeDist)); 780128277c9SStephan Aßmus break; 781128277c9SStephan Aßmus case B_LEFT_ARROW: 782128277c9SStephan Aßmus _Nudge(BPoint(-nudgeDist, 0.0)); 783128277c9SStephan Aßmus break; 784128277c9SStephan Aßmus case B_RIGHT_ARROW: 785128277c9SStephan Aßmus _Nudge(BPoint(nudgeDist, 0.0)); 786128277c9SStephan Aßmus break; 787128277c9SStephan Aßmus 788128277c9SStephan Aßmus case B_DELETE: 789128277c9SStephan Aßmus if (!fSelection->IsEmpty()) 790128277c9SStephan Aßmus *_command = _Delete(); 791128277c9SStephan Aßmus else 792128277c9SStephan Aßmus result = false; 793128277c9SStephan Aßmus break; 794128277c9SStephan Aßmus 795128277c9SStephan Aßmus default: 796128277c9SStephan Aßmus result = false; 797128277c9SStephan Aßmus } 798128277c9SStephan Aßmus return result; 799128277c9SStephan Aßmus } 800128277c9SStephan Aßmus 801128277c9SStephan Aßmus // HandleKeyUp 802128277c9SStephan Aßmus bool 803128277c9SStephan Aßmus PathManipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command) 804128277c9SStephan Aßmus { 805128277c9SStephan Aßmus bool handled = true; 806128277c9SStephan Aßmus switch (key) { 807128277c9SStephan Aßmus // nudging 808128277c9SStephan Aßmus case B_UP_ARROW: 809128277c9SStephan Aßmus case B_DOWN_ARROW: 810128277c9SStephan Aßmus case B_LEFT_ARROW: 811128277c9SStephan Aßmus case B_RIGHT_ARROW: 812128277c9SStephan Aßmus _FinishNudging(); 813128277c9SStephan Aßmus break; 814128277c9SStephan Aßmus default: 815128277c9SStephan Aßmus handled = false; 816128277c9SStephan Aßmus break; 817128277c9SStephan Aßmus } 818128277c9SStephan Aßmus return handled; 819128277c9SStephan Aßmus } 820128277c9SStephan Aßmus 821128277c9SStephan Aßmus // UpdateCursor 822128277c9SStephan Aßmus void 823128277c9SStephan Aßmus PathManipulator::UpdateCursor() 824128277c9SStephan Aßmus { 825128277c9SStephan Aßmus const uchar* cursorData; 826128277c9SStephan Aßmus switch (fMode) { 827128277c9SStephan Aßmus case ADD_POINT: 828128277c9SStephan Aßmus cursorData = kPathAddCursor; 829128277c9SStephan Aßmus break; 830128277c9SStephan Aßmus case INSERT_POINT: 831128277c9SStephan Aßmus cursorData = kPathInsertCursor; 832128277c9SStephan Aßmus break; 833128277c9SStephan Aßmus case MOVE_POINT: 834128277c9SStephan Aßmus case MOVE_POINT_IN: 835128277c9SStephan Aßmus case MOVE_POINT_OUT: 836128277c9SStephan Aßmus case TRANSLATE_POINTS: 837128277c9SStephan Aßmus cursorData = kPathMoveCursor; 838128277c9SStephan Aßmus break; 839128277c9SStephan Aßmus case CLOSE_PATH: 840128277c9SStephan Aßmus cursorData = kPathCloseCursor; 841128277c9SStephan Aßmus break; 842128277c9SStephan Aßmus case TOGGLE_SHARP: 843128277c9SStephan Aßmus case TOGGLE_SHARP_IN: 844128277c9SStephan Aßmus case TOGGLE_SHARP_OUT: 845128277c9SStephan Aßmus cursorData = kPathSharpCursor; 846128277c9SStephan Aßmus break; 847128277c9SStephan Aßmus case REMOVE_POINT: 848128277c9SStephan Aßmus case REMOVE_POINT_IN: 849128277c9SStephan Aßmus case REMOVE_POINT_OUT: 850128277c9SStephan Aßmus cursorData = kPathRemoveCursor; 851128277c9SStephan Aßmus break; 852128277c9SStephan Aßmus case SELECT_POINTS: 853128277c9SStephan Aßmus cursorData = kPathSelectCursor; 854128277c9SStephan Aßmus break; 855128277c9SStephan Aßmus 856128277c9SStephan Aßmus case SELECT_SUB_PATH: 857128277c9SStephan Aßmus cursorData = B_HAND_CURSOR; 858128277c9SStephan Aßmus break; 859128277c9SStephan Aßmus 860128277c9SStephan Aßmus case UNDEFINED: 861128277c9SStephan Aßmus default: 862128277c9SStephan Aßmus cursorData = kStopCursor; 863128277c9SStephan Aßmus break; 864128277c9SStephan Aßmus } 865128277c9SStephan Aßmus BCursor cursor(cursorData); 866128277c9SStephan Aßmus fCanvasView->SetViewCursor(&cursor, true); 867128277c9SStephan Aßmus fCanvasView->Sync(); 868128277c9SStephan Aßmus } 869128277c9SStephan Aßmus 870128277c9SStephan Aßmus // AttachedToView 871128277c9SStephan Aßmus void 872128277c9SStephan Aßmus PathManipulator::AttachedToView(BView* view) 873128277c9SStephan Aßmus { 874f67876a0SStephan Aßmus fCanvasView = dynamic_cast<CanvasView*>(view); 875128277c9SStephan Aßmus } 876128277c9SStephan Aßmus 877128277c9SStephan Aßmus // DetachedFromView 878128277c9SStephan Aßmus void 879128277c9SStephan Aßmus PathManipulator::DetachedFromView(BView* view) 880128277c9SStephan Aßmus { 881128277c9SStephan Aßmus fCanvasView = NULL; 882128277c9SStephan Aßmus } 883128277c9SStephan Aßmus 884128277c9SStephan Aßmus // #pragma mark - 885128277c9SStephan Aßmus 886128277c9SStephan Aßmus // ObjectChanged 887128277c9SStephan Aßmus void 888128277c9SStephan Aßmus PathManipulator::ObjectChanged(const Observable* object) 889128277c9SStephan Aßmus { 890128277c9SStephan Aßmus // TODO: refine VectorPath listener interface and 891128277c9SStephan Aßmus // implement more efficiently 892128277c9SStephan Aßmus BRect currentBounds = _ControlPointRect(); 893128277c9SStephan Aßmus _InvalidateCanvas(currentBounds | fPreviousBounds); 894128277c9SStephan Aßmus fPreviousBounds = currentBounds; 895128277c9SStephan Aßmus 896128277c9SStephan Aßmus // reevaluate mode 897128277c9SStephan Aßmus if (!fMouseDown) 898128277c9SStephan Aßmus _SetModeForMousePos(fLastCanvasPos); 899128277c9SStephan Aßmus } 900128277c9SStephan Aßmus 901128277c9SStephan Aßmus // #pragma mark - 902128277c9SStephan Aßmus 903128277c9SStephan Aßmus // ControlFlags 904128277c9SStephan Aßmus uint32 905128277c9SStephan Aßmus PathManipulator::ControlFlags() const 906128277c9SStephan Aßmus { 907128277c9SStephan Aßmus uint32 flags = 0; 908128277c9SStephan Aßmus 909128277c9SStephan Aßmus // flags |= SHAPE_UI_FLAGS_CAN_REVERSE_PATH; 910128277c9SStephan Aßmus // 911128277c9SStephan Aßmus // if (!fSelection->IsEmpty()) 912128277c9SStephan Aßmus // flags |= SHAPE_UI_FLAGS_HAS_SELECTION; 913128277c9SStephan Aßmus // if (fPath->CountPoints() > 1) 914128277c9SStephan Aßmus // flags |= SHAPE_UI_FLAGS_CAN_CLOSE_PATH; 915128277c9SStephan Aßmus // if (fPath->IsClosed()) 916128277c9SStephan Aßmus // flags |= SHAPE_UI_FLAGS_PATH_IS_CLOSED; 917128277c9SStephan Aßmus 918128277c9SStephan Aßmus return flags; 919128277c9SStephan Aßmus } 920128277c9SStephan Aßmus 921128277c9SStephan Aßmus // ReversePath 922128277c9SStephan Aßmus void 923128277c9SStephan Aßmus PathManipulator::ReversePath() 924128277c9SStephan Aßmus { 925128277c9SStephan Aßmus int32 count = fSelection->CountItems(); 926128277c9SStephan Aßmus int32 pointCount = fPath->CountPoints(); 927128277c9SStephan Aßmus if (count > 0) { 928128277c9SStephan Aßmus Selection temp; 929128277c9SStephan Aßmus for (int32 i = 0; i < count; i++) { 930128277c9SStephan Aßmus temp.Add((pointCount - 1) - fSelection->IndexAt(i)); 931128277c9SStephan Aßmus } 932128277c9SStephan Aßmus *fSelection = temp; 933128277c9SStephan Aßmus } 934128277c9SStephan Aßmus fPath->Reverse(); 935128277c9SStephan Aßmus } 936128277c9SStephan Aßmus 937128277c9SStephan Aßmus // #pragma mark - 938128277c9SStephan Aßmus 939128277c9SStephan Aßmus // _SetMode 940128277c9SStephan Aßmus void 941128277c9SStephan Aßmus PathManipulator::_SetMode(uint32 mode) 942128277c9SStephan Aßmus { 943128277c9SStephan Aßmus if (fMode != mode) { 944128277c9SStephan Aßmus //printf("switching mode: %s -> %s\n", string_for_mode(fMode), string_for_mode(mode)); 945128277c9SStephan Aßmus fMode = mode; 946128277c9SStephan Aßmus 947128277c9SStephan Aßmus if (BWindow* window = fCanvasView->Window()) { 948128277c9SStephan Aßmus window->PostMessage(MSG_UPDATE_SHAPE_UI); 949128277c9SStephan Aßmus } 950128277c9SStephan Aßmus UpdateCursor(); 951128277c9SStephan Aßmus } 952128277c9SStephan Aßmus } 953128277c9SStephan Aßmus 954128277c9SStephan Aßmus // _AddPoint 955128277c9SStephan Aßmus void 956128277c9SStephan Aßmus PathManipulator::_AddPoint(BPoint where) 957128277c9SStephan Aßmus { 958128277c9SStephan Aßmus if (fPath->AddPoint(where)) { 959128277c9SStephan Aßmus fCurrentPathPoint = fPath->CountPoints() - 1; 960128277c9SStephan Aßmus 961128277c9SStephan Aßmus delete fAddPointCommand; 962128277c9SStephan Aßmus fAddPointCommand = new AddPointCommand(fPath, fCurrentPathPoint, 963128277c9SStephan Aßmus fSelection->Items(), 964128277c9SStephan Aßmus fSelection->CountItems()); 965128277c9SStephan Aßmus 966128277c9SStephan Aßmus _Select(fCurrentPathPoint, fShiftDown); 967128277c9SStephan Aßmus } 968128277c9SStephan Aßmus } 969128277c9SStephan Aßmus 970128277c9SStephan Aßmus // scale_point 971128277c9SStephan Aßmus BPoint 972128277c9SStephan Aßmus scale_point(BPoint a, BPoint b, float scale) 973128277c9SStephan Aßmus { 974128277c9SStephan Aßmus return BPoint(a.x + (b.x - a.x) * scale, 975128277c9SStephan Aßmus a.y + (b.y - a.y) * scale); 976128277c9SStephan Aßmus } 977128277c9SStephan Aßmus 978128277c9SStephan Aßmus // _InsertPoint 979128277c9SStephan Aßmus void 980128277c9SStephan Aßmus PathManipulator::_InsertPoint(BPoint where, int32 index) 981128277c9SStephan Aßmus { 982128277c9SStephan Aßmus double scale; 983128277c9SStephan Aßmus 984128277c9SStephan Aßmus BPoint point; 985128277c9SStephan Aßmus BPoint pointIn; 986128277c9SStephan Aßmus BPoint pointOut; 987128277c9SStephan Aßmus 988128277c9SStephan Aßmus BPoint previous; 989128277c9SStephan Aßmus BPoint previousOut; 990128277c9SStephan Aßmus BPoint next; 991128277c9SStephan Aßmus BPoint nextIn; 992128277c9SStephan Aßmus 993128277c9SStephan Aßmus if (fPath->FindBezierScale(index - 1, where, &scale) 994128277c9SStephan Aßmus && scale >= 0.0 && scale <= 1.0 995128277c9SStephan Aßmus && fPath->GetPoint(index - 1, scale, point)) { 996128277c9SStephan Aßmus 997128277c9SStephan Aßmus fPath->GetPointAt(index - 1, previous); 998128277c9SStephan Aßmus fPath->GetPointOutAt(index - 1, previousOut); 999128277c9SStephan Aßmus fPath->GetPointAt(index, next); 1000128277c9SStephan Aßmus fPath->GetPointInAt(index, nextIn); 1001128277c9SStephan Aßmus 1002128277c9SStephan Aßmus where = scale_point(previousOut, nextIn, scale); 1003128277c9SStephan Aßmus 1004128277c9SStephan Aßmus previousOut = scale_point(previous, previousOut, scale); 1005128277c9SStephan Aßmus nextIn = scale_point(next, nextIn, 1 - scale); 1006128277c9SStephan Aßmus pointIn = scale_point(previousOut, where, scale); 1007128277c9SStephan Aßmus pointOut = scale_point(nextIn, where, 1 - scale); 1008128277c9SStephan Aßmus 1009128277c9SStephan Aßmus if (fPath->AddPoint(point, index)) { 1010128277c9SStephan Aßmus 1011128277c9SStephan Aßmus fPath->SetPointIn(index, pointIn); 1012128277c9SStephan Aßmus fPath->SetPointOut(index, pointOut); 1013128277c9SStephan Aßmus 1014128277c9SStephan Aßmus delete fInsertPointCommand; 1015128277c9SStephan Aßmus fInsertPointCommand = new InsertPointCommand(fPath, index, 1016128277c9SStephan Aßmus fSelection->Items(), 1017128277c9SStephan Aßmus fSelection->CountItems()); 1018128277c9SStephan Aßmus 1019128277c9SStephan Aßmus fPath->SetPointOut(index - 1, previousOut); 1020128277c9SStephan Aßmus fPath->SetPointIn(index + 1, nextIn); 1021128277c9SStephan Aßmus 1022128277c9SStephan Aßmus fCurrentPathPoint = index; 1023128277c9SStephan Aßmus _ShiftSelection(fCurrentPathPoint, 1); 1024128277c9SStephan Aßmus _Select(fCurrentPathPoint, fShiftDown); 1025128277c9SStephan Aßmus } 1026128277c9SStephan Aßmus } 1027128277c9SStephan Aßmus } 1028128277c9SStephan Aßmus 1029128277c9SStephan Aßmus // _SetInOutConnected 1030128277c9SStephan Aßmus void 1031128277c9SStephan Aßmus PathManipulator::_SetInOutConnected(int32 index, bool connected) 1032128277c9SStephan Aßmus { 1033128277c9SStephan Aßmus fPath->SetInOutConnected(index, connected); 1034128277c9SStephan Aßmus } 1035128277c9SStephan Aßmus 1036128277c9SStephan Aßmus // _SetSharp 1037128277c9SStephan Aßmus void 1038128277c9SStephan Aßmus PathManipulator::_SetSharp(int32 index) 1039128277c9SStephan Aßmus { 1040128277c9SStephan Aßmus BPoint p; 1041128277c9SStephan Aßmus fPath->GetPointAt(index, p); 1042128277c9SStephan Aßmus fPath->SetPoint(index, p, p, p, true); 1043128277c9SStephan Aßmus } 1044128277c9SStephan Aßmus 1045128277c9SStephan Aßmus // _RemoveSelection 1046128277c9SStephan Aßmus void 1047128277c9SStephan Aßmus PathManipulator::_RemoveSelection() 1048128277c9SStephan Aßmus { 1049128277c9SStephan Aßmus int32 count = fSelection->CountItems(); 1050128277c9SStephan Aßmus for (int32 i = 0; i < count; i++) { 1051128277c9SStephan Aßmus if (!fPath->RemovePoint(fSelection->IndexAt(i) - i)) 1052128277c9SStephan Aßmus break; 1053128277c9SStephan Aßmus } 1054128277c9SStephan Aßmus 1055128277c9SStephan Aßmus // SetClosed(fPath->IsClosed() && fPath->CountPoints() > 1); 1056128277c9SStephan Aßmus 1057128277c9SStephan Aßmus fSelection->MakeEmpty(); 1058128277c9SStephan Aßmus } 1059128277c9SStephan Aßmus 1060128277c9SStephan Aßmus 1061128277c9SStephan Aßmus // _RemovePoint 1062128277c9SStephan Aßmus void 1063128277c9SStephan Aßmus PathManipulator::_RemovePoint(int32 index) 1064128277c9SStephan Aßmus { 1065128277c9SStephan Aßmus if (fPath->RemovePoint(index)) { 1066128277c9SStephan Aßmus _Deselect(index); 1067128277c9SStephan Aßmus _ShiftSelection(index + 1, -1); 1068128277c9SStephan Aßmus } 1069128277c9SStephan Aßmus } 1070128277c9SStephan Aßmus 1071128277c9SStephan Aßmus // _RemovePointIn 1072128277c9SStephan Aßmus void 1073128277c9SStephan Aßmus PathManipulator::_RemovePointIn(int32 index) 1074128277c9SStephan Aßmus { 1075128277c9SStephan Aßmus BPoint p; 1076128277c9SStephan Aßmus if (fPath->GetPointAt(index, p)) { 1077128277c9SStephan Aßmus fPath->SetPointIn(index, p); 1078128277c9SStephan Aßmus fPath->SetInOutConnected(index, false); 1079128277c9SStephan Aßmus } 1080128277c9SStephan Aßmus } 1081128277c9SStephan Aßmus 1082128277c9SStephan Aßmus // _RemovePointOut 1083128277c9SStephan Aßmus void 1084128277c9SStephan Aßmus PathManipulator::_RemovePointOut(int32 index) 1085128277c9SStephan Aßmus { 1086128277c9SStephan Aßmus BPoint p; 1087128277c9SStephan Aßmus if (fPath->GetPointAt(index, p)) { 1088128277c9SStephan Aßmus fPath->SetPointOut(index, p); 1089128277c9SStephan Aßmus fPath->SetInOutConnected(index, false); 1090128277c9SStephan Aßmus } 1091128277c9SStephan Aßmus } 1092128277c9SStephan Aßmus 1093128277c9SStephan Aßmus // _Delete 1094128277c9SStephan Aßmus Command* 1095128277c9SStephan Aßmus PathManipulator::_Delete() 1096128277c9SStephan Aßmus { 1097128277c9SStephan Aßmus Command* command = NULL; 1098128277c9SStephan Aßmus if (!fMouseDown) { 1099128277c9SStephan Aßmus if (fSelection->CountItems() == fPath->CountPoints()) { 1100128277c9SStephan Aßmus // command = new RemovePathCommand(fPath); 1101128277c9SStephan Aßmus } else { 1102128277c9SStephan Aßmus command = new RemovePointsCommand(fPath, 1103128277c9SStephan Aßmus fSelection->Items(), 1104128277c9SStephan Aßmus fSelection->CountItems()); 1105128277c9SStephan Aßmus _RemoveSelection(); 1106128277c9SStephan Aßmus } 1107128277c9SStephan Aßmus 1108128277c9SStephan Aßmus _SetModeForMousePos(fLastCanvasPos); 1109128277c9SStephan Aßmus } 1110128277c9SStephan Aßmus 1111128277c9SStephan Aßmus return command; 1112128277c9SStephan Aßmus } 1113128277c9SStephan Aßmus 1114128277c9SStephan Aßmus // #pragma mark - 1115128277c9SStephan Aßmus 1116128277c9SStephan Aßmus // _Select 1117128277c9SStephan Aßmus void 1118128277c9SStephan Aßmus PathManipulator::_Select(BRect r) 1119128277c9SStephan Aßmus { 1120128277c9SStephan Aßmus BPoint p; 1121128277c9SStephan Aßmus int32 count = fPath->CountPoints(); 1122128277c9SStephan Aßmus Selection temp; 1123128277c9SStephan Aßmus for (int32 i = 0; i < count && fPath->GetPointAt(i, p); i++) { 1124128277c9SStephan Aßmus if (r.Contains(p)) { 1125128277c9SStephan Aßmus temp.Add(i); 1126128277c9SStephan Aßmus } 1127128277c9SStephan Aßmus } 1128128277c9SStephan Aßmus // merge old and new selection 1129128277c9SStephan Aßmus count = fOldSelection->CountItems(); 1130128277c9SStephan Aßmus for (int32 i = 0; i < count; i++) { 1131128277c9SStephan Aßmus int32 index = fOldSelection->IndexAt(i); 1132128277c9SStephan Aßmus if (temp.Contains(index)) 1133128277c9SStephan Aßmus temp.Remove(index); 1134128277c9SStephan Aßmus else 1135128277c9SStephan Aßmus temp.Add(index); 1136128277c9SStephan Aßmus } 1137128277c9SStephan Aßmus if (temp != *fSelection) { 1138128277c9SStephan Aßmus *fSelection = temp; 1139128277c9SStephan Aßmus _UpdateSelection(); 1140128277c9SStephan Aßmus } 1141128277c9SStephan Aßmus } 1142128277c9SStephan Aßmus 1143128277c9SStephan Aßmus // _Select 1144128277c9SStephan Aßmus void 1145128277c9SStephan Aßmus PathManipulator::_Select(int32 index, bool extend) 1146128277c9SStephan Aßmus { 1147128277c9SStephan Aßmus if (!extend) 1148128277c9SStephan Aßmus fSelection->MakeEmpty(); 1149128277c9SStephan Aßmus if (fSelection->Contains(index)) 1150128277c9SStephan Aßmus fSelection->Remove(index); 1151128277c9SStephan Aßmus else 1152128277c9SStephan Aßmus fSelection->Add(index); 1153128277c9SStephan Aßmus // TODO: this can lead to unnecessary invalidation (maybe need to investigate) 1154128277c9SStephan Aßmus _UpdateSelection(); 1155128277c9SStephan Aßmus } 1156128277c9SStephan Aßmus 1157128277c9SStephan Aßmus // _Select 1158128277c9SStephan Aßmus void 1159128277c9SStephan Aßmus PathManipulator::_Select(const int32* indices, int32 count, bool extend) 1160128277c9SStephan Aßmus { 1161128277c9SStephan Aßmus if (extend) { 1162128277c9SStephan Aßmus for (int32 i = 0; i < count; i++) { 1163128277c9SStephan Aßmus if (!fSelection->Contains(indices[i])) 1164128277c9SStephan Aßmus fSelection->Add(indices[i]); 1165128277c9SStephan Aßmus } 1166128277c9SStephan Aßmus } else { 1167128277c9SStephan Aßmus fSelection->MakeEmpty(); 1168128277c9SStephan Aßmus for (int32 i = 0; i < count; i++) { 1169128277c9SStephan Aßmus fSelection->Add(indices[i]); 1170128277c9SStephan Aßmus } 1171128277c9SStephan Aßmus } 1172128277c9SStephan Aßmus _UpdateSelection(); 1173128277c9SStephan Aßmus } 1174128277c9SStephan Aßmus 1175128277c9SStephan Aßmus // _Deselect 1176128277c9SStephan Aßmus void 1177128277c9SStephan Aßmus PathManipulator::_Deselect(int32 index) 1178128277c9SStephan Aßmus { 1179128277c9SStephan Aßmus if (fSelection->Contains(index)) { 1180128277c9SStephan Aßmus fSelection->Remove(index); 1181128277c9SStephan Aßmus _UpdateSelection(); 1182128277c9SStephan Aßmus } 1183128277c9SStephan Aßmus } 1184128277c9SStephan Aßmus 1185128277c9SStephan Aßmus // _ShiftSelection 1186128277c9SStephan Aßmus void 1187128277c9SStephan Aßmus PathManipulator::_ShiftSelection(int32 startIndex, int32 direction) 1188128277c9SStephan Aßmus { 1189128277c9SStephan Aßmus int32 count = fSelection->CountItems(); 1190128277c9SStephan Aßmus if (count > 0) { 1191128277c9SStephan Aßmus int32* selection = fSelection->Items(); 1192128277c9SStephan Aßmus for (int32 i = 0; i < count; i++) { 1193128277c9SStephan Aßmus if (selection[i] >= startIndex) { 1194128277c9SStephan Aßmus selection[i] += direction; 1195128277c9SStephan Aßmus } 1196128277c9SStephan Aßmus } 1197128277c9SStephan Aßmus } 1198128277c9SStephan Aßmus _UpdateSelection(); 1199128277c9SStephan Aßmus } 1200128277c9SStephan Aßmus 1201128277c9SStephan Aßmus // _IsSelected 1202128277c9SStephan Aßmus bool 1203128277c9SStephan Aßmus PathManipulator::_IsSelected(int32 index) const 1204128277c9SStephan Aßmus { 1205128277c9SStephan Aßmus return fSelection->Contains(index); 1206128277c9SStephan Aßmus } 1207128277c9SStephan Aßmus 1208128277c9SStephan Aßmus // #pragma mark - 1209128277c9SStephan Aßmus 1210128277c9SStephan Aßmus // _InvalidateCanvas 1211128277c9SStephan Aßmus void 1212128277c9SStephan Aßmus PathManipulator::_InvalidateCanvas(BRect rect) const 1213128277c9SStephan Aßmus { 1214f67876a0SStephan Aßmus // convert from canvas to view space 1215f67876a0SStephan Aßmus fCanvasView->ConvertFromCanvas(&rect); 1216128277c9SStephan Aßmus fCanvasView->Invalidate(rect); 1217128277c9SStephan Aßmus } 1218128277c9SStephan Aßmus 1219128277c9SStephan Aßmus // _InvalidateHighlightPoints 1220128277c9SStephan Aßmus void 1221128277c9SStephan Aßmus PathManipulator::_InvalidateHighlightPoints(int32 newIndex, uint32 newMode) 1222128277c9SStephan Aßmus { 1223128277c9SStephan Aßmus BRect oldRect = _ControlPointRect(fCurrentPathPoint, fMode); 1224128277c9SStephan Aßmus BRect newRect = _ControlPointRect(newIndex, newMode); 1225128277c9SStephan Aßmus if (oldRect.IsValid()) 1226128277c9SStephan Aßmus _InvalidateCanvas(oldRect); 1227128277c9SStephan Aßmus if (newRect.IsValid()) 1228128277c9SStephan Aßmus _InvalidateCanvas(newRect); 1229128277c9SStephan Aßmus } 1230128277c9SStephan Aßmus 1231128277c9SStephan Aßmus // _UpdateSelection 1232128277c9SStephan Aßmus void 1233128277c9SStephan Aßmus PathManipulator::_UpdateSelection() const 1234128277c9SStephan Aßmus { 1235128277c9SStephan Aßmus _InvalidateCanvas(_ControlPointRect()); 1236128277c9SStephan Aßmus if (BWindow* window = fCanvasView->Window()) { 1237128277c9SStephan Aßmus window->PostMessage(MSG_UPDATE_SHAPE_UI); 1238128277c9SStephan Aßmus } 1239128277c9SStephan Aßmus } 1240128277c9SStephan Aßmus 1241128277c9SStephan Aßmus // _ControlPointRect 1242128277c9SStephan Aßmus BRect 1243128277c9SStephan Aßmus PathManipulator::_ControlPointRect() const 1244128277c9SStephan Aßmus { 1245128277c9SStephan Aßmus BRect r = fPath->ControlPointBounds(); 1246128277c9SStephan Aßmus r.InsetBy(-POINT_EXTEND, -POINT_EXTEND); 1247128277c9SStephan Aßmus return r; 1248128277c9SStephan Aßmus } 1249128277c9SStephan Aßmus 1250128277c9SStephan Aßmus // _ControlPointRect 1251128277c9SStephan Aßmus BRect 1252128277c9SStephan Aßmus PathManipulator::_ControlPointRect(int32 index, uint32 mode) const 1253128277c9SStephan Aßmus { 1254128277c9SStephan Aßmus BRect rect(0.0, 0.0, -1.0, -1.0); 1255128277c9SStephan Aßmus if (index >= 0) { 1256128277c9SStephan Aßmus BPoint p, pIn, pOut; 1257128277c9SStephan Aßmus fPath->GetPointsAt(index, p, pIn, pOut); 1258128277c9SStephan Aßmus switch (mode) { 1259128277c9SStephan Aßmus case MOVE_POINT: 1260128277c9SStephan Aßmus case TOGGLE_SHARP: 1261128277c9SStephan Aßmus case REMOVE_POINT: 1262128277c9SStephan Aßmus case CLOSE_PATH: 1263128277c9SStephan Aßmus rect.Set(p.x, p.y, p.x, p.y); 1264128277c9SStephan Aßmus rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND); 1265128277c9SStephan Aßmus break; 1266128277c9SStephan Aßmus case MOVE_POINT_IN: 1267128277c9SStephan Aßmus case TOGGLE_SHARP_IN: 1268128277c9SStephan Aßmus case REMOVE_POINT_IN: 1269128277c9SStephan Aßmus rect.Set(pIn.x, pIn.y, pIn.x, pIn.y); 1270128277c9SStephan Aßmus rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND); 1271128277c9SStephan Aßmus break; 1272128277c9SStephan Aßmus case MOVE_POINT_OUT: 1273128277c9SStephan Aßmus case TOGGLE_SHARP_OUT: 1274128277c9SStephan Aßmus case REMOVE_POINT_OUT: 1275128277c9SStephan Aßmus rect.Set(pOut.x, pOut.y, pOut.x, pOut.y); 1276128277c9SStephan Aßmus rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND); 1277128277c9SStephan Aßmus break; 1278128277c9SStephan Aßmus case SELECT_POINTS: 1279128277c9SStephan Aßmus rect.Set(min4(p.x, pIn.x, pOut.x, pOut.x), 1280128277c9SStephan Aßmus min4(p.y, pIn.y, pOut.y, pOut.y), 1281128277c9SStephan Aßmus max4(p.x, pIn.x, pOut.x, pOut.x), 1282128277c9SStephan Aßmus max4(p.y, pIn.y, pOut.y, pOut.y)); 1283128277c9SStephan Aßmus rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND); 1284128277c9SStephan Aßmus break; 1285128277c9SStephan Aßmus } 1286128277c9SStephan Aßmus } 1287128277c9SStephan Aßmus return rect; 1288128277c9SStephan Aßmus } 1289128277c9SStephan Aßmus 1290128277c9SStephan Aßmus // #pragma mark - 1291128277c9SStephan Aßmus 1292128277c9SStephan Aßmus // _SetModeForMousePos 1293128277c9SStephan Aßmus void 1294128277c9SStephan Aßmus PathManipulator::_SetModeForMousePos(BPoint where) 1295128277c9SStephan Aßmus { 1296128277c9SStephan Aßmus uint32 mode = UNDEFINED; 1297128277c9SStephan Aßmus int32 index = -1; 1298128277c9SStephan Aßmus 1299f67876a0SStephan Aßmus float zoomLevel = fCanvasView->ZoomLevel(); 1300128277c9SStephan Aßmus 1301128277c9SStephan Aßmus // see if we're close enough at a control point 1302128277c9SStephan Aßmus BPoint point; 1303128277c9SStephan Aßmus BPoint pointIn; 1304128277c9SStephan Aßmus BPoint pointOut; 1305128277c9SStephan Aßmus for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut) 1306128277c9SStephan Aßmus && mode == UNDEFINED; i++) { 1307128277c9SStephan Aßmus 1308128277c9SStephan Aßmus float distM = point_point_distance(point, where) * zoomLevel; 1309128277c9SStephan Aßmus float distIn = point_point_distance(pointIn, where) * zoomLevel; 1310128277c9SStephan Aßmus float distOut = point_point_distance(pointOut, where) * zoomLevel; 1311128277c9SStephan Aßmus 1312128277c9SStephan Aßmus if (distM < MOVE_THRESHOLD) { 1313128277c9SStephan Aßmus if (i == 0 && fClickToClose 1314128277c9SStephan Aßmus && !fPath->IsClosed() && fPath->CountPoints() > 1) { 1315128277c9SStephan Aßmus mode = fCommandDown ? TOGGLE_SHARP : 1316128277c9SStephan Aßmus (fOptionDown ? REMOVE_POINT : CLOSE_PATH); 1317128277c9SStephan Aßmus index = i; 1318128277c9SStephan Aßmus } else { 1319128277c9SStephan Aßmus mode = fCommandDown ? TOGGLE_SHARP : 1320128277c9SStephan Aßmus (fOptionDown ? REMOVE_POINT : MOVE_POINT); 1321128277c9SStephan Aßmus index = i; 1322128277c9SStephan Aßmus } 1323128277c9SStephan Aßmus } 1324128277c9SStephan Aßmus if (distIn < distM && distIn < MOVE_THRESHOLD) { 1325128277c9SStephan Aßmus mode = fCommandDown ? TOGGLE_SHARP_IN : 1326128277c9SStephan Aßmus (fOptionDown ? REMOVE_POINT_IN : MOVE_POINT_IN); 1327128277c9SStephan Aßmus index = i; 1328128277c9SStephan Aßmus } 1329128277c9SStephan Aßmus if (distOut < distIn && distOut < distM && distOut < MOVE_THRESHOLD) { 1330128277c9SStephan Aßmus mode = fCommandDown ? TOGGLE_SHARP_OUT : 1331128277c9SStephan Aßmus (fOptionDown ? REMOVE_POINT_OUT : MOVE_POINT_OUT); 1332128277c9SStephan Aßmus index = i; 1333128277c9SStephan Aßmus } 1334128277c9SStephan Aßmus } 1335128277c9SStephan Aßmus // selection mode overrides any other mode, 1336128277c9SStephan Aßmus // but we need to check for it after we know 1337128277c9SStephan Aßmus // the index of the point under the mouse (code above) 1338128277c9SStephan Aßmus int32 pointCount = fPath->CountPoints(); 1339128277c9SStephan Aßmus if (fShiftDown && pointCount > 0) { 1340128277c9SStephan Aßmus mode = SELECT_POINTS; 1341128277c9SStephan Aßmus } 1342128277c9SStephan Aßmus 1343128277c9SStephan Aßmus // see if user wants to start new sub path 1344128277c9SStephan Aßmus if (fAltDown) { 1345128277c9SStephan Aßmus mode = NEW_PATH; 1346128277c9SStephan Aßmus index = -1; 1347128277c9SStephan Aßmus } 1348128277c9SStephan Aßmus 1349128277c9SStephan Aßmus // see if we're close enough at a line 1350128277c9SStephan Aßmus if (mode == UNDEFINED) { 1351128277c9SStephan Aßmus float distance; 1352128277c9SStephan Aßmus if (fPath->GetDistance(where, &distance, &index)) { 1353128277c9SStephan Aßmus if (distance < (INSERT_DIST_THRESHOLD / zoomLevel)) { 1354128277c9SStephan Aßmus mode = INSERT_POINT; 1355128277c9SStephan Aßmus } 1356128277c9SStephan Aßmus } else { 1357128277c9SStephan Aßmus // restore index, since it was changed by call above 1358128277c9SStephan Aßmus index = fCurrentPathPoint; 1359128277c9SStephan Aßmus } 1360128277c9SStephan Aßmus } 1361128277c9SStephan Aßmus 1362128277c9SStephan Aßmus // nope, still undefined mode, last fall back 1363128277c9SStephan Aßmus if (mode == UNDEFINED) { 1364128277c9SStephan Aßmus if (fFallBackMode == SELECT_POINTS) { 1365128277c9SStephan Aßmus if (fPath->IsClosed() && pointCount > 0) { 1366128277c9SStephan Aßmus mode = SELECT_POINTS; 1367128277c9SStephan Aßmus index = -1; 1368128277c9SStephan Aßmus } else { 1369128277c9SStephan Aßmus mode = ADD_POINT; 1370128277c9SStephan Aßmus index = pointCount - 1; 1371128277c9SStephan Aßmus } 1372128277c9SStephan Aßmus } else { 1373128277c9SStephan Aßmus // user had clicked "New Path" icon 1374128277c9SStephan Aßmus mode = fFallBackMode; 1375128277c9SStephan Aßmus } 1376128277c9SStephan Aßmus } 1377128277c9SStephan Aßmus // switch mode if necessary 1378128277c9SStephan Aßmus if (mode != fMode || index != fCurrentPathPoint) { 1379128277c9SStephan Aßmus // invalidate path display (to highlight the respective point) 1380128277c9SStephan Aßmus _InvalidateHighlightPoints(index, mode); 1381128277c9SStephan Aßmus _SetMode(mode); 1382128277c9SStephan Aßmus fCurrentPathPoint = index; 1383128277c9SStephan Aßmus } 1384128277c9SStephan Aßmus } 1385128277c9SStephan Aßmus 1386128277c9SStephan Aßmus // #pragma mark - 1387128277c9SStephan Aßmus 1388128277c9SStephan Aßmus // _Nudge 1389128277c9SStephan Aßmus void 1390128277c9SStephan Aßmus PathManipulator::_Nudge(BPoint direction) 1391128277c9SStephan Aßmus { 1392128277c9SStephan Aßmus bigtime_t now = system_time(); 1393128277c9SStephan Aßmus if (now - fLastNudgeTime > 500000) { 1394128277c9SStephan Aßmus _FinishNudging(); 1395128277c9SStephan Aßmus } 1396128277c9SStephan Aßmus fLastNudgeTime = now; 1397128277c9SStephan Aßmus fNudgeOffset += direction; 1398128277c9SStephan Aßmus 1399128277c9SStephan Aßmus // if (!fNudgeCommand) { 1400128277c9SStephan Aßmus // 1401128277c9SStephan Aßmus // bool fromSelection = !fSelection->IsEmpty(); 1402128277c9SStephan Aßmus // 1403128277c9SStephan Aßmus // int32 count = fromSelection ? fSelection->CountItems() 1404128277c9SStephan Aßmus // : fPath->CountPoints(); 1405128277c9SStephan Aßmus // int32* indices = new int32[count]; 1406128277c9SStephan Aßmus // control_point* points = new control_point[count]; 1407128277c9SStephan Aßmus // 1408128277c9SStephan Aßmus // // init indices and points 1409128277c9SStephan Aßmus // for (int32 i = 0; i < count; i++) { 1410128277c9SStephan Aßmus // indices[i] = fromSelection ? fSelection->IndexAt(i) : i; 1411128277c9SStephan Aßmus // fPath->GetPointsAt(indices[i], 1412128277c9SStephan Aßmus // points[i].point, 1413128277c9SStephan Aßmus // points[i].point_in, 1414128277c9SStephan Aßmus // points[i].point_out, 1415128277c9SStephan Aßmus // &points[i].connected); 1416128277c9SStephan Aßmus // } 1417128277c9SStephan Aßmus // 1418128277c9SStephan Aßmus // fNudgeCommand = new NudgePointsCommand(this, fPath, 1419128277c9SStephan Aßmus // indices, points, count); 1420128277c9SStephan Aßmus // 1421128277c9SStephan Aßmus // fNudgeCommand->SetNewTranslation(fNudgeOffset); 1422128277c9SStephan Aßmus // fNudgeCommand->Redo(fCanvasView); 1423128277c9SStephan Aßmus // 1424128277c9SStephan Aßmus // delete[] indices; 1425128277c9SStephan Aßmus // delete[] points; 1426128277c9SStephan Aßmus // 1427128277c9SStephan Aßmus // } else { 1428128277c9SStephan Aßmus // fNudgeCommand->SetNewTranslation(fNudgeOffset); 1429128277c9SStephan Aßmus // fNudgeCommand->Redo(fCanvasView); 1430128277c9SStephan Aßmus // } 1431128277c9SStephan Aßmus 1432128277c9SStephan Aßmus if (!fMouseDown) 1433128277c9SStephan Aßmus _SetModeForMousePos(fLastCanvasPos); 1434128277c9SStephan Aßmus } 1435128277c9SStephan Aßmus 1436128277c9SStephan Aßmus // _FinishNudging 1437128277c9SStephan Aßmus void 1438128277c9SStephan Aßmus PathManipulator::_FinishNudging() 1439128277c9SStephan Aßmus { 1440128277c9SStephan Aßmus fNudgeOffset = BPoint(0.0, 0.0); 1441128277c9SStephan Aßmus 1442128277c9SStephan Aßmus // if (fNudgeCommand) { 1443128277c9SStephan Aßmus // fCanvasView->Perform(fNudgeCommand); 1444128277c9SStephan Aßmus // fNudgeCommand = NULL; 1445128277c9SStephan Aßmus // } 1446128277c9SStephan Aßmus } 1447128277c9SStephan Aßmus 1448128277c9SStephan Aßmus 1449