/*
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.Mouse"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.drawing.manager.Mouse"] = true;
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,
// rightClickMenu: boolean
// If true, right clicks bubble up so that context menus
// can be attached to them or the default can be shown.
// Otherwise right click is interpreted the same as a left click.
rightClickMenu: false,
// 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){
if(evt.button != dojo.mouseButtons.RIGHT){
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(this.rightClickMenu && (evt.button == dojo.mouseButtons.RIGHT) && this.id == "mse"){
// Allow event to bubble for right click, for 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);
}
}
}
);
}