<@doc hierarchy="GMLDOM"> Base aspect for all graphic shapes (c) SAP AG 2003-2006. All rights reserved. Aspect Shape for GmlDrawing; inherit Graphic; constructor(diagram, group) this.id = base.id; this.diagram = diagram; this.board = diagram.board; this.group = group || diagram; this.diagram.allShapes[this.id] = this; this.group.subShapes[this.id] = this; this.paint(); end destructor this.shapeNode.peer = null; SVG.removeElement(this.shapeNode); SVG.removeElement(this.wireNode); this.shapeNode = null; this.frameNode = null; this.bodyNode = null; this.wireNode = null; delete this.diagram.allShapes[this.id]; delete this.group.subShapes[this.id]; this.group = this.diagram; end ////////////////////////////////////////////////////////////////////////////////////// // BASE PROPERTIES <@doc> Gets or sets the shape's rotation angle The ~angle should be normalized to an even multiple of 90 degrees, according to: Angle | Description 0 | Left-to-right orientation (default) 90 | Top-to-bottom orientation 180 | Right-to-left orientation 270 | Bottom-to-top orientation property angle = 0; <@doc>Gets or sets the shape's flip state The ~flip state is a bitwise combination of the following flags: Flag | Description SVG_FLIPX | The shape is flipped horizontally SVG_FLIPY | The shape is flipped vertically property flip = 0; <@doc> Gets or sets the shape's center position (x y) The ~pos property defines the shape's center point (around which the shape is rotated). The ~pos property is relative to the shape's coordinates system (the coordinates system of the innermost group that contains the shape, or the diagram's coordinate system if the shape is not contained in any group). The property value is a string containing the center position's x and y coordinates concatenated with a space. The x and y coordinate values should be rounded to the nearest half of a grid unit so that the shape itself is maintained aligned to whole grid units. property pos = ''; <@doc> Gets or sets the shape's size (w h) The ~size property defines the shape's width and height (prior to any rotation that may be applied on the shape). The ~size property is relative to the shape's coordinates system (the coordinates system of the innermost group that contains the shape, or the diagram's coordinate system if the shape is not contained in any group). The property value is a string containing the width and height values concatenated with a space. The width and height values should be rounded to the nearest grid unit. property size = ''; <@doc type="Object">Gets the shape's geometry constraints The ~geometry property defines the shape's geometrical constraints using the following structure: Name | Description resizeMode | Shape resizing mode (see @RESIZE_MODES) rotateMode | Shape rotating mode (see @ROTATE_MODES) | defWidth | Default shape width minWidth | Minimum shape width maxWidth | Maximum shape width | defHeight | Default shape height minHeight | Minimum shape height maxHeight | Maximum shape height | padding | An array of four numbers [top,right,bottom,left] specifying the distances to use for padding between the top, right, bottom, and left frame edges and the corresponding body content boundaries. Padding is ignored on fixed-size shapes. Aspects derived from the Shape aspect may extend the ~geometry property by adding their specific constraints. static readonly property geometry = { resizeMode: #[SVG_FREE_SIZE], rotateMode: #[SVG_ROTATE_FULL], defWidth: 100, defHeight: 70, padding: [0,0,0,0] }; <@doc type="0"> Gets or sets the shape's protection mode The shape protection mode is a bitwise combination of the following flags: Name | Description SVG_NONE | Do not protect the shape from any user interaction (default) SVG_PROTECT_MOVE | Protect the shape from moving SVG_PROTECT_RESIZE | Protect the shape from resizing SVG_PROTECT_ROTATE | Protect the shape from rotating SVG_PROTECT_FLIP | Protect the shape from flipping SVG_PROTECT_TRANSFORM | Protect the shape from all transformations (move, resize, rotate, or flip) SVG_PROTECT_SELECT | Protect the shape from selection property protect = 0; <@doc type="RGB">Gets or sets the shape's fill color property fillColor = '#F5F5F5'; metadata for fillColor = { editor: 'color', group: 'Drawing', label: '#TEXT[XFLD_FILL_COLOR]', priority:100 } <@doc type="RGB">Gets or sets the shape's stroke color property strokeColor = '#718398'; metadata for strokeColor = { editor: 'color', group: 'Drawing', label: '#TEXT[XFLD_FRAME_COLOR]', priority:100 } <@doc type="RGB">Gets or sets the shape's hilight color property hilightColor = '#FF9933'; <@doc type="RGB">Gets or sets the shape's text color property textColor = '#546374'; metadata for textColor = { editor: 'color', group: 'Drawing', label: '#TEXT[XFLD_TEXT_COLOR]', priority:100 } <@enum name="RESIZE_MODES"> An enumeration of the available shape resize modes The resize mode is a bitwise combination of the following groups of flags: Name | Description SVG_FREE_SIZE | Shape's size can be freely changed (while still honoring the other constraints) SVG_FIXED_SIZE | Both the width and the height are fixed SVG_AUTO_SIZE | Both the width and the height are set automatically SVG_KEEP_SIZE | Both the width and the height are kept greater or equal to the shape's body size | SVG_FIXED_WIDTH | Width is fixed to ~defWidth SVG_AUTO_WIDTH | Width is automatically set equal to the shape's body width SVG_KEEP_WIDTH | Width is kept greater or equal to the shape's body width | SVG_FIXED_HEIGHT | Height is fixed to ~defHeight SVG_AUTO_HEIGHT | Height is automatically set equal to the shape's body height SVG_KEEP_HEIGHT | Height is kept greater or equal to the shape's body height <@enum name="ROTATE_MODES"> An enumeration of the available shape rotating modes The rotate mode is a bitwise combination of the following groups of flags: Name | Description SVG_ROTATE_FULL | The shape can be freely rotated and flipped in all directions | SVG_ROTATE_0 | The shape can be rotated to 0 degrees (left-to-right) SVG_ROTATE_90 | The shape can be rotated to 90 degrees (top-to-bottom) SVG_ROTATE_180 | The shape can be rotated to 180 degrees (right-to-left) SVG_ROTATE_270 | The shape can be rotated to 270 degrees (bottom-to-top) | SVG_FLIPX | The shape can be flipped horizontally SVG_FLIPY | The shape can be flipped vertically | SVG_FLIP_BODY | Flips the body contents together with the shape SVG_FLIP_VBODY | Flips the body contents together with the shape, but only if the shape is oriented vertically (angle equals 90 or 270) SVG_FLIP_UPSIDE | Flips or rotates the body contents to ensure they are always oriented upside, regardless of the shape's orientation ////////////////////////////////////////////////////////////////////////////////////// // ASPECT PROPERTIES <@doc type="@Group!" scope="private">Gets the innermost group that contains this shape, or the diagram if this is a top-level shape virtual readonly property group = null; <@doc type="SVGNode" scope="private">Gets the shape body's svg node virtual readonly property bodyNode = null; <@doc type="SVGNode" scope="private">Gets the shape frame's svg node virtual readonly property frameNode = null; <@doc type="SVGNode" scope="private">Gets the shape's svg node virtual readonly property shapeNode = null; <@doc type="SVGNode" scope="private">Gets the shape wireframe's svg node virtual readonly property wireNode = null; // Aspect geometry properties: // - The shape's coordinates system origin (ox,oy) and scale (os) are defined by the containing group/diagram. All other geometry properties are defined relative to this coordinates system. // - The shape's center position (cx,cy) and size (w,h) do not depend on the rotation angle. // - The shape's bounding box (x1,y1,x2,y2) always reflect the rotation angle // - The shape's rotation angle (rot) is normalized to: 0x00=0, 0x01=90, 0x02=180, 0x03=270 <@doc scope="private">Gets the shape's coordinates system x-origin (relative to the canvas) virtual readonly property ox = 0; <@doc scope="private">Gets the shape's coordinates system y-origin (relative to the canvas) virtual readonly property oy = 0; <@doc scope="private">Gets the shape's coordinates system scale (relative to the canvas) virtual readonly property os = 1; <@doc scope="private">Gets the shape's center x-coordinate (relative to the shape's coordinates system) virtual readonly property cx = 0; <@doc scope="private">Gets the shape's left x-coordinate (relative to the shape's coordinates system) virtual readonly property x1 = 0; <@doc scope="private">Gets the shape's right x-coordinate (relative to the shape's coordinates system) virtual readonly property x2 = 0; <@doc scope="private">Gets the shape's center y-coordinate (relative to the shape's coordinates system) virtual readonly property cy = 0; <@doc scope="private">Gets the shape's top y-coordinate (relative to the shape's coordinates system) virtual readonly property y1 = 0; <@doc scope="private">Gets the shape's bottom y-coordinate (relative to the shape's coordinates system) virtual readonly property y2 = 0; <@doc scope="private">Gets the shape's width (relative to the shape's coordinates system) virtual readonly property w = 0; <@doc scope="private">Gets the shape's height (relative to the shape's coordinates system) virtual readonly property h = 0; <@doc scope="private">Gets the shape's rotation angle (in multiples of 90 degrees) virtual readonly property rot = 0; ////////////////////////////////////////////////////////////////////////////////////// // ASPECT METHODS <@doc scope="private"> Recalculates the shape's size to best fit its contents virtual method autoFit() var geo=base.@geometry, szmode=geo.resizeMode||0; if (!(szmode & #[SVG_AUTO_SIZE])) return; var bbox=this.bodyNode.getBBox(), u=#[SVG_SNAPUNIT]; var w=Math.round(this.w/u)*u, h=Math.round(this.h/u)*u; if (szmode & #[SVG_AUTO_WIDTH]) { var padding = (geo.padding ? geo.padding[1]+geo.padding[3] : 0); w = Math.ceil(Math.max(bbox.width+padding, this.minWidth||geo.minWidth||u)/u)*u; if ((szmode & #[SVG_KEEP_WIDTH]) != #[SVG_AUTO_WIDTH]) w = Math.max(w, this.w); } if (szmode & #[SVG_AUTO_HEIGHT]) { var padding = (geo.padding ? geo.padding[0]+geo.padding[2] : 0); h = Math.ceil(Math.max(bbox.height+padding, this.minHeight||geo.minHeight||u)/u)*u; if ((szmode & #[SVG_KEEP_HEIGHT]) != #[SVG_AUTO_HEIGHT]) h = Math.max(h, this.h); } if (w != this.w || h != this.h) { this.diagram.bufferedUpdate(this, '@size', Math.round(w)+' '+Math.round(h)); } end <@doc scope="private"> Tests whether the shape is enclosed by a given rectangle virtual method enclosedIn(rect) var ox=this.ox, oy=this.oy, os=this.os; return (rect.x <= ox+this.x1*os && rect.x+rect.w >= ox+this.x2*os && rect.y <= oy+this.y1*os && rect.y+rect.h >= oy+this.y2*os); end <@doc scope="private"> Flips the shape horizontally virtual method flipX() var mask = (this.rot & 1 ? #[SVG_FLIPY] : #[SVG_FLIPX]); if (!(base.@geometry.rotateMode & mask) || (base.@protect & #[SVG_PROTECT_FLIP])) return; // Buffered update: part of "rotate/flip shapes" transaction this.diagram.bufferedUpdate(this, '@flip', (base.@flip ^ mask)); end <@doc scope="private"> Flips the shape vertically virtual method flipY() var mask = (this.rot & 1 ? #[SVG_FLIPX] : #[SVG_FLIPY]); if (!(base.@geometry.rotateMode & mask) || (base.@protect & #[SVG_PROTECT_FLIP])) return; // Buffered update: part of "rotate/flip shapes" transaction this.diagram.bufferedUpdate(this, '@flip', (base.@flip ^ mask)); end <@doc scope="private"> Gets the group that should be selected when the shape is selected virtual method getSelGroup() return this.group; end <@doc scope="private"> Gets the shape's size limits virtual method getSizeLimits() var geo=base.@geometry, u=#[SVG_SNAPUNIT]; var szmode=geo.resizeMode||0, data={}, bbox=null; if (szmode & #[SVG_FIXED_WIDTH]) { data.minWidth = data.maxWidth = this.minWidth||geo.defWidth; } else if (szmode & #[SVG_AUTO_WIDTH]) { if (!bbox) bbox=this.bodyNode.getBBox(); var padding = (geo.padding ? geo.padding[1]+geo.padding[3] : 0); data.minWidth = Math.ceil(Math.max(bbox.width+padding, this.minWidth||geo.minWidth||u)/u)*u; data.maxWidth = ((szmode & #[SVG_KEEP_WIDTH]) == #[SVG_AUTO_WIDTH]) ? data.minWidth : (geo.maxWidth||10000); } else { data.minWidth = this.minWidth||geo.minWidth||u; data.maxWidth = geo.maxWidth||10000; } if (szmode & #[SVG_FIXED_HEIGHT]) { data.minHeight = data.maxHeight = this.minHeight||geo.defHeight; } else if (szmode & #[SVG_AUTO_HEIGHT]) { if (!bbox) bbox=this.bodyNode.getBBox(); var padding = (geo.padding ? geo.padding[0]+geo.padding[2] : 0); data.minHeight = Math.ceil(Math.max(bbox.height+padding, this.minHeight||geo.minHeight||u)/u)*u; data.maxHeight = ((szmode & #[SVG_KEEP_HEIGHT]) == #[SVG_AUTO_HEIGHT]) ? data.minHeight : (geo.maxHeight||10000); } else { data.minHeight = this.minHeight||geo.minHeight||u; data.maxHeight = geo.maxHeight||10000; } return data; end <@doc scope="private"> Tests whether the shape is in focus virtual method inFocus() return (this.diagram.focus == this); end <@doc scope="private"> Tests whether the shape intersects a given rectangle virtual method intersects(rect) var ox=this.ox, oy=this.oy, os=this.os; return (Math.max(ox+this.x1*os, rect.x) <= Math.min(ox+this.x2*os, rect.x+rect.w) && Math.max(oy+this.y1*os, rect.y) <= Math.min(oy+this.y2*os, rect.y+rect.h)); end <@doc scope="private"> Moves the shape by a specified offset virtual method moveby(offx, offy) this.moveto(this.cx+offx, this.cy+offy); end <@doc scope="private"> Moves the shape to a specified position virtual method moveto(cx, cy) if (base.@protect & #[SVG_PROTECT_MOVE]) return; var u = #[SVG_SNAPUNIT]; var dx = (((this.rot & 1) ? this.h : this.w) % (2*u))/2; var dy = (((this.rot & 1) ? this.w : this.h) % (2*u))/2; var cx = Math.round((cx-dx)/u)*u+dx; var cy = Math.round((cy-dy)/u)*u+dy; // Buffered update: part of "move shapes", "resize shape", "rotate/flip shapes", or "create shape" transactions this.diagram.bufferedUpdate(this, '@pos', Math.round(cx)+' '+Math.round(cy)); end <@doc scope="private"> Handles a mutation event on the base object of this shape virtual method onObjectMutate(evt) switch (evt.type) { case 'onupdateobject': this.repaint(evt.name); break; case 'onreattachelement': var diag = this.diagram; var shape = diag.allGraphics[evt.object.id]; var newgroup = evt.newstate && diag.allGroups[evt.newstate.id] || null; var oldgroup = evt.oldstate && diag.allGroups[evt.oldstate.id] || null; if (newgroup) { //XXX: Group handling newgroup.onAttachShape(shape); } else if (oldgroup) { //XXX: Group handling oldgroup.onDetachShape(shape); } break; } end <@doc scope="private"> Handles a mutation event on a sub-object of this shape virtual method onSubObjectMutate(evt) // override end <@doc scope="private"> Rotates the shape clockwise (dir=+1) or counter-clockwise (dir=-1) virtual method rotate(dir) if (base.@protect & #[SVG_PROTECT_ROTATE]) return; // calculate new rotation angle var dir = (dir < 0 ? -1 : 1), rot=this.rot, geo=base.@geometry; do { rot = (rot+dir) % 4; if (rot<0) rot += 4; if (Math.pow(2,rot) & geo.rotateMode) break; } while (rot != this.rot); if (rot == this.rot) return; // calculate new center point (adjusted to keep shape snapped to grid) var u = #[SVG_SNAPUNIT], vert=(rot & 1); var fn = (vert ? Math.ceil : Math.floor); var dx = ((vert ? this.h : this.w) % (2*u))/2; var dy = ((vert ? this.w : this.h) % (2*u))/2; var cx = fn((this.cx-dx)/u)*u+dx; var cy = fn((this.cy-dy)/u)*u+dy; // Buffered update: part of "rotate/flip shapes" transaction if (cx == this.cx && cy == this.cy) { this.diagram.bufferedUpdate(this, '@angle', rot*90); } else { this.diagram.bufferedUpdate(this, '@angle', rot*90); this.diagram.bufferedUpdate(this, '@pos', Math.round(cx)+' '+Math.round(cy)); } end <@doc scope="private"> Selects the shape virtual method select() if (base.@protect & #[SVG_PROTECT_SELECT]) return false; if (!this.wireNode) this.createWireframe(); if (!this.selected) { SVG.display(this.wireNode, true); if (!GETVAR('DndPlow')) SVG.raiseElement(this.shapeNode); this.frameNode.setAttribute('color', base.@strokeColor); this.selected = true; } this.adjustWireframe(); return true; end <@doc scope="private"> Starts a shape rename operation virtual method startRename() // override end <@doc scope="private"> Unselects the shape virtual method unselect() if (!this.selected) return; if (this.wireNode) SVG.display(this.wireNode, false); this.selected = false; end ////////////////////////////////////////////////////////////////////////////////////// // EVENT HANDLERS <@doc scope="private"> Setup the mouse pointer for shape move/click/double-click interactions virtual method setupPointer(evt) this.diagram.selectById(this.id, evt.ctrlKey); if (evt.detail>1) { var evt = SVG.createModelEvent('onDrawingAction'); evt.object = this.base; evt.detail = #[SVG_ACTIVATE]; $ENV.fireModelEvent(evt); } else { return new SvgPointer('moveHandler', evt, this.diagram, this.diagram.canvas); } return null; end <@doc scope="private"> Handles the shapes's mouseover event virtual method mouseover(evt, ptr) if (ptr && ptr.method == 'moveHandler' && this.group != this.diagram) { this.group.mouseover(evt, ptr); } if (!ptr) this.diagram.showTooltip(this); if (this.selected || ptr || !this.diagram.isEditable) return; this.frameNode.setAttribute('color', base.@hilightColor); end <@doc scope="private"> Handles the shapes's mouseout event virtual method mouseout(evt, ptr) if (ptr && ptr.method == 'moveHandler' && this.group != this.diagram) { this.group.mouseout(evt, ptr); } if (!ptr) this.diagram.hideTooltip(); if (this.selected || ptr || !this.diagram.isEditable) return; this.frameNode.setAttribute('color', base.@strokeColor); end <@doc scope="private"> Prepares the shape for an ongoing pointer interaction virtual method setPointerMode(mode) // override end <@doc scope="private"> Gets the shape's control handles data virtual method getHandlesData() if (base.@protect & #[SVG_PROTECT_RESIZE]) return null; var ox=this.ox, oy=this.oy, os=this.os; var L=ox+this.x1*os, R=ox+this.x2*os; var T=oy+this.y1*os, B=oy+this.y2*os; return { NW:{cx:L,cy:T}, NE:{cx:R,cy:T}, SW:{cx:L,cy:B}, SE:{cx:R,cy:B} } end <@doc scope="private"> Gets the shape's tooltip positioning data virtual method getTooltipData() var ox=this.ox, oy=this.oy, os=this.os; return {x:ox+(this.x1+this.x2)*os/2, y:oy+this.y2*os, align:'center below', offset:4}; end <@doc scope="private"> Resizes the shape when its control handles are dragged virtual method onHandleMove(ptr) switch (ptr.phase) { case #[PTR_START]: ptr.scrollMode = #[PTR_AUTOSCROLL]; ptr.snapUnit = GETVAR('DndSmooth') ? 1 : #[SVG_SNAPUNIT]; ptr.snapshot = this.board.captureSnapshot(); ptr.handles = {}; var H=this.diagram.handlesList, hndl=ptr.source.getAttribute('handle'); var dw=(this.rot & 1 ? this.h : this.w), dh=(this.rot & 1 ? this.w : this.h); var ox=this.ox, oy=this.oy, os=this.os; for (var k in H) { if(H[k].style.getPropertyValue('display') != 'block') continue; var dx = 2*((FLOAT(H[k].getAttribute('cx'))-ox)/os-this.cx)/dw; var dy = 2*((FLOAT(H[k].getAttribute('cy'))-oy)/os-this.cy)/dh; ptr.handles[k] = {dx:dx, dy:dy}; if (k == hndl) { ptr.dx=dx; ptr.dy=dy; } } ptr.oldbox = {x1:ox+this.x1*os, y1:oy+this.y1*os, x2:ox+this.x2*os, y2:oy+this.y2*os}; ptr.newbox = {x1:ox+this.x1*os, y1:oy+this.y1*os, x2:ox+this.x2*os, y2:oy+this.y2*os}; ptr.limits = this.getSizeLimits(); if (this.rot & 1) { var L=ptr.limits, M; M=L.minWidth; L.minWidth=L.minHeight; L.minHeight=M; M=L.maxWidth; L.maxWidth=L.maxHeight; L.maxHeight=M; } for (var k in ptr.limits) ptr.limits[k] *= os; break; case #[PTR_MOVE]: var ob=ptr.oldbox, nb=ptr.newbox, L=ptr.limits, off=ptr.offset, org=ptr.origin, u=ptr.snapUnit; if (ptr.dy < 0) nb.y1 = Math.min(nb.y2-L.minHeight, Math.max(nb.y2-L.maxHeight, org.y-off.y/ptr.dy)); if (ptr.dy > 0) nb.y2 = Math.max(nb.y1+L.minHeight, Math.min(nb.y1+L.maxHeight, org.y+off.y/ptr.dy)); if (ptr.dx < 0) nb.x1 = Math.min(nb.x2-L.minWidth, Math.max(nb.x2-L.maxWidth, org.x-off.x/ptr.dx)); if (ptr.dx > 0) nb.x2 = Math.max(nb.x1+L.minWidth, Math.min(nb.x1+L.maxWidth, org.x+off.x/ptr.dx)); if (u != 1) nb.x1=Math.round(nb.x1/u)*u, nb.y1=Math.round(nb.y1/u)*u, nb.x2=Math.round(nb.x2/u)*u, nb.y2=Math.round(nb.y2/u)*u; var w=nb.x2-nb.x1, h=nb.y2-nb.y1, cx=nb.x1+w/2, cy=nb.y1+h/2; var diag=this.diagram, H=ptr.handles; for (var k in H) { diag.moveHandle(k, cx+H[k].dx*w/2, cy+H[k].dy*h/2); } if (this.rot & 1) { var t=w; w=h; h=t; } var ox=this.ox, oy=this.oy, os=this.os; this.adjustWireframe((cx-ox)/os, (cy-oy)/os, w/os, h/os); break; case #[PTR_FINISH]: if (!ptr.isOverSource) SVG.setProperty(ptr.source, 'class', 'handleNormal'); var nb=ptr.newbox, ox=this.ox, oy=this.oy, os=this.os; var w=nb.x2-nb.x1, h=nb.y2-nb.y1, cx=nb.x1+w/2, cy=nb.y1+h/2, t; if (this.rot & 1) { var t=w; w=h; h=t; } var size = {w:Math.round(w/os), h:Math.round(h/os)}; var pos = {x:Math.round((cx-ox)/os), y:Math.round((cy-oy)/os)}; this.diagram.resizeSelection(size, pos, ptr); break; case #[PTR_CANCEL]: if (!ptr.isOverSource) SVG.setProperty(ptr.source, 'class', 'handleNormal'); this.repaint('@shape'); break; } end ////////////////////////////////////////////////////////////////////////////////////// // PAINTING METHODS <@doc scope="private"> Paints the shape virtual method paint() this.shapeNode = SVG.createElement(this.group.shapesLayer, 'g'); this.frameNode = SVG.createElement(this.shapeNode, 'g', {color:base.@strokeColor}); this.bodyNode = SVG.createElement(this.shapeNode, 'g'); this.shapeNode.peer = this; this.reposition('@shape', true); // Calculate tolerance pos - u/2 < base.@pos < pos + u/2 var pos=(base.@pos+''), k=pos.indexOf(' '), u=#[SVG_SNAPUNIT]; var basex = parseInt(pos.slice(0,k)), basey = parseInt(pos.slice(k+1)); var minx = this.cx - u/2, maxx = minx + u, miny = this.cy - u/2, maxy = miny + u; if (basex > maxx || basex < minx || basey > maxy || basey < miny) // SILENT UPDATE: // In case the coordinates were rounded off, the position needs to be updated, // but the shape does not need repositioning. this.diagram.silentUpdate(this, '@pos', pos); end <@doc scope="private"> Repaints the shape after a property has changed virtual method repaint(prop) this.reposition(prop, false); if (prop == '@shape' || prop == '@size') { this.reshape(); } this.board.adjustCanvas(); if (this.selected) { this.adjustWireframe(); if (this == this.diagram.focus) this.diagram.adjustHandles(); } end <@doc scope="private"> Repositions the shape after its size, position, or orientation have changed virtual method reposition(prop, firstTime) var fullDraw=false; switch (prop) { case '@shape': fullDraw=true; // fall-through case '@size': var size=(base.@size+''), k=size.indexOf(' '), u=#[SVG_SNAPUNIT]; this.w = Math.round((parseInt(size.slice(0,k))||base.@geometry.defWidth)/u)*u; this.h = Math.round((parseInt(size.slice(k+1))||base.@geometry.defHeight)/u)*u; if (!fullDraw) break; // fall-through case '@angle': this.rot = Math.round((parseInt(base.@angle+'')||0)/90) % 4; if (this.rot<0) this.rot+=4; if (!fullDraw) break; // fall-through case '@flip': if (!fullDraw) break; // fall-through case '@pos': var pos=(base.@pos+''), k=pos.indexOf(' '), u=#[SVG_SNAPUNIT]; var dw=((this.rot & 1) ? this.h : this.w), dx=(dw % (2*u))/2; var dh=((this.rot & 1) ? this.w : this.h), dy=(dh % (2*u))/2; this.cx = Math.round(((parseInt(pos.slice(0,k))||0)-dx)/u)*u+dx; this.cy = Math.round(((parseInt(pos.slice(k+1))||0)-dy)/u)*u+dy; break; default: return false; } var dx = ((this.rot & 1) ? this.h : this.w)/2; var dy = ((this.rot & 1) ? this.w : this.h)/2; this.x1 = this.cx-dx; this.x2 = this.cx+dx; this.y1 = this.cy-dy; this.y2 = this.cy+dy; if (fullDraw || prop == '@angle' || prop == '@pos') { this.shapeNode.setAttribute('transform', 'translate('+this.cx+','+this.cy+')'+(this.rot ? ' rotate('+(this.rot*90)+')' : '')); } if (fullDraw || prop == '@angle' || prop == '@flip') { var flip=base.@flip, geo=base.@geometry, tr1='', tr2='', tr3=''; var fx = (flip & #[SVG_FLIPX] ? -1 : 1); var fy = (flip & #[SVG_FLIPY] ? -1 : 1); var bx = geo.padding ? fx*(geo.padding[3]-geo.padding[1])/2 : 0; var by = geo.padding ? fy*(geo.padding[0]-geo.padding[2])/2 : 0; if (flip) tr1 = 'matrix('+fx+' 0 0 '+fy+' 0 0)'; if (bx || by) tr2 = 'translate('+bx+' '+by+')'; if (geo.rotateMode & #[SVG_FLIP_FLAGS]) { if (geo.rotateMode & #[SVG_FLIP_VBODY]) { if ((this.rot & 1) && (flip & #[SVG_FLIPY])) tr3 = 'rotate(180)'; } else if (geo.rotateMode & #[SVG_FLIP_BODY]) { if (flip & #[SVG_FLIPY]) tr3 = 'rotate(180)'; } else if (geo.rotateMode & #[SVG_FLIP_UPSIDE]) { if (this.rot) tr3 = 'rotate('+(360-this.rot*90)+')'; } if (tr3) tr2 += ' '+tr3; } if (tr1 || !firstTime) this.frameNode.setAttribute('transform', tr1); if (tr2 || !firstTime) this.bodyNode.setAttribute('transform', tr2); } return true; end <@doc scope="private"> Repaints the shape after its size has changed virtual method reshape() // override end <@doc scope="private"> Updates the shape's coordinates system virtual method updateCoordSys(ox,oy,os) if (arguments.length != 3) return; this.ox = ox; this.oy = oy; this.os = os; end <@doc scope="private"> Creates the shape's wireframe virtual method createWireframe() this.wireNode = SVG.createElement(this.diagram.wireframesLayer, 'rect', {'class':'wireframe'}); end <@doc scope="private"> Adjusts the shape's wireframe to fit the shape bounding box virtual method adjustWireframe(cx, cy, w, h) if (!this.wireNode) return; if (arguments.length < 4) w=this.w, h=this.h; if (arguments.length < 2) cx=this.cx, cy=this.cy; var tr = 'translate('+cx+','+cy+')'; if (this.rot) tr += ' rotate('+(this.rot*90)+')'; if (this.group != this.diagram) tr = 'translate('+this.ox+','+this.oy+') scale('+this.os+') '+tr; this.wireNode.setAttribute('transform', tr); if (arguments == 2) return; this.wireNode.setAttribute('x', -w/2); this.wireNode.setAttribute('y', -h/2); this.wireNode.setAttribute('width', w); this.wireNode.setAttribute('height', h); end <@doc scope="private"> Creates the shape's sprite object virtual method createSprite(parentNode, aspectClass, baseClass, w, h) return SVG.createElement(parentNode, 'rect', {x:0, y:0, width:w, height:h, 'class':'spriteframe', $stroke:baseClass.prototype.@strokeColor}); end