align elements (work in progress)

git-svn-id: http://svg-edit.googlecode.com/svn/trunk@397 eee81c28-f429-11dd-99c0-75d572ba1ddd
master
Pavol Rusnak 2009-08-17 06:56:55 +00:00
parent 8923f6d746
commit d4ff808c83
10 changed files with 353 additions and 148 deletions

View File

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 291 B

View File

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 449 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 459 B

After

Width:  |  Height:  |  Size: 459 B

View File

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 339 B

View File

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

BIN
editor/images/clone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

View File

@ -53,13 +53,15 @@
<img class="tool_button tool_button_disabled" id="tool_redo" src="images/redo.png" title="Redo [Y]" alt="Redo"/>
</div>
<!-- Buttons when something a single element is selected -->
<!-- Buttons when a single element is selected -->
<div id="selected_panel">
<img class="tool_sep" src="images/sep.png" alt="|"/>
<img class="tool_button" id="tool_clone" src="images/copy.png" title="Clone Element [C]" alt="Copy"/>
<img class="tool_button" id="tool_clone" src="images/clone.png" title="Clone Element [C]" alt="Copy"/>
<img class="tool_button" id="tool_delete" src="images/delete.png" title="Delete Element [Delete/Backspace]" alt="Delete"/>
<img class="tool_sep" src="images/sep.png" alt="|"/>
<img class="tool_button" id="tool_move_top" src="images/move_top.png" title="Move to Top [Shift+Up]" alt="Top"/>
<img class="tool_button" id="tool_move_bottom" src="images/move_bottom.png" title="Move to Bottom [Shift+Down]" alt="Bottom"/>
<img class="tool_sep" src="images/sep.png" alt="|"/>
<select id="group_opacity" class="selected_tool" title="Change selected item opacity">
<option selected="selected" value="1">100 %</option>
<option value="0.9">90 %</option>
@ -76,11 +78,19 @@
<input id="angle" class="selected_tool" title="Change rotation angle" alt="Rotation Angle" size="2" value="0" type="text"/>
</div>
<!-- Buttons when something a single element is selected -->
<!-- Buttons when multiple elements are selected -->
<div id="multiselected_panel">
<img class="tool_sep" src="images/sep.png" alt="|"/>
<img class="tool_button" id="tool_clone_multi" src="images/copy.png" title="Clone Elements [C]" alt="Clone"/>
<img class="tool_button" id="tool_clone_multi" src="images/clone.png" title="Clone Elements [C]" alt="Clone"/>
<img class="tool_button" id="tool_delete_multi" src="images/delete.png" title="Delete Selected Elements [Delete/Backspace]" alt="Delete"/>
<img class="tool_sep" src="images/sep.png" alt="|"/>
<img class="tool_button" id="tool_alignleft" src="images/align-left.png" title="Align Left" alt="Left"/>
<img class="tool_button" id="tool_aligncenter" src="images/align-center.png" title="Align Center" alt="Center"/>
<img class="tool_button" id="tool_alignright" src="images/align-right.png" title="Align Right" alt="Right"/>
<img class="tool_sep" src="images/sep.png" alt="|"/>
<img class="tool_button" id="tool_aligntop" src="images/align-top.png" title="Align Top" alt="Top"/>
<img class="tool_button" id="tool_alignmiddle" src="images/align-middle.png" title="Align Middle" alt="Middle"/>
<img class="tool_button" id="tool_alignbottom" src="images/align-bottom.png" title="Align Bottom" alt="Bottom"/>
</div>
<div id="rect_panel">

View File

@ -475,6 +475,25 @@ function svg_edit_setup() {
svgCanvas.cloneSelectedElements();
};
var clickAlignLeft = function(){
svgCanvas.alignSelectedElements('l');
};
var clickAlignCenter = function(){
svgCanvas.alignSelectedElements('c');
};
var clickAlignRight = function(){
svgCanvas.alignSelectedElements('r');
};
var clickAlignTop = function(){
svgCanvas.alignSelectedElements('t');
};
var clickAlignMiddle = function(){
svgCanvas.alignSelectedElements('m');
};
var clickAlignBottom = function(){
svgCanvas.alignSelectedElements('b');
};
var showSourceEditor = function(){
if (editingsource) return;
editingsource = true;
@ -533,6 +552,12 @@ function svg_edit_setup() {
$('#tool_redo').click(clickRedo);
$('#tool_clone').click(clickClone);
$('#tool_clone_multi').click(clickClone);
$('#tool_alignleft').click(clickAlignLeft);
$('#tool_aligncenter').click(clickAlignCenter);
$('#tool_alignright').click(clickAlignRight);
$('#tool_aligntop').click(clickAlignTop);
$('#tool_alignmiddle').click(clickAlignMiddle);
$('#tool_alignbottom').click(clickAlignBottom);
// these two lines are required to make Opera work properly with the flyout mechanism
$('#tools_rect_show').click(clickSquare);
$('#tools_ellipse_show').click(clickCircle);
@ -565,12 +590,12 @@ function svg_edit_setup() {
$('#tool_paste').mousedown(function(){$('#tool_paste').addClass('tool_button_current');});
$('#tool_paste').mouseup(function(){$('#tool_paste').removeClass('tool_button_current');});
$('#tool_paste').mouseout(function(){$('#tool_paste').removeClass('tool_button_current');});
$('#tool_copy').mousedown(function(){$('#tool_copy').addClass('tool_button_current');});
$('#tool_copy').mouseup(function(){$('#tool_copy').removeClass('tool_button_current');});
$('#tool_copy').mouseout(function(){$('#tool_copy').removeClass('tool_button_current');});
$('#tool_copy_multi').mousedown(function(){$('#tool_copy').addClass('tool_button_current');});
$('#tool_copy_multi').mouseup(function(){$('#tool_copy').removeClass('tool_button_current');});
$('#tool_copy_multi').mouseout(function(){$('#tool_copy').removeClass('tool_button_current');});
$('#tool_clone').mousedown(function(){$('#tool_clone').addClass('tool_button_current');});
$('#tool_clone').mouseup(function(){$('#tool_clone').removeClass('tool_button_current');});
$('#tool_clone').mouseout(function(){$('#tool_clone').removeClass('tool_button_current');});
$('#tool_clone_multi').mousedown(function(){$('#tool_clone').addClass('tool_button_current');});
$('#tool_clone_multi').mouseup(function(){$('#tool_clone').removeClass('tool_button_current');});
$('#tool_clone_multi').mouseout(function(){$('#tool_clone').removeClass('tool_button_current');});
$('#tool_move_top').mousedown(function(){$('#tool_move_top').addClass('tool_button_current');});
$('#tool_move_top').mouseup(function(){$('#tool_move_top').removeClass('tool_button_current');});
$('#tool_move_top').mouseout(function(){$('#tool_move_top').removeClass('tool_button_current');});

View File

@ -7,7 +7,6 @@ if(!window.console) {
// this defines which elements and attributes that we support
// TODO: add <g> elements to this
// TODO: add <polygon> elements to this
// TODO: add <a> elements to this
// TODO: add xmlns:xlink attr to <svg> element
var svgWhiteList = {
@ -24,8 +23,7 @@ var svgWhiteList = {
"stop": ["id", "offset", "stop-color", "stop-opacity"],
"svg": ["id", "height", "transform", "width", "xmlns"],
"text": ["fill", "fill-opacity", "font-family", "font-size", "font-style", "font-weight", "id", "stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-opacity", "stroke-width", "transform", "x", "y"],
};
};
// These command objects are used for the Undo/Redo stack
// attrs contains the values that the attributes had before the change
@ -2405,6 +2403,178 @@ function SvgCanvas(c)
}
};
// aligns selected elements (type is a char - see switch below for explanation)
this.alignSelectedElements = function(type) {
var minx = 100000, maxx = -100000, miny = 100000, maxy = -100000;
var len = selectedElements.length;
if (!len) return;
for (var i = 0; i < len; ++i) {
if (selectedElements[i] == null) break;
var elem = selectedElements[i];
switch (elem.tagName) {
case 'circle':
if (parseInt(elem.getAttribute(('cx'))) - parseInt(elem.getAttribute(('r'))) < minx) minx = parseInt(elem.getAttribute(('cx'))) - parseInt(elem.getAttribute(('r')));
if (parseInt(elem.getAttribute(('cx'))) + parseInt(elem.getAttribute(('r'))) > maxx) maxx = parseInt(elem.getAttribute(('cx'))) + parseInt(elem.getAttribute(('r')));
if (parseInt(elem.getAttribute(('cy'))) - parseInt(elem.getAttribute(('r'))) < miny) miny = parseInt(elem.getAttribute(('cy'))) - parseInt(elem.getAttribute(('r')));
if (parseInt(elem.getAttribute(('cy'))) + parseInt(elem.getAttribute(('r'))) > maxy) maxy = parseInt(elem.getAttribute(('cy'))) + parseInt(elem.getAttribute(('r')));
break;
case 'ellipse':
if (parseInt(elem.getAttribute(('cx'))) - parseInt(elem.getAttribute(('rx'))) < minx) minx = parseInt(elem.getAttribute(('cx'))) - parseInt(elem.getAttribute(('rx')));
if (parseInt(elem.getAttribute(('cx'))) + parseInt(elem.getAttribute(('rx'))) > maxx) maxx = parseInt(elem.getAttribute(('cx'))) + parseInt(elem.getAttribute(('rx')));
if (parseInt(elem.getAttribute(('cy'))) - parseInt(elem.getAttribute(('ry'))) < miny) miny = parseInt(elem.getAttribute(('cy'))) - parseInt(elem.getAttribute(('ry')));
if (parseInt(elem.getAttribute(('cy'))) + parseInt(elem.getAttribute(('ry'))) > maxy) maxy = parseInt(elem.getAttribute(('cy'))) + parseInt(elem.getAttribute(('ry')));
break;
case 'rect':
if (parseInt(elem.getAttribute(('x'))) < minx) minx = parseInt(elem.getAttribute(('x')));
if (parseInt(elem.getAttribute(('x'))) + parseInt(elem.getAttribute(('width'))) > maxx) maxx = parseInt(elem.getAttribute(('x'))) + parseInt(elem.getAttribute(('width')));
if (parseInt(elem.getAttribute(('y'))) < minx) miny = parseInt(elem.getAttribute(('y')));
if (parseInt(elem.getAttribute(('y'))) + parseInt(elem.getAttribute(('height'))) > maxx) maxy = parseInt(elem.getAttribute(('y'))) + parseInt(elem.getAttribute(('height')));
break;
case 'line':
if (parseInt(elem.getAttribute(('x1'))) < minx) minx = parseInt(elem.getAttribute(('x1')));
if (parseInt(elem.getAttribute(('x2'))) < minx) minx = parseInt(elem.getAttribute(('x2')));
if (parseInt(elem.getAttribute(('x1'))) > maxx) maxx = parseInt(elem.getAttribute(('x1')));
if (parseInt(elem.getAttribute(('x2'))) > maxx) maxx = parseInt(elem.getAttribute(('x2')));
if (parseInt(elem.getAttribute(('y1'))) < miny) miny = parseInt(elem.getAttribute(('y1')));
if (parseInt(elem.getAttribute(('y2'))) < miny) miny = parseInt(elem.getAttribute(('y2')));
if (parseInt(elem.getAttribute(('y1'))) > maxy) maxy = parseInt(elem.getAttribute(('y1')));
if (parseInt(elem.getAttribute(('y2'))) > maxy) maxy = parseInt(elem.getAttribute(('y2')));
break;
case 'path':
// TODO: implement
break;
case 'polygon':
// TODO: implement
break;
case 'polyline':
// TODO: implement
break;
case 'text':
// TODO: how to handle text?
break;
}
}
var len = selectedElements.length;
for (var i = 0; i < len; ++i) {
if (selectedElements[i] == null) break;
var elem = selectedElements[i];
switch (elem.tagName) {
case 'circle':
switch (type) {
case 'l': // left (horizontal)
elem.setAttribute('cx', minx+parseInt(elem.getAttribute(('r'))));
break;
case 'c': // center (horizontal)
elem.setAttribute('cx', (minx+maxx)/2);
break;
case 'r': // right (horizontal)
elem.setAttribute('cx', maxx-parseInt(elem.getAttribute(('r'))));
break;
case 't': // top (vertical)
elem.setAttribute('cy', miny+parseInt(elem.getAttribute(('r'))));
break;
case 'm': // middle (vertical)
elem.setAttribute('cy', (miny+maxy)/2);
break;
case 'b': // bottom (vertical)
elem.setAttribute('cy', maxy-parseInt(elem.getAttribute(('r'))));
break;
}
break;
case 'ellipse':
switch (type) {
case 'l': // left (horizontal)
elem.setAttribute('cx', minx+parseInt(elem.getAttribute(('rx'))));
break;
case 'c': // center (horizontal)
elem.setAttribute('cx', (minx+maxx)/2);
break;
case 'r': // right (horizontal)
elem.setAttribute('cx', maxx-parseInt(elem.getAttribute(('rx'))));
break;
case 't': // top (vertical)
elem.setAttribute('cy', miny+parseInt(elem.getAttribute(('ry'))));
break;
case 'm': // middle (vertical)
elem.setAttribute('cy', (miny+maxy)/2);
break;
case 'b': // bottom (vertical)
elem.setAttribute('cy', maxy-parseInt(elem.getAttribute(('ry'))));
break;
}
break;
case 'rect':
switch (type) {
case 'l': // left (horizontal)
elem.setAttribute('x', minx);
break;
case 'c': // center (horizontal)
elem.setAttribute('x', (minx+maxx-parseInt(elem.getAttribute(('width'))))/2);
break;
case 'r': // right (horizontal)
elem.setAttribute('x', maxx-parseInt(elem.getAttribute(('width'))));
break;
case 't': // top (vertical)
elem.setAttribute('y', miny);
break;
case 'm': // middle (vertical)
elem.setAttribute('y', (miny+maxy-parseInt(elem.getAttribute(('width'))))/2);
break;
case 'b': // bottom (vertical)
elem.setAttribute('y', maxx-parseInt(elem.getAttribute(('width'))));
break;
}
break;
case 'line':
switch (type) {
case 'l': // left (horizontal)
var d = Math.min(parseInt(elem.getAttribute(('x1'))), parseInt(elem.getAttribute(('x2')))) - minx;
elem.setAttribute('x1', parseInt(elem.getAttribute(('x1'))) - d);
elem.setAttribute('x2', parseInt(elem.getAttribute(('x2'))) - d);
break;
case 'c': // center (horizontal)
var d = Math.abs(parseInt(elem.getAttribute(('x1'))) - parseInt(elem.getAttribute(('x2'))));
elem.setAttribute('x1', (minx+maxx-d)/2 );
elem.setAttribute('x2', (minx+maxx+d)/2 );
break;
case 'r': // right (horizontal)
var d = Math.max(parseInt(elem.getAttribute(('x1'))), parseInt(elem.getAttribute(('x2')))) - maxx;
elem.setAttribute('x1', parseInt(elem.getAttribute(('x1'))) - d);
elem.setAttribute('x2', parseInt(elem.getAttribute(('x2'))) - d);
break;
case 't': // top (vertical)
var d = Math.min(parseInt(elem.getAttribute(('y1'))), parseInt(elem.getAttribute(('y2')))) - miny;
elem.setAttribute('y1', parseInt(elem.getAttribute(('y1')))-d);
elem.setAttribute('y2', parseInt(elem.getAttribute(('y2')))-d);
break;
case 'm': // middle (vertical)
var d = Math.abs(parseInt(elem.getAttribute(('y1'))) - parseInt(elem.getAttribute(('y2'))));
elem.setAttribute('y1', (miny+maxy-d)/2 );
elem.setAttribute('y2', (miny+maxy+d)/2 );
break;
case 'b': // bottom (vertical)
var d = Math.max(parseInt(elem.getAttribute(('y1'))), parseInt(elem.getAttribute(('y2')))) - maxy;
elem.setAttribute('y1', parseInt(elem.getAttribute(('y1'))) - d);
elem.setAttribute('y2', parseInt(elem.getAttribute(('y2'))) - d);
break;
}
break;
case 'path':
// TODO: implement
break;
case 'polygon':
// TODO: implement
break;
case 'polyline':
// TODO: implement
break;
case 'text':
// TODO: how to handle text?
break;
}
}
};
}
// Static class for various utility functions
@ -2461,20 +2631,20 @@ var Utils = {
// found this function http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f
"text2xml": function(sXML) {
// NOTE: I'd like to use jQuery for this, but jQuery makes all tags uppercase
//return $(xml)[0];
var out;
try{
var dXML = ($.browser.msie)?new ActiveXObject("Microsoft.XMLDOM"):new DOMParser();
dXML.async = false;
} catch(e){
throw new Error("XML Parser could not be instantiated");
};
try{
if($.browser.msie) out = (dXML.loadXML(sXML))?dXML:false;
else out = dXML.parseFromString(sXML, "text/xml");
}
catch(e){ throw new Error("Error parsing XML string"); };
return out;
}
// NOTE: I'd like to use jQuery for this, but jQuery makes all tags uppercase
//return $(xml)[0];
var out;
try{
var dXML = ($.browser.msie)?new ActiveXObject("Microsoft.XMLDOM"):new DOMParser();
dXML.async = false;
} catch(e){
throw new Error("XML Parser could not be instantiated");
};
try{
if($.browser.msie) out = (dXML.loadXML(sXML))?dXML:false;
else out = dXML.parseFromString(sXML, "text/xml");
}
catch(e){ throw new Error("Error parsing XML string"); };
return out;
}
};