/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.widgets;

import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.Accessible;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DragDetectListener;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Drawable;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GCData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics2.Surface;

public abstract class Control extends Widget implements Drawable {
	int x, y, width, height;
	int drawCount;
	String toolTipText;
	Object layoutData;
	Composite parent;
	Cursor cursor;
	Color foreground;
	Color background;
	Image backgroundImage;
	Font font;
	Menu menu;
	
Control () {
}
	
public Control (Composite parent, int style) {
	super (parent, style);
	this.parent = parent;
}

public void addControlListener (ControlListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.Resize,typedListener);
	addListener (SWT.Move,typedListener);
}

public void addDragDetectListener (DragDetectListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.DragDetect,typedListener);
}

public void addFocusListener (FocusListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.FocusIn,typedListener);
	addListener (SWT.FocusOut,typedListener);
}

public void addHelpListener (HelpListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.Help, typedListener);
}

public void addKeyListener (KeyListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.KeyUp,typedListener);
	addListener (SWT.KeyDown,typedListener);
}

public void addMenuDetectListener (MenuDetectListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.MenuDetect, typedListener);
}

public void addMouseListener (MouseListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.MouseDown,typedListener);
	addListener (SWT.MouseUp,typedListener);
	addListener (SWT.MouseDoubleClick,typedListener);
}

public void addMouseMoveListener (MouseMoveListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.MouseMove,typedListener);
}

public void addMouseTrackListener (MouseTrackListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.MouseEnter,typedListener);
	addListener (SWT.MouseExit,typedListener);
	addListener (SWT.MouseHover,typedListener);
}

public void addMouseWheelListener (MouseWheelListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.MouseWheel, typedListener);
}

public void addPaintListener (PaintListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.Paint,typedListener);
}

void addParent () {
	parent.addControl (this);
}

public void addTraverseListener (TraverseListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener (listener);
	addListener (SWT.Traverse,typedListener);
}

void createHandle (Widget parent, int style, int index) {
	_createHandle (parent, style, index);
}

void createWidget (Display display, Widget parent, int style, int index) {
	state |= DRAG_DETECT;
	this.parent = (Composite) parent;
	super.createWidget (display, parent, style, index);
//	checkOrientation (parent);
//	checkBackground ();
//	checkBuffered ();
	init();
	createHandle (parent, style, index);
	hookEvents ();
	setInitialBounds ();
	register ();	
//	subclass ();
//	setDefaultFont ();
//	checkMirrored ();
//	checkBorder ();
//	if ((state & PARENT_BACKGROUND) != 0) {
//		setBackground ();
//	}
	addParent();
}

public Point computeSize (int wHint, int hHint) {
	return computeSize (wHint, hHint, true);
}

public Point computeSize (int wHint, int hHint, boolean changed) {
//TODO make use of changed
	Rectangle rectangle = getNativeBounds();
	int width = rectangle.width;
	int height = rectangle.height;
	if (wHint != SWT.DEFAULT) width = wHint;
	if (hHint != SWT.DEFAULT) height = hHint;
	return new Point (width, height);
}

Control computeTabGroup () {
	if (isTabGroup ()) return this;
	return parent.computeTabGroup ();
}

Control computeTabRoot () {
	Control [] tabList = parent._getTabList ();
	if (tabList != null) {
		int index = 0;
		while (index < tabList.length) {
			if (tabList [index] == this) break;
			index++;
		}
		if (index == tabList.length) {
			if (isTabGroup ()) return this;
		}
	}
	return parent.computeTabRoot ();
}

Control [] computeTabList () {
	if (isTabGroup ()) {
		if (getVisible () && getEnabled ()) {
			return new Control [] {this};
		}
	}
	return new Control [0];
}


public boolean dragDetect (Event event) {
	checkWidget ();
	if (event == null) error (SWT.ERROR_NULL_ARGUMENT);
	return _dragDetect (event.button, event.count, event.stateMask, event.x, event.y);
}

public boolean dragDetect (MouseEvent event) {
	checkWidget ();
	if (event == null) error (SWT.ERROR_NULL_ARGUMENT);
	return _dragDetect (event.button, event.count, event.stateMask, event.x, event.y);
}


Control findBackgroundControl () {
	if (background != null || backgroundImage != null) return this;
	return (state & PARENT_BACKGROUND) != 0 ? parent.findBackgroundControl () : null;
}

void fixFocus (Control focusControl) {
	//TODO - fixFocus
//	Shell shell = getShell ();
//	Control control = this;
//	while (control != shell && (control = control.parent) != null) {
//		if (control.setFixedFocus ()) return;
//	}
//	shell.setSavedFocus (focusControl);
//	OS.SetFocus (0);
}

public boolean forceFocus () {
	checkWidget ();
//	if (display.focusEvent == SWT.FocusOut) return false;
//	Decorations shell = menuShell ();
//	shell.setSavedFocus (this);
	if (!isEnabled () || !isVisible ()/* || !isActive ()*/) return false;
	if (isFocusControl ()) return true;
//	shell.setSavedFocus (null);
	_forceFocus ();
	if (isDisposed ()) return false;
//	shell.setSavedFocus (this);
	return isFocusControl ();
}

public Accessible getAccessible () {
	//TODO-
	return null;
}

public Color getBackground () {
	checkWidget();
	Control control = findBackgroundControl ();
	if (control == null) control = this;
	return control.background != null ? control.background : control._defaultBackground ();
}

public Image getBackgroundImage () {
	checkWidget();
	Control control = findBackgroundControl ();
	if (control == null) control = this;
	return control.backgroundImage;
}

public int getBorderWidth () {
	//TODO-
	return 0;
}

public Rectangle getBounds () {
	checkWidget ();
	return new Rectangle (x, y, width, height);
}

public Cursor getCursor () {
	checkWidget ();
	return cursor;
}

public boolean getDragDetect () {
	checkWidget ();
	return (state & DRAG_DETECT) != 0;
}

public boolean getEnabled () {
	checkWidget ();
	return (state & DISABLED) == 0;
}

public Font getFont () {
	checkWidget ();
	return font != null ? font : _defaultFont ();
}

public Color getForeground () {
	checkWidget ();
	return foreground != null ? foreground : _defaultForeground ();
}

public Object getLayoutData () {
	checkWidget ();
	return layoutData;
}

public Point getLocation () {
	checkWidget ();
	return new Point (x, y);
}

public Menu getMenu () {
	checkWidget ();
	return menu;
}

public Monitor getMonitor () {
	checkWidget ();
	return _getMonitor ();
}

protected Rectangle getNativeBounds () {
	checkWidget ();
	Rectangle rectangle = new Rectangle (0,0,0,0);
	_getNativeBounds(rectangle);
	return rectangle;
}

public Composite getParent () {
	checkWidget ();
	return parent;
}

public Shell getShell () {
	checkWidget ();
	return parent.getShell ();
}

public Point getSize () {
	checkWidget ();
	return new Point (width, height);
}

Surface getSurface () {
	return null;
}

public String getToolTipText () {
	checkWidget ();
	return toolTipText;
}

public boolean getVisible () {
	checkWidget ();
	return (state & HIDDEN) == 0;
}

//TODO implement all events hook 
void hookEvents () {
//	_hookEvents("none", 		SWT.DragDetect);
	_hookEvents("onfocus", 		SWT.FocusIn);
	_hookEvents("onblur", 		SWT.FocusOut);
//	_hookEvents("none", 		SWT.Help);
	_hookEvents("onkeydown", 	SWT.KeyDown);
	_hookEvents("onkeyup", 		SWT.KeyUp);
	_hookEvents("ondblclick", 	SWT.MouseDoubleClick);
	_hookEvents("onmousedown", 	SWT.MouseDown);
//TODO Find mouseenter event, not sure if really necessary!
	//_hookEvents("onmouseover", 	SWT.MouseEnter);
	//_hookEvents("onmouseout", 	SWT.MouseExit);
	//_hookEvents("onmouseover", 	SWT.MouseHover);
	_hookEvents("onmouseup", 	SWT.MouseUp);
	_hookEvents("mousemove", 	SWT.MouseMove);
//	_hookEvents("none",		 	SWT.Move);
//	_hookEvents("none", 		SWT.Paint);
	_hookEvents("onresize", 	SWT.Resize);
//	_hookEvents("none",		 	SWT.Traverse);
	_hookContextMenu("oncontextmenu");
}

public int internal_new_GC (GCData data) {
	checkWidget ();
	if (data != null) {
		data.surface = getSurface ();
		data.surface.clear();
		int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
		if ((data.style & mask) == 0) {
			data.style |= style & (mask | SWT.MIRRORED);
		} else {
			if ((data.style & SWT.RIGHT_TO_LEFT) != 0) {
				data.style |= SWT.MIRRORED;
			}
		}
		data.device = display;
		data.background = getBackground();
		data.foreground = getForeground();
		data.font = getFont();
		return 1;
	}
	return 0;
}

public void internal_dispose_GC (int hDC, GCData data) {
	
}

public boolean isEnabled () {
	checkWidget ();
	return getEnabled () && parent.isEnabled ();
}

boolean isFocusAncestor (Control control) {
	while (control != null && control != this && !(control instanceof Shell)) {
		control = control.parent;
	}
	return control == this;
}

public boolean isFocusControl () {
	checkWidget();
	return this == display.getFocusControl();
}

public boolean isReparentable () {
	checkWidget();
	return _isReparentable ();
}

boolean isShowing () {
	/*
	* This is not complete.  Need to check if the
	* widget is obscured by a parent or sibling.
	*/
	if (!isVisible ()) return false;
	Control control = this;
	while (control != null) {
		Point size = control.getSize ();
		if (size.x == 0 || size.y == 0) {
			return false;
		}
		control = control.parent;
	}
	return true;
}

boolean isTabGroup () {
	Control [] tabList = parent._getTabList ();
	if (tabList != null) {
		for (int i=0; i<tabList.length; i++) {
			if (tabList [i] == this) return true;
		}
	}
	return _isTabGroup ();
}

boolean isTabItem () {
	Control [] tabList = parent._getTabList ();
	if (tabList != null) {
		for (int i=0; i<tabList.length; i++) {
			if (tabList [i] == this) return false;
		}
	}
	return _isTabItem ();
}


public boolean isVisible () {
	checkWidget ();
	return getVisible () && parent.isVisible ();
}

void markLayout (boolean changed, boolean all) {
	/* Do nothing */
}

Decorations menuShell () {
	return parent.menuShell ();
}

public void moveAbove (Control control) {
	checkWidget ();
	if (control != null) {
		if (control.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
		if (parent != control.parent) return;
	}
	_setZOrder (control, true);
}

public void moveBelow (Control control) {
	checkWidget ();
	if (control != null) {
		if (control.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
		if (parent != control.parent) return;
	}
	_setZOrder (control, false);
}

public void pack () {
	checkWidget ();
	pack (true);
}

public void pack (boolean changed) {
	checkWidget ();
	setSize (computeSize (SWT.DEFAULT, SWT.DEFAULT, changed));
}

public void redraw () {
	checkWidget ();
	_redraw (0, 0, 0, 0, false);
}

public void redraw (int x, int y, int width, int height, boolean all) {
	checkWidget ();
	if (width == 0 || height == 0) return;
	_redraw (x, y, width, height, false);
}

void releaseHandle () {
	super.releaseHandle ();
//	handle = 0;
	parent = null;
}

void releaseParent () {
	parent.removeControl (this);
}

void releaseWidget () {
	super.releaseWidget ();
//	if (OS.IsDBLocale) {
//		OS.ImmAssociateContext (handle, 0);
//	}
//	if (toolTipText != null) {
//		setToolTipText (getShell (), null);
//	}
	if (menu != null && !menu.isDisposed ()) {
		menu.dispose ();
	}
	background = null;
	backgroundImage = null;
	cursor = null;
	font = null;
	foreground = null;
	layoutData = null;
	menu = null;
	toolTipText = null;
//	unsubclass ();
//	deregister ();
//	if (accessible != null) {
//		accessible.internal_dispose_Accessible ();
//	}
//	accessible = null;
}

public void removeControlListener (ControlListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.Move, listener);
	eventTable.unhook (SWT.Resize, listener);
}

public void removeDragDetectListener (DragDetectListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.DragDetect, listener);
}

public void removeFocusListener (FocusListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.FocusIn, listener);
	eventTable.unhook (SWT.FocusOut, listener);
}

public void removeHelpListener (HelpListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.Help, listener);
}

public void removeKeyListener (KeyListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.KeyUp, listener);
	eventTable.unhook (SWT.KeyDown, listener);
}

public void removeMenuDetectListener (MenuDetectListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.MenuDetect, listener);
}

public void removeMouseListener (MouseListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.MouseDown, listener);
	eventTable.unhook (SWT.MouseUp, listener);
	eventTable.unhook (SWT.MouseDoubleClick, listener);

}

public void removeMouseMoveListener (MouseMoveListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.MouseMove, listener);
}

public void removeMouseTrackListener (MouseTrackListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.MouseEnter, listener);
	eventTable.unhook (SWT.MouseExit, listener);
	eventTable.unhook (SWT.MouseHover, listener);
}

public void removeMouseWheelListener (MouseWheelListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.MouseWheel, listener);
}

public void removePaintListener (PaintListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook(SWT.Paint, listener);
}

public void removeTraverseListener (TraverseListener listener) {
	checkWidget ();
	if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (eventTable == null) return;
	eventTable.unhook (SWT.Traverse, listener);
}

void sendEnter () {
	if (this == display.currentControl) return;
	if (display.currentControl != null) {
		//TODO: Put in x,y and state mask
		display.currentControl.sendEvent(SWT.MouseExit);
	}
	display.currentControl = this;
	//TODO: Put in x,y and state mask
	sendEvent(SWT.MouseEnter);
}

public void sendResize	() {
	Rectangle newBounds = new Rectangle(0,0,0,0); 
	_getBounds(newBounds);
	boolean sameOrigin = x == newBounds.x && y == newBounds.y;
	boolean sameExtent = width == newBounds.width && height == newBounds.height;
	x = newBounds.x;
	y = newBounds.y;
	width = newBounds.width;
	height = newBounds.height;
	//TODO: Layout needs to be called;
	if (!sameOrigin) sendEvent(SWT.Move);
	if (!sameExtent) sendEvent(SWT.Resize);
}

public void setBackground (Color color) {
	checkWidget ();
	if (color != null && color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
	if (this.background == color) return;
	if (this.background != null && this.background.equals (color)) {
		this.background = color;
		return;
	}
	this.background = color;
	_setBackground (color);
}

public void setBackgroundImage (Image image) {
	checkWidget();
	if (image != null && image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
	if (image == backgroundImage) return;
	if (backgroundImage != null && backgroundImage.equals (image)) {
		backgroundImage = image;
		return;
	}
	backgroundImage = image;
	_setBackgroundImage (image);
}

public void setBounds (int x, int y, int width, int height) {
	checkWidget();
	setBounds (x, y, Math.max (0, width), Math.max (0, height), MOVED | RESIZED);
}

int setBounds (int x, int y, int width, int height, int flags) {
	boolean sameOrigin = true, sameExtent = true;
	if ((flags & MOVED) != 0) {
		sameOrigin = x == this.x && y == this.y;
	} else {
		x = this.x;
		y = this.y;
	}
	if ((flags & RESIZED) != 0) {
		sameExtent = width == this.width && height == this.height;
	} else {
		width = this.width;
		height = this.height;
	}
	if (!sameOrigin || !sameExtent) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		_setBounds (x, y, width, height);
	}
	int result = 0;
	if (!sameOrigin) {
		sendEvent (SWT.Move);
		result |= MOVED;
	}
	if (!sameExtent) {
		sendEvent (SWT.Resize);
		result |= RESIZED;
	}
	return result;
}

public void setBounds (Rectangle rect) {
	checkWidget ();
	if (rect == null) error (SWT.ERROR_NULL_ARGUMENT);
	setBounds (rect.x, rect.y, rect.width, rect.height);
}

public void setCapture (boolean capture) {
	checkWidget();
	_setCapture (capture);
}

public void setCursor (Cursor cursor) {
	checkWidget ();
	if (cursor != null && cursor.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
	this.cursor = cursor;
	_setCursor (cursor);
}

public void setDragDetect (boolean dragDetect) {
	checkWidget ();
	state = dragDetect ? state & ~DRAG_DETECT : state | DRAG_DETECT;
}

public void setEnabled (boolean enabled) {
	checkWidget();
	if (((state & DISABLED) == 0) == enabled) return;
	Control control = null;
	boolean fixFocus = false;
	if (!enabled) {
//		if (display.focusEvent != SWT.FocusOut) {
			control = display.getFocusControl ();
			fixFocus = isFocusAncestor (control);
//		}
	}
	state = enabled ? state & ~DISABLED : state | DISABLED;
	_setEnabled (enabled);
	if (isDisposed ()) return;	
	if (fixFocus) fixFocus (control);
}

public boolean setFocus () {
	checkWidget();
	if ((style & SWT.NO_FOCUS) != 0) return false;
	return forceFocus ();
}

public void setFont (Font font) {
	checkWidget ();
	if (font != null && font.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
	if (this.font == font) return;
	if (this.font != null && this.font.equals (font)) {
		this.font = font;
		return;
	}
	this.font = font;
	_setFont (font);
}

public void setForeground (Color color) {
	checkWidget ();
	if (color != null && color.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
	if (this.foreground == color) return;
	if (this.foreground != null && this.foreground.equals (color)) {
		this.foreground = color;
		return;
	}
	this.foreground = color;
	_setForeground (color);
}

void setInitialBounds () {
	this._setBounds(0,0,0,0);
}

public void setLayoutData (Object layoutData) {
	checkWidget ();
	this.layoutData = layoutData;
}

public void setLocation (int x, int y) {
	checkWidget();
	setBounds (x, y, 0, 0, MOVED);
}

public void setLocation (Point location) {
	checkWidget ();
	if (location == null) error (SWT.ERROR_NULL_ARGUMENT);
	setLocation (location.x, location.y);
}

public void setMenu (Menu menu) {
	checkWidget ();
	if (menu != null) {
		if (menu.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
		if ((menu.style & SWT.POP_UP) == 0) {
			error (SWT.ERROR_MENU_NOT_POP_UP);
		}
		if (menu.parent != menuShell ()) {
			error (SWT.ERROR_INVALID_PARENT);
		}
	}
	this.menu = menu;
}

public void setRedraw (boolean redraw) {
	checkWidget();
	if (redraw) {
		if (--drawCount == 0) {
			_setRedraw (redraw);
		}
	} else {
		if (drawCount++ == 0) {
			_setRedraw (redraw);
		}
	}
}

public void setSize (int width, int height) {
	checkWidget();
	setBounds (0, 0, Math.max (0, width), Math.max (0, height), RESIZED);
}

public void setSize (Point size) {
	checkWidget ();
	if (size == null) error (SWT.ERROR_NULL_ARGUMENT);
	setSize (size.x, size.y);
}

boolean setTabGroupFocus () {
	return setTabItemFocus ();
}

boolean setTabItemFocus () {
	if (!isShowing ()) return false;
	return forceFocus ();
}

public void setToolTipText (String string) {
	checkWidget ();
	toolTipText = string;
	_setToolTipText (string);
}

public void setVisible (boolean visible) {
	checkWidget ();
	if (((state & HIDDEN) == 0) == visible) return;
	if (visible) {
		sendEvent (SWT.Show);
		if (isDisposed ()) return;
	}
	Control control = null;
	boolean fixFocus = false;
	if (!visible) {
//		if (display.focusEvent != SWT.FocusOut) {
			control = display.getFocusControl ();
			fixFocus = isFocusAncestor (control);
//		}
	}
	state = visible ? state & ~HIDDEN : state | HIDDEN;
	_setVisible (visible);
	if (!visible) {
		sendEvent (SWT.Hide);
		if (isDisposed ()) return;
	}
	if (fixFocus) fixFocus (control);
}

public Point toControl (int x, int y) {
	checkWidget ();
	return display.map (null, this, x, y);
}

public Point toControl (Point point) {
	checkWidget ();
	if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
	return toControl (point.x, point.y);
}

public Point toDisplay (int x, int y) {
	checkWidget ();
	return display.map(this, null, x, y);
}

public Point toDisplay (Point point) {
	checkWidget ();
	if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
	return toDisplay (point.x, point.y);
}

public boolean traverse (int traversal) {
	checkWidget ();
	Event event = new Event ();
	event.doit = true;
	event.detail = traversal;
	return traverse (event);
}

boolean traverseEscape () {
	return false;
}

boolean traverseGroup (boolean next) {
	Control root = computeTabRoot ();
	Control group = computeTabGroup ();
	Control [] list = root.computeTabList ();
	int length = list.length;
	int index = 0;
	while (index < length) {
		if (list [index] == group) break;
		index++;
	}
	/*
	* It is possible (but unlikely), that application
	* code could have disposed the widget in focus in
	* or out events.  Ensure that a disposed widget is
	* not accessed.
	*/
	if (index == length) return false;
	int start = index, offset = (next) ? 1 : -1;
	while ((index = ((index + offset + length) % length)) != start) {
		Control control = list [index];
		if (!control.isDisposed () && control.setTabGroupFocus ()) {
			return true;
		}
	}
	if (group.isDisposed ()) return false;
	return group.setTabGroupFocus ();
}

boolean traverseItem (boolean next) {
	Control [] children = parent._getChildren ();
	int length = children.length;
	int index = 0;
	while (index < length) {
		if (children [index] == this) break;
		index++;
	}
	/*
	* It is possible (but unlikely), that application
	* code could have disposed the widget in focus in
	* or out events.  Ensure that a disposed widget is
	* not accessed.
	*/
	if (index == length) return false;
	int start = index, offset = (next) ? 1 : -1;
	while ((index = (index + offset + length) % length) != start) {
		Control child = children [index];
		if (!child.isDisposed () && child.isTabItem ()) {
			if (child.setTabItemFocus ()) return true;
		}
	}
	return false;
}

boolean traverseMnemonic (char key) {
//	if (mnemonicHit (key)) {
//		OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
//		return true;
//	}
	return false;
}

boolean traversePage (boolean next) {
	return false;
}

boolean traverseReturn () {
	return false;
}

boolean traverse (Event event) {
	/*
	* It is possible (but unlikely), that application
	* code could have disposed the widget in the traverse
	* event.  If this happens, return true to stop further
	* event processing.
	*/	
	sendEvent (SWT.Traverse, event);
	if (isDisposed ()) return true;
	if (!event.doit) return false;
	switch (event.detail) {
		case SWT.TRAVERSE_NONE: return true;
		case SWT.TRAVERSE_ESCAPE: return traverseEscape ();
		case SWT.TRAVERSE_RETURN: return traverseReturn ();
		case SWT.TRAVERSE_TAB_NEXT: return traverseGroup (true);
		case SWT.TRAVERSE_TAB_PREVIOUS: return traverseGroup (false);
		case SWT.TRAVERSE_ARROW_NEXT: return traverseItem (true);
		case SWT.TRAVERSE_ARROW_PREVIOUS: return traverseItem (false);
		case SWT.TRAVERSE_MNEMONIC: return traverseMnemonic (event.character);	
		case SWT.TRAVERSE_PAGE_NEXT: return traversePage (true);
		case SWT.TRAVERSE_PAGE_PREVIOUS: return traversePage (false);
	}
	return false;
}

public void update () {
	checkWidget ();
	_update (false);
}

void updateLayout (boolean all) {
	/* Do nothing */
}

public boolean setParent (Composite parent) {
	checkWidget ();
	if (parent == null) error (SWT.ERROR_NULL_ARGUMENT);
	if (parent.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
	if (this.parent == parent) return true;
	if (!isReparentable ()) return false;
	return _setParent (parent);
}

void sort (int [] items) {
	/* Shell Sort from K&R, pg 108 */
	int length = items.length;
	for (int gap=length/2; gap>0; gap/=2) {
		for (int i=gap; i<length; i++) {
			for (int j=i-gap; j>=0; j-=gap) {
		   		if (items [j] <= items [j + gap]) {
					int swap = items [j];
					items [j] = items [j + gap];
					items [j + gap] = swap;
		   		}
	    	}
	    }
	}
}

/*--------------------- NATIVE INTERFACE ------------------*/

void _createHandle (Widget parent, int style, int index) {
}

native void _getNativeBounds(Rectangle rectangle) /*-{
	var self = this.@org.eclipse.swt.widgets.Widget::jsObject;
	var dim = self.getNativeBounds ? self.getNativeBounds() : $wnd.swt.getNativeBounds(self.domNode);
	rectangle.@org.eclipse.swt.graphics.Rectangle::x = dim.l;
	rectangle.@org.eclipse.swt.graphics.Rectangle::y = dim.t;
	rectangle.@org.eclipse.swt.graphics.Rectangle::width = dim.w;
	rectangle.@org.eclipse.swt.graphics.Rectangle::height = dim.h;
}-*/;


Color _defaultBackground () {
	return null;
}

Font _defaultFont () {
	return null;
}

Color _defaultForeground () {
	return null;
}

boolean _dragDetect (int button, int count, int stateMask, int x, int y) {
	return false;
}

void _forceFocus () {
}

//TODO fix this by using _getNativeBounds
native void _getBounds(Rectangle rect) /*-{
	var domNode = this.@org.eclipse.swt.widgets.Widget::jsObject.domNode;
	rect.@org.eclipse.swt.graphics.Rectangle::x = parseInt(domNode.style.x);
	rect.@org.eclipse.swt.graphics.Rectangle::y = parseInt(domNode.style.y);
	rect.@org.eclipse.swt.graphics.Rectangle::width = parseInt(domNode.style.width);
	rect.@org.eclipse.swt.graphics.Rectangle::height = parseInt(domNode.style.height);	
}-*/;

Monitor _getMonitor () {
	return null;
}

native void _hookContextMenu (String eventType) /*-{
	var self = this;
	$wnd.dojo.connect(
		self.@org.eclipse.swt.widgets.Widget::jsObject.domNode,
		eventType, 
		function(e){
			$wnd.dojo.stopEvent(e);
			self.@org.eclipse.swt.widgets.Widget::showMenu(II)(e.pageX, e.pageY);
		}
	);
}-*/;

native void _hookEnter (String eventType) /*-{
	var self = this;
	$wnd.dojo.connect(
		self.@org.eclipse.swt.widgets.Widget::jsObject.domNode,
		eventType, 
		function(arg){
			//$wnd.console.log("_hookEnter " + self.@org.eclipse.swt.widgets.Widget::jsObject.id) ;
			
			self.@org.eclipse.swt.widgets.Control::sendEnter()();
		}
	);
}-*/;

boolean _isReparentable () {
	return false;
}

boolean _isTabGroup () {
	return false;
}

boolean _isTabItem () {
	return false;
}

void _redraw (int x, int y, int width, int height, boolean all) {	
}

native void _setBackground (Color color) /*-{
	this.@org.eclipse.swt.widgets.Widget::jsObject.domNode.style.background = "rgb(" +
		color.@org.eclipse.swt.graphics.Color::getRed()() + "," +
		color.@org.eclipse.swt.graphics.Color::getGreen()() + "," +
		color.@org.eclipse.swt.graphics.Color::getBlue()() + 
	")";
}-*/;

void _setBackgroundImage (Image image) {
}

native void _setBounds (int x, int y, int width, int height) /*-{
	var self = this.@org.eclipse.swt.widgets.Widget::jsObject;
	var dim = $wnd.swt.copyBounds({l:x,t:y,w:width,h:height});
	if (self.domNode.parentNode) {
		self.setWidgetBounds ? self.setWidgetBounds(dim) : $wnd.swt.setBounds(self.domNode, dim);
		self.domNode.style.position="absolute";
	}
}-*/;

void _setCapture (boolean capture) {
}

void _setCursor (Cursor cursor) {
}

native void _setEnabled (boolean enabled) /*-{
//TODO implement for non html input element also
	this.@org.eclipse.swt.widgets.Widget::jsObject.setAttribute("disabled", !enabled);
}-*/;

void _setFont (Font font) {
	callMethod("_setFont", null, new Object[]{font});
}

void _setForeground (Color color) {
	callMethod("_setForeground", null, new Object[]{new Integer(color.handle)});
}

boolean _setParent (Composite parent) {
	return false;
}

void _setRedraw (boolean redraw) {
}

void _setToolTipText (String string) {
	callMethod ("_setToolTipText", null, new Object[]{string});
}

native void _setVisible (boolean visible) /*-{
	this.@org.eclipse.swt.widgets.Widget::jsObject.domNode.style.visibility = visible ? "visible" : "hidden";
}-*/;

void _setZOrder (Control sibling, boolean above) {	
}

void _update (boolean all) {
}

}