diff options
Diffstat (limited to 'js/dojo-1.6/dojox/drawing/manager/Stencil.js')
| -rw-r--r-- | js/dojo-1.6/dojox/drawing/manager/Stencil.js | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/js/dojo-1.6/dojox/drawing/manager/Stencil.js b/js/dojo-1.6/dojox/drawing/manager/Stencil.js new file mode 100644 index 0000000..3ee6d74 --- /dev/null +++ b/js/dojo-1.6/dojox/drawing/manager/Stencil.js @@ -0,0 +1,651 @@ +/*
+ Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
+ Available via Academic Free License >= 2.1 OR the modified BSD license.
+ see: http://dojotoolkit.org/license for details
+*/
+
+
+if(!dojo._hasResource["dojox.drawing.manager.Stencil"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.drawing.manager.Stencil"] = true;
+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
+ }
+ }
+
+ );
+})();
+
+}
|
