diff options
Diffstat (limited to 'js/dojo/dojox/drawing/manager')
| -rw-r--r-- | js/dojo/dojox/drawing/manager/Anchors.js | 469 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/manager/Canvas.js | 165 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/manager/Mouse.js | 519 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/manager/Stencil.js | 645 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/manager/StencilUI.js | 69 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/manager/Undo.js | 61 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/manager/_registry.js | 35 | ||||
| -rw-r--r-- | js/dojo/dojox/drawing/manager/keys.js | 269 |
8 files changed, 2232 insertions, 0 deletions
diff --git a/js/dojo/dojox/drawing/manager/Anchors.js b/js/dojo/dojox/drawing/manager/Anchors.js new file mode 100644 index 0000000..100d5bc --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Anchors.js @@ -0,0 +1,469 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Anchors", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Anchors"); + +dojox.drawing.manager.Anchors = dojox.drawing.util.oo.declare( + // summary: + // Creates and manages the anchor points that are attached to + // (usually) the corners of a Stencil. + // description: + // Used internally, but there are some things that should be known: + // Anchors attach to a Stencil's 'points' (See stencil.points) + // To not display an anchor on a certain point, add noAnchor:true + // to the point. + + function(/* dojox.__stencilArgs */options){ + // arguments: See stencil._Base + this.mouse = options.mouse; + this.undo = options.undo; + this.util = options.util; + this.drawing = options.drawing; + this.items = {}; + }, + { + onAddAnchor: function(/*Anchor*/anchor){ + // summary: + // Event fires when anchor is created + }, + + + onReset: function(/*Stencil*/stencil){ + // summary: + // Event fires when an anchor's reset method is called + // + // a desperate hack in order to get the anchor point to reset. + // FIXME: Is this still used? I think its item.deselect();item.select(); + var st = this.util.byId("drawing").stencils; + st.onDeselect(stencil); + st.onSelect(stencil); + }, + + onRenderStencil: function(){ + // summary: + // Event fires when an anchor calls a Stencil's render method + // + for(var nm in this.items){ + dojo.forEach(this.items[nm].anchors, function(a){ + a.shape.moveToFront(); + }); + } + }, + + onTransformPoint: function(/*Anchor*/anchor){ + // summary: + // Event fired on anchor drag + // + // If anchors are a "group", it's corresponding anchor + // is set. All anchors then moved to front. + var anchors = this.items[anchor.stencil.id].anchors; + var item = this.items[anchor.stencil.id].item; + var pts = []; + dojo.forEach(anchors, function(a, i){ + + + if(anchor.id == a.id || anchor.stencil.anchorType!="group"){ + // nothing + }else{ + if(anchor.org.y == a.org.y){ + a.setPoint({ + dx: 0, + dy: anchor.shape.getTransform().dy - a.shape.getTransform().dy + }); + }else if(anchor.org.x == a.org.x){ + a.setPoint({ + dx: anchor.shape.getTransform().dx - a.shape.getTransform().dx, + dy: 0 + }); + } + a.shape.moveToFront(); + } + + var mx = a.shape.getTransform(); + pts.push({x:mx.dx + a.org.x, y:mx.dy+ a.org.y}); + + if(a.point.t){ + pts[pts.length-1].t = a.point.t; + } + + }, this); + item.setPoints(pts); + item.onTransform(anchor); + this.onRenderStencil(); + }, + + onAnchorUp: function(/*Anchor*/anchor){ + // summary: + // Event fired on anchor mouseup + }, + + onAnchorDown: function(/*Anchor*/anchor){ + // summary: + // Event fired on anchor mousedown + }, + + onAnchorDrag: function(/*Anchor*/anchor){ + // summary: + // Event fired when anchor is moved + }, + + onChangeStyle: function(/*Object*/stencil){ + // summary: + // if the Stencil changes color while were's selected + // this moves the anchors to the back. Fix it. + + for(var nm in this.items){ + dojo.forEach(this.items[nm].anchors, function(a){ + a.shape.moveToFront(); + }); + } + }, + + add: function(/*Stencil*/item){ + // summary: + // Creates anchor points on a Stencil, based on the + // Stencil's points. + // + this.items[item.id] = { + item:item, + anchors:[] + }; + if(item.anchorType=="none"){ return; } + var pts = item.points; + dojo.forEach(pts, function(p, i){ + if(p.noAnchor){ return; } + if(i==0 || i == item.points.length-1){ + console.log("ITEM TYPE:", item.type, item.shortType); + } + var a = new dojox.drawing.manager.Anchor({stencil:item, point:p, pointIdx:i, mouse:this.mouse, util:this.util}); + this.items[item.id]._cons = [ + dojo.connect(a, "onRenderStencil", this, "onRenderStencil"), + dojo.connect(a, "reset", this, "onReset"), + dojo.connect(a, "onAnchorUp", this, "onAnchorUp"), + dojo.connect(a, "onAnchorDown", this, "onAnchorDown"), + dojo.connect(a, "onAnchorDrag", this, "onAnchorDrag"), + dojo.connect(a, "onTransformPoint", this, "onTransformPoint"), + // FIXME: this will fire for each anchor. yech. + dojo.connect(item, "onChangeStyle", this, "onChangeStyle") + ]; + + this.items[item.id].anchors.push(a); + this.onAddAnchor(a); + }, this); + + if(item.shortType=="path"){ + // check if we have a double-point of a closed-curve-path + var f = pts[0], l = pts[pts.length-1], a = this.items[item.id].anchors; + if(f.x ==l.x && f.y==l.y){ + console.warn("LINK ANVHROS", a[0], a[a.length-1]); + a[0].linkedAnchor = a[a.length-1]; + a[a.length-1].linkedAnchor = a[0]; + } + } + + if(item.anchorType=="group"){ + dojo.forEach(this.items[item.id].anchors, function(anchor){ + dojo.forEach(this.items[item.id].anchors, function(a){ + if(anchor.id != a.id){ + if(anchor.org.y == a.org.y){ + anchor.x_anchor = a; + }else if(anchor.org.x == a.org.x){ + anchor.y_anchor = a; + } + } + },this); + },this); + + } + }, + + remove: function(/*Stencil*/item){ + // summary: + // Destroys the anchor points for a Stencil. + // + if(!this.items[item.id]){ + return; + } + dojo.forEach(this.items[item.id].anchors, function(a){ + a.destroy(); + }); + dojo.forEach(this.items[item.id]._cons, dojo.disconnect, dojo); + this.items[item.id].anchors = null; + delete this.items[item.id]; + } + } +); + +dojox.drawing.manager.Anchor = dojox.drawing.util.oo.declare( + // summary: + // An anchor point that is attached to (usually) one of the + // corners of a Stencil. + // Used internally. + function(/* Object */options){ + // summary: + // constructor. + // arguments: + // dojox.__stencilArgs plus some additional + // data, like which point this is (pointIdx) + // + this.defaults = dojox.drawing.defaults.copy(); + this.mouse = options.mouse; + this.point = options.point; + this.pointIdx = options.pointIdx; + this.util = options.util; + this.id = options.id || this.util.uid("anchor"); + this.org = dojo.mixin({}, this.point); + this.stencil = options.stencil; + if(this.stencil.anchorPositionCheck){ + this.anchorPositionCheck = dojo.hitch(this.stencil, this.stencil.anchorPositionCheck); + } + if(this.stencil.anchorConstrain){ + this.anchorConstrain = dojo.hitch(this.stencil, this.stencil.anchorConstrain); + } + this._zCon = dojo.connect(this.mouse, "setZoom", this, "render"); + this.render(); + this.connectMouse(); + }, + { + y_anchor:null, + x_anchor:null, + render: function(){ + // summary: + // Creates the anchor point. Unlike most render methods + // in Drawing, this is only called once. + // + this.shape && this.shape.removeShape(); + var d = this.defaults.anchors, + z = this.mouse.zoom, + b = d.width * z, + s = d.size * z, + p = s/2, + line = { + width:b, + style:d.style, + color:d.color, + cap:d.cap + }; + + + var _r = { + x: this.point.x-p, + y: this.point.y-p, + width: s, + height: s + }; + this.shape = this.stencil.container.createRect(_r) + .setStroke(line) + .setFill(d.fill); + + this.shape.setTransform({dx:0, dy:0}); + this.util.attr(this, "drawingType", "anchor"); + this.util.attr(this, "id", this.id); + }, + onRenderStencil: function(/*Anchor*/anchor){ + // summary: + // Event fires when an anchor calls a Stencil's render method + }, + onTransformPoint: function(/*Anchor*/anchor){ + // summary: + // Event fires when an anchor changes the points of a Stencil + }, + onAnchorDown: function(/*Mouse.EventObject*/obj){ + // summary: + // Event fires for mousedown on anchor + this.selected = obj.id == this.id; + }, + onAnchorUp: function(/*Mouse.EventObject*/obj){ + // summary: + // Event fires for mouseup on anchor + this.selected = false; + this.stencil.onTransformEnd(this); + }, + + onAnchorDrag: function(/*Mouse.EventObject*/obj){ + // summary: + // Event fires for on dragging of an anchor + if(this.selected){ + // mx is the original transform from when the anchor + // was created. It does not change + var mx = this.shape.getTransform(); + + var pmx = this.shape.getParent().getParent().getTransform(); + + var marginZero = this.defaults.anchors.marginZero; + + var orgx = pmx.dx + this.org.x, + orgy = pmx.dy + this.org.y, + x = obj.x - orgx, + y = obj.y - orgy, + s = this.defaults.anchors.minSize; + + var conL, conR, conT, conB; + + var chk = this.anchorPositionCheck(x, y, this); + if(chk.x<0){ + console.warn("X<0 Shift"); + while(this.anchorPositionCheck(x, y, this).x<0){ + this.shape.getParent().getParent().applyTransform({dx:2, dy:0}); + } + } + if(chk.y<0){ + console.warn("Y<0 Shift"); + while(this.anchorPositionCheck(x, y, this).y<0){ + this.shape.getParent().getParent().applyTransform({dx:0, dy:2}); + } + } + + if(this.y_anchor){ + // prevent y overlap of opposite anchor + if(this.org.y > this.y_anchor.org.y){ + // bottom anchor + + conT = this.y_anchor.point.y + s - this.org.y; + conB = Infinity; + + if(y < conT){ + // overlapping other anchor + y = conT; + } + + + }else{ + // top anchor + + conT = -orgy + marginZero; + conB = this.y_anchor.point.y - s - this.org.y; + + if(y < conT){ + // less than zero + y = conT; + }else if(y > conB){ + // overlapping other anchor + y = conB; + } + } + }else{ + // Lines - check for zero + conT = -orgy + marginZero; + if(y < conT){ + // less than zero + y = conT; + } + } + + + + + if(this.x_anchor){ + // prevent x overlap of opposite anchor + + if(this.org.x>this.x_anchor.org.x){ + // right anchor + + conL = this.x_anchor.point.x + s - this.org.x; + conR = Infinity; + + if(x < conL){ + // overlapping other anchor + x = conL; + } + + }else{ + // left anchor + + conL = -orgx + marginZero; + conR = this.x_anchor.point.x - s - this.org.x; + + if(x < conL){ + x = conL; + }else if(x > conR){ + // overlapping other anchor + x = conR; + } + } + }else{ + // Lines check for zero + conL = -orgx + marginZero; + if(x < conL){ + x = conL; + } + } + //Constrains anchor point, returns null if not overwritten by stencil + var constrained = this.anchorConstrain(x, y); + if(constrained != null){ + x=constrained.x; + y=constrained.y; + } + + this.shape.setTransform({ + dx:x, + dy:y + }); + if(this.linkedAnchor){ + // first and last points of a closed-curve-path + this.linkedAnchor.shape.setTransform({ + dx:x, + dy:y + }); + } + this.onTransformPoint(this); + } + }, + + anchorConstrain: function(/* Number */x,/* Number */ y){ + // summary: + // To be over written by tool! + // Add an anchorConstrain method to the tool + // and it will automatically overwrite this stub. + // Should return a constrained x & y value. + return null; + }, + + anchorPositionCheck: function(/* Number */x,/* Number */ y, /* Anchor */anchor){ + // summary: + // To be over written by tool! + // Add a anchorPositionCheck method to the tool + // and it will automatically overwrite this stub. + // Should return x and y coords. Success is both + // being greater than zero, fail is if one or both + // are less than zero. + return {x:1, y:1}; + }, + + setPoint: function(mx){ + // summary: + // Internal. Sets the Stencil's point + this.shape.applyTransform(mx); + }, + + connectMouse: function(){ + // summary: + // Internal. Connects anchor to manager.mouse + this._mouseHandle = this.mouse.register(this); + }, + + disconnectMouse: function(){ + // summary: + // Internal. Disconnects anchor to manager.mouse + this.mouse.unregister(this._mouseHandle); + }, + + reset: function(stencil){ + // summary: + // Called (usually) from a Stencil when that Stencil + // needed to make modifications to the position of the + // point. Basically used when teh anchor causes a + // less than zero condition. + }, + + destroy: function(){ + // summary: + // Destroys anchor. + dojo.disconnect(this._zCon); + this.disconnectMouse(); + this.shape.removeShape(); + } + } +); + +}); diff --git a/js/dojo/dojox/drawing/manager/Canvas.js b/js/dojo/dojox/drawing/manager/Canvas.js new file mode 100644 index 0000000..5db1a25 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Canvas.js @@ -0,0 +1,165 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Canvas", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Canvas"); + +(function(){ + + dojox.drawing.manager.Canvas = dojox.drawing.util.oo.declare( + // summary: + // Creates a dojox.gfx.surface to be used for Drawing. Note that + // The 'surface' that Drawing uses is actually a dojox.gfx.group. + // This allows for more versatility. + // + // Called internally from a dojox.Drawing. + // + // Note: Surface creation is asynchrous. Connect to + // onSurfaceReady in Drawing. + // + function(/*Object*/options){ + dojo.mixin(this, options); + + var dim = dojo.contentBox(this.srcRefNode); + this.height = this.parentHeight = dim.h; + this.width = this.parentWidth = dim.w; + this.domNode = dojo.create("div", {id:"canvasNode"}, this.srcRefNode); + dojo.style(this.domNode, { + width:this.width, + height:"auto" + }); + + dojo.setSelectable(this.domNode, false); + + this.id = this.id || this.util.uid("surface"); + + console.info("create canvas"); + this.gfxSurface = dojox.gfx.createSurface(this.domNode, this.width, this.height); + this.gfxSurface.whenLoaded(this, function(){ + setTimeout(dojo.hitch(this, function(){ + this.surfaceReady = true; + if(dojo.isIE){ + //this.gfxSurface.rawNode.parentNode.id = this.id; + }else if(dojox.gfx.renderer == "silverlight"){ + this.id = this.domNode.firstChild.id + }else{ + //this.gfxSurface.rawNode.id = this.id; + } + + this.underlay = this.gfxSurface.createGroup(); + this.surface = this.gfxSurface.createGroup(); + this.overlay = this.gfxSurface.createGroup(); + this.surface.setTransform({dx:0, dy:0,xx:1,yy:1}); + + this.gfxSurface.getDimensions = dojo.hitch(this.gfxSurface, "getDimensions"); + if(options.callback){ + options.callback(this.domNode); + } + }),500); + }); + this._mouseHandle = this.mouse.register(this); + }, + { + // zoom: [readonly] Number + // The amount the canvas is zoomed + zoom:1, + + useScrollbars: true, + baseClass:"drawingCanvas", + + resize: function(width, height){ + // summary: + // Method used to change size of canvas. Potentially + // called from a container like ContentPane. May be + // called directly. + // + this.parentWidth = width; + this.parentHeight = height; + this.setDimensions(width, height); + }, + + setDimensions: function(width, height, scrollx, scrolly){ + // summary: + // Internal. Changes canvas size and sets scroll position. + // Do not call this, use resize(). + // + // changing the size of the surface and setting scroll + // if items are off screen + var sw = this.getScrollWidth(); //+ 10; + this.width = Math.max(width, this.parentWidth); + this.height = Math.max(height, this.parentHeight); + + if(this.height>this.parentHeight){ + this.width -= sw; + } + if(this.width>this.parentWidth){ + this.height -= sw; + } + + this.mouse.resize(this.width,this.height); + this.gfxSurface.setDimensions(this.width, this.height); + + + this.domNode.parentNode.scrollTop = scrolly || 0; + this.domNode.parentNode.scrollLeft = scrollx || 0; + + + if(this.useScrollbars){ + //console.info("Set Canvas Scroll", (this.height > this.parentHeight), this.height, this.parentHeight) + dojo.style(this.domNode.parentNode, { + overflowY: this.height > this.parentHeight ? "scroll" : "hidden", + overflowX: this.width > this.parentWidth ? "scroll" : "hidden" + }); + }else{ + dojo.style(this.domNode.parentNode, { + overflowY: "hidden", + overflowX: "hidden" + }); + } + }, + + + setZoom: function(zoom){ + // summary: + // Internal. Zooms canvas in and out. + this.zoom = zoom; + this.surface.setTransform({xx:zoom, yy:zoom}); + this.setDimensions(this.width*zoom, this.height*zoom) + }, + + onScroll: function(){ + // summary: + // Event fires on scroll.NOT IMPLEMENTED + }, + + getScrollOffset: function(){ + // summary: + // Get the scroll position of the canvas + return { + top:this.domNode.parentNode.scrollTop, + left:this.domNode.parentNode.scrollLeft + }; // Object + }, + + getScrollWidth: function(){ + // summary: + // Special method used to detect the width (and height) + // of the browser scrollbars. Becomes memoized. + // + var p = dojo.create('div'); + p.innerHTML = '<div style="width:50px;height:50px;overflow:hidden;position:absolute;top:0;left:-1000px;"><div style="height:100px;"></div>'; + var div = p.firstChild; + dojo.body().appendChild(div); + var noscroll = dojo.contentBox(div).h; + dojo.style(div, "overflow", "scroll"); + var scrollWidth = noscroll - dojo.contentBox(div).h; + dojo.destroy(div); + this.getScrollWidth = function(){ + return scrollWidth; + }; + return scrollWidth; // Object + } + } + ); + +})(); +}); diff --git a/js/dojo/dojox/drawing/manager/Mouse.js b/js/dojo/dojox/drawing/manager/Mouse.js new file mode 100644 index 0000000..ea8b105 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Mouse.js @@ -0,0 +1,519 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Mouse", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Mouse"); + +dojox.drawing.manager.Mouse = dojox.drawing.util.oo.declare( + // summary: + // Master object (instance) that tracks mouse + // events. A new instance is created for each + // Drawing object. + // description: + // You could connect to any method or event in this + // class, but it is designed to have the object + // 'registered'. All objects with the current event + // will be called directly. + // + // Custom events are used often. In addition to + // standard events onDown, onUp, onDrag, etc, if + // a certain object is clicked upon (or dragged, etc), + // that object's drawingType will create the custom event, + // such as onAnchorDown, or onStencilDown. + // + function(/* Object */options){ + this.util = options.util; + this.keys = options.keys; + this.id = options.id || this.util.uid("mouse"); + this.currentNodeId = ""; + this.registered = {}; + }, + + { + // doublClickSpeed: Number + // Milliseconds between clicks to + // register as for onDoubleClick + doublClickSpeed:400, + + // private properties + + _lastx:0, + _lasty:0, + __reg:0, + _downOnCanvas:false, + +/*===== +CustomEventMethod: function(){ + // summary: + // The custom event method that an Object that has + // registered with manager.Mouse can receive. + // Can contain any or all of the following methods + // and they will be called as mouse events. All events + // will be sent a EventObject event object. + // NOTE: + // Events happen anywhere in the document unless + // otherwise noted. + // + // onMove + // Fires on mousemove when mouse is up + // onDown + // Fires on mousedown *on the canvas* + // onDrag + // Fires on mousemove when mouse is down + // onUp + // Fires on mouseup, anywhere in the document + // onStencilDown + // Fired on mousedown on a Stencil + // onStencilDrag + // Fired when mouse moves and mose is down on a Stencil + // onStencilUp + // Fired on mouseup off of a Stencil + // on[Custom]Up|Down|Move + // Custom events can bet set and fired by setting a + // different drawingType on a Stencil, or by calling + // setEventMode(customEventName) +}, +EventObject: function(){ + // summary: + // The custom event object that is sent to registered objects + // and their respective methods. + // NOTE: Most event objects are the same with the exception + // of the onDown events, which have fewer. + // + // All event properties included onDown: + // + // id: String + // Id of the focused object + // pageX: Number + // The X coordinate of the mouse from the left side of + // the document. + // pageY: Number + // The Y coordinate of the mouse from the top of + // the document. + // x: Number + // The X coordinate of the mouse from the left side + // of the canvas + // y: Number + // The Y coordinate of the mouse from the top + // of the canvas + // + // These event properties are *not* in onDown: + // + // last: Object + // The x and y coordinates of the last mousemove + // relative to the canvas + // move: Object + // The x and y amounts the mouse moved since the last event + // orgX: Number + // The left side of the canvas from the side of the document + // orgY: Number + // The top of the canvas from the top of the document + // scroll: Object + // The 'top' and 'left' scroll amounts of the canvas. + // start: Object + // The x and y coordinates of the mousedown event + // withinCanvas: Boolean + // Whether the event happened within the Canvas or not + +}, +=====*/ + + init: function(/* HTMLNode*/node){ + // summary: + // Internal. Initializes mouse. + // + this.container = node; + this.setCanvas(); + var c; + var _isDown = false; + dojo.connect(this.container, "rightclick", this, function(evt){ + console.warn("RIGHTCLICK") + }); + + dojo.connect(document.body, "mousedown", this, function(evt){ + //evt.preventDefault(); + //dojo.stopEvent(evt); + }); + + dojo.connect(this.container, "mousedown", this, function(evt){ + this.down(evt); + // Right click shouldn't trigger drag + if(evt.button != dojo.mouseButtons.RIGHT){ + _isDown = true; + c = dojo.connect(document, "mousemove", this, "drag"); + } + }); + dojo.connect(document, "mouseup", this, function(evt){ + dojo.disconnect(c); + _isDown = false; + this.up(evt); + }); + dojo.connect(document, "mousemove", this, function(evt){ + if(!_isDown){ + this.move(evt); + } + }); + dojo.connect(this.keys, "onEsc", this, function(evt){ + this._dragged = false; + }); + }, + + setCanvas: function(){ + // summary: + // Internal. Sets canvas position + var pos = dojo.coords(this.container.parentNode); + this.origin = dojo.clone(pos); + }, + + scrollOffset: function(){ + // summary: + // Gets scroll offset of canvas + return { + top:this.container.parentNode.scrollTop, + left:this.container.parentNode.scrollLeft + }; // Object + }, + + resize: function(width,height){ + if(this.origin){ + this.origin.w=width; + this.origin.h=height; + } + }, + + register: function(/* Object*/scope){ + // summary: + // All objects (Stencils) should register here if they + // use mouse events. When registering, the object will + // be called if it has that method. + // argument: + // The object to be called + // Returns: handle + // Keep the handle to be used for disconnection. + // See: CustomEventMethod and EventObject + // + var handle = scope.id || "reg_"+(this.__reg++); + if(!this.registered[handle]){ this.registered[handle] = scope; } + return handle; // String + }, + unregister: function(handle){ + // summary: + // Disconnects object. Mouse events are no longer + // called for it. + if(!this.registered[handle]){ return; } + delete this.registered[handle]; + }, + + _broadcastEvent:function(strEvt, obj){ + // summary: + // Fire events to all registered objects. + // + //console.log("mouse.broadcast:", strEvt, obj) + for(var nm in this.registered){ + if(this.registered[nm][strEvt]) this.registered[nm][strEvt](obj); + } + }, + + onDown: function(obj){ + // summary: + // Create on[xx]Down event and send to broadcaster. + // Could be connected to. + //console.info("onDown:", this.eventName("down")) + this._broadcastEvent(this.eventName("down"), obj); + }, + + onDrag: function(obj){ + // summary: + // Create on[xx]Drag event and send to broadcaster. + // Could be connected to. + // + var nm = this.eventName("drag"); + if(this._selected && nm == "onDrag"){ + nm = "onStencilDrag" + } + this._broadcastEvent(nm, obj); + }, + + onMove: function(obj){ + // summary: + // Create onMove event and send to broadcaster. + // Could be connected to. + // Note: onMove never uses a custom event + // Note: onMove is currently not enabled in the app. + // + this._broadcastEvent("onMove", obj); + }, + + overName: function(obj,evt){ + var nm = obj.id.split("."); + evt = evt.charAt(0).toUpperCase() + evt.substring(1); + if(nm[0] == "dojox" && (dojox.drawing.defaults.clickable || !dojox.drawing.defaults.clickMode)){ + return "onStencil"+evt; + }else{ + return "on"+evt; + } + + }, + + onOver: function(obj){ + // summary: + // + this._broadcastEvent(this.overName(obj,"over"), obj); + }, + + onOut: function(obj){ + // summary: + // + this._broadcastEvent(this.overName(obj,"out"), obj); + }, + + onUp: function(obj){ + // summary: + // Create on[xx]Up event and send to broadcaster. + // Could be connected to. + // + // blocking first click-off (deselect), largely for TextBlock + // TODO: should have param to make this optional? + var nm = this.eventName("up"); + + if(nm == "onStencilUp"){ + this._selected = true; + }else if(this._selected && nm == "onUp"){ ////////////////////////////////////////// + nm = "onStencilUp"; + this._selected = false; + } + + console.info("Up Event:", this.id, nm, "id:", obj.id); + this._broadcastEvent(nm, obj); + + // Silverlight double-click handled in Silverlight class + if(dojox.gfx.renderer == "silverlight"){ return; } + + // Check Double Click + // If a double click is detected, the onDoubleClick event fires, + // but does not replace the normal event. They both fire. + this._clickTime = new Date().getTime(); + if(this._lastClickTime){ + if(this._clickTime-this._lastClickTime<this.doublClickSpeed){ + var dnm = this.eventName("doubleClick"); + console.warn("DOUBLE CLICK", dnm, obj); + this._broadcastEvent(dnm, obj); + }else{ + //console.log(" slow:", this._clickTime-this._lastClickTime) + } + } + this._lastClickTime = this._clickTime; + + }, + + zoom: 1, + setZoom: function(zoom){ + // summary: + // Internal. Sets the mouse zoom percentage to + // that of the canvas + this.zoom = 1/zoom; + }, + + setEventMode: function(mode){ + // summary: + // Sets the mouse mode s that custom events can be called. + // Also can 'disable' events by using a bogus mode: + // | mouse.setEventMode("DISABLED") + // (unless any object subscribes to this event, + // it is effectively disabled) + // + this.mode = mode ? "on" + mode.charAt(0).toUpperCase() + mode.substring(1) : ""; + }, + + eventName: function(name){ + // summary: + // Internal. Determine the event name + // + name = name.charAt(0).toUpperCase() + name.substring(1); + if(this.mode){ + if(this.mode == "onPathEdit"){ + return "on"+name; + } + if(this.mode == "onUI"){ + //return "on"+name; + } + return this.mode + name; + }else{ + //Allow a mode where stencils aren't clickable + if(!dojox.drawing.defaults.clickable && dojox.drawing.defaults.clickMode){return "on"+name;} + var dt = !this.drawingType || this.drawingType=="surface" || this.drawingType=="canvas" ? "" : this.drawingType; + var t = !dt ? "" : dt.charAt(0).toUpperCase() + dt.substring(1); + return "on"+t+name; + } + }, + + up: function(evt){ + // summary: + // Internal. Create onUp event + // + this.onUp(this.create(evt)); + }, + + down: function(evt){ + // summary: + // Internal. Create onDown event + // + this._downOnCanvas = true; + var sc = this.scrollOffset(); + var dim = this._getXY(evt); + this._lastpagex = dim.x; + this._lastpagey = dim.y; + var o = this.origin; + var x = dim.x - o.x + sc.left; + var y = dim.y - o.y + sc.top; + + var withinCanvas = x>=0 && y>=0 && x<=o.w && y<=o.h; + x*= this.zoom; + y*= this.zoom; + + o.startx = x; + o.starty = y; + this._lastx = x; + this._lasty = y; + + this.drawingType = this.util.attr(evt, "drawingType") || ""; + var id = this._getId(evt); + //console.log("DOWN:", this.id, id, withinCanvas); + //console.log("this.drawingType:", this.drawingType); + + if(evt.button == dojo.mouseButtons.RIGHT && this.id == "mse"){ + //Allow right click events to bubble for context menus + }else{ + evt.preventDefault(); + dojo.stopEvent(evt); + } + this.onDown({ + mid:this.id, + x:x, + y:y, + pageX:dim.x, + pageY:dim.y, + withinCanvas:withinCanvas, + id:id + }); + + }, + over: function(obj){ + // summary: + // Internal. + // + this.onOver(obj); + }, + out: function(obj){ + // summary: + // Internal. + // + this.onOut(obj); + }, + move: function(evt){ + // summary: + // Internal. + // + var obj = this.create(evt); + if(this.id=="MUI"){ + //console.log("obj.id:", obj.id, "was:", this.currentNodeId) + } + if(obj.id != this.currentNodeId){ + // TODO: I wonder if an ID is good enough + // that would avoid the mixin + var outObj = {}; + for(var nm in obj){ + outObj[nm] = obj[nm]; + } + outObj.id = this.currentNodeId; + this.currentNodeId && this.out(outObj); + obj.id && this.over(obj); + this.currentNodeId = obj.id; + } + this.onMove(obj); + }, + drag: function(evt){ + // summary: + // Internal. Create onDrag event + this.onDrag(this.create(evt, true)); + }, + create: function(evt, squelchErrors){ + // summary: + // Internal. Create EventObject + // + var sc = this.scrollOffset(); + var dim = this._getXY(evt); + + var pagex = dim.x; + var pagey = dim.y; + + var o = this.origin; + var x = dim.x - o.x + sc.left; + var y = dim.y - o.y + sc.top; + + var withinCanvas = x>=0 && y>=0 && x<=o.w && y<=o.h; + x*= this.zoom; + y*= this.zoom; + + var id = withinCanvas ? this._getId(evt, squelchErrors) : ""; + var ret = { + mid:this.id, + x:x, + y:y, + pageX:dim.x, + pageY:dim.y, + page:{ + x:dim.x, + y:dim.y + }, + orgX:o.x, + orgY:o.y, + last:{ + x: this._lastx, + y: this._lasty + }, + start:{ + x: this.origin.startx, //+ sc.left, + y: this.origin.starty //+ sc.top + }, + move:{ + x:pagex - this._lastpagex, + y:pagey - this._lastpagey + }, + scroll:sc, + id:id, + withinCanvas:withinCanvas + }; + + //console.warn("MSE LAST:", x-this._lastx, y-this._lasty) + this._lastx = x; + this._lasty = y; + this._lastpagex = pagex; + this._lastpagey = pagey; + dojo.stopEvent(evt); + return ret; //Object + }, + _getId: function(evt, squelchErrors){ + // summary: + // Internal. Gets ID of focused node. + return this.util.attr(evt, "id", null, squelchErrors); // String + }, + _getXY: function(evt){ + // summary: + // Internal. Gets mouse coords to page. + return {x:evt.pageX, y:evt.pageY}; // Object + }, + + setCursor: function(cursor,/* HTMLNode*/node){ + // summary: + // Sets the cursor for a given node. If no + // node is specified the containing node is used. + if(!node){ + dojo.style(this.container, "cursor", cursor); + }else{ + dojo.style(node, "cursor", cursor); + } + } + } +); + +}); diff --git a/js/dojo/dojox/drawing/manager/Stencil.js b/js/dojo/dojox/drawing/manager/Stencil.js new file mode 100644 index 0000000..17e7a82 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Stencil.js @@ -0,0 +1,645 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Stencil", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Stencil"); + +(function(){ + var surface, surfaceNode; + dojox.drawing.manager.Stencil = dojox.drawing.util.oo.declare( + // summary: + // The main class for tracking Stencils that are cretaed, added, + // selected, or deleted. Also handles selections, multiple + // selections, adding and removing from selections, and dragging + // selections. It's this class that triggers the anchors to + // appear on a Stencil and whther there are anchor on a multiple + // select or not (currently not) + // + function(options){ + // + // TODO: mixin props + // + surface = options.surface; + this.canvas = options.canvas; + + this.defaults = dojox.drawing.defaults.copy(); + this.undo = options.undo; + this.mouse = options.mouse; + this.keys = options.keys; + this.anchors = options.anchors; + this.stencils = {}; + this.selectedStencils = {}; + this._mouseHandle = this.mouse.register(this); + + dojo.connect(this.keys, "onArrow", this, "onArrow"); + dojo.connect(this.keys, "onEsc", this, "deselect"); + dojo.connect(this.keys, "onDelete", this, "onDelete"); + + }, + { + _dragBegun: false, + _wasDragged:false, + _secondClick:false, + _isBusy:false, + + setRecentStencil: function(stencil){ + // summary: + // Keeps track of the most recent stencil interacted + // with, whether created or selected. + this.recent = stencil; + }, + + getRecentStencil: function(){ + // summary: + // Returns the stencil most recently interacted + // with whether it's last created or last selected + return this.recent; + }, + + register: function(/*Object*/stencil){ + // summary: + // Key method for adding Stencils. Stencils + // can be added to the canvas without adding + // them to this, but they won't have selection + // or drag ability. + // + console.log("Selection.register ::::::", stencil.id); + if(stencil.isText && !stencil.editMode && stencil.deleteEmptyCreate && !stencil.getText()){ + // created empty text field + // defaults say to delete + console.warn("EMPTY CREATE DELETE", stencil); + stencil.destroy(); + return false; + } + + this.stencils[stencil.id] = stencil; + this.setRecentStencil(stencil); + + if(stencil.execText){ + if(stencil._text && !stencil.editMode){ + console.log("select text"); + this.selectItem(stencil); + } + stencil.connect("execText", this, function(){ + if(stencil.isText && stencil.deleteEmptyModify && !stencil.getText()){ + console.warn("EMPTY MOD DELETE", stencil); + // text deleted + // defaults say to delete + this.deleteItem(stencil); + }else if(stencil.selectOnExec){ + this.selectItem(stencil); + } + }); + } + + stencil.connect("deselect", this, function(){ + if(!this._isBusy && this.isSelected(stencil)){ + // called from within stencil. do action. + this.deselectItem(stencil); + } + }); + + stencil.connect("select", this, function(){ + if(!this._isBusy && !this.isSelected(stencil)){ + // called from within stencil. do action. + this.selectItem(stencil); + } + }); + + return stencil; + }, + unregister: function(/*Object*/stencil){ + // summary: + // Method for removing Stencils from the manager. + // This doesn't delete them, only removes them from + // the list. + // + console.log("Selection.unregister ::::::", stencil.id, "sel:", stencil.selected); + if(stencil){ + stencil.selected && this.onDeselect(stencil); + delete this.stencils[stencil.id]; + } + }, + + onArrow: function(/*Key Event*/evt){ + // summary: + // Moves selection based on keyboard arrow keys + // + // FIXME: Check constraints + if(this.hasSelected()){ + this.saveThrottledState(); + this.group.applyTransform({dx:evt.x, dy: evt.y}); + } + }, + + _throttleVrl:null, + _throttle: false, + throttleTime:400, + _lastmxx:-1, + _lastmxy:-1, + saveMoveState: function(){ + // summary: + // Internal. Used for the prototype undo stack. + // Saves selection position. + // + var mx = this.group.getTransform(); + if(mx.dx == this._lastmxx && mx.dy == this._lastmxy){ return; } + this._lastmxx = mx.dx; + this._lastmxy = mx.dy; + //console.warn("SAVE MOVE!", mx.dx, mx.dy); + this.undo.add({ + before:dojo.hitch(this.group, "setTransform", mx) + }); + }, + + saveThrottledState: function(){ + // summary: + // Internal. Used for the prototype undo stack. + // Prevents an undo point on every mouse move. + // Only does a point when the mouse hesitates. + // + clearTimeout(this._throttleVrl); + clearInterval(this._throttleVrl); + this._throttleVrl = setTimeout(dojo.hitch(this, function(){ + this._throttle = false; + this.saveMoveState(); + }), this.throttleTime); + if(this._throttle){ return; } + this._throttle = true; + + this.saveMoveState(); + + }, + unDelete: function(/*Array*/stencils){ + // summary: + // Undeletes a stencil. Used in undo stack. + // + console.log("unDelete:", stencils); + for(var s in stencils){ + stencils[s].render(); + this.onSelect(stencils[s]); + } + }, + onDelete: function(/*Boolean*/noundo){ + // summary: + // Event fired on deletion of a stencil + // + console.log("Stencil onDelete", noundo); + if(noundo!==true){ + this.undo.add({ + before:dojo.hitch(this, "unDelete", this.selectedStencils), + after:dojo.hitch(this, "onDelete", true) + }); + } + this.withSelected(function(m){ + this.anchors.remove(m); + var id = m.id; + console.log("delete:", m); + m.destroy(); + delete this.stencils[id]; + }); + this.selectedStencils = {}; + }, + + deleteItem: function(/*Object*/stencil){ + // summary: + // Deletes a stencil. + // NOTE: supports limited undo. + // + // manipulating the selection to fire onDelete properly + if(this.hasSelected()){ + // there is a selection + var sids = []; + for(var m in this.selectedStencils){ + if(this.selectedStencils.id == stencil.id){ + if(this.hasSelected()==1){ + // the deleting stencil is the only one selected + this.onDelete(); + return; + } + }else{ + sids.push(this.selectedStencils.id); + } + } + // remove selection, delete, restore selection + this.deselect(); + this.selectItem(stencil); + this.onDelete(); + dojo.forEach(sids, function(id){ + this.selectItem(id); + }, this); + }else{ + // there is not a selection. select it, delete it + this.selectItem(stencil); + // now delete selection + this.onDelete(); + } + }, + + removeAll: function(){ + // summary: + // Deletes all Stencils on the canvas. + + this.selectAll(); + this._isBusy = true; + this.onDelete(); + this.stencils = {}; + this._isBusy = false; + }, + + setSelectionGroup: function(){ + // summary: + // Internal. Creates a new selection group + // used to hold selected stencils. + // + this.withSelected(function(m){ + this.onDeselect(m, true); + }); + + if(this.group){ + surface.remove(this.group); + this.group.removeShape(); + } + this.group = surface.createGroup(); + this.group.setTransform({dx:0, dy: 0}); + + this.withSelected(function(m){ + this.group.add(m.container); + m.select(); + }); + }, + + setConstraint: function(){ + // summary: + // Internal. Gets all selected stencils' coordinates + // and determines how far left and up the selection + // can go without going below zero + // + var t = Infinity, l = Infinity; + this.withSelected(function(m){ + var o = m.getBounds(); + t = Math.min(o.y1, t); + l = Math.min(o.x1, l); + }); + this.constrain = {l:-l, t:-t}; + }, + + + + onDeselect: function(stencil, keepObject){ + // summary: + // Event fired on deselection of a stencil + // + if(!keepObject){ + delete this.selectedStencils[stencil.id]; + } + //console.log('onDeselect, keep:', keepObject, "stencil:", stencil.type) + + this.anchors.remove(stencil); + + surface.add(stencil.container); + stencil.selected && stencil.deselect(); + stencil.applyTransform(this.group.getTransform()); + }, + + deselectItem: function(/*Object*/stencil){ + // summary: + // Deselect passed stencil + // + // note: just keeping with standardized methods + this.onDeselect(stencil); + }, + + deselect: function(){ // all stencils + // summary: + // Deselect all stencils + // + this.withSelected(function(m){ + this.onDeselect(m); + }); + this._dragBegun = false; + this._wasDragged = false; + }, + + onSelect: function(/*Object*/stencil){ + // summary: + // Event fired on selection of a stencil + // + //console.log("stencil.onSelect", stencil); + if(!stencil){ + console.error("null stencil is not selected:", this.stencils) + } + if(this.selectedStencils[stencil.id]){ return; } + this.selectedStencils[stencil.id] = stencil; + this.group.add(stencil.container); + stencil.select(); + if(this.hasSelected()==1){ + this.anchors.add(stencil, this.group); + } + }, + + selectAll: function(){ + // summary: + // Selects all items + this._isBusy = true; + for(var m in this.stencils){ + //if(!this.stencils[m].selected){ + this.selectItem(m); + //} + } + this._isBusy = false; + }, + + selectItem: function(/*String|Object*/ idOrItem){ + // summary: + // Method used to select a stencil. + // + var id = typeof(idOrItem)=="string" ? idOrItem : idOrItem.id; + var stencil = this.stencils[id]; + this.setSelectionGroup(); + this.onSelect(stencil); + this.group.moveToFront(); + this.setConstraint(); + }, + + onLabelDoubleClick: function(/*EventObject*/obj){ + // summary: + // Event to connect a textbox to + // for label edits + console.info("mgr.onLabelDoubleClick:", obj); + if(this.selectedStencils[obj.id]){ + this.deselect(); + } + }, + + onStencilDoubleClick: function(/*EventObject*/obj){ + // summary: + // Event fired on the double-click of a stencil + // + console.info("mgr.onStencilDoubleClick:", obj); + if(this.selectedStencils[obj.id]){ + if(this.selectedStencils[obj.id].edit){ + console.info("Mgr Stencil Edit -> ", this.selectedStencils[obj.id]); + var m = this.selectedStencils[obj.id]; + // deselect must happen first to set the transform + // then edit knows where to set the text box + m.editMode = true; + this.deselect(); + m.edit(); + } + } + + }, + + onAnchorUp: function(){ + // summary: + // Event fire on mouseup off of an anchor point + this.setConstraint(); + }, + + onStencilDown: function(/*EventObject*/obj, evt){ + // summary: + // Event fired on mousedown on a stencil + // + console.info(" >>> onStencilDown:", obj.id, this.keys.meta); + if(!this.stencils[obj.id]){ return; } + this.setRecentStencil(this.stencils[obj.id]); + this._isBusy = true; + + + if(this.selectedStencils[obj.id] && this.keys.meta){ + if(dojo.isMac && this.keys.cmmd){ + // block context menu + + } + console.log(" shift remove"); + this.onDeselect(this.selectedStencils[obj.id]); + if(this.hasSelected()==1){ + this.withSelected(function(m){ + this.anchors.add(m, this.group); + }); + } + this.group.moveToFront(); + this.setConstraint(); + return; + + }else if(this.selectedStencils[obj.id]){ + console.log(" clicked on selected"); + // clicking on same selected item(s) + // RESET OFFSETS + var mx = this.group.getTransform(); + this._offx = obj.x - mx.dx; + this._offy = obj.y - mx.dy; + return; + + }else if(!this.keys.meta){ + + console.log(" deselect all"); + this.deselect(); + + }else{ + // meta-key add + //console.log("reset sel and add stencil") + } + console.log(" add stencil to selection"); + // add a stencil + this.selectItem(obj.id); + + mx = this.group.getTransform(); + this._offx = obj.x - mx.dx; + this._offy = obj.y - mx.dx; + + this.orgx = obj.x; + this.orgy = obj.y; + + this._isBusy = false; + + // TODO: + // dojo.style(surfaceNode, "cursor", "pointer"); + + // TODO: + this.undo.add({ + before:function(){ + + }, + after: function(){ + + } + }); + }, + + onLabelDown: function(/*EventObject*/obj, evt){ + // summary: + // Event fired on mousedown of a stencil's label + // Because it's an annotation the id will be the + // master stencil. + //console.info("===============>>>Label click: ",obj, " evt: ",evt); + this.onStencilDown(obj,evt); + }, + + onStencilUp: function(/*EventObject*/obj){ + // summary: + // Event fired on mouseup off of a stencil + // + }, + + onLabelUp: function(/*EventObject*/obj){ + this.onStencilUp(obj); + }, + + onStencilDrag: function(/*EventObject*/obj){ + // summary: + // Event fired on every mousemove of a stencil drag + // + if(!this._dragBegun){ + // bug, in FF anyway - first mouse move shows x=0 + // the 'else' fixes it + this.onBeginDrag(obj); + this._dragBegun = true; + }else{ + this.saveThrottledState(); + + var x = obj.x - obj.last.x, + y = obj.y - obj.last.y, + c = this.constrain, + mz = this.defaults.anchors.marginZero; + + + x = obj.x - this._offx; + y = obj.y - this._offy; + + if(x < c.l + mz){ + x = c.l + mz; + } + if(y < c.t + mz){ + y = c.t + mz; + } + + this.group.setTransform({ + dx: x, + dy: y + }); + + + } + }, + + onLabelDrag: function(/*EventObject*/obj){ + this.onStencilDrag(obj); + }, + + onDragEnd: function(/*EventObject*/obj){ + // summary: + // Event fired at the end of a stencil drag + // + this._dragBegun = false; + }, + onBeginDrag: function(/*EventObject*/obj){ + // summary: + // Event fired at the beginning of a stencil drag + // + this._wasDragged = true; + }, + + onDown: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on the canvas + // + this.deselect(); + }, + + + onStencilOver: function(obj){ + // summary: + // This changes the cursor when hovering over + // a selectable stencil. + //console.log("OVER") + dojo.style(obj.id, "cursor", "move"); + }, + + onStencilOut: function(obj){ + // summary: + // This restores the cursor. + //console.log("OUT") + dojo.style(obj.id, "cursor", "crosshair"); + }, + + exporter: function(){ + // summary: + // Collects all Stencil data and returns an + // Array of objects. + var items = []; + for(var m in this.stencils){ + this.stencils[m].enabled && items.push(this.stencils[m].exporter()); + } + return items; // Array + }, + + listStencils: function(){ + return this.stencils; + }, + + toSelected: function(/*String*/func){ + // summary: + // Convenience function calls function *within* + // all selected stencils + var args = Array.prototype.slice.call(arguments).splice(1); + for(var m in this.selectedStencils){ + var item = this.selectedStencils[m]; + item[func].apply(item, args); + } + }, + + withSelected: function(/*Function*/func){ + // summary: + // Convenience function calls function on + // all selected stencils + var f = dojo.hitch(this, func); + for(var m in this.selectedStencils){ + f(this.selectedStencils[m]); + } + }, + + withUnselected: function(/*Function*/func){ + // summary: + // Convenience function calls function on + // all stencils that are not selected + var f = dojo.hitch(this, func); + for(var m in this.stencils){ + !this.stencils[m].selected && f(this.stencils[m]); + } + }, + + withStencils: function(/*Function*/func){ + // summary: + // Convenience function calls function on + // all stencils + var f = dojo.hitch(this, func); + for(var m in this.stencils){ + f(this.stencils[m]); + } + }, + + hasSelected: function(){ + // summary: + // Returns number of selected (generally used + // as truthy or falsey) + // + // FIXME: should be areSelected? + var ln = 0; + for(var m in this.selectedStencils){ ln++; } + return ln; // Number + }, + + isSelected: function(/*Object*/stencil){ + // summary: + // Returns if passed stencil is selected or not + // based on internal collection, not on stencil + // boolean + return !!this.selectedStencils[stencil.id]; // Boolean + } + } + + ); +})(); + +}); diff --git a/js/dojo/dojox/drawing/manager/StencilUI.js b/js/dojo/dojox/drawing/manager/StencilUI.js new file mode 100644 index 0000000..7fdaa14 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/StencilUI.js @@ -0,0 +1,69 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/StencilUI", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.StencilUI"); + +(function(){ + var surface, surfaceNode; + dojox.drawing.manager.StencilUI = dojox.drawing.util.oo.declare( + // summary: + // Used for handling Stencils as UI components. + // description: + // Replaces manager.Stencil. Handles basic UI mouse + // events like onmouseover. Does not handle selections + // or support delete, etc. + // + function(options){ + // + // TODO: mixin props + // + surface = options.surface; + this.canvas = options.canvas; + + this.defaults = dojox.drawing.defaults.copy(); + this.mouse = options.mouse; + this.keys = options.keys; + this._mouseHandle = this.mouse.register(this); + this.stencils = {}; + }, + { + register: function(/*Object*/stencil){ + this.stencils[stencil.id] = stencil; + return stencil; + }, + onUiDown: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on a stencil + // + if(!this._isStencil(obj)){ return; } + this.stencils[obj.id].onDown(obj); + }, + onUiUp: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on a stencil + // + if(!this._isStencil(obj)){ return; } + this.stencils[obj.id].onUp(obj); + }, + onOver: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on a stencil + // + if(!this._isStencil(obj)){ return; } + this.stencils[obj.id].onOver(obj); + }, + onOut: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on a stencil + // + if(!this._isStencil(obj)){ return; } + this.stencils[obj.id].onOut(obj); + }, + _isStencil: function(/*EventObject*/obj){ + return !!obj.id && !!this.stencils[obj.id] && this.stencils[obj.id].type == "drawing.library.UI.Button"; + } + } + ); + +})(); +}); diff --git a/js/dojo/dojox/drawing/manager/Undo.js b/js/dojo/dojox/drawing/manager/Undo.js new file mode 100644 index 0000000..c904240 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Undo.js @@ -0,0 +1,61 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Undo", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Undo"); + +dojox.drawing.manager.Undo = dojox.drawing.util.oo.declare( + // summary + // Handles the Undo in drawing. + // NOTE: Only partially implemented!!! There is very + // little actual undo functionality! + // + function(options){ + this.keys = options.keys; + this.undostack = []; + this.redostack = []; + dojo.connect(this.keys, "onKeyDown", this, "onKeyDown"); + }, + { + onKeyDown: function(evt){ + if(!evt.cmmd){ return; } + + if(evt.keyCode==90 && !evt.shift){ + this.undo(); + }else if((evt.keyCode == 90 && evt.shift) || evt.keyCode==89){ + this.redo(); + } + + }, + add: function(stack){ + //console.log("undo add", stack) + stack.args = dojo.mixin({}, stack.args); + this.undostack.push(stack); + }, + apply: function(scope, method, args){ + dojo.hitch(scope, method)(args); + }, + undo: function(){ + + var o = this.undostack.pop(); + console.log("undo!", o); + if(!o){ return; } + + o.before(); + + this.redostack.push(o); + }, + redo: function(){ + console.log("redo!"); + var o = this.redostack.pop(); + if(!o){ return; } + if(o.after){ + o.after(); + }else{ + o.before(); ///?????? + } + + this.undostack.push(o); + } + } +); +}); diff --git a/js/dojo/dojox/drawing/manager/_registry.js b/js/dojo/dojox/drawing/manager/_registry.js new file mode 100644 index 0000000..53c5a7e --- /dev/null +++ b/js/dojo/dojox/drawing/manager/_registry.js @@ -0,0 +1,35 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/_registry", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager._registry"); + +(function(){ + + var _registered = { + tool:{}, + stencil:{}, + drawing:{}, + plugin:{}, + button:{} + }; + + dojox.drawing.register = function(item, type){ + if(type=="drawing"){ + _registered.drawing[item.id] = item; + }else if(type=="tool"){ + _registered.tool[item.name] = item; + }else if(type=="stencil"){ + _registered.stencil[item.name] = item; + }else if(type=="plugin"){ + _registered.plugin[item.name] = item; + }else if(type=="button"){ + _registered.button[item.toolType] = item; + } + }; + + dojox.drawing.getRegistered = function(type, id){ + return id ? _registered[type][id] : _registered[type]; + } + +})(); +}); diff --git a/js/dojo/dojox/drawing/manager/keys.js b/js/dojo/dojox/drawing/manager/keys.js new file mode 100644 index 0000000..1acb06b --- /dev/null +++ b/js/dojo/dojox/drawing/manager/keys.js @@ -0,0 +1,269 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/keys", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.keys"); + +(function(){ + + // Ref: isEdit allows events to happen in Drawing, like TextBlocks + var isEdit = false; + + // Ref: enabled = false allows inputs outside of drawing to function + var enabled = true; + + var alphabet = "abcdefghijklmnopqrstuvwxyz"; + + dojox.drawing.manager.keys = { + // summary: + // A singleton, master object that detects + // keyboard keys and events + // Connect to it like: + // dojo.connect(this.keys, "onEnter", ....); + // + // arrowIncrement:Number + // The amount, in pixels, a selected Stencil will + // move on an arrow key event + arrowIncrement:1, + // + // arrowShiftIncrement: Number + // The amount, in pixels, a selected Stencil will + // move on an arrow key + SHIFT event + arrowShiftIncrement:10, + // + // shift: [readonly] Boolean + // Indicates whether the Shift key is currently pressed + shift:false, + // + // ctrl: [readonly] Boolean + // Indicates whether the Control key is currently pressed + ctrl:false, + // + // alt: [readonly] Boolean + // Indicates whether the Alt or Option key is currently pressed + alt:false, + // + // cmmd: [readonly] Boolean + // Indicates whether the Apple Command key is currently pressed + cmmd:false, // apple key + // + // meta: [readonly] Boolean + // Indicates whether any 'meta' key is currently pressed: + // shift || ctrl || cmmd || alt + meta:false, // any meta key + + onDelete: function(/* Event */evt){ + // summary: + // Event fires when Delete key is released + }, + onEsc: function(/* Event */evt){ + // summary: + // Event fires when ESC key is released + }, + onEnter: function(/* Event */evt){ + // summary: + // Event fires when Enter key is released + }, + onArrow: function(/* Event */evt){ + // summary: + // Event fires when an Arrow key is released + // You will have to further check if evt.keyCode + // is 37,38,39, or 40 + }, + onKeyDown: function(/* Event */evt){ + // summary: + // Event fires when any key is pressed + }, + onKeyUp: function(/* Event */evt){ + // summary: + // Event fires when any key is released + }, + + listeners:[], + register: function(options){ + // summary: + // Register an object and callback to be notified + // of events. + // NOTE: Not really used in code, but should work. + // See manager.mouse for similar usage + // + var _handle = dojox.drawing.util.common.uid("listener"); + this.listeners.push({ + handle:_handle, + scope: options.scope || window, + callback:options.callback, + keyCode:options.keyCode + }); + }, + + _getLetter: function(evt){ + if(!evt.meta && evt.keyCode>=65 && evt.keyCode<=90){ + return alphabet.charAt(evt.keyCode-65); + } + return null; + }, + + _mixin: function(evt){ + // summary: + // Internal. Mixes in key events. + evt.meta = this.meta; + evt.shift = this.shift; + evt.alt = this.alt; + evt.cmmd = this.cmmd; + evt.letter = this._getLetter(evt); + return evt; + }, + + editMode: function(_isedit){ + // summary: + // Relinquishes control of events to another portion + // of Drawing; namely the TextBlock. + isEdit = _isedit; + }, + + enable: function(_enabled){ + // summary: + // Enables or disables key events, to relinquish + // control to something outside of Drawing; input + // fields for example. + // You may need to call this directly if you are + // using textareas or contenteditables. + // NOTE: See scanForFields + enabled = _enabled; + }, + + scanForFields: function(){ + // summary: + // Scans the document for inputs + // and calls this automatically. However you may need + // to call this if you create inputs after the fact. + // + if(this._fieldCons){ + dojo.forEach(this._fieldCons, dojo.disconnect, dojo); + } + this._fieldCons = []; + dojo.query("input").forEach(function(n){ + var a = dojo.connect(n, "focus", this, function(evt){ + this.enable(false); + }); + var b = dojo.connect(n, "blur", this, function(evt){ + this.enable(true); + }); + this._fieldCons.push(a); + this._fieldCons.push(b); + }, this); + + }, + + init: function(){ + // summary: + // Initialize the keys object + // + // a little extra time is needed in some browsers + setTimeout(dojo.hitch(this, "scanForFields"), 500); + + dojo.connect(document, "blur", this, function(evt){ + // when command tabbing to another application, the key "sticks" + // this clears any key used for such activity + this.meta = this.shift = this.ctrl = this.cmmd = this.alt = false; + }); + + dojo.connect(document, "keydown", this, function(evt){ + if(!enabled){ return; } + if(evt.keyCode==16){ + this.shift = true; + } + if(evt.keyCode==17){ + this.ctrl = true; + } + if(evt.keyCode==18){ + this.alt = true; + } + if(evt.keyCode==224){ + this.cmmd = true; + } + + this.meta = this.shift || this.ctrl || this.cmmd || this.alt; + + if(!isEdit){ + this.onKeyDown(this._mixin(evt)); + if(evt.keyCode==8 || evt.keyCode==46){ + dojo.stopEvent(evt); + } + } + }); + dojo.connect(document, "keyup", this, function(evt){ + if(!enabled){ return; } + //console.log("KEY UP:", evt.keyCode); + var _stop = false; + if(evt.keyCode==16){ + this.shift = false; + } + if(evt.keyCode==17){ + this.ctrl = false; + } + if(evt.keyCode==18){ + this.alt = false; + } + if(evt.keyCode==224){ + this.cmmd = false; + } + + this.meta = this.shift || this.ctrl || this.cmmd || this.alt; + + !isEdit && this.onKeyUp(this._mixin(evt)); + + if(evt.keyCode==13){ + console.warn("KEY ENTER"); + this.onEnter(evt); + _stop = true; + } + if(evt.keyCode==27){ + this.onEsc(evt); + _stop = true; + } + if(evt.keyCode==8 || evt.keyCode==46){ + this.onDelete(evt); + _stop = true; + } + + if(_stop && !isEdit){ + dojo.stopEvent(evt); + } + }); + + dojo.connect(document, "keypress", this, function(evt){ + if(!enabled){ return; } + var inc = this.shift ? this.arrowIncrement*this.arrowShiftIncrement : this.arrowIncrement; + + var x =0, y =0; + if(evt.keyCode==32 && !isEdit){ //space + dojo.stopEvent(evt); + } + if(evt.keyCode==37){ //left + x = -inc; + } + if(evt.keyCode==38){ //up + y = -inc; + } + if(evt.keyCode==39){ //right + x = inc; + } + if(evt.keyCode==40){ //down + y = inc; + } + if(x || y){ + evt.x = x; + evt.y = y; + evt.shift = this.shift; + if(!isEdit){ + this.onArrow(evt); + dojo.stopEvent(evt); + } + } + }); + } + }; + dojo.addOnLoad(dojox.drawing.manager.keys, "init"); +})(); + +}); |
