diff --git a/src/item/Item.js b/src/item/Item.js index ea71eebd..2a043a20 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -279,7 +279,7 @@ var Item = this.Item = Base.extend({ * Removes the item. */ remove: function() { - if(this._selected) + if(this.getSelected()) this.setSelected(false); return this.removeFromParent(); }, diff --git a/src/path/Path.js b/src/path/Path.js index e3f678d4..09b00920 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -20,6 +20,7 @@ var Path = this.Path = PathItem.extend({ initialize: function(segments) { this.base(); this.closed = false; + this._selectedSegmentCount = 0; // Support both passing of segments as array or arguments // If it is an array, it can also be a description of a point, so // check its first entry for object as well @@ -128,6 +129,37 @@ var Path = this.Path = PathItem.extend({ } }, + getSelected: function() { + return this._selectedSegmentCount > 0; + }, + + setSelected: function(selected) { + var wasSelected = this.getSelected(); + var length = this._segments.length; + if (wasSelected != selected && length) { + var selectedItems = this._document._selectedItems; + if (selected) { + selectedItems.push(this); + } else { + // TODO: is there a faster way? + var index = selectedItems.indexOf(this); + if (index != -1) + selectedItems.splice(index, 1); + } + } + this._selectedSegmentCount = selected ? length : 0; + for (var i = 0; i < length; i++) + this._segments[i]._selectionState = selected ? 'point' : null; + }, + + isFullySelected: function() { + return this._selectedSegmentCount == this._segments.length; + }, + + setFullySelected: function(selected) { + this.setSelected(selected); + }, + // TODO: pointsToCurves([tolerance[, threshold[, cornerRadius[, scale]]]]) // TODO: curvesToPoints([maxPointDistance[, flatness]]) // TODO: reduceSegments([flatness]) @@ -254,23 +286,28 @@ var Path = this.Path = PathItem.extend({ function drawHandles(ctx, segments) { for (var i = 0, l = segments.length; i < l; i++) { var segment = segments[i], - point = segment._point; + point = segment._point, + pointSelected = segment._selectionState == 'point'; // TODO: draw handles depending on selection state of // segment.point and neighbouring segments. - drawHandle(ctx, point, segment._handleIn); - drawHandle(ctx, point, segment._handleOut); + if (pointSelected || segment.getSelected(segment._handleIn)) + drawHandle(ctx, point, segment._handleIn); + if (pointSelected || segment.getSelected(segment._handleOut)) + drawHandle(ctx, point, segment._handleOut); // Draw a rectangle at segment.point: ctx.save(); ctx.beginPath(); ctx.rect(point._x - 2, point._y - 2, 4, 4); ctx.fill(); - // TODO: Only draw white rectangle if point.isSelected() + // TODO: Only draw white rectangle if point.getSelected() // is false: - ctx.beginPath(); - ctx.rect(point._x - 1, point._y - 1, 2, 2); - ctx.fillStyle = '#ffffff'; - ctx.fill(); - ctx.restore(); + if (!pointSelected) { + ctx.beginPath(); + ctx.rect(point._x - 1, point._y - 1, 2, 2); + ctx.fillStyle = '#ffffff'; + ctx.fill(); + ctx.restore(); + } } } diff --git a/src/path/Segment.js b/src/path/Segment.js index 35e27cec..d25044c9 100644 --- a/src/path/Segment.js +++ b/src/path/Segment.js @@ -125,12 +125,112 @@ var Segment = this.Segment = Base.extend({ return this._path && this._path._segments[this.getIndex() - 1] || null; }, - // TODO: - // isSelected: function() { - // - // } - // - // setSelected: function(pt, selected) + getSelected: function(/* point */) { + var point = arguments.length ? arguments[0] : this.point; + var state = this._selectionState; + if (point == this.point) { + return state == 'point'; + } else if (point == this.handleIn) { + return state == 'handle-in' || state == 'handle-both'; + } else if (point == this.handleOut) { + return state == 'handle-out' || state == 'handle-both'; + } + return false; + }, + + setSelected: function(/* pt, selected */) { + var pt, selected; + if (arguments.length == 2) { + // setSelected(pt, selected) + pt = arguments[0]; + selected = arguments[1]; + } else { + // setSelected(selected) + pt = this._point; + selected = arguments[0]; + } + if (!this._path) + return; + var wasSelected = !!this._selectionState; + var state = this._selectionState, + pointSelected = state == 'point', + handleInSelected = state == 'handle-in' + || state == 'handle-both', + handleOutSelected = state == 'handle-out' + || state == 'handle-both', + previous = this.getPrevious(), + next = this.getNext(), + closed = this._path.closed, + segments = this._path._segments, + length = segments.length; + if (length > 1 && closed) { + if (previous == null) + previous = segments[length - 1]; + if (next == null) + next = segments[0]; + } + if (pt == this._point) { + if (pointSelected != selected) { + if (selected) { + handleInSelected = handleOutSelected = false; + } else { + // When deselecting a point, the handles get selected + // instead depending on the selection state of their + // neighbors. + handleInSelected = previous != null + && (previous._point.getSelected() + || previous._handleOut.getSelected()); + handleOutSelected = next != null + && (next._point.getSelected() + || next._handleOut.getSelected()); + } + pointSelected = selected; + } + } else if (pt == this._handleIn) { + if (handleInSelected != selected) { + // When selecting handles, the point get deselected. + if (selected) + pointSelected = false; + handleInSelected = selected; + } + } else if (pt == this._handleOut) { + if (handleOutSelected != selected) { + // When selecting handles, the point get deselected. + if (selected) + pointSelected = false; + handleOutSelected = selected; + } + } + this._selectionState = pointSelected + ? 'point' + : handleInSelected + ? handleOutSelected + ? 'handle-both' + : 'handle-in' + : handleOutSelected + ? 'handle-out' + : null; + // If the selection state of the segment has changed, we need to let + // it's path know and possibly add or remove it from + // document._selectedItems + if (wasSelected == !this._selectionState) { + var path = this._path, + selectedItems = path._document._selectedItems; + if (!this._selectionState) { + path._selectedSegmentCount--; + if (path._selectedSegmentCount == 0) { + var index = selectedItems.indexOf(this); + selectedItems.slice(index, 1); + } + } else { + path._selectedSegmentCount++; + if (path._selectedSegmentCount == 1) { + selectedItems.push(path); + } + } + } + + }, reverse: function() { return new Segment(this._point, this._handleOut, this._handleIn); @@ -141,8 +241,12 @@ var Segment = this.Segment = Base.extend({ }, remove: function() { - if (this._path && this._path._segments) - return !!this._path._segments.splice(this.getIndex(), 1).length; + if (this._path) { + this._path._segments.splice(this.getIndex(), 1); + if (this.getSelected()) + this._path._selectedSegmentCount--; + return true; + } return false; }, diff --git a/src/path/SegmentPoint.js b/src/path/SegmentPoint.js index c3659939..4b2ec0f4 100644 --- a/src/path/SegmentPoint.js +++ b/src/path/SegmentPoint.js @@ -29,7 +29,15 @@ var SegmentPoint = Point.extend({ this._y = y; // this._segment._markDirty(DirtyFlags.BOUNDS); }, - + + setSelected: function(selected) { + this._segment.setSelected(this, selected); + }, + + getSelected: function() { + return this._segment.getSelected(this); + }, + statics: { create: function(segment, arg1, arg2) { var point;