Fix Issue 158: Problems with redo/undo and rotated elements. Refactor of code for Issue 170.
git-svn-id: http://svg-edit.googlecode.com/svn/trunk@553 eee81c28-f429-11dd-99c0-75d572ba1dddmaster
parent
9ead34fca1
commit
c6847f23ad
|
@ -37,6 +37,9 @@ var svgWhiteList = {
|
||||||
"text": ["fill", "fill-opacity", "font-family", "font-size", "font-style", "font-weight", "id", "opacity", "stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-opacity", "stroke-width", "transform", "text-anchor", "x", "y"],
|
"text": ["fill", "fill-opacity", "font-family", "font-size", "font-style", "font-weight", "id", "opacity", "stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-opacity", "stroke-width", "transform", "text-anchor", "x", "y"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function SvgCanvas(c)
|
||||||
|
{
|
||||||
|
|
||||||
// These command objects are used for the Undo/Redo stack
|
// These command objects are used for the Undo/Redo stack
|
||||||
// attrs contains the values that the attributes had before the change
|
// attrs contains the values that the attributes had before the change
|
||||||
function ChangeElementCommand(elem, attrs, text) {
|
function ChangeElementCommand(elem, attrs, text) {
|
||||||
|
@ -60,6 +63,19 @@ function ChangeElementCommand(elem, attrs, text) {
|
||||||
else this.elem.removeAttribute(attr);
|
else this.elem.removeAttribute(attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// relocate rotational transform, if necessary
|
||||||
|
if (attr != "transform") {
|
||||||
|
var angle = canvas.getRotationAngle(elem);
|
||||||
|
if (angle) {
|
||||||
|
var bbox = elem.getBBox();
|
||||||
|
var cx = bbox.x + bbox.width/2,
|
||||||
|
cy = bbox.y + bbox.height/2;
|
||||||
|
var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
|
||||||
|
if (rotate != elem.getAttribute("transform")) {
|
||||||
|
elem.setAttribute("transform", rotate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,6 +90,19 @@ function ChangeElementCommand(elem, attrs, text) {
|
||||||
else this.elem.removeAttribute(attr);
|
else this.elem.removeAttribute(attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// relocate rotational transform, if necessary
|
||||||
|
if (attr != "transform") {
|
||||||
|
var angle = canvas.getRotationAngle(elem);
|
||||||
|
if (angle) {
|
||||||
|
var bbox = elem.getBBox();
|
||||||
|
var cx = bbox.x + bbox.width/2,
|
||||||
|
cy = bbox.y + bbox.height/2;
|
||||||
|
var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
|
||||||
|
if (rotate != elem.getAttribute("transform")) {
|
||||||
|
elem.setAttribute("transform", rotate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -170,8 +199,6 @@ function BatchCommand(text) {
|
||||||
this.isEmpty = function() { return this.stack.length == 0; };
|
this.isEmpty = function() { return this.stack.length == 0; };
|
||||||
}
|
}
|
||||||
|
|
||||||
function SvgCanvas(c)
|
|
||||||
{
|
|
||||||
// private members
|
// private members
|
||||||
|
|
||||||
// **************************************************************************************
|
// **************************************************************************************
|
||||||
|
@ -1300,7 +1327,6 @@ function SvgCanvas(c)
|
||||||
case "line":
|
case "line":
|
||||||
started = true;
|
started = true;
|
||||||
var stroke_w = cur_shape.stroke_width == 0?1:cur_shape.stroke_width;
|
var stroke_w = cur_shape.stroke_width == 0?1:cur_shape.stroke_width;
|
||||||
console.log(stroke_w);
|
|
||||||
addSvgElementFromJson({
|
addSvgElementFromJson({
|
||||||
"element": "line",
|
"element": "line",
|
||||||
"attr": {
|
"attr": {
|
||||||
|
@ -1409,7 +1435,8 @@ function SvgCanvas(c)
|
||||||
break;
|
break;
|
||||||
case "rotate":
|
case "rotate":
|
||||||
started = true;
|
started = true;
|
||||||
canvas.setRotationAngle(canvas.getRotationAngle());
|
// we are starting an undoable change (a drag-rotation)
|
||||||
|
canvas.beginUndoableChange("transform", selectedElements);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("Unknown mode in mousedown: " + current_mode);
|
console.log("Unknown mode in mousedown: " + current_mode);
|
||||||
|
@ -1718,10 +1745,9 @@ function SvgCanvas(c)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "rotate":
|
case "rotate":
|
||||||
canvas.dragging = true;
|
|
||||||
var box = canvas.getBBox(selected),cx = box.x + box.width/2, cy = box.y + box.height/2;
|
var box = canvas.getBBox(selected),cx = box.x + box.width/2, cy = box.y + box.height/2;
|
||||||
var angle = parseInt(((Math.atan2(cy-y,cx-x) * (180/Math.PI))-90) % 360);
|
var angle = parseInt(((Math.atan2(cy-y,cx-x) * (180/Math.PI))-90) % 360);
|
||||||
canvas.setRotationAngle(angle<-180?(360+angle):angle);
|
canvas.setRotationAngle(angle<-180?(360+angle):angle, true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1811,7 +1837,6 @@ function SvgCanvas(c)
|
||||||
started = false;
|
started = false;
|
||||||
var element = svgdoc.getElementById(getId());
|
var element = svgdoc.getElementById(getId());
|
||||||
var keep = false;
|
var keep = false;
|
||||||
canvas.dragging = false;
|
|
||||||
switch (current_mode)
|
switch (current_mode)
|
||||||
{
|
{
|
||||||
// intentionally fall-through to select here
|
// intentionally fall-through to select here
|
||||||
|
@ -2190,6 +2215,11 @@ function SvgCanvas(c)
|
||||||
keep = true;
|
keep = true;
|
||||||
element = null;
|
element = null;
|
||||||
current_mode = "select";
|
current_mode = "select";
|
||||||
|
canvas.setRotationAngle(canvas.getRotationAngle(), true);
|
||||||
|
var batchCmd = canvas.finishUndoableChange();
|
||||||
|
if (!batchCmd.isEmpty()) {
|
||||||
|
addCommandToHistory(batchCmd);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("Unknown mode in mouseup: " + current_mode);
|
console.log("Unknown mode in mouseup: " + current_mode);
|
||||||
|
@ -2563,15 +2593,19 @@ function SvgCanvas(c)
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setRotationAngle = function(val) {
|
this.setRotationAngle = function(val,preventUndo) {
|
||||||
var elem = selectedElements[0];
|
var elem = selectedElements[0];
|
||||||
// we use the actual element's bbox (not the calculated one) since the
|
// we use the actual element's bbox (not the calculated one) since the
|
||||||
// calculated bbox's center can change depending on the angle
|
// calculated bbox's center can change depending on the angle
|
||||||
var bbox = elem.getBBox(); //this.getBBox(elem);
|
var bbox = elem.getBBox();
|
||||||
var rotate = "rotate(" + val + " " +
|
var cx = (bbox.x+bbox.width/2), cy = (bbox.y+bbox.height/2);
|
||||||
(bbox.x+bbox.width/2) + "," +
|
var rotate = "rotate(" + val + " " + cx + "," + cy + ")";
|
||||||
(bbox.y+bbox.height/2) + ")";
|
if (preventUndo) {
|
||||||
this.changeSelectedAttribute("transform", rotate);
|
this.changeSelectedAttributeNoUndo("transform", rotate, selectedElements);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.changeSelectedAttribute("transform",rotate,selectedElements);
|
||||||
|
}
|
||||||
var pointGripContainer = document.getElementById("polypointgrip_container");
|
var pointGripContainer = document.getElementById("polypointgrip_container");
|
||||||
if(elem.nodeName == "path" && pointGripContainer) {
|
if(elem.nodeName == "path" && pointGripContainer) {
|
||||||
pointGripContainer.setAttribute("transform", rotate);
|
pointGripContainer.setAttribute("transform", rotate);
|
||||||
|
@ -2684,27 +2718,49 @@ function SvgCanvas(c)
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you want to change all selectedElements, ignore the elems argument.
|
// New functions for refactoring of Undo/Redo
|
||||||
// If you want to change only a subset of selectedElements, then send the
|
|
||||||
// subset to this function in the elems argument.
|
// this is the stack that stores the original values, the elements and
|
||||||
this.changeSelectedAttribute = function(attr, val, elems) {
|
// the attribute name for begin/finish
|
||||||
var elems = elems || selectedElements;
|
var undoChangeStackPointer = -1;
|
||||||
var batchCmd = new BatchCommand("Change " + attr);
|
var undoableChangeStack = [];
|
||||||
|
|
||||||
|
// This function tells the canvas to remember the old values of the
|
||||||
|
// attrName attribute for each element sent in. The elements and values
|
||||||
|
// are stored on a stack, so the next call to finishUndoableChange() will
|
||||||
|
// pop the elements and old values off the stack, gets the current values
|
||||||
|
// from the DOM and uses all of these to construct the undo-able command.
|
||||||
|
this.beginUndoableChange = function(attrName, elems) {
|
||||||
|
var p = ++undoChangeStackPointer;
|
||||||
var i = elems.length;
|
var i = elems.length;
|
||||||
var handle = svgroot.suspendRedraw(1000);
|
var oldValues = new Array(i), elements = new Array(i);
|
||||||
while (i--) {
|
while (i--) {
|
||||||
var elem = elems[i];
|
var elem = elems[i];
|
||||||
if (elem == null) continue;
|
if (elem == null) continue;
|
||||||
|
elements[i] = elem;
|
||||||
|
oldValues[i] = elem.getAttribute(attrName);
|
||||||
|
}
|
||||||
|
undoableChangeStack[p] = {'attrName': attrName,
|
||||||
|
'oldValues': oldValues,
|
||||||
|
'elements': elements};
|
||||||
|
};
|
||||||
|
|
||||||
var oldval = (attr == "#text" ? elem.textContent : elem.getAttribute(attr));
|
// This function makes the changes to the elements and then
|
||||||
// if the attribute was currently not set, store an empty string (cannot store null)
|
// fires the 'changed' event
|
||||||
if (oldval == null) { oldval = ""; }
|
this.changeSelectedAttributeNoUndo = function(attr, newValue, elems) {
|
||||||
if (oldval != val) {
|
var handle = svgroot.suspendRedraw(1000);
|
||||||
|
var i = elems.length;
|
||||||
|
while (i--) {
|
||||||
|
var elem = elems[i];
|
||||||
|
if (elem == null) continue;
|
||||||
|
var oldval = attr == "#text" ? elem.textContent : elem.getAttribute(attr);
|
||||||
|
if (oldval == null) oldval = "";
|
||||||
|
if (oldval != newValue) {
|
||||||
if (attr == "#text") {
|
if (attr == "#text") {
|
||||||
elem.textContent = val;
|
elem.textContent = newValue;
|
||||||
elem = canvas.quickClone(elem);
|
elem = canvas.quickClone(elem);
|
||||||
}
|
}
|
||||||
else elem.setAttribute(attr, val);
|
else elem.setAttribute(attr, newValue);
|
||||||
selectedBBoxes[i] = this.getBBox(elem);
|
selectedBBoxes[i] = this.getBBox(elem);
|
||||||
// Use the Firefox quickClone hack for text elements with gradients or
|
// Use the Firefox quickClone hack for text elements with gradients or
|
||||||
// where other text attributes are changed.
|
// where other text attributes are changed.
|
||||||
|
@ -2717,12 +2773,8 @@ function SvgCanvas(c)
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
selectorManager.requestSelector(elem).resize(selectedBBoxes[i]);
|
selectorManager.requestSelector(elem).resize(selectedBBoxes[i]);
|
||||||
},0);
|
},0);
|
||||||
var changes = {};
|
|
||||||
changes[attr] = oldval;
|
|
||||||
|
|
||||||
// if this element was rotated, and we changed the position of this element
|
// if this element was rotated, and we changed the position of this element
|
||||||
// we need to update the rotational transform attribute and store it as part
|
// we need to update the rotational transform attribute
|
||||||
// of our changeset
|
|
||||||
var angle = canvas.getRotationAngle(elem);
|
var angle = canvas.getRotationAngle(elem);
|
||||||
if (angle && attr != "transform") {
|
if (angle && attr != "transform") {
|
||||||
var cx = selectedBBoxes[i].x + selectedBBoxes[i].width/2,
|
var cx = selectedBBoxes[i].x + selectedBBoxes[i].width/2,
|
||||||
|
@ -2730,16 +2782,49 @@ function SvgCanvas(c)
|
||||||
var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
|
var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
|
||||||
if (rotate != elem.getAttribute("transform")) {
|
if (rotate != elem.getAttribute("transform")) {
|
||||||
elem.setAttribute("transform", rotate);
|
elem.setAttribute("transform", rotate);
|
||||||
changes['transform'] = rotate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
batchCmd.addSubCommand(new ChangeElementCommand(elem, changes, attr));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // if oldValue != newValue
|
||||||
|
} // for each elem
|
||||||
svgroot.unsuspendRedraw(handle);
|
svgroot.unsuspendRedraw(handle);
|
||||||
if (!batchCmd.isEmpty()) {
|
|
||||||
if(!canvas.dragging) addCommandToHistory(batchCmd);
|
|
||||||
call("changed", elems);
|
call("changed", elems);
|
||||||
|
};
|
||||||
|
|
||||||
|
// This function returns a BatchCommand object which summarizes the
|
||||||
|
// change since beginUndoableChange was called. The command can then
|
||||||
|
// be added to the command history
|
||||||
|
this.finishUndoableChange = function() {
|
||||||
|
var p = undoChangeStackPointer--;
|
||||||
|
var changeset = undoableChangeStack[p];
|
||||||
|
var i = changeset['elements'].length;
|
||||||
|
var attrName = changeset['attrName'];
|
||||||
|
var batchCmd = new BatchCommand("Change " + attrName);
|
||||||
|
while (i--) {
|
||||||
|
var elem = changeset['elements'][i];
|
||||||
|
if (elem == null) continue;
|
||||||
|
var changes = {};
|
||||||
|
changes[attrName] = changeset['oldValues'][i];
|
||||||
|
if (changes[attrName] != elem.getAttribute(attrName)) {
|
||||||
|
batchCmd.addSubCommand(new ChangeElementCommand(elem, changes, attrName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
undoableChangeStack[p] = null;
|
||||||
|
return batchCmd;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If you want to change all selectedElements, ignore the elems argument.
|
||||||
|
// If you want to change only a subset of selectedElements, then send the
|
||||||
|
// subset to this function in the elems argument.
|
||||||
|
this.changeSelectedAttribute = function(attr, val, elems) {
|
||||||
|
var elems = elems || selectedElements;
|
||||||
|
canvas.beginUndoableChange(attr, elems);
|
||||||
|
var i = elems.length;
|
||||||
|
|
||||||
|
canvas.changeSelectedAttributeNoUndo(attr, val, elems);
|
||||||
|
|
||||||
|
var batchCmd = canvas.finishUndoableChange();
|
||||||
|
if (!batchCmd.isEmpty()) {
|
||||||
|
addCommandToHistory(batchCmd);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue