/*
 * ***** BEGIN LICENSE BLOCK *****
 * Version: ZAPL 1.1
 * 
 * The contents of this file are subject to the Zimbra AJAX Public
 * License Version 1.1 ("License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.zimbra.com/license
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is: Zimbra AJAX Toolkit.
 * 
 * The Initial Developer of the Original Code is Zimbra, Inc.
 * Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
 * All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK *****
 */

/**
* Creates a control. A control may be created in "deferred" mode, meaning that the UI portion of the control
* will be created "Just In Time". This is useful for widgets which may want to defer construction
* of elements (e.g. DwtTreeItem) until such time as is needed, in the interest of efficiency. Note that if 
* the control is a child of the shell, it won't become visible until its z-index is set.
* @constructor
* @class
* This class represents a control, the highest-level useable widget. A control is a displayable element with
* a set of attributes (size, location, etc) and behaviors (event handlers). A control does not have child
* elements.
*
* @author Ross Dargahi
* @param parent		the parent widget
* @param className	CSS class
* @param posStyle	positioning style (absolute, static, or relative)
* @param deferred	postpone initialization until needed
*/

function DwtControl(parent, className, posStyle, deferred) {

	if (arguments.length == 0) return;
 	this.parent = parent;
	if (parent != null && !(parent instanceof DwtComposite))
		throw new DwtException("Parent must be a subclass of Composite", DwtException.INVALIDPARENT, "DwtWidget");

	this._data = new Object();
	this._eventMgr = new AjxEventMgr();
	this._disposed = false;
    
 	if (parent == null) 
 		return;

	if (!(parent instanceof DwtComposite))
		throw new DwtException("DwtControl parent must be a DwtComposite", DwtException.INVALIDPARENT, "DwtControl");
	
	this._className = className ? className : "DwtControl";
	this._posStyle = posStyle;
	if (!deferred)
		this._initCtrl();
		
	this._hoverOverListener = new AjxListener(this, this._handleHoverOver);
	this._hoverOutListener = new AjxListener(this, this._handleHoverOut);
}

DwtControl.prototype.toString = 
function() {
	return "DwtControl";
}

DwtControl.STATIC_STYLE = Dwt.STATIC_STYLE;
DwtControl.ABSOLUTE_STYLE = Dwt.ABSOLUTE_STYLE;
DwtControl.RELATIVE_STYLE = Dwt.RELATIVE_STYLE;

DwtControl.CLIP = Dwt.CLIP;
DwtControl.VISIBLE = Dwt.VISIBLE;
DwtControl.SCROLL = Dwt.SCROLL;
DwtControl.FIXED_SCROLL = Dwt.FIXED_SCROLL;

DwtControl.DEFAULT = Dwt.DEFAULT;

DwtControl._NO_DRAG = 1;
DwtControl._DRAGGING = 2;
DwtControl._DRAG_REJECTED = 3;

DwtControl._DRAG_THRESHOLD = 3;


DwtControl.prototype.addControlListener = 
function(listener) {
	this.addListener(DwtEvent.CONTROL, listener);
}

DwtControl.prototype.removeControlListener = 
function(listener) { 
	this.removeListener(DwtEvent.CONTROL, listener);
}

DwtControl.prototype.addDisposeListener = 
function(listener) {
	this.addListener(DwtEvent.DISPOSE, listener);
}

DwtControl.prototype.removeDisposeListener = 
function(listener) { 
	this.removeListener(DwtEvent.DISPOSE, listener);
}

DwtControl.prototype.addListener =
function(eventType, listener) {
	return this._eventMgr.addListener(eventType, listener); 	
}

DwtControl.prototype.notifyListeners =
function(eventType, event) {
	return this._eventMgr.notifyListeners(eventType, event);
}

DwtControl.prototype.isListenerRegistered =
function(eventType) {
	return this._eventMgr.isListenerRegistered(eventType);
}

DwtControl.prototype.removeListener = 
function(eventType, listener) {
	return this._eventMgr.removeListener(eventType, listener);
}

DwtControl.prototype.removeAllListeners = 
function(eventType) {
	return this._eventMgr.removeAll(eventType);
}

DwtControl.prototype.dispose =
function() {
	if (this._disposed) return;

	if (this.parent != null)
		this.parent._removeChild(this);

	Dwt.disassociateElementFromObject(null, this);

	this._disposed = true;
	var ev = new DwtDisposeEvent();
	ev.dwtObj = this;
	this.notifyListeners(DwtEvent.DISPOSE, ev);
}

DwtControl.prototype.getDocument =
function() {
	return document;
}

DwtControl.prototype.getData = 
function(key) {
	return this._data[key];
}

DwtControl.prototype.setData = 
function(key, value) {
  this._data[key] = value;
}

DwtControl.prototype.isDisposed =
function() {
	return this._isDisposed;
}

DwtControl.prototype.isInitialized =
function() {
	return this._ctrlInited;
}

DwtControl.prototype.reparent =
function(newParent) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
	var htmlEl = this.getHtmlElement();
	this.parent._removeChild(this);
	DwtComposite._pendingElements[this._htmlElId] = htmlEl;
	newParent._addChild(this);
	this.parent = newParent;
	// TODO do we need a reparent event?
}

DwtControl.prototype.getBounds =
function(incScroll) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getBounds(this.getHtmlElement(), incScroll);
}

DwtControl.prototype.setBounds =
function(x, y, width, height) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	var htmlElement = this.getHtmlElement();
	if (this.isListenerRegistered(DwtEvent.CONTROL)) {
		this._controlEvent.reset();
		var bds = Dwt.getBounds(htmlElement);
		this._controlEvent.oldX = bds.x;
		this._controlEvent.oldY = bds.y;
		this._controlEvent.oldWidth = bds.width;
		this._controlEvent.oldHeight = bds.height;
		Dwt.setBounds(htmlElement, x, y, width, height);
		bds = Dwt.getBounds(htmlElement);
		this._controlEvent.newX = bds.x;
		this._controlEvent.newY = bds.y;
		this._controlEvent.newWidth = bds.width;
		this._controlEvent.newHeight = bds.height;
		this.notifyListeners(DwtEvent.CONTROL, this._controlEvent);
	} else {
		Dwt.setBounds(htmlElement, x, y, width, height);
	}
	
	return this;
}

DwtControl.prototype.getClassName =
function() {
	return this._className;
}

DwtControl.prototype.setClassName =
function(className) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	this._className = className;
	this.getHtmlElement().className = className;
}

DwtControl.prototype.getCursor = 
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getCursor(this.getHtmlElement());
}

DwtControl.prototype.setCursor =
function(cursorName) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	Dwt.setCursor(this.getHtmlElement(), cursorName);
}

DwtControl.prototype.getDragSource =
function() {
	return this._dragSource;
}

DwtControl.prototype.setDragSource =
function(dragSource) {
	this._dragSource = dragSource;
	if (dragSource != null && this._ctrlCaptureObj == null) {
		this._ctrlCaptureObj = new DwtMouseEventCapture(this, DwtControl._mouseOverHdlr,
				DwtControl._mouseDownHdlr, DwtControl._mouseMoveHdlr, 
				DwtControl._mouseUpHdlr, DwtControl._mouseOutHdlr);
	}
}

DwtControl.prototype.getDropTarget =
function() {
	return this._dropTarget;
}

DwtControl.prototype.setDropTarget =
function(dropTarget) {
	this._dropTarget = dropTarget;
}

DwtControl.prototype.getEnabled =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return this._enabled;
}

DwtControl.prototype.setEnabled =
function(enabled, setHtmlElement) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	if (enabled != this._enabled) {
		this._enabled = enabled;
		if (setHtmlElement)
			this.getHtmlElement().disabled = !enabled;
	}
};

DwtControl.prototype.getElementById = 
function (anId) {
    // From my crude testing it seems that getElementById on IE6 is 
    // faster than document.all[id].
    return document.getElementById(anId);
};

DwtControl.prototype.getHtmlElement =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();

	var htmlEl = this.getElementById(this._htmlElId);

	if (htmlEl == null) {
		htmlEl = DwtComposite._pendingElements[this._htmlElId];
	} else if (!htmlEl._rendered) {
		delete DwtComposite._pendingElements[this._htmlElId];
		htmlEl._rendered = true;
	}
	
	return htmlEl;
}

DwtControl.prototype.setHtmlElementId =
function(id) {
	if (this._disposed) return;
	
	if (this._ctrlInited) {
		var htmlEl = this.getHtmlElement();
		if (!htmlEl._rendered) {
			delete DwtComposite._pendingElements[this._htmlElId];
			DwtComposite._pendingElements[id] = htmlEl;
		}
		htmlEl.id = id;
	}
	this._htmlElId = id;
}

DwtControl.prototype.getX =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getLocation(this.getHtmlElement()).x;
}

DwtControl.prototype.getXW =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
    var bounds = this.getBounds();
	return bounds.x+bounds.width;
}

DwtControl.prototype.getY =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getLocation(this.getHtmlElement()).y;
}

DwtControl.prototype.getYH =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
    var bounds = this.getBounds();
	return bounds.y+bounds.height;
}

DwtControl.prototype.getLocation =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getLocation(this.getHtmlElement());
}

DwtControl.prototype.setLocation =
function(x, y) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	if (this.isListenerRegistered(DwtEvent.CONTROL)) {
		var htmlElement = this.getHtmlElement();
		this._controlEvent.reset();
		var loc = Dwt.getLocation(htmlElement);
		this._controlEvent.oldX = loc.x;
		this._controlEvent.oldY = loc.y;
		Dwt.setLocation(htmlElement, x, y);
		loc = Dwt.getLocation(htmlElement);
		this._controlEvent.newX = loc.x;
		this._controlEvent.newY = loc.y;
		this.notifyListeners(DwtEvent.CONTROL, this._controlEvent);
	} else {
		Dwt.setLocation(this.getHtmlElement(), x, y);
	}
	return this;
}

DwtControl.prototype.getScrollStyle =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getScrollStyle(this.getHtmlElement());
}

DwtControl.prototype.setScrollStyle =
function(scrollStyle) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	Dwt.setScrollStyle(this.getHtmlElement(), scrollStyle);
}

DwtControl.prototype.getW = 
function(incScroll) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getSize(this.getHtmlElement(), incScroll).x;
}

DwtControl.prototype.getH = 
function(incScroll) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getSize(this.getHtmlElement(), incScroll).y;
}

DwtControl.prototype.getSize = 
function(incScroll) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getSize(this.getHtmlElement(), incScroll);
}

DwtControl.prototype.setSize = 
function(width, height) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	if (this.isListenerRegistered(DwtEvent.CONTROL)) {
		var htmlElement = this.getHtmlElement();
		this._controlEvent.reset();
		var sz = Dwt.getSize(htmlElement);
		this._controlEvent.oldWidth = sz.x;
		this._controlEvent.oldHeight = sz.y;
		Dwt.setSize(htmlElement, width, height);
		sz = Dwt.getSize(htmlElement);
		this._controlEvent.newWidth = sz.x;
		this._controlEvent.newHeight = sz.y;
		this.notifyListeners(DwtEvent.CONTROL, this._controlEvent);
	} else {
		Dwt.setSize(this.getHtmlElement(), width, height);
	}
	return this;
}

DwtControl.prototype.getToolTipContent =
function() {
	if (this._disposed) return;

	return this._toolTipContent;
}

DwtControl.prototype.setToolTipContent =
function(text) {
	if (this._disposed) return;

	this._toolTipContent = text;
}

DwtControl.prototype.getVisible =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getVisible(this.getHtmlElement());
}

DwtControl.prototype.setVisible =
function(visible) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	Dwt.setVisible(this.getHtmlElement(), visible);
}

DwtControl.prototype.getZIndex =
function() {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	return Dwt.getZIndex(this.getHtmlElement());
}

/**
* Sets the z-index for this object's HTML element. Since z-index is only relevant among peer
* elements, we make sure that all elements that are being displayed via z-index hang off the
* main shell.
*
* @param idx	the new z-index for this element
*/
DwtControl.prototype.setZIndex =
function(idx) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
//	if (!(this.parent instanceof DwtShell))
//		throw new DwtException("Element must have DwtShell as parent", DwtException.INVALIDPARENT, "DwtControl.setZIndex");
	Dwt.setZIndex(this.getHtmlElement(), idx);
}

/**
* Convenience function to toggle visibility using z-index. It uses the two lowest level
* z-indexes. Any further stacking will have to use setZIndex() directly.
*
* @param show	true if we want to show the element, false if we want to hide it
*/
DwtControl.prototype.zShow =
function(show) {
	this.setZIndex(show ? Dwt.Z_VIEW : Dwt.Z_HIDDEN);
}

DwtControl.prototype.preventSelection = 
function(targetEl) {
	return !this._isInputEl(targetEl);
}

DwtControl.prototype.preventContextMenu = 
function(targetEl) {
	return !this._isInputEl(targetEl);
}

DwtControl.prototype._isInputEl = 
function(targetEl) {
	var bIsInput = false;
	var tagName = targetEl.tagName.toLowerCase();
	var type = tagName == "input" ? targetEl.type.toLowerCase() : null;
	
	if (tagName == "textarea" || (type && (type == "text" || type == "password")))
		bIsInput = true;
	
	return bIsInput;
}

/* The next two methods are called by subclasses to enable event handling by DwtControl */
DwtControl.prototype._setMouseEventHdlrs =
function(clear) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	var htmlElement = this.getHtmlElement();
	if (clear !== true) {
		htmlElement.ondblclick = DwtControl._dblClick; 
		htmlElement.onmouseover = DwtControl._mouseOverHdlr;
		htmlElement.onmousemove = DwtControl._mouseMoveHdlr;
		htmlElement.onmousedown = DwtControl._mouseDownHdlr;
		htmlElement.onmouseup = DwtControl._mouseUpHdlr;
		htmlElement.onmouseout = DwtControl._mouseOutHdlr;
		htmlElement.onselectstart = DwtControl._onselectStartHdlr;
		htmlElement.oncontextmenu = DwtControl._oncontextMenuHdlr;
	} else {
		htmlElement.ondblclick = htmlElement.onmouseover = htmlElement.onmousemove =
		htmlElement.onmouseenter = htmlElement.onmouseleave = 
		htmlElement.onmousedown = htmlElement.onmouseup = htmlElement.onmouseout = null; 
	}
}

/**
 * This sets mouseenter and mouseleave to handle rollover events.
 * NOTE: It has the side effect of unsetting mouseover and mouseout.
 */
DwtControl.prototype._setIERolloverEventHdlrs =
function(clear) {
	if (AjxEnv.isIE) {
		if (this._disposed) return;
		
		if (!this._ctrlInited) 
			this._initCtrl();
		
		var htmlElement = this.getHtmlElement();
		
		
		if (clear !== true) {
			htmlElement.onmouseenter = DwtControl._mouseEnterHdlr;
			htmlElement.onmouseleave = DwtControl._mouseLeaveHdlr;
			htmlElement.onmouseover = null;
			htmlElement.onmouseout = null;
		} else {
			htmlElement.onmouseenter = null;
			htmlElement.onmouseleave = null;
		}
	}
};

DwtControl.prototype._setKeyEventHdlrs =
function(clear) {
	if (this._disposed) return;

	if (!this._ctrlInited) 
		this._initCtrl();
		
	var htmlElement = this.getHtmlElement();
	if (clear !== true) {
		htmlElement.onkeydown = DwtControl._keyDownHdlr;
		htmlElement.onkeyup = DwtControl._keyUpHdlr;
		htmlElement.onkeypress = DwtControl._keyPressHdlr;
	} else {
		htmlElement.onkeydown = htmlElement.onkeyup = htmlElement.onkeypress = null;
	}
}

/* Subclasses may override this method to return an HTML element that will represent
 * the dragging icon. The icon must be created on the DwtShell widget. If this method returns
 * null, it indicates that the drag failed*/
DwtControl.prototype._getDnDIcon =
function(dragOp) {
	DBG.println("DwtControl.prototype._getDnDIcon");
	return null;
}

/* Subclasses may override this method to set the DnD icon properties based on whether drops are
 * allowed */
DwtControl.prototype._setDnDIconState =
function(dropAllowed) {
	this._dndIcon.className = (dropAllowed) ? "DropAllowed" : "DropNotAllowed";
}


/* Subclasses may override this method to destroy the Dnd icon HTML element. */
DwtControl._junkIconId = 0;
DwtControl.prototype._destroyDnDIcon =
function(icon) {
	if (icon != null) {
		// not sure why there is no parent node, but if there isn't one,
		// let's try and do our best to get rid of the icon
		if (icon.parentNode) {
			icon.parentNode.removeChild(icon);
		} else {
			// at least hide the icon, and change the id so we can't get it
			// back later
			icon.style.zIndex = -100;
			icon.id = "DwtJunkIcon" + DwtControl._junkIconId++;
			icon = void 0;
		}
	}
}

/* Subclasses may override this method to provide feedback as to whether a possibly
 * valid capture is taking place. For example, there are instances such as when a mouse
 * down happens on a scroll bar in a DwtListView that are reported in the context of
 * the DwtListView, but which are not really a valid mouse down i.e. on a list item. In
 * such cases this function would return false */
 DwtControl.prototype._isValidDragObject =
 function(ev) {
 	return true;
 }

/* subclasses may override the following  functions to provide UI behaviour for DnD operations.
 * _dragEnter is called when a drag operation enters a control. _dragOver is called multiple times
 * as an item crossed over the control. _dragLeave is called when the drag operation exits the control. 
 * _drop is called when the item is dropped on the target.
 */
DwtControl.prototype._dragEnter =
function() {
}

DwtControl.prototype._dragOver =
function() {
}

DwtControl.prototype._dragLeave =
function() {
}

DwtControl.prototype._drop =
function() {
}

DwtControl.prototype._initCtrl = 
function() {
	this.shell = this.parent.shell || this.parent;
	var htmlElement = this.parent.getDocument().createElement("div");
	this._htmlElId = htmlElement.id = (this._htmlElId == null) ? Dwt.getNextId() : this._htmlElId;
	DwtComposite._pendingElements[this._htmlElId] = htmlElement;
	Dwt.associateElementWithObject(htmlElement, this);
	if (this._posStyle == null || this._posStyle == DwtControl.STATIC_STYLE) {
        htmlElement.style.position = DwtControl.STATIC_STYLE;
	} else {
        htmlElement.style.position = this._posStyle;
	}
	htmlElement.className = this._className;
	htmlElement.style.overflow = "visible";
	this._enabled = true;
	this._controlEvent = new DwtControlEvent();
	this._dragging = DwtControl._NO_DRAG;
	this._ctrlInited = true;
	// Make sure this is the last thing we do
	this.parent._addChild(this);
}

DwtControl._keyDownHdlr =
function(ev) {
}

DwtControl._keyUpHdlr =
function(ev) {
}

DwtControl._keyPressHdlr =
function(ev) {
}

DwtControl._dblClick = 
function(ev) {
	return DwtControl._mouseEvent(ev, DwtEvent.ONDBLCLICK);
}

DwtControl._mouseEnterHdlr =
function(ev) {
	return DwtControl._mouseOverGeneralHdlr(ev, DwtEvent.ONMOUSEENTER);
};

DwtControl._mouseOverHdlr =
function(ev) {
	return DwtControl._mouseOverGeneralHdlr(ev, DwtEvent.ONMOUSEOVER);
}

DwtControl._mouseOverGeneralHdlr = function (ev, eventName){
	// Check to see if a drag is occurring. If so, don't process the mouse
	// over events.
	var captureObj = DwtMouseEventCapture.getCaptureObj();
	if (captureObj != null) {
		ev = DwtUiEvent.getEvent(ev);
		ev._stopPropagation = true;
		return false;
	}
	var obj = DwtUiEvent.getDwtObjFromEvent(ev);
	if (obj == null)
		return false;
	var mouseEv = DwtShell.mouseEvent;
	if (obj._dragging == DwtControl._NO_DRAG) {
		mouseEv.setFromDhtmlEvent(ev);
		if (obj.isListenerRegistered(eventName))
			obj.notifyListeners(eventName, mouseEv);
		// Call the tooltip after the listeners to give them a 
		// chance to change the tooltip text
		if (obj._toolTipContent != null && !obj._hoverOverHandled) {
			var shell = DwtShell.getShell(window);
			var manager = shell.getHoverMgr();
			manager.reset();
			manager.setHoverOverDelay(DwtToolTip.TOOLTIP_DELAY);
			manager.setHoverOverListener(obj._hoverOverListener);
			manager.hoverOver(mouseEv.docX, mouseEv.docY);
		}
	}
	mouseEv._stopPropagation = true;
	mouseEv._returnValue = false;
	mouseEv.setToDhtmlEvent(ev);
	return false;

};

DwtControl._mouseDownHdlr =
function(ev) {
  		
	var obj = DwtUiEvent.getDwtObjFromEvent(ev);
	if (!obj)
		return false;
		
	if (obj._toolTipContent != null) {
		var shell = DwtShell.getShell(window);
		var manager = shell.getHoverMgr();
		manager.setHoverOutListener(obj._hoverOutListener);
		manager.hoverOut();
	}
	
	// If we have a dragSource, then we need to start capturing mouse events
	var mouseEv = DwtShell.mouseEvent;
	mouseEv.setFromDhtmlEvent(ev);
	if (obj._dragSource != null && mouseEv.button == DwtMouseEvent.LEFT
			&& obj._isValidDragObject(mouseEv)) 
	{
		try {
			obj._ctrlCaptureObj.capture();
		} catch (ex) {
			DBG.dumpObj(ex);
		}
		obj._dragOp = (mouseEv.ctrlKey) ? Dwt.DND_DROP_COPY : Dwt.DND_DROP_MOVE;
		obj._dragStartX = mouseEv.docX;
		obj._dragStartY = mouseEv.docY;
	}
	
	return DwtControl._mouseEvent(ev, DwtEvent.ONMOUSEDOWN, mouseEv);
}

DwtControl._mouseMoveHdlr =
function(ev) {
	// If captureObj == null, then we are not a Draggable control or a 
	// mousedown event has not occurred , so do the default behaviour, 
	// else do the draggable behaviour 
	var captureObj = DwtMouseEventCapture.getCaptureObj();
	var obj = (captureObj == null) ? DwtUiEvent.getDwtObjFromEvent(ev) : 
	            captureObj.targetObj;
 	if (obj == null)
		return false;  
	var mouseEv = DwtShell.mouseEvent;
	mouseEv.setFromDhtmlEvent(ev);

	// This following can happen during a DnD operation if the mouse moves
	// out the window. This seems to happen on IE only
	if (mouseEv.docX < 0 || mouseEv.docY < 0) {
		mouseEv._stopPropagation = true;
		mouseEv._returnValue = false;
		mouseEv.setToDhtmlEvent(ev);
		return false;
	}
	
	// If we are not draggable or if have not started dragging and are 
	// within the Drag threshold then simple handle it as a move
	if (obj._dragSource == null || captureObj == null
		|| (obj != null && obj._dragging == DwtControl._NO_DRAG 
			&& Math.abs(obj._dragStartX - mouseEv.docX) < 
			   DwtControl._DRAG_THRESHOLD 
			&& Math.abs(obj._dragStartY - mouseEv.docY) < 
			   DwtControl._DRAG_THRESHOLD)) {
		if (obj._toolTipContent != null && !obj._hoverOverHandled) {
			var shell = DwtShell.getShell(window);
			var manager = shell.getHoverMgr();
			// NOTE: mouseOver already init'd other hover settings
			manager.hoverOver(mouseEv.docX, mouseEv.docY);
		}
		return DwtControl._mouseEvent(ev, DwtEvent.ONMOUSEMOVE);
	} else {
		// Deal with mouse moving out of the window etc...
		
		// If we are not dragging, then see if we can drag. 
		// If we cannot drag this control, then
		// we will set dragging status to DwtControl._DRAG_REJECTED 
		if (obj._dragging == DwtControl._NO_DRAG) {
			obj._dragOp = obj._dragSource._beginDrag(obj._dragOp, obj);
			if (obj._dragOp != Dwt.DND_DROP_NONE) {
				obj._dragging = DwtControl._DRAGGING;
				obj._dndIcon = obj._getDnDIcon(obj._dragOp);
				if (obj._dndIcon == null)
					obj._dragging = DwtControl._DRAG_REJECTED;
			} else {
				obj._dragging = DwtControl._DRAG_REJECTED;
			}
		}
		
		// If we are draggable, then see if the control under the mouse 
		// (if one exists) will allow us to be dropped on it. 
		// This is done by (a) making sure that the drag source data type
		// can be dropped onto the target, and (b) that the application 
		// will allow it (i.e. via the listeners on the DropTarget
		if (obj._dragging != DwtControl._DRAG_REJECTED) {
			var destDwtObj = mouseEv.dwtObj;
			if (destDwtObj && destDwtObj._dropTarget && destDwtObj != obj) {
				if (destDwtObj != obj._lastDestDwtObj || 
					destDwtObj._dropTarget.hasMultipleTargets()) {
					if (destDwtObj._dropTarget._dragEnter(
										obj._dragOp, 
										destDwtObj, 
										obj._dragSource._getData(), ev)) {

						obj._setDnDIconState(true);
						obj._dropAllowed = true;
						destDwtObj._dragEnter();
					} else {
						obj._setDnDIconState(false);;
						obj._dropAllowed = false;
					}
				}
			} else {
				obj._setDnDIconState(false);
			}
			
			if (obj._lastDestDwtObj && obj._lastDestDwtObj != destDwtObj 
				&& obj._lastDestDwtObj._dropTarget 
				&& obj._lastDestDwtObj != obj) {

				obj._lastDestDwtObj._dragLeave();
				obj._lastDestDwtObj._dropTarget._dragLeave();
			}
			
			obj._lastDestDwtObj = destDwtObj;
					
			Dwt.setLocation(obj._dndIcon, mouseEv.docX + 2, mouseEv.docY + 2);
			// TODO set up timed event to fire off another mouseover event. 
			// Also need to cancel
			// any pending event, though we should do the cancel earlier 
			// in the code
		} else {
			// XXX: confirm w/ ROSS!
			DwtControl._mouseEvent(ev, DwtEvent.ONMOUSEMOVE);
		}
		mouseEv._stopPropagation = true;
		mouseEv._returnValue = false;
		mouseEv.setToDhtmlEvent(ev);
		return false;
	}
}

DwtControl._mouseUpHdlr =
function(ev) {
	// See if are doing a drag n drop operation
	var captureObj = DwtMouseEventCapture.getCaptureObj();
	var obj = (captureObj == null) ? DwtUiEvent.getDwtObjFromEvent(ev) : captureObj.targetObj;
	if (!obj._dragSource || !captureObj) {
		return DwtControl._mouseEvent(ev, DwtEvent.ONMOUSEUP);
	} else {
		captureObj.release();
		var mouseEv = DwtShell.mouseEvent;
		mouseEv.setFromDhtmlEvent(ev);
		if (obj._dragging != DwtControl._DRAGGING) {
			obj._dragging = DwtControl._NO_DRAG;
			return DwtControl._mouseEvent(ev, DwtEvent.ONMOUSEUP, mouseEv);
		} else {
			obj._lastDestDwtObj = null;
			var destDwtObj = mouseEv.dwtObj;
			if (destDwtObj != null && destDwtObj._dropTarget != null && 
				obj._dropAllowed && destDwtObj != obj) {
				destDwtObj._drop();
				destDwtObj._dropTarget._drop(obj._dragSource._getData(), ev);
				obj._dragSource._endDrag();
				obj._destroyDnDIcon(obj._dndIcon);
				obj._dragging = DwtControl._NO_DRAG;
			} else {
				// The following code sets up the drop effect for when an 
				// item is dropped onto an invalid target. Basically the 
				// drag icon will spring back to its starting location.
				obj._dragEndX = mouseEv.docX;
				obj._dragEndY = mouseEv.docY;
				if (obj._badDropAction == null) {
					obj._badDropAction = new AjxTimedAction();
					obj._badDropAction.method = 
						DwtControl.prototype._badDropEffect;
					obj._badDropAction.obj = obj;	
				}
				obj._badDropAction.params.removeAll();
				
				// Line equation is y = mx + c. Solve for c, and set up d (direction)
				var m = (obj._dragEndY - obj._dragStartY) / (obj._dragEndX - obj._dragStartX);
				obj._badDropAction.params.add(m);
				obj._badDropAction.params.add(obj._dragStartY - (m * obj._dragStartX));
				obj._badDropAction.params.add((obj._dragStartX - obj._dragEndX < 0) ? -1 : 1);
				AjxTimedAction.scheduleAction(obj._badDropAction, 0);
			}
			mouseEv._stopPropagation = true;
			mouseEv._returnValue = false;
			mouseEv.setToDhtmlEvent(ev);
			return false;
		}
	}
}


DwtControl.prototype._badDropEffect =
function(m, c, d) {
	var usingX = (Math.abs(m) <= 1);
	// Use the bigger delta to control the snap effect
	var delta = usingX ? this._dragStartX - this._dragEndX : this._dragStartY - this._dragEndY;
	if (delta * d > 0) {
		if (usingX) {
			this._dragEndX += (30 * d);
			this._dndIcon.style.top = m * this._dragEndX + c;
			this._dndIcon.style.left = this._dragEndX;
		} else {
			this._dragEndY += (30 * d);
			this._dndIcon.style.top = this._dragEndY;
			this._dndIcon.style.left = (this._dragEndY - c) / m;
		}	
		AjxTimedAction.scheduleAction(this._badDropAction, 0);
 	} else {
  		this._destroyDnDIcon(this._dndIcon);
		this._dragging = DwtControl._NO_DRAG;
  	}
}

DwtControl._mouseOutHdlr =
function(ev) {
	return DwtControl._mouseOutGeneralHdlr(ev, DwtEvent.ONMOUSEOUT);
};

DwtControl._mouseLeaveHdlr =
function(ev) {
	return DwtControl._mouseOutGeneralHdlr(ev, DwtEvent.ONMOUSELEAVE);
};

DwtControl._mouseOutGeneralHdlr = function (ev, eventName){
	var obj = DwtUiEvent.getDwtObjFromEvent(ev);
	if (obj == null)
		return false;
	if (obj._toolTipContent != null) {
		var shell = DwtShell.getShell(window);
		var manager = shell.getHoverMgr();
		manager.setHoverOutListener(obj._hoverOutListener);
		manager.hoverOut();
	}
	return DwtControl._mouseEvent(ev, eventName);
};

DwtControl._onselectStartHdlr = 
function(ev) {
	var obj = DwtUiEvent.getDwtObjFromEvent(ev);
	if (!obj)
		return false;

	return DwtControl._mouseEvent(ev, DwtEvent.ONSELECTSTART);
}

DwtControl._oncontextMenuHdlr = 
function(ev) {
	var obj = DwtUiEvent.getDwtObjFromEvent(ev);
	if (!obj)
		return false;

	return DwtControl._mouseEvent(ev, DwtEvent.ONCONTEXTMENU);
}

DwtControl._mouseEvent = 
function(ev, eventType, mouseEvIn) {
	var obj = DwtUiEvent.getDwtObjFromEvent(ev);
	
	if (obj == null)
		return false;
		
	var mouseEv = (mouseEvIn == null ) ? DwtShell.mouseEvent : mouseEvIn;
        // notify the world that we have a mouse event.
        // By notifying before any widget registered listener receives the
        // messages, we're saying that all widget related listeners will take
        // precedence over outside listeners.
	DwtEventManager.notifyListeners( eventType, mouseEv);

	if (obj.isListenerRegistered(eventType)) {
		if (mouseEvIn == null)
			mouseEv.setFromDhtmlEvent(ev);	
		obj.notifyListeners(eventType, mouseEv);
	}
	
	if (!mouseEv._populated) {
		mouseEv._stopPropagation = true;
		mouseEv._returnValue = false;
	}
	mouseEv.setToDhtmlEvent(ev);
	return mouseEv._returnValue;
}

DwtControl.prototype.setContent =
function(content) {
	if (content)
		this.getHtmlElement().innerHTML = content;
}

DwtControl.prototype.clearContent =
function() {
	this.getHtmlElement().innerHTML = "";
}

/** Sub-classes that override this method must set this._hoverHandled to true. */
DwtControl.prototype._handleHoverOver = function(event) {
	if (this._toolTipContent != null) {
		var shell = DwtShell.getShell(window);
		var tooltip = shell.getToolTip();
		tooltip.setContent(this._toolTipContent);
		tooltip.popup(event.x, event.y);
		this._hoverHandled = true;
	}
}

/** Sub-classes that override this method must set this._hoverHandled to false. */
DwtControl.prototype._handleHoverOut = function(event) {
	var shell = DwtShell.getShell(window);
	var tooltip = shell.getToolTip();
	tooltip.popdown();
	this._hoverHandled = false;
}
