diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js
index 35960de1..97bcec44 100644
--- a/editor/svgcanvas.js
+++ b/editor/svgcanvas.js
@@ -2808,7 +2808,7 @@ var getBBox = this.getBBox = function(elem) {
// Parameters:
// elem - The (text) DOM element to clone
var ffClone = function(elem) {
- if(isGecko) return elem;
+ if(!isGecko) return elem;
var clone = elem.cloneNode(true)
elem.parentNode.insertBefore(clone, elem);
elem.parentNode.removeChild(elem);
@@ -5575,8 +5575,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) {
// this (similar to editing rotated paths)
// Ungroup and regroup
- canvas.ungroupSelectedElement();
- canvas.groupSelectedElements();
+ pushGroupProperties(mouse_target);
mouse_target = selectedElements[0];
clearSelection(true);
}
@@ -8518,8 +8517,9 @@ var convertToGroup = this.convertToGroup = function(elem) {
// Temporary hack to get rid of matrix
// TODO: See what ungroupSelectedElement does to absorb matrix
- canvas.ungroupSelectedElement();
- canvas.groupSelectedElements();
+// canvas.ungroupSelectedElement();
+// canvas.groupSelectedElements();
+ batchCmd.addSubCommand(pushGroupProperties(g, true));
//
addCommandToHistory(batchCmd);
@@ -10838,6 +10838,187 @@ this.groupSelectedElements = function() {
selectOnly([g], true);
};
+
+// Function: pushGroupProperties
+// Pushes all appropriate parent group properties down to its children, then
+// removes them from the group
+var pushGroupProperties = this.pushGroupProperties = function(g, undoable) {
+
+ var children = g.childNodes;
+ var len = children.length;
+ var xform = g.getAttribute("transform");
+
+ var glist = getTransformList(g);
+ var m = transformListToTransform(glist).matrix;
+
+ var batchCmd = new BatchCommand("Push group properties");
+
+ // TODO: get all fill/stroke properties from the group that we are about to destroy
+ // "fill", "fill-opacity", "fill-rule", "stroke", "stroke-dasharray", "stroke-dashoffset",
+ // "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity",
+ // "stroke-width"
+ // and then for each child, if they do not have the attribute (or the value is 'inherit')
+ // then set the child's attribute
+
+ var i = 0;
+ var gangle = getRotationAngle(g);
+
+ var gattrs = $(g).attr(['filter', 'opacity']);
+ var gfilter, gblur;
+
+ for(var i = 0; i < len; i++) {
+ var elem = children[i];
+
+ if(gattrs.opacity !== null && gattrs.opacity !== 1) {
+ var c_opac = elem.getAttribute('opacity') || 1;
+ var new_opac = Math.round((elem.getAttribute('opacity') || 1) * gattrs.opacity * 100)/100;
+ changeSelectedAttribute('opacity', new_opac, [elem]);
+ }
+
+ if(gattrs.filter) {
+ var cblur = this.getBlur(elem);
+ var orig_cblur = cblur;
+ if(!gblur) gblur = this.getBlur(g);
+ if(cblur) {
+ // Is this formula correct?
+ cblur = (gblur-0) + (cblur-0);
+ } else if(cblur === 0) {
+ cblur = gblur;
+ }
+
+ // If child has no current filter, get group's filter or clone it.
+ if(!orig_cblur) {
+ // Set group's filter to use first child's ID
+ if(!gfilter) {
+ gfilter = getRefElem(gattrs.filter);
+ } else {
+ // Clone the group's filter
+ gfilter = copyElem(gfilter);
+ findDefs().appendChild(gfilter);
+ }
+ } else {
+ gfilter = getRefElem(elem.getAttribute('filter'));
+ }
+
+ // Change this in future for different filters
+ var suffix = (gfilter.firstChild.tagName === 'feGaussianBlur')?'blur':'filter';
+ gfilter.id = elem.id + '_' + suffix;
+ changeSelectedAttribute('filter', 'url(#' + gfilter.id + ')', [elem]);
+
+ // Update blur value
+ if(cblur) {
+ changeSelectedAttribute('stdDeviation', cblur, [gfilter.firstChild]);
+ canvas.setBlurOffsets(gfilter, cblur);
+ }
+ }
+
+ var chtlist = getTransformList(elem);
+
+ // Don't process gradient transforms
+ if(~elem.tagName.indexOf('Gradient')) chtlist = null;
+
+ // Hopefully not a problem to add this. Necessary for elements like
+ if(!chtlist) continue;
+
+ if (glist.numberOfItems) {
+ // TODO: if the group's transform is just a rotate, we can always transfer the
+ // rotate() down to the children (collapsing consecutive rotates and factoring
+ // out any translates)
+ if (gangle && glist.numberOfItems == 1) {
+ // [Rg] [Rc] [Mc]
+ // we want [Tr] [Rc2] [Mc] where:
+ // - [Rc2] is at the child's current center but has the
+ // sum of the group and child's rotation angles
+ // - [Tr] is the equivalent translation that this child
+ // undergoes if the group wasn't there
+
+ // [Tr] = [Rg] [Rc] [Rc2_inv]
+
+ // get group's rotation matrix (Rg)
+ var rgm = glist.getItem(0).matrix;
+
+ // get child's rotation matrix (Rc)
+ var rcm = svgroot.createSVGMatrix();
+ var cangle = getRotationAngle(elem);
+ if (cangle) {
+ rcm = chtlist.getItem(0).matrix;
+ }
+
+ // get child's old center of rotation
+ var cbox = getBBox(elem);
+ var ceqm = transformListToTransform(chtlist).matrix;
+ var coldc = transformPoint(cbox.x+cbox.width/2, cbox.y+cbox.height/2,ceqm);
+
+ // sum group and child's angles
+ var sangle = gangle + cangle;
+
+ // get child's rotation at the old center (Rc2_inv)
+ var r2 = svgroot.createSVGTransform();
+ r2.setRotate(sangle, coldc.x, coldc.y);
+
+ // calculate equivalent translate
+ var trm = matrixMultiply(rgm, rcm, r2.matrix.inverse());
+
+ // set up tlist
+ if (cangle) {
+ chtlist.removeItem(0);
+ }
+
+ if (sangle) {
+ if(chtlist.numberOfItems) {
+ chtlist.insertItemBefore(r2, 0);
+ } else {
+ chtlist.appendItem(r2);
+ }
+ }
+
+ if (trm.e || trm.f) {
+ var tr = svgroot.createSVGTransform();
+ tr.setTranslate(trm.e, trm.f);
+ if(chtlist.numberOfItems) {
+ chtlist.insertItemBefore(tr, 0);
+ } else {
+ chtlist.appendItem(tr);
+ }
+ }
+ }
+ else { // more complicated than just a rotate
+ // transfer the group's transform down to each child and then
+ // call recalculateDimensions()
+ var oldxform = elem.getAttribute("transform");
+ var changes = {};
+ changes["transform"] = oldxform ? oldxform : "";
+
+ var newxform = svgroot.createSVGTransform();
+
+ // [ gm ] [ chm ] = [ chm ] [ gm' ]
+ // [ gm' ] = [ chm_inv ] [ gm ] [ chm ]
+ var chm = transformListToTransform(chtlist).matrix,
+ chm_inv = chm.inverse();
+ var gm = matrixMultiply( chm_inv, m, chm );
+ newxform.setMatrix(gm);
+ chtlist.appendItem(newxform);
+ }
+ batchCmd.addSubCommand(recalculateDimensions(elem));
+ }
+ }
+
+
+ // remove transform and make it undo-able
+ if (xform) {
+ var changes = {};
+ changes["transform"] = xform;
+ g.setAttribute("transform", "");
+ g.removeAttribute("transform");
+ batchCmd.addSubCommand(new ChangeElementCommand(g, changes));
+ }
+
+ if (undoable && !batchCmd.isEmpty()) {
+ return batchCmd;
+ }
+}
+
+
// Function: ungroupSelectedElement
// Unwraps all the elements in a selected group (g) element. This requires
// significant recalculations to apply group's transforms, etc to its children
@@ -10855,30 +11036,17 @@ this.ungroupSelectedElement = function() {
convertToGroup(g);
return;
}
- if (g.tagName == "g") {
+ if (g.tagName === "g") {
var batchCmd = new BatchCommand("Ungroup Elements");
+ var cmd = pushGroupProperties(g, true);
+ if(cmd) batchCmd.addSubCommand(cmd);
+
var parent = g.parentNode;
var anchor = g.nextSibling;
var children = new Array(g.childNodes.length);
- var xform = g.getAttribute("transform");
-
- // get consolidated matrix
- var glist = getTransformList(g);
- var m = transformListToTransform(glist).matrix;
-
- // TODO: get all fill/stroke properties from the group that we are about to destroy
- // "fill", "fill-opacity", "fill-rule", "stroke", "stroke-dasharray", "stroke-dashoffset",
- // "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity",
- // "stroke-width"
- // and then for each child, if they do not have the attribute (or the value is 'inherit')
- // then set the child's attribute
var i = 0;
- var gangle = getRotationAngle(g);
-
- var gattrs = $(g).attr(['filter', 'opacity']);
- var gfilter, gblur;
while (g.firstChild) {
var elem = g.firstChild;
@@ -10886,7 +11054,7 @@ this.ungroupSelectedElement = function() {
var oldParent = elem.parentNode;
// Remove child title elements
- if(elem.tagName == 'title') {
+ if(elem.tagName === 'title') {
batchCmd.addSubCommand(new RemoveElementCommand(elem, oldParent));
oldParent.removeChild(elem);
continue;
@@ -10894,149 +11062,6 @@ this.ungroupSelectedElement = function() {
children[i++] = elem = parent.insertBefore(elem, anchor);
batchCmd.addSubCommand(new MoveElementCommand(elem, oldNextSibling, oldParent));
-
- if(gattrs.opacity !== null && gattrs.opacity !== 1) {
- var c_opac = elem.getAttribute('opacity') || 1;
- var new_opac = Math.round((elem.getAttribute('opacity') || 1) * gattrs.opacity * 100)/100;
- changeSelectedAttribute('opacity', new_opac, [elem]);
- }
-
- if(gattrs.filter) {
- var cblur = this.getBlur(elem);
- var orig_cblur = cblur;
- if(!gblur) gblur = this.getBlur(g);
- if(cblur) {
- // Is this formula correct?
- cblur = (gblur-0) + (cblur-0);
- } else if(cblur === 0) {
- cblur = gblur;
- }
-
- // If child has no current filter, get group's filter or clone it.
- if(!orig_cblur) {
- // Set group's filter to use first child's ID
- if(!gfilter) {
- gfilter = getRefElem(gattrs.filter);
- } else {
- // Clone the group's filter
- gfilter = copyElem(gfilter);
- findDefs().appendChild(gfilter);
- }
- } else {
- gfilter = getRefElem(elem.getAttribute('filter'));
- }
-
- // Change this in future for different filters
- var suffix = (gfilter.firstChild.tagName === 'feGaussianBlur')?'blur':'filter';
- gfilter.id = elem.id + '_' + suffix;
- changeSelectedAttribute('filter', 'url(#' + gfilter.id + ')', [elem]);
-
- // Update blur value
- if(cblur) {
- changeSelectedAttribute('stdDeviation', cblur, [gfilter.firstChild]);
- canvas.setBlurOffsets(gfilter, cblur);
- }
- }
-
- var chtlist = getTransformList(elem);
-
- // Don't process gradient transforms
- if(~elem.tagName.indexOf('Gradient')) chtlist = null;
-
- // Hopefully not a problem to add this. Necessary for elements like
- if(!chtlist) continue;
-
- if (glist.numberOfItems) {
- // TODO: if the group's transform is just a rotate, we can always transfer the
- // rotate() down to the children (collapsing consecutive rotates and factoring
- // out any translates)
- if (gangle && glist.numberOfItems == 1) {
- // [Rg] [Rc] [Mc]
- // we want [Tr] [Rc2] [Mc] where:
- // - [Rc2] is at the child's current center but has the
- // sum of the group and child's rotation angles
- // - [Tr] is the equivalent translation that this child
- // undergoes if the group wasn't there
-
- // [Tr] = [Rg] [Rc] [Rc2_inv]
-
- // get group's rotation matrix (Rg)
- var rgm = glist.getItem(0).matrix;
-
- // get child's rotation matrix (Rc)
- var rcm = svgroot.createSVGMatrix();
- var cangle = getRotationAngle(elem);
- if (cangle) {
- rcm = chtlist.getItem(0).matrix;
- }
-
- // get child's old center of rotation
- var cbox = getBBox(elem);
- var ceqm = transformListToTransform(chtlist).matrix;
- var coldc = transformPoint(cbox.x+cbox.width/2, cbox.y+cbox.height/2,ceqm);
-
- // sum group and child's angles
- var sangle = gangle + cangle;
-
- // get child's rotation at the old center (Rc2_inv)
- var r2 = svgroot.createSVGTransform();
- r2.setRotate(sangle, coldc.x, coldc.y);
-
- // calculate equivalent translate
- var trm = matrixMultiply(rgm, rcm, r2.matrix.inverse());
-
- // set up tlist
- if (cangle) {
- chtlist.removeItem(0);
- }
-
- if (sangle) {
- if(chtlist.numberOfItems) {
- chtlist.insertItemBefore(r2, 0);
- } else {
- chtlist.appendItem(r2);
- }
- }
-
- if (trm.e || trm.f) {
- var tr = svgroot.createSVGTransform();
- tr.setTranslate(trm.e, trm.f);
- if(chtlist.numberOfItems) {
- chtlist.insertItemBefore(tr, 0);
- } else {
- chtlist.appendItem(tr);
- }
- }
- }
- else { // more complicated than just a rotate
- // transfer the group's transform down to each child and then
- // call recalculateDimensions()
- var oldxform = elem.getAttribute("transform");
- var changes = {};
- changes["transform"] = oldxform ? oldxform : "";
-
- var newxform = svgroot.createSVGTransform();
-
- // [ gm ] [ chm ] = [ chm ] [ gm' ]
- // [ gm' ] = [ chm_inv ] [ gm ] [ chm ]
- var chm = transformListToTransform(chtlist).matrix,
- chm_inv = chm.inverse();
- var gm = matrixMultiply( chm_inv, m, chm );
- newxform.setMatrix(gm);
- chtlist.appendItem(newxform);
- }
- batchCmd.addSubCommand(recalculateDimensions(elem));
- }
- }
-
-
- // remove transform and make it undo-able
- if (xform) {
- var changes = {};
- changes["transform"] = xform;
- g.setAttribute("transform", "");
- g.removeAttribute("transform");
- batchCmd.addSubCommand(new ChangeElementCommand(g, changes));
}
// remove the group from the selection